18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci// Copyright 2018 IBM Corporation
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <linux/clk.h>
58c2ecf20Sopenharmony_ci#include <linux/reset.h>
68c2ecf20Sopenharmony_ci#include <linux/regmap.h>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <drm/drm_crtc_helper.h>
98c2ecf20Sopenharmony_ci#include <drm/drm_device.h>
108c2ecf20Sopenharmony_ci#include <drm/drm_fb_cma_helper.h>
118c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h>
128c2ecf20Sopenharmony_ci#include <drm/drm_gem_cma_helper.h>
138c2ecf20Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h>
148c2ecf20Sopenharmony_ci#include <drm/drm_panel.h>
158c2ecf20Sopenharmony_ci#include <drm/drm_simple_kms_helper.h>
168c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "aspeed_gfx.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic struct aspeed_gfx *
218c2ecf20Sopenharmony_cidrm_pipe_to_aspeed_gfx(struct drm_simple_display_pipe *pipe)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	return container_of(pipe, struct aspeed_gfx, pipe);
248c2ecf20Sopenharmony_ci}
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cistatic int aspeed_gfx_set_pixel_fmt(struct aspeed_gfx *priv, u32 *bpp)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	struct drm_crtc *crtc = &priv->pipe.crtc;
298c2ecf20Sopenharmony_ci	struct drm_device *drm = crtc->dev;
308c2ecf20Sopenharmony_ci	const u32 format = crtc->primary->state->fb->format->format;
318c2ecf20Sopenharmony_ci	u32 ctrl1;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	ctrl1 = readl(priv->base + CRT_CTRL1);
348c2ecf20Sopenharmony_ci	ctrl1 &= ~CRT_CTRL_COLOR_MASK;
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci	switch (format) {
378c2ecf20Sopenharmony_ci	case DRM_FORMAT_RGB565:
388c2ecf20Sopenharmony_ci		dev_dbg(drm->dev, "Setting up RGB565 mode\n");
398c2ecf20Sopenharmony_ci		ctrl1 |= CRT_CTRL_COLOR_RGB565;
408c2ecf20Sopenharmony_ci		*bpp = 16;
418c2ecf20Sopenharmony_ci		break;
428c2ecf20Sopenharmony_ci	case DRM_FORMAT_XRGB8888:
438c2ecf20Sopenharmony_ci		dev_dbg(drm->dev, "Setting up XRGB8888 mode\n");
448c2ecf20Sopenharmony_ci		ctrl1 |= CRT_CTRL_COLOR_XRGB8888;
458c2ecf20Sopenharmony_ci		*bpp = 32;
468c2ecf20Sopenharmony_ci		break;
478c2ecf20Sopenharmony_ci	default:
488c2ecf20Sopenharmony_ci		dev_err(drm->dev, "Unhandled pixel format %08x\n", format);
498c2ecf20Sopenharmony_ci		return -EINVAL;
508c2ecf20Sopenharmony_ci	}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	writel(ctrl1, priv->base + CRT_CTRL1);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	return 0;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic void aspeed_gfx_enable_controller(struct aspeed_gfx *priv)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	u32 ctrl1 = readl(priv->base + CRT_CTRL1);
608c2ecf20Sopenharmony_ci	u32 ctrl2 = readl(priv->base + CRT_CTRL2);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	/* SCU2C: set DAC source for display output to Graphics CRT (GFX) */
638c2ecf20Sopenharmony_ci	regmap_update_bits(priv->scu, 0x2c, BIT(16), BIT(16));
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	writel(ctrl1 | CRT_CTRL_EN, priv->base + CRT_CTRL1);
668c2ecf20Sopenharmony_ci	writel(ctrl2 | CRT_CTRL_DAC_EN, priv->base + CRT_CTRL2);
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic void aspeed_gfx_disable_controller(struct aspeed_gfx *priv)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	u32 ctrl1 = readl(priv->base + CRT_CTRL1);
728c2ecf20Sopenharmony_ci	u32 ctrl2 = readl(priv->base + CRT_CTRL2);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	writel(ctrl1 & ~CRT_CTRL_EN, priv->base + CRT_CTRL1);
758c2ecf20Sopenharmony_ci	writel(ctrl2 & ~CRT_CTRL_DAC_EN, priv->base + CRT_CTRL2);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	regmap_update_bits(priv->scu, 0x2c, BIT(16), 0);
788c2ecf20Sopenharmony_ci}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_cistatic void aspeed_gfx_crtc_mode_set_nofb(struct aspeed_gfx *priv)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	struct drm_display_mode *m = &priv->pipe.crtc.state->adjusted_mode;
838c2ecf20Sopenharmony_ci	u32 ctrl1, d_offset, t_count, bpp;
848c2ecf20Sopenharmony_ci	int err;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	err = aspeed_gfx_set_pixel_fmt(priv, &bpp);
878c2ecf20Sopenharmony_ci	if (err)
888c2ecf20Sopenharmony_ci		return;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci#if 0
918c2ecf20Sopenharmony_ci	/* TODO: we have only been able to test with the 40MHz USB clock. The
928c2ecf20Sopenharmony_ci	 * clock is fixed, so we cannot adjust it here. */
938c2ecf20Sopenharmony_ci	clk_set_rate(priv->pixel_clk, m->crtc_clock * 1000);
948c2ecf20Sopenharmony_ci#endif
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	ctrl1 = readl(priv->base + CRT_CTRL1);
978c2ecf20Sopenharmony_ci	ctrl1 &= ~(CRT_CTRL_INTERLACED |
988c2ecf20Sopenharmony_ci			CRT_CTRL_HSYNC_NEGATIVE |
998c2ecf20Sopenharmony_ci			CRT_CTRL_VSYNC_NEGATIVE);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if (m->flags & DRM_MODE_FLAG_INTERLACE)
1028c2ecf20Sopenharmony_ci		ctrl1 |= CRT_CTRL_INTERLACED;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	if (!(m->flags & DRM_MODE_FLAG_PHSYNC))
1058c2ecf20Sopenharmony_ci		ctrl1 |= CRT_CTRL_HSYNC_NEGATIVE;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	if (!(m->flags & DRM_MODE_FLAG_PVSYNC))
1088c2ecf20Sopenharmony_ci		ctrl1 |= CRT_CTRL_VSYNC_NEGATIVE;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	writel(ctrl1, priv->base + CRT_CTRL1);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	/* Horizontal timing */
1138c2ecf20Sopenharmony_ci	writel(CRT_H_TOTAL(m->htotal - 1) | CRT_H_DE(m->hdisplay - 1),
1148c2ecf20Sopenharmony_ci			priv->base + CRT_HORIZ0);
1158c2ecf20Sopenharmony_ci	writel(CRT_H_RS_START(m->hsync_start - 1) | CRT_H_RS_END(m->hsync_end),
1168c2ecf20Sopenharmony_ci			priv->base + CRT_HORIZ1);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	/* Vertical timing */
1208c2ecf20Sopenharmony_ci	writel(CRT_V_TOTAL(m->vtotal - 1) | CRT_V_DE(m->vdisplay - 1),
1218c2ecf20Sopenharmony_ci			priv->base + CRT_VERT0);
1228c2ecf20Sopenharmony_ci	writel(CRT_V_RS_START(m->vsync_start) | CRT_V_RS_END(m->vsync_end),
1238c2ecf20Sopenharmony_ci			priv->base + CRT_VERT1);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	/*
1268c2ecf20Sopenharmony_ci	 * Display Offset: address difference between consecutive scan lines
1278c2ecf20Sopenharmony_ci	 * Terminal Count: memory size of one scan line
1288c2ecf20Sopenharmony_ci	 */
1298c2ecf20Sopenharmony_ci	d_offset = m->hdisplay * bpp / 8;
1308c2ecf20Sopenharmony_ci	t_count = (m->hdisplay * bpp + 127) / 128;
1318c2ecf20Sopenharmony_ci	writel(CRT_DISP_OFFSET(d_offset) | CRT_TERM_COUNT(t_count),
1328c2ecf20Sopenharmony_ci			priv->base + CRT_OFFSET);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	/*
1358c2ecf20Sopenharmony_ci	 * Threshold: FIFO thresholds of refill and stop (16 byte chunks
1368c2ecf20Sopenharmony_ci	 * per line, rounded up)
1378c2ecf20Sopenharmony_ci	 */
1388c2ecf20Sopenharmony_ci	writel(G5_CRT_THROD_VAL, priv->base + CRT_THROD);
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic void aspeed_gfx_pipe_enable(struct drm_simple_display_pipe *pipe,
1428c2ecf20Sopenharmony_ci			      struct drm_crtc_state *crtc_state,
1438c2ecf20Sopenharmony_ci			      struct drm_plane_state *plane_state)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
1468c2ecf20Sopenharmony_ci	struct drm_crtc *crtc = &pipe->crtc;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	aspeed_gfx_crtc_mode_set_nofb(priv);
1498c2ecf20Sopenharmony_ci	aspeed_gfx_enable_controller(priv);
1508c2ecf20Sopenharmony_ci	drm_crtc_vblank_on(crtc);
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cistatic void aspeed_gfx_pipe_disable(struct drm_simple_display_pipe *pipe)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
1568c2ecf20Sopenharmony_ci	struct drm_crtc *crtc = &pipe->crtc;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	drm_crtc_vblank_off(crtc);
1598c2ecf20Sopenharmony_ci	aspeed_gfx_disable_controller(priv);
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic void aspeed_gfx_pipe_update(struct drm_simple_display_pipe *pipe,
1638c2ecf20Sopenharmony_ci				   struct drm_plane_state *plane_state)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
1668c2ecf20Sopenharmony_ci	struct drm_crtc *crtc = &pipe->crtc;
1678c2ecf20Sopenharmony_ci	struct drm_framebuffer *fb = pipe->plane.state->fb;
1688c2ecf20Sopenharmony_ci	struct drm_pending_vblank_event *event;
1698c2ecf20Sopenharmony_ci	struct drm_gem_cma_object *gem;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	spin_lock_irq(&crtc->dev->event_lock);
1728c2ecf20Sopenharmony_ci	event = crtc->state->event;
1738c2ecf20Sopenharmony_ci	if (event) {
1748c2ecf20Sopenharmony_ci		crtc->state->event = NULL;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci		if (drm_crtc_vblank_get(crtc) == 0)
1778c2ecf20Sopenharmony_ci			drm_crtc_arm_vblank_event(crtc, event);
1788c2ecf20Sopenharmony_ci		else
1798c2ecf20Sopenharmony_ci			drm_crtc_send_vblank_event(crtc, event);
1808c2ecf20Sopenharmony_ci	}
1818c2ecf20Sopenharmony_ci	spin_unlock_irq(&crtc->dev->event_lock);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	if (!fb)
1848c2ecf20Sopenharmony_ci		return;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	gem = drm_fb_cma_get_gem_obj(fb, 0);
1878c2ecf20Sopenharmony_ci	if (!gem)
1888c2ecf20Sopenharmony_ci		return;
1898c2ecf20Sopenharmony_ci	writel(gem->paddr, priv->base + CRT_ADDR);
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic int aspeed_gfx_enable_vblank(struct drm_simple_display_pipe *pipe)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
1958c2ecf20Sopenharmony_ci	u32 reg = readl(priv->base + CRT_CTRL1);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	/* Clear pending VBLANK IRQ */
1988c2ecf20Sopenharmony_ci	writel(reg | CRT_CTRL_VERTICAL_INTR_STS, priv->base + CRT_CTRL1);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	reg |= CRT_CTRL_VERTICAL_INTR_EN;
2018c2ecf20Sopenharmony_ci	writel(reg, priv->base + CRT_CTRL1);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	return 0;
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic void aspeed_gfx_disable_vblank(struct drm_simple_display_pipe *pipe)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	struct aspeed_gfx *priv = drm_pipe_to_aspeed_gfx(pipe);
2098c2ecf20Sopenharmony_ci	u32 reg = readl(priv->base + CRT_CTRL1);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	reg &= ~CRT_CTRL_VERTICAL_INTR_EN;
2128c2ecf20Sopenharmony_ci	writel(reg, priv->base + CRT_CTRL1);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	/* Clear pending VBLANK IRQ */
2158c2ecf20Sopenharmony_ci	writel(reg | CRT_CTRL_VERTICAL_INTR_STS, priv->base + CRT_CTRL1);
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cistatic const struct drm_simple_display_pipe_funcs aspeed_gfx_funcs = {
2198c2ecf20Sopenharmony_ci	.enable		= aspeed_gfx_pipe_enable,
2208c2ecf20Sopenharmony_ci	.disable	= aspeed_gfx_pipe_disable,
2218c2ecf20Sopenharmony_ci	.update		= aspeed_gfx_pipe_update,
2228c2ecf20Sopenharmony_ci	.prepare_fb	= drm_gem_fb_simple_display_pipe_prepare_fb,
2238c2ecf20Sopenharmony_ci	.enable_vblank	= aspeed_gfx_enable_vblank,
2248c2ecf20Sopenharmony_ci	.disable_vblank	= aspeed_gfx_disable_vblank,
2258c2ecf20Sopenharmony_ci};
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cistatic const uint32_t aspeed_gfx_formats[] = {
2288c2ecf20Sopenharmony_ci	DRM_FORMAT_XRGB8888,
2298c2ecf20Sopenharmony_ci	DRM_FORMAT_RGB565,
2308c2ecf20Sopenharmony_ci};
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ciint aspeed_gfx_create_pipe(struct drm_device *drm)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	struct aspeed_gfx *priv = to_aspeed_gfx(drm);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	return drm_simple_display_pipe_init(drm, &priv->pipe, &aspeed_gfx_funcs,
2378c2ecf20Sopenharmony_ci					    aspeed_gfx_formats,
2388c2ecf20Sopenharmony_ci					    ARRAY_SIZE(aspeed_gfx_formats),
2398c2ecf20Sopenharmony_ci					    NULL,
2408c2ecf20Sopenharmony_ci					    &priv->connector);
2418c2ecf20Sopenharmony_ci}
242