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.6-rc7 343 lines 9.2 kB view raw
1/* 2 * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 3 * Author:Mark Yao <mark.yao@rock-chips.com> 4 * 5 * This software is licensed under the terms of the GNU General Public 6 * License version 2, as published by the Free Software Foundation, and 7 * may be copied, distributed, and modified under those terms. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 */ 14 15#include <linux/kernel.h> 16#include <drm/drm.h> 17#include <drm/drmP.h> 18#include <drm/drm_atomic.h> 19#include <drm/drm_fb_helper.h> 20#include <drm/drm_crtc_helper.h> 21 22#include "rockchip_drm_drv.h" 23#include "rockchip_drm_gem.h" 24 25#define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb) 26 27struct rockchip_drm_fb { 28 struct drm_framebuffer fb; 29 struct drm_gem_object *obj[ROCKCHIP_MAX_FB_BUFFER]; 30}; 31 32struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb, 33 unsigned int plane) 34{ 35 struct rockchip_drm_fb *rk_fb = to_rockchip_fb(fb); 36 37 if (plane >= ROCKCHIP_MAX_FB_BUFFER) 38 return NULL; 39 40 return rk_fb->obj[plane]; 41} 42 43static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb) 44{ 45 struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb); 46 struct drm_gem_object *obj; 47 int i; 48 49 for (i = 0; i < ROCKCHIP_MAX_FB_BUFFER; i++) { 50 obj = rockchip_fb->obj[i]; 51 if (obj) 52 drm_gem_object_unreference_unlocked(obj); 53 } 54 55 drm_framebuffer_cleanup(fb); 56 kfree(rockchip_fb); 57} 58 59static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb, 60 struct drm_file *file_priv, 61 unsigned int *handle) 62{ 63 struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb); 64 65 return drm_gem_handle_create(file_priv, 66 rockchip_fb->obj[0], handle); 67} 68 69static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = { 70 .destroy = rockchip_drm_fb_destroy, 71 .create_handle = rockchip_drm_fb_create_handle, 72}; 73 74static struct rockchip_drm_fb * 75rockchip_fb_alloc(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cmd, 76 struct drm_gem_object **obj, unsigned int num_planes) 77{ 78 struct rockchip_drm_fb *rockchip_fb; 79 int ret; 80 int i; 81 82 rockchip_fb = kzalloc(sizeof(*rockchip_fb), GFP_KERNEL); 83 if (!rockchip_fb) 84 return ERR_PTR(-ENOMEM); 85 86 drm_helper_mode_fill_fb_struct(&rockchip_fb->fb, mode_cmd); 87 88 for (i = 0; i < num_planes; i++) 89 rockchip_fb->obj[i] = obj[i]; 90 91 ret = drm_framebuffer_init(dev, &rockchip_fb->fb, 92 &rockchip_drm_fb_funcs); 93 if (ret) { 94 dev_err(dev->dev, "Failed to initialize framebuffer: %d\n", 95 ret); 96 kfree(rockchip_fb); 97 return ERR_PTR(ret); 98 } 99 100 return rockchip_fb; 101} 102 103static struct drm_framebuffer * 104rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, 105 const struct drm_mode_fb_cmd2 *mode_cmd) 106{ 107 struct rockchip_drm_fb *rockchip_fb; 108 struct drm_gem_object *objs[ROCKCHIP_MAX_FB_BUFFER]; 109 struct drm_gem_object *obj; 110 unsigned int hsub; 111 unsigned int vsub; 112 int num_planes; 113 int ret; 114 int i; 115 116 hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format); 117 vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format); 118 num_planes = min(drm_format_num_planes(mode_cmd->pixel_format), 119 ROCKCHIP_MAX_FB_BUFFER); 120 121 for (i = 0; i < num_planes; i++) { 122 unsigned int width = mode_cmd->width / (i ? hsub : 1); 123 unsigned int height = mode_cmd->height / (i ? vsub : 1); 124 unsigned int min_size; 125 126 obj = drm_gem_object_lookup(dev, file_priv, 127 mode_cmd->handles[i]); 128 if (!obj) { 129 dev_err(dev->dev, "Failed to lookup GEM object\n"); 130 ret = -ENXIO; 131 goto err_gem_object_unreference; 132 } 133 134 min_size = (height - 1) * mode_cmd->pitches[i] + 135 mode_cmd->offsets[i] + 136 width * drm_format_plane_cpp(mode_cmd->pixel_format, i); 137 138 if (obj->size < min_size) { 139 drm_gem_object_unreference_unlocked(obj); 140 ret = -EINVAL; 141 goto err_gem_object_unreference; 142 } 143 objs[i] = obj; 144 } 145 146 rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, objs, i); 147 if (IS_ERR(rockchip_fb)) { 148 ret = PTR_ERR(rockchip_fb); 149 goto err_gem_object_unreference; 150 } 151 152 return &rockchip_fb->fb; 153 154err_gem_object_unreference: 155 for (i--; i >= 0; i--) 156 drm_gem_object_unreference_unlocked(objs[i]); 157 return ERR_PTR(ret); 158} 159 160static void rockchip_drm_output_poll_changed(struct drm_device *dev) 161{ 162 struct rockchip_drm_private *private = dev->dev_private; 163 struct drm_fb_helper *fb_helper = &private->fbdev_helper; 164 165 if (fb_helper) 166 drm_fb_helper_hotplug_event(fb_helper); 167} 168 169static void rockchip_crtc_wait_for_update(struct drm_crtc *crtc) 170{ 171 struct rockchip_drm_private *priv = crtc->dev->dev_private; 172 int pipe = drm_crtc_index(crtc); 173 const struct rockchip_crtc_funcs *crtc_funcs = priv->crtc_funcs[pipe]; 174 175 if (crtc_funcs && crtc_funcs->wait_for_update) 176 crtc_funcs->wait_for_update(crtc); 177} 178 179/* 180 * We can't use drm_atomic_helper_wait_for_vblanks() because rk3288 and rk3066 181 * have hardware counters for neither vblanks nor scanlines, which results in 182 * a race where: 183 * | <-- HW vsync irq and reg take effect 184 * plane_commit --> | 185 * get_vblank and wait --> | 186 * | <-- handle_vblank, vblank->count + 1 187 * cleanup_fb --> | 188 * iommu crash --> | 189 * | <-- HW vsync irq and reg take effect 190 * 191 * This function is equivalent but uses rockchip_crtc_wait_for_update() instead 192 * of waiting for vblank_count to change. 193 */ 194static void 195rockchip_atomic_wait_for_complete(struct drm_device *dev, struct drm_atomic_state *old_state) 196{ 197 struct drm_crtc_state *old_crtc_state; 198 struct drm_crtc *crtc; 199 int i, ret; 200 201 for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { 202 /* No one cares about the old state, so abuse it for tracking 203 * and store whether we hold a vblank reference (and should do a 204 * vblank wait) in the ->enable boolean. 205 */ 206 old_crtc_state->enable = false; 207 208 if (!crtc->state->active) 209 continue; 210 211 if (!drm_atomic_helper_framebuffer_changed(dev, 212 old_state, crtc)) 213 continue; 214 215 ret = drm_crtc_vblank_get(crtc); 216 if (ret != 0) 217 continue; 218 219 old_crtc_state->enable = true; 220 } 221 222 for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { 223 if (!old_crtc_state->enable) 224 continue; 225 226 rockchip_crtc_wait_for_update(crtc); 227 drm_crtc_vblank_put(crtc); 228 } 229} 230 231static void 232rockchip_atomic_commit_complete(struct rockchip_atomic_commit *commit) 233{ 234 struct drm_atomic_state *state = commit->state; 235 struct drm_device *dev = commit->dev; 236 237 /* 238 * TODO: do fence wait here. 239 */ 240 241 /* 242 * Rockchip crtc support runtime PM, can't update display planes 243 * when crtc is disabled. 244 * 245 * drm_atomic_helper_commit comments detail that: 246 * For drivers supporting runtime PM the recommended sequence is 247 * 248 * drm_atomic_helper_commit_modeset_disables(dev, state); 249 * 250 * drm_atomic_helper_commit_modeset_enables(dev, state); 251 * 252 * drm_atomic_helper_commit_planes(dev, state, true); 253 * 254 * See the kerneldoc entries for these three functions for more details. 255 */ 256 drm_atomic_helper_commit_modeset_disables(dev, state); 257 258 drm_atomic_helper_commit_modeset_enables(dev, state); 259 260 drm_atomic_helper_commit_planes(dev, state, true); 261 262 rockchip_atomic_wait_for_complete(dev, state); 263 264 drm_atomic_helper_cleanup_planes(dev, state); 265 266 drm_atomic_state_free(state); 267} 268 269void rockchip_drm_atomic_work(struct work_struct *work) 270{ 271 struct rockchip_atomic_commit *commit = container_of(work, 272 struct rockchip_atomic_commit, work); 273 274 rockchip_atomic_commit_complete(commit); 275} 276 277int rockchip_drm_atomic_commit(struct drm_device *dev, 278 struct drm_atomic_state *state, 279 bool async) 280{ 281 struct rockchip_drm_private *private = dev->dev_private; 282 struct rockchip_atomic_commit *commit = &private->commit; 283 int ret; 284 285 ret = drm_atomic_helper_prepare_planes(dev, state); 286 if (ret) 287 return ret; 288 289 /* serialize outstanding asynchronous commits */ 290 mutex_lock(&commit->lock); 291 flush_work(&commit->work); 292 293 drm_atomic_helper_swap_state(dev, state); 294 295 commit->dev = dev; 296 commit->state = state; 297 298 if (async) 299 schedule_work(&commit->work); 300 else 301 rockchip_atomic_commit_complete(commit); 302 303 mutex_unlock(&commit->lock); 304 305 return 0; 306} 307 308static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = { 309 .fb_create = rockchip_user_fb_create, 310 .output_poll_changed = rockchip_drm_output_poll_changed, 311 .atomic_check = drm_atomic_helper_check, 312 .atomic_commit = rockchip_drm_atomic_commit, 313}; 314 315struct drm_framebuffer * 316rockchip_drm_framebuffer_init(struct drm_device *dev, 317 const struct drm_mode_fb_cmd2 *mode_cmd, 318 struct drm_gem_object *obj) 319{ 320 struct rockchip_drm_fb *rockchip_fb; 321 322 rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, &obj, 1); 323 if (IS_ERR(rockchip_fb)) 324 return NULL; 325 326 return &rockchip_fb->fb; 327} 328 329void rockchip_drm_mode_config_init(struct drm_device *dev) 330{ 331 dev->mode_config.min_width = 0; 332 dev->mode_config.min_height = 0; 333 334 /* 335 * set max width and height as default value(4096x4096). 336 * this value would be used to check framebuffer size limitation 337 * at drm_mode_addfb(). 338 */ 339 dev->mode_config.max_width = 4096; 340 dev->mode_config.max_height = 4096; 341 342 dev->mode_config.funcs = &rockchip_drm_mode_config_funcs; 343}