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