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 27 struct rockchip_drm_fb { 28 struct drm_framebuffer fb; 29 struct drm_gem_object *obj[ROCKCHIP_MAX_FB_BUFFER]; 30 }; 31 32 struct 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 43 static 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 59 static 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 69 static 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 74 static struct rockchip_drm_fb * 75 rockchip_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 103 static struct drm_framebuffer * 104 rockchip_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 154 err_gem_object_unreference: 155 for (i--; i >= 0; i--) 156 drm_gem_object_unreference_unlocked(objs[i]); 157 return ERR_PTR(ret); 158 } 159 160 static 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 169 static 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 */ 194 static void 195 rockchip_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 231 static void 232 rockchip_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 269 void 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 277 int 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 308 static 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 315 struct drm_framebuffer * 316 rockchip_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 329 void 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 } 344