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