18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * ARC PGU DRM driver.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com)
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h>
98c2ecf20Sopenharmony_ci#include <drm/drm_device.h>
108c2ecf20Sopenharmony_ci#include <drm/drm_fb_cma_helper.h>
118c2ecf20Sopenharmony_ci#include <drm/drm_gem_cma_helper.h>
128c2ecf20Sopenharmony_ci#include <drm/drm_plane_helper.h>
138c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h>
148c2ecf20Sopenharmony_ci#include <linux/clk.h>
158c2ecf20Sopenharmony_ci#include <linux/platform_data/simplefb.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "arcpgu.h"
188c2ecf20Sopenharmony_ci#include "arcpgu_regs.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define ENCODE_PGU_XY(x, y)	((((x) - 1) << 16) | ((y) - 1))
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic const u32 arc_pgu_supported_formats[] = {
238c2ecf20Sopenharmony_ci	DRM_FORMAT_RGB565,
248c2ecf20Sopenharmony_ci	DRM_FORMAT_XRGB8888,
258c2ecf20Sopenharmony_ci	DRM_FORMAT_ARGB8888,
268c2ecf20Sopenharmony_ci};
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic void arc_pgu_set_pxl_fmt(struct drm_crtc *crtc)
298c2ecf20Sopenharmony_ci{
308c2ecf20Sopenharmony_ci	struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc);
318c2ecf20Sopenharmony_ci	const struct drm_framebuffer *fb = crtc->primary->state->fb;
328c2ecf20Sopenharmony_ci	uint32_t pixel_format = fb->format->format;
338c2ecf20Sopenharmony_ci	u32 format = DRM_FORMAT_INVALID;
348c2ecf20Sopenharmony_ci	int i;
358c2ecf20Sopenharmony_ci	u32 reg_ctrl;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(arc_pgu_supported_formats); i++) {
388c2ecf20Sopenharmony_ci		if (arc_pgu_supported_formats[i] == pixel_format)
398c2ecf20Sopenharmony_ci			format = arc_pgu_supported_formats[i];
408c2ecf20Sopenharmony_ci	}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	if (WARN_ON(format == DRM_FORMAT_INVALID))
438c2ecf20Sopenharmony_ci		return;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	reg_ctrl = arc_pgu_read(arcpgu, ARCPGU_REG_CTRL);
468c2ecf20Sopenharmony_ci	if (format == DRM_FORMAT_RGB565)
478c2ecf20Sopenharmony_ci		reg_ctrl &= ~ARCPGU_MODE_XRGB8888;
488c2ecf20Sopenharmony_ci	else
498c2ecf20Sopenharmony_ci		reg_ctrl |= ARCPGU_MODE_XRGB8888;
508c2ecf20Sopenharmony_ci	arc_pgu_write(arcpgu, ARCPGU_REG_CTRL, reg_ctrl);
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic const struct drm_crtc_funcs arc_pgu_crtc_funcs = {
548c2ecf20Sopenharmony_ci	.destroy = drm_crtc_cleanup,
558c2ecf20Sopenharmony_ci	.set_config = drm_atomic_helper_set_config,
568c2ecf20Sopenharmony_ci	.page_flip = drm_atomic_helper_page_flip,
578c2ecf20Sopenharmony_ci	.reset = drm_atomic_helper_crtc_reset,
588c2ecf20Sopenharmony_ci	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
598c2ecf20Sopenharmony_ci	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
608c2ecf20Sopenharmony_ci};
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic enum drm_mode_status arc_pgu_crtc_mode_valid(struct drm_crtc *crtc,
638c2ecf20Sopenharmony_ci						    const struct drm_display_mode *mode)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc);
668c2ecf20Sopenharmony_ci	long rate, clk_rate = mode->clock * 1000;
678c2ecf20Sopenharmony_ci	long diff = clk_rate / 200; /* +-0.5% allowed by HDMI spec */
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	rate = clk_round_rate(arcpgu->clk, clk_rate);
708c2ecf20Sopenharmony_ci	if ((max(rate, clk_rate) - min(rate, clk_rate) < diff) && (rate > 0))
718c2ecf20Sopenharmony_ci		return MODE_OK;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	return MODE_NOCLOCK;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic void arc_pgu_crtc_mode_set_nofb(struct drm_crtc *crtc)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc);
798c2ecf20Sopenharmony_ci	struct drm_display_mode *m = &crtc->state->adjusted_mode;
808c2ecf20Sopenharmony_ci	u32 val;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	arc_pgu_write(arcpgu, ARCPGU_REG_FMT,
838c2ecf20Sopenharmony_ci		      ENCODE_PGU_XY(m->crtc_htotal, m->crtc_vtotal));
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	arc_pgu_write(arcpgu, ARCPGU_REG_HSYNC,
868c2ecf20Sopenharmony_ci		      ENCODE_PGU_XY(m->crtc_hsync_start - m->crtc_hdisplay,
878c2ecf20Sopenharmony_ci				    m->crtc_hsync_end - m->crtc_hdisplay));
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	arc_pgu_write(arcpgu, ARCPGU_REG_VSYNC,
908c2ecf20Sopenharmony_ci		      ENCODE_PGU_XY(m->crtc_vsync_start - m->crtc_vdisplay,
918c2ecf20Sopenharmony_ci				    m->crtc_vsync_end - m->crtc_vdisplay));
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	arc_pgu_write(arcpgu, ARCPGU_REG_ACTIVE,
948c2ecf20Sopenharmony_ci		      ENCODE_PGU_XY(m->crtc_hblank_end - m->crtc_hblank_start,
958c2ecf20Sopenharmony_ci				    m->crtc_vblank_end - m->crtc_vblank_start));
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	val = arc_pgu_read(arcpgu, ARCPGU_REG_CTRL);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	if (m->flags & DRM_MODE_FLAG_PVSYNC)
1008c2ecf20Sopenharmony_ci		val |= ARCPGU_CTRL_VS_POL_MASK << ARCPGU_CTRL_VS_POL_OFST;
1018c2ecf20Sopenharmony_ci	else
1028c2ecf20Sopenharmony_ci		val &= ~(ARCPGU_CTRL_VS_POL_MASK << ARCPGU_CTRL_VS_POL_OFST);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	if (m->flags & DRM_MODE_FLAG_PHSYNC)
1058c2ecf20Sopenharmony_ci		val |= ARCPGU_CTRL_HS_POL_MASK << ARCPGU_CTRL_HS_POL_OFST;
1068c2ecf20Sopenharmony_ci	else
1078c2ecf20Sopenharmony_ci		val &= ~(ARCPGU_CTRL_HS_POL_MASK << ARCPGU_CTRL_HS_POL_OFST);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	arc_pgu_write(arcpgu, ARCPGU_REG_CTRL, val);
1108c2ecf20Sopenharmony_ci	arc_pgu_write(arcpgu, ARCPGU_REG_STRIDE, 0);
1118c2ecf20Sopenharmony_ci	arc_pgu_write(arcpgu, ARCPGU_REG_START_SET, 1);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	arc_pgu_set_pxl_fmt(crtc);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	clk_set_rate(arcpgu->clk, m->crtc_clock * 1000);
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic void arc_pgu_crtc_atomic_enable(struct drm_crtc *crtc,
1198c2ecf20Sopenharmony_ci				       struct drm_crtc_state *old_state)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	clk_prepare_enable(arcpgu->clk);
1248c2ecf20Sopenharmony_ci	arc_pgu_write(arcpgu, ARCPGU_REG_CTRL,
1258c2ecf20Sopenharmony_ci		      arc_pgu_read(arcpgu, ARCPGU_REG_CTRL) |
1268c2ecf20Sopenharmony_ci		      ARCPGU_CTRL_ENABLE_MASK);
1278c2ecf20Sopenharmony_ci}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_cistatic void arc_pgu_crtc_atomic_disable(struct drm_crtc *crtc,
1308c2ecf20Sopenharmony_ci					struct drm_crtc_state *old_state)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	clk_disable_unprepare(arcpgu->clk);
1358c2ecf20Sopenharmony_ci	arc_pgu_write(arcpgu, ARCPGU_REG_CTRL,
1368c2ecf20Sopenharmony_ci			      arc_pgu_read(arcpgu, ARCPGU_REG_CTRL) &
1378c2ecf20Sopenharmony_ci			      ~ARCPGU_CTRL_ENABLE_MASK);
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic const struct drm_crtc_helper_funcs arc_pgu_crtc_helper_funcs = {
1418c2ecf20Sopenharmony_ci	.mode_valid	= arc_pgu_crtc_mode_valid,
1428c2ecf20Sopenharmony_ci	.mode_set_nofb	= arc_pgu_crtc_mode_set_nofb,
1438c2ecf20Sopenharmony_ci	.atomic_enable	= arc_pgu_crtc_atomic_enable,
1448c2ecf20Sopenharmony_ci	.atomic_disable	= arc_pgu_crtc_atomic_disable,
1458c2ecf20Sopenharmony_ci};
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic void arc_pgu_plane_atomic_update(struct drm_plane *plane,
1488c2ecf20Sopenharmony_ci					struct drm_plane_state *state)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	struct arcpgu_drm_private *arcpgu;
1518c2ecf20Sopenharmony_ci	struct drm_gem_cma_object *gem;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	if (!plane->state->crtc || !plane->state->fb)
1548c2ecf20Sopenharmony_ci		return;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	arcpgu = crtc_to_arcpgu_priv(plane->state->crtc);
1578c2ecf20Sopenharmony_ci	gem = drm_fb_cma_get_gem_obj(plane->state->fb, 0);
1588c2ecf20Sopenharmony_ci	arc_pgu_write(arcpgu, ARCPGU_REG_BUF0_ADDR, gem->paddr);
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic const struct drm_plane_helper_funcs arc_pgu_plane_helper_funcs = {
1628c2ecf20Sopenharmony_ci	.atomic_update = arc_pgu_plane_atomic_update,
1638c2ecf20Sopenharmony_ci};
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic void arc_pgu_plane_destroy(struct drm_plane *plane)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	drm_plane_cleanup(plane);
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic const struct drm_plane_funcs arc_pgu_plane_funcs = {
1718c2ecf20Sopenharmony_ci	.update_plane		= drm_atomic_helper_update_plane,
1728c2ecf20Sopenharmony_ci	.disable_plane		= drm_atomic_helper_disable_plane,
1738c2ecf20Sopenharmony_ci	.destroy		= arc_pgu_plane_destroy,
1748c2ecf20Sopenharmony_ci	.reset			= drm_atomic_helper_plane_reset,
1758c2ecf20Sopenharmony_ci	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
1768c2ecf20Sopenharmony_ci	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
1778c2ecf20Sopenharmony_ci};
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic struct drm_plane *arc_pgu_plane_init(struct drm_device *drm)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	struct arcpgu_drm_private *arcpgu = drm->dev_private;
1828c2ecf20Sopenharmony_ci	struct drm_plane *plane = NULL;
1838c2ecf20Sopenharmony_ci	int ret;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	plane = devm_kzalloc(drm->dev, sizeof(*plane), GFP_KERNEL);
1868c2ecf20Sopenharmony_ci	if (!plane)
1878c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	ret = drm_universal_plane_init(drm, plane, 0xff, &arc_pgu_plane_funcs,
1908c2ecf20Sopenharmony_ci				       arc_pgu_supported_formats,
1918c2ecf20Sopenharmony_ci				       ARRAY_SIZE(arc_pgu_supported_formats),
1928c2ecf20Sopenharmony_ci				       NULL,
1938c2ecf20Sopenharmony_ci				       DRM_PLANE_TYPE_PRIMARY, NULL);
1948c2ecf20Sopenharmony_ci	if (ret)
1958c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	drm_plane_helper_add(plane, &arc_pgu_plane_helper_funcs);
1988c2ecf20Sopenharmony_ci	arcpgu->plane = plane;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	return plane;
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ciint arc_pgu_setup_crtc(struct drm_device *drm)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	struct arcpgu_drm_private *arcpgu = drm->dev_private;
2068c2ecf20Sopenharmony_ci	struct drm_plane *primary;
2078c2ecf20Sopenharmony_ci	int ret;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	primary = arc_pgu_plane_init(drm);
2108c2ecf20Sopenharmony_ci	if (IS_ERR(primary))
2118c2ecf20Sopenharmony_ci		return PTR_ERR(primary);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	ret = drm_crtc_init_with_planes(drm, &arcpgu->crtc, primary, NULL,
2148c2ecf20Sopenharmony_ci					&arc_pgu_crtc_funcs, NULL);
2158c2ecf20Sopenharmony_ci	if (ret) {
2168c2ecf20Sopenharmony_ci		arc_pgu_plane_destroy(primary);
2178c2ecf20Sopenharmony_ci		return ret;
2188c2ecf20Sopenharmony_ci	}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	drm_crtc_helper_add(&arcpgu->crtc, &arc_pgu_crtc_helper_funcs);
2218c2ecf20Sopenharmony_ci	return 0;
2228c2ecf20Sopenharmony_ci}
223