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 v5.0-rc3 245 lines 7.4 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_nofb = arc_pgu_crtc_mode_set_nofb, 162 .atomic_begin = arc_pgu_crtc_atomic_begin, 163 .atomic_enable = arc_pgu_crtc_atomic_enable, 164 .atomic_disable = arc_pgu_crtc_atomic_disable, 165}; 166 167static void arc_pgu_plane_atomic_update(struct drm_plane *plane, 168 struct drm_plane_state *state) 169{ 170 struct arcpgu_drm_private *arcpgu; 171 struct drm_gem_cma_object *gem; 172 173 if (!plane->state->crtc || !plane->state->fb) 174 return; 175 176 arcpgu = crtc_to_arcpgu_priv(plane->state->crtc); 177 gem = drm_fb_cma_get_gem_obj(plane->state->fb, 0); 178 arc_pgu_write(arcpgu, ARCPGU_REG_BUF0_ADDR, gem->paddr); 179} 180 181static const struct drm_plane_helper_funcs arc_pgu_plane_helper_funcs = { 182 .atomic_update = arc_pgu_plane_atomic_update, 183}; 184 185static void arc_pgu_plane_destroy(struct drm_plane *plane) 186{ 187 drm_plane_cleanup(plane); 188} 189 190static const struct drm_plane_funcs arc_pgu_plane_funcs = { 191 .update_plane = drm_atomic_helper_update_plane, 192 .disable_plane = drm_atomic_helper_disable_plane, 193 .destroy = arc_pgu_plane_destroy, 194 .reset = drm_atomic_helper_plane_reset, 195 .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 196 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 197}; 198 199static struct drm_plane *arc_pgu_plane_init(struct drm_device *drm) 200{ 201 struct arcpgu_drm_private *arcpgu = drm->dev_private; 202 struct drm_plane *plane = NULL; 203 u32 formats[ARRAY_SIZE(supported_formats)], i; 204 int ret; 205 206 plane = devm_kzalloc(drm->dev, sizeof(*plane), GFP_KERNEL); 207 if (!plane) 208 return ERR_PTR(-ENOMEM); 209 210 for (i = 0; i < ARRAY_SIZE(supported_formats); i++) 211 formats[i] = supported_formats[i].fourcc; 212 213 ret = drm_universal_plane_init(drm, plane, 0xff, &arc_pgu_plane_funcs, 214 formats, ARRAY_SIZE(formats), 215 NULL, 216 DRM_PLANE_TYPE_PRIMARY, NULL); 217 if (ret) 218 return ERR_PTR(ret); 219 220 drm_plane_helper_add(plane, &arc_pgu_plane_helper_funcs); 221 arcpgu->plane = plane; 222 223 return plane; 224} 225 226int arc_pgu_setup_crtc(struct drm_device *drm) 227{ 228 struct arcpgu_drm_private *arcpgu = drm->dev_private; 229 struct drm_plane *primary; 230 int ret; 231 232 primary = arc_pgu_plane_init(drm); 233 if (IS_ERR(primary)) 234 return PTR_ERR(primary); 235 236 ret = drm_crtc_init_with_planes(drm, &arcpgu->crtc, primary, NULL, 237 &arc_pgu_crtc_funcs, NULL); 238 if (ret) { 239 arc_pgu_plane_destroy(primary); 240 return ret; 241 } 242 243 drm_crtc_helper_add(&arcpgu->crtc, &arc_pgu_crtc_helper_funcs); 244 return 0; 245}