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.15-rc5 251 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 if (!crtc->primary->fb) 140 return; 141 142 clk_disable_unprepare(arcpgu->clk); 143 arc_pgu_write(arcpgu, ARCPGU_REG_CTRL, 144 arc_pgu_read(arcpgu, ARCPGU_REG_CTRL) & 145 ~ARCPGU_CTRL_ENABLE_MASK); 146} 147 148static void arc_pgu_crtc_atomic_begin(struct drm_crtc *crtc, 149 struct drm_crtc_state *state) 150{ 151 struct drm_pending_vblank_event *event = crtc->state->event; 152 153 if (event) { 154 crtc->state->event = NULL; 155 156 spin_lock_irq(&crtc->dev->event_lock); 157 drm_crtc_send_vblank_event(crtc, event); 158 spin_unlock_irq(&crtc->dev->event_lock); 159 } 160} 161 162static const struct drm_crtc_helper_funcs arc_pgu_crtc_helper_funcs = { 163 .mode_valid = arc_pgu_crtc_mode_valid, 164 .mode_set = drm_helper_crtc_mode_set, 165 .mode_set_base = drm_helper_crtc_mode_set_base, 166 .mode_set_nofb = arc_pgu_crtc_mode_set_nofb, 167 .atomic_begin = arc_pgu_crtc_atomic_begin, 168 .atomic_enable = arc_pgu_crtc_atomic_enable, 169 .atomic_disable = arc_pgu_crtc_atomic_disable, 170}; 171 172static void arc_pgu_plane_atomic_update(struct drm_plane *plane, 173 struct drm_plane_state *state) 174{ 175 struct arcpgu_drm_private *arcpgu; 176 struct drm_gem_cma_object *gem; 177 178 if (!plane->state->crtc || !plane->state->fb) 179 return; 180 181 arcpgu = crtc_to_arcpgu_priv(plane->state->crtc); 182 gem = drm_fb_cma_get_gem_obj(plane->state->fb, 0); 183 arc_pgu_write(arcpgu, ARCPGU_REG_BUF0_ADDR, gem->paddr); 184} 185 186static const struct drm_plane_helper_funcs arc_pgu_plane_helper_funcs = { 187 .atomic_update = arc_pgu_plane_atomic_update, 188}; 189 190static void arc_pgu_plane_destroy(struct drm_plane *plane) 191{ 192 drm_plane_helper_disable(plane); 193 drm_plane_cleanup(plane); 194} 195 196static const struct drm_plane_funcs arc_pgu_plane_funcs = { 197 .update_plane = drm_atomic_helper_update_plane, 198 .disable_plane = drm_atomic_helper_disable_plane, 199 .destroy = arc_pgu_plane_destroy, 200 .reset = drm_atomic_helper_plane_reset, 201 .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 202 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 203}; 204 205static struct drm_plane *arc_pgu_plane_init(struct drm_device *drm) 206{ 207 struct arcpgu_drm_private *arcpgu = drm->dev_private; 208 struct drm_plane *plane = NULL; 209 u32 formats[ARRAY_SIZE(supported_formats)], i; 210 int ret; 211 212 plane = devm_kzalloc(drm->dev, sizeof(*plane), GFP_KERNEL); 213 if (!plane) 214 return ERR_PTR(-ENOMEM); 215 216 for (i = 0; i < ARRAY_SIZE(supported_formats); i++) 217 formats[i] = supported_formats[i].fourcc; 218 219 ret = drm_universal_plane_init(drm, plane, 0xff, &arc_pgu_plane_funcs, 220 formats, ARRAY_SIZE(formats), 221 NULL, 222 DRM_PLANE_TYPE_PRIMARY, NULL); 223 if (ret) 224 return ERR_PTR(ret); 225 226 drm_plane_helper_add(plane, &arc_pgu_plane_helper_funcs); 227 arcpgu->plane = plane; 228 229 return plane; 230} 231 232int arc_pgu_setup_crtc(struct drm_device *drm) 233{ 234 struct arcpgu_drm_private *arcpgu = drm->dev_private; 235 struct drm_plane *primary; 236 int ret; 237 238 primary = arc_pgu_plane_init(drm); 239 if (IS_ERR(primary)) 240 return PTR_ERR(primary); 241 242 ret = drm_crtc_init_with_planes(drm, &arcpgu->crtc, primary, NULL, 243 &arc_pgu_crtc_funcs, NULL); 244 if (ret) { 245 arc_pgu_plane_destroy(primary); 246 return ret; 247 } 248 249 drm_crtc_helper_add(&arcpgu->crtc, &arc_pgu_crtc_helper_funcs); 250 return 0; 251}