1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * ARC PGU DRM driver. 4 * 5 * Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com) 6 */ 7 8#include <drm/drm_atomic_helper.h> 9#include <drm/drm_device.h> 10#include <drm/drm_fb_cma_helper.h> 11#include <drm/drm_gem_cma_helper.h> 12#include <drm/drm_plane_helper.h> 13#include <drm/drm_probe_helper.h> 14#include <linux/clk.h> 15#include <linux/platform_data/simplefb.h> 16 17#include "arcpgu.h" 18#include "arcpgu_regs.h" 19 20#define ENCODE_PGU_XY(x, y) ((((x) - 1) << 16) | ((y) - 1)) 21 22static const u32 arc_pgu_supported_formats[] = { 23 DRM_FORMAT_RGB565, 24 DRM_FORMAT_XRGB8888, 25 DRM_FORMAT_ARGB8888, 26}; 27 28static void arc_pgu_set_pxl_fmt(struct drm_crtc *crtc) 29{ 30 struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc); 31 const struct drm_framebuffer *fb = crtc->primary->state->fb; 32 uint32_t pixel_format = fb->format->format; 33 u32 format = DRM_FORMAT_INVALID; 34 int i; 35 u32 reg_ctrl; 36 37 for (i = 0; i < ARRAY_SIZE(arc_pgu_supported_formats); i++) { 38 if (arc_pgu_supported_formats[i] == pixel_format) 39 format = arc_pgu_supported_formats[i]; 40 } 41 42 if (WARN_ON(format == DRM_FORMAT_INVALID)) 43 return; 44 45 reg_ctrl = arc_pgu_read(arcpgu, ARCPGU_REG_CTRL); 46 if (format == DRM_FORMAT_RGB565) 47 reg_ctrl &= ~ARCPGU_MODE_XRGB8888; 48 else 49 reg_ctrl |= ARCPGU_MODE_XRGB8888; 50 arc_pgu_write(arcpgu, ARCPGU_REG_CTRL, reg_ctrl); 51} 52 53static const struct drm_crtc_funcs arc_pgu_crtc_funcs = { 54 .destroy = drm_crtc_cleanup, 55 .set_config = drm_atomic_helper_set_config, 56 .page_flip = drm_atomic_helper_page_flip, 57 .reset = drm_atomic_helper_crtc_reset, 58 .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 59 .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 60}; 61 62static enum drm_mode_status arc_pgu_crtc_mode_valid(struct drm_crtc *crtc, 63 const struct drm_display_mode *mode) 64{ 65 struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc); 66 long rate, clk_rate = mode->clock * 1000; 67 long diff = clk_rate / 200; /* +-0.5% allowed by HDMI spec */ 68 69 rate = clk_round_rate(arcpgu->clk, clk_rate); 70 if ((max(rate, clk_rate) - min(rate, clk_rate) < diff) && (rate > 0)) 71 return MODE_OK; 72 73 return MODE_NOCLOCK; 74} 75 76static void arc_pgu_crtc_mode_set_nofb(struct drm_crtc *crtc) 77{ 78 struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc); 79 struct drm_display_mode *m = &crtc->state->adjusted_mode; 80 u32 val; 81 82 arc_pgu_write(arcpgu, ARCPGU_REG_FMT, 83 ENCODE_PGU_XY(m->crtc_htotal, m->crtc_vtotal)); 84 85 arc_pgu_write(arcpgu, ARCPGU_REG_HSYNC, 86 ENCODE_PGU_XY(m->crtc_hsync_start - m->crtc_hdisplay, 87 m->crtc_hsync_end - m->crtc_hdisplay)); 88 89 arc_pgu_write(arcpgu, ARCPGU_REG_VSYNC, 90 ENCODE_PGU_XY(m->crtc_vsync_start - m->crtc_vdisplay, 91 m->crtc_vsync_end - m->crtc_vdisplay)); 92 93 arc_pgu_write(arcpgu, ARCPGU_REG_ACTIVE, 94 ENCODE_PGU_XY(m->crtc_hblank_end - m->crtc_hblank_start, 95 m->crtc_vblank_end - m->crtc_vblank_start)); 96 97 val = arc_pgu_read(arcpgu, ARCPGU_REG_CTRL); 98 99 if (m->flags & DRM_MODE_FLAG_PVSYNC) 100 val |= ARCPGU_CTRL_VS_POL_MASK << ARCPGU_CTRL_VS_POL_OFST; 101 else 102 val &= ~(ARCPGU_CTRL_VS_POL_MASK << ARCPGU_CTRL_VS_POL_OFST); 103 104 if (m->flags & DRM_MODE_FLAG_PHSYNC) 105 val |= ARCPGU_CTRL_HS_POL_MASK << ARCPGU_CTRL_HS_POL_OFST; 106 else 107 val &= ~(ARCPGU_CTRL_HS_POL_MASK << ARCPGU_CTRL_HS_POL_OFST); 108 109 arc_pgu_write(arcpgu, ARCPGU_REG_CTRL, val); 110 arc_pgu_write(arcpgu, ARCPGU_REG_STRIDE, 0); 111 arc_pgu_write(arcpgu, ARCPGU_REG_START_SET, 1); 112 113 arc_pgu_set_pxl_fmt(crtc); 114 115 clk_set_rate(arcpgu->clk, m->crtc_clock * 1000); 116} 117 118static void arc_pgu_crtc_atomic_enable(struct drm_crtc *crtc, 119 struct drm_crtc_state *old_state) 120{ 121 struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc); 122 123 clk_prepare_enable(arcpgu->clk); 124 arc_pgu_write(arcpgu, ARCPGU_REG_CTRL, 125 arc_pgu_read(arcpgu, ARCPGU_REG_CTRL) | 126 ARCPGU_CTRL_ENABLE_MASK); 127} 128 129static void arc_pgu_crtc_atomic_disable(struct drm_crtc *crtc, 130 struct drm_crtc_state *old_state) 131{ 132 struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc); 133 134 clk_disable_unprepare(arcpgu->clk); 135 arc_pgu_write(arcpgu, ARCPGU_REG_CTRL, 136 arc_pgu_read(arcpgu, ARCPGU_REG_CTRL) & 137 ~ARCPGU_CTRL_ENABLE_MASK); 138} 139 140static const struct drm_crtc_helper_funcs arc_pgu_crtc_helper_funcs = { 141 .mode_valid = arc_pgu_crtc_mode_valid, 142 .mode_set_nofb = arc_pgu_crtc_mode_set_nofb, 143 .atomic_enable = arc_pgu_crtc_atomic_enable, 144 .atomic_disable = arc_pgu_crtc_atomic_disable, 145}; 146 147static void arc_pgu_plane_atomic_update(struct drm_plane *plane, 148 struct drm_plane_state *state) 149{ 150 struct arcpgu_drm_private *arcpgu; 151 struct drm_gem_cma_object *gem; 152 153 if (!plane->state->crtc || !plane->state->fb) 154 return; 155 156 arcpgu = crtc_to_arcpgu_priv(plane->state->crtc); 157 gem = drm_fb_cma_get_gem_obj(plane->state->fb, 0); 158 arc_pgu_write(arcpgu, ARCPGU_REG_BUF0_ADDR, gem->paddr); 159} 160 161static const struct drm_plane_helper_funcs arc_pgu_plane_helper_funcs = { 162 .atomic_update = arc_pgu_plane_atomic_update, 163}; 164 165static void arc_pgu_plane_destroy(struct drm_plane *plane) 166{ 167 drm_plane_cleanup(plane); 168} 169 170static const struct drm_plane_funcs arc_pgu_plane_funcs = { 171 .update_plane = drm_atomic_helper_update_plane, 172 .disable_plane = drm_atomic_helper_disable_plane, 173 .destroy = arc_pgu_plane_destroy, 174 .reset = drm_atomic_helper_plane_reset, 175 .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 176 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 177}; 178 179static struct drm_plane *arc_pgu_plane_init(struct drm_device *drm) 180{ 181 struct arcpgu_drm_private *arcpgu = drm->dev_private; 182 struct drm_plane *plane = NULL; 183 int ret; 184 185 plane = devm_kzalloc(drm->dev, sizeof(*plane), GFP_KERNEL); 186 if (!plane) 187 return ERR_PTR(-ENOMEM); 188 189 ret = drm_universal_plane_init(drm, plane, 0xff, &arc_pgu_plane_funcs, 190 arc_pgu_supported_formats, 191 ARRAY_SIZE(arc_pgu_supported_formats), 192 NULL, 193 DRM_PLANE_TYPE_PRIMARY, NULL); 194 if (ret) 195 return ERR_PTR(ret); 196 197 drm_plane_helper_add(plane, &arc_pgu_plane_helper_funcs); 198 arcpgu->plane = plane; 199 200 return plane; 201} 202 203int arc_pgu_setup_crtc(struct drm_device *drm) 204{ 205 struct arcpgu_drm_private *arcpgu = drm->dev_private; 206 struct drm_plane *primary; 207 int ret; 208 209 primary = arc_pgu_plane_init(drm); 210 if (IS_ERR(primary)) 211 return PTR_ERR(primary); 212 213 ret = drm_crtc_init_with_planes(drm, &arcpgu->crtc, primary, NULL, 214 &arc_pgu_crtc_funcs, NULL); 215 if (ret) { 216 arc_pgu_plane_destroy(primary); 217 return ret; 218 } 219 220 drm_crtc_helper_add(&arcpgu->crtc, &arc_pgu_crtc_helper_funcs); 221 return 0; 222} 223