Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.20-rc6 248 lines 7.5 kB view raw
1/* 2 * ARC PGU DRM driver. 3 * 4 * Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com) 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 */ 16 17#include <drm/drm_atomic_helper.h> 18#include <drm/drm_crtc_helper.h> 19#include <drm/drm_fb_cma_helper.h> 20#include <drm/drm_gem_cma_helper.h> 21#include <drm/drm_plane_helper.h> 22#include <linux/clk.h> 23#include <linux/platform_data/simplefb.h> 24 25#include "arcpgu.h" 26#include "arcpgu_regs.h" 27 28#define ENCODE_PGU_XY(x, y) ((((x) - 1) << 16) | ((y) - 1)) 29 30static struct simplefb_format supported_formats[] = { 31 { "r5g6b5", 16, {11, 5}, {5, 6}, {0, 5}, {0, 0}, DRM_FORMAT_RGB565 }, 32 { "r8g8b8", 24, {16, 8}, {8, 8}, {0, 8}, {0, 0}, DRM_FORMAT_RGB888 }, 33}; 34 35static void arc_pgu_set_pxl_fmt(struct drm_crtc *crtc) 36{ 37 struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc); 38 const struct drm_framebuffer *fb = crtc->primary->state->fb; 39 uint32_t pixel_format = fb->format->format; 40 struct simplefb_format *format = NULL; 41 int i; 42 43 for (i = 0; i < ARRAY_SIZE(supported_formats); i++) { 44 if (supported_formats[i].fourcc == pixel_format) 45 format = &supported_formats[i]; 46 } 47 48 if (WARN_ON(!format)) 49 return; 50 51 if (format->fourcc == DRM_FORMAT_RGB888) 52 arc_pgu_write(arcpgu, ARCPGU_REG_CTRL, 53 arc_pgu_read(arcpgu, ARCPGU_REG_CTRL) | 54 ARCPGU_MODE_RGB888_MASK); 55 56} 57 58static const struct drm_crtc_funcs arc_pgu_crtc_funcs = { 59 .destroy = drm_crtc_cleanup, 60 .set_config = drm_atomic_helper_set_config, 61 .page_flip = drm_atomic_helper_page_flip, 62 .reset = drm_atomic_helper_crtc_reset, 63 .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 64 .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 65}; 66 67static enum drm_mode_status arc_pgu_crtc_mode_valid(struct drm_crtc *crtc, 68 const struct drm_display_mode *mode) 69{ 70 struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc); 71 long rate, clk_rate = mode->clock * 1000; 72 long diff = clk_rate / 200; /* +-0.5% allowed by HDMI spec */ 73 74 rate = clk_round_rate(arcpgu->clk, clk_rate); 75 if ((max(rate, clk_rate) - min(rate, clk_rate) < diff) && (rate > 0)) 76 return MODE_OK; 77 78 return MODE_NOCLOCK; 79} 80 81static void arc_pgu_crtc_mode_set_nofb(struct drm_crtc *crtc) 82{ 83 struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc); 84 struct drm_display_mode *m = &crtc->state->adjusted_mode; 85 u32 val; 86 87 arc_pgu_write(arcpgu, ARCPGU_REG_FMT, 88 ENCODE_PGU_XY(m->crtc_htotal, m->crtc_vtotal)); 89 90 arc_pgu_write(arcpgu, ARCPGU_REG_HSYNC, 91 ENCODE_PGU_XY(m->crtc_hsync_start - m->crtc_hdisplay, 92 m->crtc_hsync_end - m->crtc_hdisplay)); 93 94 arc_pgu_write(arcpgu, ARCPGU_REG_VSYNC, 95 ENCODE_PGU_XY(m->crtc_vsync_start - m->crtc_vdisplay, 96 m->crtc_vsync_end - m->crtc_vdisplay)); 97 98 arc_pgu_write(arcpgu, ARCPGU_REG_ACTIVE, 99 ENCODE_PGU_XY(m->crtc_hblank_end - m->crtc_hblank_start, 100 m->crtc_vblank_end - m->crtc_vblank_start)); 101 102 val = arc_pgu_read(arcpgu, ARCPGU_REG_CTRL); 103 104 if (m->flags & DRM_MODE_FLAG_PVSYNC) 105 val |= ARCPGU_CTRL_VS_POL_MASK << ARCPGU_CTRL_VS_POL_OFST; 106 else 107 val &= ~(ARCPGU_CTRL_VS_POL_MASK << ARCPGU_CTRL_VS_POL_OFST); 108 109 if (m->flags & DRM_MODE_FLAG_PHSYNC) 110 val |= ARCPGU_CTRL_HS_POL_MASK << ARCPGU_CTRL_HS_POL_OFST; 111 else 112 val &= ~(ARCPGU_CTRL_HS_POL_MASK << ARCPGU_CTRL_HS_POL_OFST); 113 114 arc_pgu_write(arcpgu, ARCPGU_REG_CTRL, val); 115 arc_pgu_write(arcpgu, ARCPGU_REG_STRIDE, 0); 116 arc_pgu_write(arcpgu, ARCPGU_REG_START_SET, 1); 117 118 arc_pgu_set_pxl_fmt(crtc); 119 120 clk_set_rate(arcpgu->clk, m->crtc_clock * 1000); 121} 122 123static void arc_pgu_crtc_atomic_enable(struct drm_crtc *crtc, 124 struct drm_crtc_state *old_state) 125{ 126 struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc); 127 128 clk_prepare_enable(arcpgu->clk); 129 arc_pgu_write(arcpgu, ARCPGU_REG_CTRL, 130 arc_pgu_read(arcpgu, ARCPGU_REG_CTRL) | 131 ARCPGU_CTRL_ENABLE_MASK); 132} 133 134static void arc_pgu_crtc_atomic_disable(struct drm_crtc *crtc, 135 struct drm_crtc_state *old_state) 136{ 137 struct arcpgu_drm_private *arcpgu = crtc_to_arcpgu_priv(crtc); 138 139 clk_disable_unprepare(arcpgu->clk); 140 arc_pgu_write(arcpgu, ARCPGU_REG_CTRL, 141 arc_pgu_read(arcpgu, ARCPGU_REG_CTRL) & 142 ~ARCPGU_CTRL_ENABLE_MASK); 143} 144 145static void arc_pgu_crtc_atomic_begin(struct drm_crtc *crtc, 146 struct drm_crtc_state *state) 147{ 148 struct drm_pending_vblank_event *event = crtc->state->event; 149 150 if (event) { 151 crtc->state->event = NULL; 152 153 spin_lock_irq(&crtc->dev->event_lock); 154 drm_crtc_send_vblank_event(crtc, event); 155 spin_unlock_irq(&crtc->dev->event_lock); 156 } 157} 158 159static const struct drm_crtc_helper_funcs arc_pgu_crtc_helper_funcs = { 160 .mode_valid = arc_pgu_crtc_mode_valid, 161 .mode_set = drm_helper_crtc_mode_set, 162 .mode_set_base = drm_helper_crtc_mode_set_base, 163 .mode_set_nofb = arc_pgu_crtc_mode_set_nofb, 164 .atomic_begin = arc_pgu_crtc_atomic_begin, 165 .atomic_enable = arc_pgu_crtc_atomic_enable, 166 .atomic_disable = arc_pgu_crtc_atomic_disable, 167}; 168 169static void arc_pgu_plane_atomic_update(struct drm_plane *plane, 170 struct drm_plane_state *state) 171{ 172 struct arcpgu_drm_private *arcpgu; 173 struct drm_gem_cma_object *gem; 174 175 if (!plane->state->crtc || !plane->state->fb) 176 return; 177 178 arcpgu = crtc_to_arcpgu_priv(plane->state->crtc); 179 gem = drm_fb_cma_get_gem_obj(plane->state->fb, 0); 180 arc_pgu_write(arcpgu, ARCPGU_REG_BUF0_ADDR, gem->paddr); 181} 182 183static const struct drm_plane_helper_funcs arc_pgu_plane_helper_funcs = { 184 .atomic_update = arc_pgu_plane_atomic_update, 185}; 186 187static void arc_pgu_plane_destroy(struct drm_plane *plane) 188{ 189 drm_plane_helper_disable(plane, NULL); 190 drm_plane_cleanup(plane); 191} 192 193static const struct drm_plane_funcs arc_pgu_plane_funcs = { 194 .update_plane = drm_atomic_helper_update_plane, 195 .disable_plane = drm_atomic_helper_disable_plane, 196 .destroy = arc_pgu_plane_destroy, 197 .reset = drm_atomic_helper_plane_reset, 198 .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 199 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 200}; 201 202static struct drm_plane *arc_pgu_plane_init(struct drm_device *drm) 203{ 204 struct arcpgu_drm_private *arcpgu = drm->dev_private; 205 struct drm_plane *plane = NULL; 206 u32 formats[ARRAY_SIZE(supported_formats)], i; 207 int ret; 208 209 plane = devm_kzalloc(drm->dev, sizeof(*plane), GFP_KERNEL); 210 if (!plane) 211 return ERR_PTR(-ENOMEM); 212 213 for (i = 0; i < ARRAY_SIZE(supported_formats); i++) 214 formats[i] = supported_formats[i].fourcc; 215 216 ret = drm_universal_plane_init(drm, plane, 0xff, &arc_pgu_plane_funcs, 217 formats, ARRAY_SIZE(formats), 218 NULL, 219 DRM_PLANE_TYPE_PRIMARY, NULL); 220 if (ret) 221 return ERR_PTR(ret); 222 223 drm_plane_helper_add(plane, &arc_pgu_plane_helper_funcs); 224 arcpgu->plane = plane; 225 226 return plane; 227} 228 229int arc_pgu_setup_crtc(struct drm_device *drm) 230{ 231 struct arcpgu_drm_private *arcpgu = drm->dev_private; 232 struct drm_plane *primary; 233 int ret; 234 235 primary = arc_pgu_plane_init(drm); 236 if (IS_ERR(primary)) 237 return PTR_ERR(primary); 238 239 ret = drm_crtc_init_with_planes(drm, &arcpgu->crtc, primary, NULL, 240 &arc_pgu_crtc_funcs, NULL); 241 if (ret) { 242 arc_pgu_plane_destroy(primary); 243 return ret; 244 } 245 246 drm_crtc_helper_add(&arcpgu->crtc, &arc_pgu_crtc_helper_funcs); 247 return 0; 248}