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 EXPORT_SYMBOL_GPL(rockchip_fb_get_gem_obj); 43 44 static void rockchip_drm_fb_destroy(struct drm_framebuffer *fb) 45 { 46 struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb); 47 struct drm_gem_object *obj; 48 int i; 49 50 for (i = 0; i < ROCKCHIP_MAX_FB_BUFFER; i++) { 51 obj = rockchip_fb->obj[i]; 52 if (obj) 53 drm_gem_object_unreference_unlocked(obj); 54 } 55 56 drm_framebuffer_cleanup(fb); 57 kfree(rockchip_fb); 58 } 59 60 static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb, 61 struct drm_file *file_priv, 62 unsigned int *handle) 63 { 64 struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb); 65 66 return drm_gem_handle_create(file_priv, 67 rockchip_fb->obj[0], handle); 68 } 69 70 static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = { 71 .destroy = rockchip_drm_fb_destroy, 72 .create_handle = rockchip_drm_fb_create_handle, 73 }; 74 75 static struct rockchip_drm_fb * 76 rockchip_fb_alloc(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cmd, 77 struct drm_gem_object **obj, unsigned int num_planes) 78 { 79 struct rockchip_drm_fb *rockchip_fb; 80 int ret; 81 int i; 82 83 rockchip_fb = kzalloc(sizeof(*rockchip_fb), GFP_KERNEL); 84 if (!rockchip_fb) 85 return ERR_PTR(-ENOMEM); 86 87 drm_helper_mode_fill_fb_struct(&rockchip_fb->fb, mode_cmd); 88 89 for (i = 0; i < num_planes; i++) 90 rockchip_fb->obj[i] = obj[i]; 91 92 ret = drm_framebuffer_init(dev, &rockchip_fb->fb, 93 &rockchip_drm_fb_funcs); 94 if (ret) { 95 dev_err(dev->dev, "Failed to initialize framebuffer: %d\n", 96 ret); 97 kfree(rockchip_fb); 98 return ERR_PTR(ret); 99 } 100 101 return rockchip_fb; 102 } 103 104 static struct drm_framebuffer * 105 rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, 106 const struct drm_mode_fb_cmd2 *mode_cmd) 107 { 108 struct rockchip_drm_fb *rockchip_fb; 109 struct drm_gem_object *objs[ROCKCHIP_MAX_FB_BUFFER]; 110 struct drm_gem_object *obj; 111 unsigned int hsub; 112 unsigned int vsub; 113 int num_planes; 114 int ret; 115 int i; 116 117 hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format); 118 vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format); 119 num_planes = min(drm_format_num_planes(mode_cmd->pixel_format), 120 ROCKCHIP_MAX_FB_BUFFER); 121 122 for (i = 0; i < num_planes; i++) { 123 unsigned int width = mode_cmd->width / (i ? hsub : 1); 124 unsigned int height = mode_cmd->height / (i ? vsub : 1); 125 unsigned int min_size; 126 127 obj = drm_gem_object_lookup(dev, file_priv, 128 mode_cmd->handles[i]); 129 if (!obj) { 130 dev_err(dev->dev, "Failed to lookup GEM object\n"); 131 ret = -ENXIO; 132 goto err_gem_object_unreference; 133 } 134 135 min_size = (height - 1) * mode_cmd->pitches[i] + 136 mode_cmd->offsets[i] + 137 width * drm_format_plane_cpp(mode_cmd->pixel_format, i); 138 139 if (obj->size < min_size) { 140 drm_gem_object_unreference_unlocked(obj); 141 ret = -EINVAL; 142 goto err_gem_object_unreference; 143 } 144 objs[i] = obj; 145 } 146 147 rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, objs, i); 148 if (IS_ERR(rockchip_fb)) { 149 ret = PTR_ERR(rockchip_fb); 150 goto err_gem_object_unreference; 151 } 152 153 return &rockchip_fb->fb; 154 155 err_gem_object_unreference: 156 for (i--; i >= 0; i--) 157 drm_gem_object_unreference_unlocked(objs[i]); 158 return ERR_PTR(ret); 159 } 160 161 static void rockchip_drm_output_poll_changed(struct drm_device *dev) 162 { 163 struct rockchip_drm_private *private = dev->dev_private; 164 struct drm_fb_helper *fb_helper = &private->fbdev_helper; 165 166 if (fb_helper) 167 drm_fb_helper_hotplug_event(fb_helper); 168 } 169 170 static void rockchip_crtc_wait_for_update(struct drm_crtc *crtc) 171 { 172 struct rockchip_drm_private *priv = crtc->dev->dev_private; 173 int pipe = drm_crtc_index(crtc); 174 const struct rockchip_crtc_funcs *crtc_funcs = priv->crtc_funcs[pipe]; 175 176 if (crtc_funcs && crtc_funcs->wait_for_update) 177 crtc_funcs->wait_for_update(crtc); 178 } 179 180 static void 181 rockchip_atomic_wait_for_complete(struct drm_atomic_state *old_state) 182 { 183 struct drm_crtc_state *old_crtc_state; 184 struct drm_crtc *crtc; 185 int i, ret; 186 187 for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { 188 /* No one cares about the old state, so abuse it for tracking 189 * and store whether we hold a vblank reference (and should do a 190 * vblank wait) in the ->enable boolean. 191 */ 192 old_crtc_state->enable = false; 193 194 if (!crtc->state->active) 195 continue; 196 197 ret = drm_crtc_vblank_get(crtc); 198 if (ret != 0) 199 continue; 200 201 old_crtc_state->enable = true; 202 } 203 204 for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { 205 if (!old_crtc_state->enable) 206 continue; 207 208 rockchip_crtc_wait_for_update(crtc); 209 drm_crtc_vblank_put(crtc); 210 } 211 } 212 213 static void 214 rockchip_atomic_commit_complete(struct rockchip_atomic_commit *commit) 215 { 216 struct drm_atomic_state *state = commit->state; 217 struct drm_device *dev = commit->dev; 218 219 /* 220 * TODO: do fence wait here. 221 */ 222 223 /* 224 * Rockchip crtc support runtime PM, can't update display planes 225 * when crtc is disabled. 226 * 227 * drm_atomic_helper_commit comments detail that: 228 * For drivers supporting runtime PM the recommended sequence is 229 * 230 * drm_atomic_helper_commit_modeset_disables(dev, state); 231 * 232 * drm_atomic_helper_commit_modeset_enables(dev, state); 233 * 234 * drm_atomic_helper_commit_planes(dev, state, true); 235 * 236 * See the kerneldoc entries for these three functions for more details. 237 */ 238 drm_atomic_helper_commit_modeset_disables(dev, state); 239 240 drm_atomic_helper_commit_modeset_enables(dev, state); 241 242 drm_atomic_helper_commit_planes(dev, state, true); 243 244 rockchip_atomic_wait_for_complete(state); 245 246 drm_atomic_helper_cleanup_planes(dev, state); 247 248 drm_atomic_state_free(state); 249 } 250 251 void rockchip_drm_atomic_work(struct work_struct *work) 252 { 253 struct rockchip_atomic_commit *commit = container_of(work, 254 struct rockchip_atomic_commit, work); 255 256 rockchip_atomic_commit_complete(commit); 257 } 258 259 int rockchip_drm_atomic_commit(struct drm_device *dev, 260 struct drm_atomic_state *state, 261 bool async) 262 { 263 struct rockchip_drm_private *private = dev->dev_private; 264 struct rockchip_atomic_commit *commit = &private->commit; 265 int ret; 266 267 ret = drm_atomic_helper_prepare_planes(dev, state); 268 if (ret) 269 return ret; 270 271 /* serialize outstanding asynchronous commits */ 272 mutex_lock(&commit->lock); 273 flush_work(&commit->work); 274 275 drm_atomic_helper_swap_state(dev, state); 276 277 commit->dev = dev; 278 commit->state = state; 279 280 if (async) 281 schedule_work(&commit->work); 282 else 283 rockchip_atomic_commit_complete(commit); 284 285 mutex_unlock(&commit->lock); 286 287 return 0; 288 } 289 290 static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = { 291 .fb_create = rockchip_user_fb_create, 292 .output_poll_changed = rockchip_drm_output_poll_changed, 293 .atomic_check = drm_atomic_helper_check, 294 .atomic_commit = rockchip_drm_atomic_commit, 295 }; 296 297 struct drm_framebuffer * 298 rockchip_drm_framebuffer_init(struct drm_device *dev, 299 const struct drm_mode_fb_cmd2 *mode_cmd, 300 struct drm_gem_object *obj) 301 { 302 struct rockchip_drm_fb *rockchip_fb; 303 304 rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, &obj, 1); 305 if (IS_ERR(rockchip_fb)) 306 return NULL; 307 308 return &rockchip_fb->fb; 309 } 310 311 void rockchip_drm_mode_config_init(struct drm_device *dev) 312 { 313 dev->mode_config.min_width = 0; 314 dev->mode_config.min_height = 0; 315 316 /* 317 * set max width and height as default value(4096x4096). 318 * this value would be used to check framebuffer size limitation 319 * at drm_mode_addfb(). 320 */ 321 dev->mode_config.max_width = 4096; 322 dev->mode_config.max_height = 4096; 323 324 dev->mode_config.funcs = &rockchip_drm_mode_config_funcs; 325 } 326