18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Parts of this file were based on sources as follows: 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (c) 2006-2008 Intel Corporation 88c2ecf20Sopenharmony_ci * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> 98c2ecf20Sopenharmony_ci * Copyright (C) 2011 Texas Instruments 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/clk.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/version.h> 158c2ecf20Sopenharmony_ci#include <linux/dma-buf.h> 168c2ecf20Sopenharmony_ci#include <linux/of_graph.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <drm/drm_fb_cma_helper.h> 198c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h> 208c2ecf20Sopenharmony_ci#include <drm/drm_gem_cma_helper.h> 218c2ecf20Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h> 228c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "pl111_drm.h" 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ciirqreturn_t pl111_irq(int irq, void *data) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci struct pl111_drm_dev_private *priv = data; 298c2ecf20Sopenharmony_ci u32 irq_stat; 308c2ecf20Sopenharmony_ci irqreturn_t status = IRQ_NONE; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci irq_stat = readl(priv->regs + CLCD_PL111_MIS); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci if (!irq_stat) 358c2ecf20Sopenharmony_ci return IRQ_NONE; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci if (irq_stat & CLCD_IRQ_NEXTBASE_UPDATE) { 388c2ecf20Sopenharmony_ci drm_crtc_handle_vblank(&priv->pipe.crtc); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci status = IRQ_HANDLED; 418c2ecf20Sopenharmony_ci } 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci /* Clear the interrupt once done */ 448c2ecf20Sopenharmony_ci writel(irq_stat, priv->regs + CLCD_PL111_ICR); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci return status; 478c2ecf20Sopenharmony_ci} 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistatic enum drm_mode_status 508c2ecf20Sopenharmony_cipl111_mode_valid(struct drm_simple_display_pipe *pipe, 518c2ecf20Sopenharmony_ci const struct drm_display_mode *mode) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci struct drm_device *drm = pipe->crtc.dev; 548c2ecf20Sopenharmony_ci struct pl111_drm_dev_private *priv = drm->dev_private; 558c2ecf20Sopenharmony_ci u32 cpp = priv->variant->fb_bpp / 8; 568c2ecf20Sopenharmony_ci u64 bw; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci /* 598c2ecf20Sopenharmony_ci * We use the pixelclock to also account for interlaced modes, the 608c2ecf20Sopenharmony_ci * resulting bandwidth is in bytes per second. 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_ci bw = mode->clock * 1000ULL; /* In Hz */ 638c2ecf20Sopenharmony_ci bw = bw * mode->hdisplay * mode->vdisplay * cpp; 648c2ecf20Sopenharmony_ci bw = div_u64(bw, mode->htotal * mode->vtotal); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* 678c2ecf20Sopenharmony_ci * If no bandwidth constraints, anything goes, else 688c2ecf20Sopenharmony_ci * check if we are too fast. 698c2ecf20Sopenharmony_ci */ 708c2ecf20Sopenharmony_ci if (priv->memory_bw && (bw > priv->memory_bw)) { 718c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("%d x %d @ %d Hz, %d cpp, bw %llu too fast\n", 728c2ecf20Sopenharmony_ci mode->hdisplay, mode->vdisplay, 738c2ecf20Sopenharmony_ci mode->clock * 1000, cpp, bw); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci return MODE_BAD; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci DRM_DEBUG_KMS("%d x %d @ %d Hz, %d cpp, bw %llu bytes/s OK\n", 788c2ecf20Sopenharmony_ci mode->hdisplay, mode->vdisplay, 798c2ecf20Sopenharmony_ci mode->clock * 1000, cpp, bw); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci return MODE_OK; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int pl111_display_check(struct drm_simple_display_pipe *pipe, 858c2ecf20Sopenharmony_ci struct drm_plane_state *pstate, 868c2ecf20Sopenharmony_ci struct drm_crtc_state *cstate) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci const struct drm_display_mode *mode = &cstate->mode; 898c2ecf20Sopenharmony_ci struct drm_framebuffer *old_fb = pipe->plane.state->fb; 908c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = pstate->fb; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (mode->hdisplay % 16) 938c2ecf20Sopenharmony_ci return -EINVAL; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (fb) { 968c2ecf20Sopenharmony_ci u32 offset = drm_fb_cma_get_gem_addr(fb, pstate, 0); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* FB base address must be dword aligned. */ 998c2ecf20Sopenharmony_ci if (offset & 3) 1008c2ecf20Sopenharmony_ci return -EINVAL; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* There's no pitch register -- the mode's hdisplay 1038c2ecf20Sopenharmony_ci * controls it. 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ci if (fb->pitches[0] != mode->hdisplay * fb->format->cpp[0]) 1068c2ecf20Sopenharmony_ci return -EINVAL; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* We can't change the FB format in a flicker-free 1098c2ecf20Sopenharmony_ci * manner (and only update it during CRTC enable). 1108c2ecf20Sopenharmony_ci */ 1118c2ecf20Sopenharmony_ci if (old_fb && old_fb->format != fb->format) 1128c2ecf20Sopenharmony_ci cstate->mode_changed = true; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return 0; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic void pl111_display_enable(struct drm_simple_display_pipe *pipe, 1198c2ecf20Sopenharmony_ci struct drm_crtc_state *cstate, 1208c2ecf20Sopenharmony_ci struct drm_plane_state *plane_state) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct drm_crtc *crtc = &pipe->crtc; 1238c2ecf20Sopenharmony_ci struct drm_plane *plane = &pipe->plane; 1248c2ecf20Sopenharmony_ci struct drm_device *drm = crtc->dev; 1258c2ecf20Sopenharmony_ci struct pl111_drm_dev_private *priv = drm->dev_private; 1268c2ecf20Sopenharmony_ci const struct drm_display_mode *mode = &cstate->mode; 1278c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = plane->state->fb; 1288c2ecf20Sopenharmony_ci struct drm_connector *connector = priv->connector; 1298c2ecf20Sopenharmony_ci struct drm_bridge *bridge = priv->bridge; 1308c2ecf20Sopenharmony_ci bool grayscale = false; 1318c2ecf20Sopenharmony_ci u32 cntl; 1328c2ecf20Sopenharmony_ci u32 ppl, hsw, hfp, hbp; 1338c2ecf20Sopenharmony_ci u32 lpp, vsw, vfp, vbp; 1348c2ecf20Sopenharmony_ci u32 cpl, tim2; 1358c2ecf20Sopenharmony_ci int ret; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci ret = clk_set_rate(priv->clk, mode->clock * 1000); 1388c2ecf20Sopenharmony_ci if (ret) { 1398c2ecf20Sopenharmony_ci dev_err(drm->dev, 1408c2ecf20Sopenharmony_ci "Failed to set pixel clock rate to %d: %d\n", 1418c2ecf20Sopenharmony_ci mode->clock * 1000, ret); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci clk_prepare_enable(priv->clk); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci ppl = (mode->hdisplay / 16) - 1; 1478c2ecf20Sopenharmony_ci hsw = mode->hsync_end - mode->hsync_start - 1; 1488c2ecf20Sopenharmony_ci hfp = mode->hsync_start - mode->hdisplay - 1; 1498c2ecf20Sopenharmony_ci hbp = mode->htotal - mode->hsync_end - 1; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci lpp = mode->vdisplay - 1; 1528c2ecf20Sopenharmony_ci vsw = mode->vsync_end - mode->vsync_start - 1; 1538c2ecf20Sopenharmony_ci vfp = mode->vsync_start - mode->vdisplay; 1548c2ecf20Sopenharmony_ci vbp = mode->vtotal - mode->vsync_end; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci cpl = mode->hdisplay - 1; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci writel((ppl << 2) | 1598c2ecf20Sopenharmony_ci (hsw << 8) | 1608c2ecf20Sopenharmony_ci (hfp << 16) | 1618c2ecf20Sopenharmony_ci (hbp << 24), 1628c2ecf20Sopenharmony_ci priv->regs + CLCD_TIM0); 1638c2ecf20Sopenharmony_ci writel(lpp | 1648c2ecf20Sopenharmony_ci (vsw << 10) | 1658c2ecf20Sopenharmony_ci (vfp << 16) | 1668c2ecf20Sopenharmony_ci (vbp << 24), 1678c2ecf20Sopenharmony_ci priv->regs + CLCD_TIM1); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci spin_lock(&priv->tim2_lock); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci tim2 = readl(priv->regs + CLCD_TIM2); 1728c2ecf20Sopenharmony_ci tim2 &= (TIM2_BCD | TIM2_PCD_LO_MASK | TIM2_PCD_HI_MASK); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (priv->variant->broken_clockdivider) 1758c2ecf20Sopenharmony_ci tim2 |= TIM2_BCD; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_NHSYNC) 1788c2ecf20Sopenharmony_ci tim2 |= TIM2_IHS; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_NVSYNC) 1818c2ecf20Sopenharmony_ci tim2 |= TIM2_IVS; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci if (connector) { 1848c2ecf20Sopenharmony_ci if (connector->display_info.bus_flags & DRM_BUS_FLAG_DE_LOW) 1858c2ecf20Sopenharmony_ci tim2 |= TIM2_IOE; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (connector->display_info.bus_flags & 1888c2ecf20Sopenharmony_ci DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE) 1898c2ecf20Sopenharmony_ci tim2 |= TIM2_IPC; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (connector->display_info.num_bus_formats == 1 && 1928c2ecf20Sopenharmony_ci connector->display_info.bus_formats[0] == 1938c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_Y8_1X8) 1948c2ecf20Sopenharmony_ci grayscale = true; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* 1978c2ecf20Sopenharmony_ci * The AC pin bias frequency is set to max count when using 1988c2ecf20Sopenharmony_ci * grayscale so at least once in a while we will reverse 1998c2ecf20Sopenharmony_ci * polarity and get rid of any DC built up that could 2008c2ecf20Sopenharmony_ci * damage the display. 2018c2ecf20Sopenharmony_ci */ 2028c2ecf20Sopenharmony_ci if (grayscale) 2038c2ecf20Sopenharmony_ci tim2 |= TIM2_ACB_MASK; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (bridge) { 2078c2ecf20Sopenharmony_ci const struct drm_bridge_timings *btimings = bridge->timings; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* 2108c2ecf20Sopenharmony_ci * Here is when things get really fun. Sometimes the bridge 2118c2ecf20Sopenharmony_ci * timings are such that the signal out from PL11x is not 2128c2ecf20Sopenharmony_ci * stable before the receiving bridge (such as a dumb VGA DAC 2138c2ecf20Sopenharmony_ci * or similar) samples it. If that happens, we compensate by 2148c2ecf20Sopenharmony_ci * the only method we have: output the data on the opposite 2158c2ecf20Sopenharmony_ci * edge of the clock so it is for sure stable when it gets 2168c2ecf20Sopenharmony_ci * sampled. 2178c2ecf20Sopenharmony_ci * 2188c2ecf20Sopenharmony_ci * The PL111 manual does not contain proper timining diagrams 2198c2ecf20Sopenharmony_ci * or data for these details, but we know from experiments 2208c2ecf20Sopenharmony_ci * that the setup time is more than 3000 picoseconds (3 ns). 2218c2ecf20Sopenharmony_ci * If we have a bridge that requires the signal to be stable 2228c2ecf20Sopenharmony_ci * earlier than 3000 ps before the clock pulse, we have to 2238c2ecf20Sopenharmony_ci * output the data on the opposite edge to avoid flicker. 2248c2ecf20Sopenharmony_ci */ 2258c2ecf20Sopenharmony_ci if (btimings && btimings->setup_time_ps >= 3000) 2268c2ecf20Sopenharmony_ci tim2 ^= TIM2_IPC; 2278c2ecf20Sopenharmony_ci } 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci tim2 |= cpl << 16; 2308c2ecf20Sopenharmony_ci writel(tim2, priv->regs + CLCD_TIM2); 2318c2ecf20Sopenharmony_ci spin_unlock(&priv->tim2_lock); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci writel(0, priv->regs + CLCD_TIM3); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* 2368c2ecf20Sopenharmony_ci * Detect grayscale bus format. We do not support a grayscale mode 2378c2ecf20Sopenharmony_ci * toward userspace, instead we expose an RGB24 buffer and then the 2388c2ecf20Sopenharmony_ci * hardware will activate its grayscaler to convert to the grayscale 2398c2ecf20Sopenharmony_ci * format. 2408c2ecf20Sopenharmony_ci */ 2418c2ecf20Sopenharmony_ci if (grayscale) 2428c2ecf20Sopenharmony_ci cntl = CNTL_LCDEN | CNTL_LCDMONO8; 2438c2ecf20Sopenharmony_ci else 2448c2ecf20Sopenharmony_ci /* Else we assume TFT display */ 2458c2ecf20Sopenharmony_ci cntl = CNTL_LCDEN | CNTL_LCDTFT | CNTL_LCDVCOMP(1); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* On the ST Micro variant, assume all 24 bits are connected */ 2488c2ecf20Sopenharmony_ci if (priv->variant->st_bitmux_control) 2498c2ecf20Sopenharmony_ci cntl |= CNTL_ST_CDWID_24; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci /* 2528c2ecf20Sopenharmony_ci * Note that the the ARM hardware's format reader takes 'r' from 2538c2ecf20Sopenharmony_ci * the low bit, while DRM formats list channels from high bit 2548c2ecf20Sopenharmony_ci * to low bit as you read left to right. The ST Micro version of 2558c2ecf20Sopenharmony_ci * the PL110 (LCDC) however uses the standard DRM format. 2568c2ecf20Sopenharmony_ci */ 2578c2ecf20Sopenharmony_ci switch (fb->format->format) { 2588c2ecf20Sopenharmony_ci case DRM_FORMAT_BGR888: 2598c2ecf20Sopenharmony_ci /* Only supported on the ST Micro variant */ 2608c2ecf20Sopenharmony_ci if (priv->variant->st_bitmux_control) 2618c2ecf20Sopenharmony_ci cntl |= CNTL_ST_LCDBPP24_PACKED | CNTL_BGR; 2628c2ecf20Sopenharmony_ci break; 2638c2ecf20Sopenharmony_ci case DRM_FORMAT_RGB888: 2648c2ecf20Sopenharmony_ci /* Only supported on the ST Micro variant */ 2658c2ecf20Sopenharmony_ci if (priv->variant->st_bitmux_control) 2668c2ecf20Sopenharmony_ci cntl |= CNTL_ST_LCDBPP24_PACKED; 2678c2ecf20Sopenharmony_ci break; 2688c2ecf20Sopenharmony_ci case DRM_FORMAT_ABGR8888: 2698c2ecf20Sopenharmony_ci case DRM_FORMAT_XBGR8888: 2708c2ecf20Sopenharmony_ci if (priv->variant->st_bitmux_control) 2718c2ecf20Sopenharmony_ci cntl |= CNTL_LCDBPP24 | CNTL_BGR; 2728c2ecf20Sopenharmony_ci else 2738c2ecf20Sopenharmony_ci cntl |= CNTL_LCDBPP24; 2748c2ecf20Sopenharmony_ci break; 2758c2ecf20Sopenharmony_ci case DRM_FORMAT_ARGB8888: 2768c2ecf20Sopenharmony_ci case DRM_FORMAT_XRGB8888: 2778c2ecf20Sopenharmony_ci if (priv->variant->st_bitmux_control) 2788c2ecf20Sopenharmony_ci cntl |= CNTL_LCDBPP24; 2798c2ecf20Sopenharmony_ci else 2808c2ecf20Sopenharmony_ci cntl |= CNTL_LCDBPP24 | CNTL_BGR; 2818c2ecf20Sopenharmony_ci break; 2828c2ecf20Sopenharmony_ci case DRM_FORMAT_BGR565: 2838c2ecf20Sopenharmony_ci if (priv->variant->is_pl110) 2848c2ecf20Sopenharmony_ci cntl |= CNTL_LCDBPP16; 2858c2ecf20Sopenharmony_ci else if (priv->variant->st_bitmux_control) 2868c2ecf20Sopenharmony_ci cntl |= CNTL_LCDBPP16 | CNTL_ST_1XBPP_565 | CNTL_BGR; 2878c2ecf20Sopenharmony_ci else 2888c2ecf20Sopenharmony_ci cntl |= CNTL_LCDBPP16_565; 2898c2ecf20Sopenharmony_ci break; 2908c2ecf20Sopenharmony_ci case DRM_FORMAT_RGB565: 2918c2ecf20Sopenharmony_ci if (priv->variant->is_pl110) 2928c2ecf20Sopenharmony_ci cntl |= CNTL_LCDBPP16 | CNTL_BGR; 2938c2ecf20Sopenharmony_ci else if (priv->variant->st_bitmux_control) 2948c2ecf20Sopenharmony_ci cntl |= CNTL_LCDBPP16 | CNTL_ST_1XBPP_565; 2958c2ecf20Sopenharmony_ci else 2968c2ecf20Sopenharmony_ci cntl |= CNTL_LCDBPP16_565 | CNTL_BGR; 2978c2ecf20Sopenharmony_ci break; 2988c2ecf20Sopenharmony_ci case DRM_FORMAT_ABGR1555: 2998c2ecf20Sopenharmony_ci case DRM_FORMAT_XBGR1555: 3008c2ecf20Sopenharmony_ci cntl |= CNTL_LCDBPP16; 3018c2ecf20Sopenharmony_ci if (priv->variant->st_bitmux_control) 3028c2ecf20Sopenharmony_ci cntl |= CNTL_ST_1XBPP_5551 | CNTL_BGR; 3038c2ecf20Sopenharmony_ci break; 3048c2ecf20Sopenharmony_ci case DRM_FORMAT_ARGB1555: 3058c2ecf20Sopenharmony_ci case DRM_FORMAT_XRGB1555: 3068c2ecf20Sopenharmony_ci cntl |= CNTL_LCDBPP16; 3078c2ecf20Sopenharmony_ci if (priv->variant->st_bitmux_control) 3088c2ecf20Sopenharmony_ci cntl |= CNTL_ST_1XBPP_5551; 3098c2ecf20Sopenharmony_ci else 3108c2ecf20Sopenharmony_ci cntl |= CNTL_BGR; 3118c2ecf20Sopenharmony_ci break; 3128c2ecf20Sopenharmony_ci case DRM_FORMAT_ABGR4444: 3138c2ecf20Sopenharmony_ci case DRM_FORMAT_XBGR4444: 3148c2ecf20Sopenharmony_ci cntl |= CNTL_LCDBPP16_444; 3158c2ecf20Sopenharmony_ci if (priv->variant->st_bitmux_control) 3168c2ecf20Sopenharmony_ci cntl |= CNTL_ST_1XBPP_444 | CNTL_BGR; 3178c2ecf20Sopenharmony_ci break; 3188c2ecf20Sopenharmony_ci case DRM_FORMAT_ARGB4444: 3198c2ecf20Sopenharmony_ci case DRM_FORMAT_XRGB4444: 3208c2ecf20Sopenharmony_ci cntl |= CNTL_LCDBPP16_444; 3218c2ecf20Sopenharmony_ci if (priv->variant->st_bitmux_control) 3228c2ecf20Sopenharmony_ci cntl |= CNTL_ST_1XBPP_444; 3238c2ecf20Sopenharmony_ci else 3248c2ecf20Sopenharmony_ci cntl |= CNTL_BGR; 3258c2ecf20Sopenharmony_ci break; 3268c2ecf20Sopenharmony_ci default: 3278c2ecf20Sopenharmony_ci WARN_ONCE(true, "Unknown FB format 0x%08x\n", 3288c2ecf20Sopenharmony_ci fb->format->format); 3298c2ecf20Sopenharmony_ci break; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci /* The PL110 in Integrator/Versatile does the BGR routing externally */ 3338c2ecf20Sopenharmony_ci if (priv->variant->external_bgr) 3348c2ecf20Sopenharmony_ci cntl &= ~CNTL_BGR; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci /* Power sequence: first enable and chill */ 3378c2ecf20Sopenharmony_ci writel(cntl, priv->regs + priv->ctrl); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* 3408c2ecf20Sopenharmony_ci * We expect this delay to stabilize the contrast 3418c2ecf20Sopenharmony_ci * voltage Vee as stipulated by the manual 3428c2ecf20Sopenharmony_ci */ 3438c2ecf20Sopenharmony_ci msleep(20); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (priv->variant_display_enable) 3468c2ecf20Sopenharmony_ci priv->variant_display_enable(drm, fb->format->format); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci /* Power Up */ 3498c2ecf20Sopenharmony_ci cntl |= CNTL_LCDPWR; 3508c2ecf20Sopenharmony_ci writel(cntl, priv->regs + priv->ctrl); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (!priv->variant->broken_vblank) 3538c2ecf20Sopenharmony_ci drm_crtc_vblank_on(crtc); 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_civoid pl111_display_disable(struct drm_simple_display_pipe *pipe) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci struct drm_crtc *crtc = &pipe->crtc; 3598c2ecf20Sopenharmony_ci struct drm_device *drm = crtc->dev; 3608c2ecf20Sopenharmony_ci struct pl111_drm_dev_private *priv = drm->dev_private; 3618c2ecf20Sopenharmony_ci u32 cntl; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (!priv->variant->broken_vblank) 3648c2ecf20Sopenharmony_ci drm_crtc_vblank_off(crtc); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci /* Power Down */ 3678c2ecf20Sopenharmony_ci cntl = readl(priv->regs + priv->ctrl); 3688c2ecf20Sopenharmony_ci if (cntl & CNTL_LCDPWR) { 3698c2ecf20Sopenharmony_ci cntl &= ~CNTL_LCDPWR; 3708c2ecf20Sopenharmony_ci writel(cntl, priv->regs + priv->ctrl); 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* 3748c2ecf20Sopenharmony_ci * We expect this delay to stabilize the contrast voltage Vee as 3758c2ecf20Sopenharmony_ci * stipulated by the manual 3768c2ecf20Sopenharmony_ci */ 3778c2ecf20Sopenharmony_ci msleep(20); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (priv->variant_display_disable) 3808c2ecf20Sopenharmony_ci priv->variant_display_disable(drm); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci /* Disable */ 3838c2ecf20Sopenharmony_ci writel(0, priv->regs + priv->ctrl); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk); 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic void pl111_display_update(struct drm_simple_display_pipe *pipe, 3898c2ecf20Sopenharmony_ci struct drm_plane_state *old_pstate) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci struct drm_crtc *crtc = &pipe->crtc; 3928c2ecf20Sopenharmony_ci struct drm_device *drm = crtc->dev; 3938c2ecf20Sopenharmony_ci struct pl111_drm_dev_private *priv = drm->dev_private; 3948c2ecf20Sopenharmony_ci struct drm_pending_vblank_event *event = crtc->state->event; 3958c2ecf20Sopenharmony_ci struct drm_plane *plane = &pipe->plane; 3968c2ecf20Sopenharmony_ci struct drm_plane_state *pstate = plane->state; 3978c2ecf20Sopenharmony_ci struct drm_framebuffer *fb = pstate->fb; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci if (fb) { 4008c2ecf20Sopenharmony_ci u32 addr = drm_fb_cma_get_gem_addr(fb, pstate, 0); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci writel(addr, priv->regs + CLCD_UBAS); 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci if (event) { 4068c2ecf20Sopenharmony_ci crtc->state->event = NULL; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci spin_lock_irq(&crtc->dev->event_lock); 4098c2ecf20Sopenharmony_ci if (crtc->state->active && drm_crtc_vblank_get(crtc) == 0) 4108c2ecf20Sopenharmony_ci drm_crtc_arm_vblank_event(crtc, event); 4118c2ecf20Sopenharmony_ci else 4128c2ecf20Sopenharmony_ci drm_crtc_send_vblank_event(crtc, event); 4138c2ecf20Sopenharmony_ci spin_unlock_irq(&crtc->dev->event_lock); 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci} 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_cistatic int pl111_display_enable_vblank(struct drm_simple_display_pipe *pipe) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci struct drm_crtc *crtc = &pipe->crtc; 4208c2ecf20Sopenharmony_ci struct drm_device *drm = crtc->dev; 4218c2ecf20Sopenharmony_ci struct pl111_drm_dev_private *priv = drm->dev_private; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci writel(CLCD_IRQ_NEXTBASE_UPDATE, priv->regs + priv->ienb); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci return 0; 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistatic void pl111_display_disable_vblank(struct drm_simple_display_pipe *pipe) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci struct drm_crtc *crtc = &pipe->crtc; 4318c2ecf20Sopenharmony_ci struct drm_device *drm = crtc->dev; 4328c2ecf20Sopenharmony_ci struct pl111_drm_dev_private *priv = drm->dev_private; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci writel(0, priv->regs + priv->ienb); 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic struct drm_simple_display_pipe_funcs pl111_display_funcs = { 4388c2ecf20Sopenharmony_ci .mode_valid = pl111_mode_valid, 4398c2ecf20Sopenharmony_ci .check = pl111_display_check, 4408c2ecf20Sopenharmony_ci .enable = pl111_display_enable, 4418c2ecf20Sopenharmony_ci .disable = pl111_display_disable, 4428c2ecf20Sopenharmony_ci .update = pl111_display_update, 4438c2ecf20Sopenharmony_ci .prepare_fb = drm_gem_fb_simple_display_pipe_prepare_fb, 4448c2ecf20Sopenharmony_ci}; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic int pl111_clk_div_choose_div(struct clk_hw *hw, unsigned long rate, 4478c2ecf20Sopenharmony_ci unsigned long *prate, bool set_parent) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci int best_div = 1, div; 4508c2ecf20Sopenharmony_ci struct clk_hw *parent = clk_hw_get_parent(hw); 4518c2ecf20Sopenharmony_ci unsigned long best_prate = 0; 4528c2ecf20Sopenharmony_ci unsigned long best_diff = ~0ul; 4538c2ecf20Sopenharmony_ci int max_div = (1 << (TIM2_PCD_LO_BITS + TIM2_PCD_HI_BITS)) - 1; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci for (div = 1; div < max_div; div++) { 4568c2ecf20Sopenharmony_ci unsigned long this_prate, div_rate, diff; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (set_parent) 4598c2ecf20Sopenharmony_ci this_prate = clk_hw_round_rate(parent, rate * div); 4608c2ecf20Sopenharmony_ci else 4618c2ecf20Sopenharmony_ci this_prate = *prate; 4628c2ecf20Sopenharmony_ci div_rate = DIV_ROUND_UP_ULL(this_prate, div); 4638c2ecf20Sopenharmony_ci diff = abs(rate - div_rate); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci if (diff < best_diff) { 4668c2ecf20Sopenharmony_ci best_div = div; 4678c2ecf20Sopenharmony_ci best_diff = diff; 4688c2ecf20Sopenharmony_ci best_prate = this_prate; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci *prate = best_prate; 4738c2ecf20Sopenharmony_ci return best_div; 4748c2ecf20Sopenharmony_ci} 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic long pl111_clk_div_round_rate(struct clk_hw *hw, unsigned long rate, 4778c2ecf20Sopenharmony_ci unsigned long *prate) 4788c2ecf20Sopenharmony_ci{ 4798c2ecf20Sopenharmony_ci int div = pl111_clk_div_choose_div(hw, rate, prate, true); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci return DIV_ROUND_UP_ULL(*prate, div); 4828c2ecf20Sopenharmony_ci} 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistatic unsigned long pl111_clk_div_recalc_rate(struct clk_hw *hw, 4858c2ecf20Sopenharmony_ci unsigned long prate) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci struct pl111_drm_dev_private *priv = 4888c2ecf20Sopenharmony_ci container_of(hw, struct pl111_drm_dev_private, clk_div); 4898c2ecf20Sopenharmony_ci u32 tim2 = readl(priv->regs + CLCD_TIM2); 4908c2ecf20Sopenharmony_ci int div; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci if (tim2 & TIM2_BCD) 4938c2ecf20Sopenharmony_ci return prate; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci div = tim2 & TIM2_PCD_LO_MASK; 4968c2ecf20Sopenharmony_ci div |= (tim2 & TIM2_PCD_HI_MASK) >> 4978c2ecf20Sopenharmony_ci (TIM2_PCD_HI_SHIFT - TIM2_PCD_LO_BITS); 4988c2ecf20Sopenharmony_ci div += 2; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci return DIV_ROUND_UP_ULL(prate, div); 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_cistatic int pl111_clk_div_set_rate(struct clk_hw *hw, unsigned long rate, 5048c2ecf20Sopenharmony_ci unsigned long prate) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci struct pl111_drm_dev_private *priv = 5078c2ecf20Sopenharmony_ci container_of(hw, struct pl111_drm_dev_private, clk_div); 5088c2ecf20Sopenharmony_ci int div = pl111_clk_div_choose_div(hw, rate, &prate, false); 5098c2ecf20Sopenharmony_ci u32 tim2; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci spin_lock(&priv->tim2_lock); 5128c2ecf20Sopenharmony_ci tim2 = readl(priv->regs + CLCD_TIM2); 5138c2ecf20Sopenharmony_ci tim2 &= ~(TIM2_BCD | TIM2_PCD_LO_MASK | TIM2_PCD_HI_MASK); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci if (div == 1) { 5168c2ecf20Sopenharmony_ci tim2 |= TIM2_BCD; 5178c2ecf20Sopenharmony_ci } else { 5188c2ecf20Sopenharmony_ci div -= 2; 5198c2ecf20Sopenharmony_ci tim2 |= div & TIM2_PCD_LO_MASK; 5208c2ecf20Sopenharmony_ci tim2 |= (div >> TIM2_PCD_LO_BITS) << TIM2_PCD_HI_SHIFT; 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci writel(tim2, priv->regs + CLCD_TIM2); 5248c2ecf20Sopenharmony_ci spin_unlock(&priv->tim2_lock); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci return 0; 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cistatic const struct clk_ops pl111_clk_div_ops = { 5308c2ecf20Sopenharmony_ci .recalc_rate = pl111_clk_div_recalc_rate, 5318c2ecf20Sopenharmony_ci .round_rate = pl111_clk_div_round_rate, 5328c2ecf20Sopenharmony_ci .set_rate = pl111_clk_div_set_rate, 5338c2ecf20Sopenharmony_ci}; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic int 5368c2ecf20Sopenharmony_cipl111_init_clock_divider(struct drm_device *drm) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci struct pl111_drm_dev_private *priv = drm->dev_private; 5398c2ecf20Sopenharmony_ci struct clk *parent = devm_clk_get(drm->dev, "clcdclk"); 5408c2ecf20Sopenharmony_ci struct clk_hw *div = &priv->clk_div; 5418c2ecf20Sopenharmony_ci const char *parent_name; 5428c2ecf20Sopenharmony_ci struct clk_init_data init = { 5438c2ecf20Sopenharmony_ci .name = "pl111_div", 5448c2ecf20Sopenharmony_ci .ops = &pl111_clk_div_ops, 5458c2ecf20Sopenharmony_ci .parent_names = &parent_name, 5468c2ecf20Sopenharmony_ci .num_parents = 1, 5478c2ecf20Sopenharmony_ci .flags = CLK_SET_RATE_PARENT, 5488c2ecf20Sopenharmony_ci }; 5498c2ecf20Sopenharmony_ci int ret; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci if (IS_ERR(parent)) { 5528c2ecf20Sopenharmony_ci dev_err(drm->dev, "CLCD: unable to get clcdclk.\n"); 5538c2ecf20Sopenharmony_ci return PTR_ERR(parent); 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci spin_lock_init(&priv->tim2_lock); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci /* If the clock divider is broken, use the parent directly */ 5598c2ecf20Sopenharmony_ci if (priv->variant->broken_clockdivider) { 5608c2ecf20Sopenharmony_ci priv->clk = parent; 5618c2ecf20Sopenharmony_ci return 0; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci parent_name = __clk_get_name(parent); 5648c2ecf20Sopenharmony_ci div->init = &init; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci ret = devm_clk_hw_register(drm->dev, div); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci priv->clk = div->clk; 5698c2ecf20Sopenharmony_ci return ret; 5708c2ecf20Sopenharmony_ci} 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ciint pl111_display_init(struct drm_device *drm) 5738c2ecf20Sopenharmony_ci{ 5748c2ecf20Sopenharmony_ci struct pl111_drm_dev_private *priv = drm->dev_private; 5758c2ecf20Sopenharmony_ci int ret; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci ret = pl111_init_clock_divider(drm); 5788c2ecf20Sopenharmony_ci if (ret) 5798c2ecf20Sopenharmony_ci return ret; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci if (!priv->variant->broken_vblank) { 5828c2ecf20Sopenharmony_ci pl111_display_funcs.enable_vblank = pl111_display_enable_vblank; 5838c2ecf20Sopenharmony_ci pl111_display_funcs.disable_vblank = pl111_display_disable_vblank; 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci ret = drm_simple_display_pipe_init(drm, &priv->pipe, 5878c2ecf20Sopenharmony_ci &pl111_display_funcs, 5888c2ecf20Sopenharmony_ci priv->variant->formats, 5898c2ecf20Sopenharmony_ci priv->variant->nformats, 5908c2ecf20Sopenharmony_ci NULL, 5918c2ecf20Sopenharmony_ci priv->connector); 5928c2ecf20Sopenharmony_ci if (ret) 5938c2ecf20Sopenharmony_ci return ret; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci return 0; 5968c2ecf20Sopenharmony_ci} 597