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_fb.h" 24 #include "rockchip_drm_gem.h" 25 26 #define to_rockchip_fb(x) container_of(x, struct rockchip_drm_fb, fb) 27 28 struct rockchip_drm_fb { 29 struct drm_framebuffer fb; 30 struct drm_gem_object *obj[ROCKCHIP_MAX_FB_BUFFER]; 31 }; 32 33 struct drm_gem_object *rockchip_fb_get_gem_obj(struct drm_framebuffer *fb, 34 unsigned int plane) 35 { 36 struct rockchip_drm_fb *rk_fb = to_rockchip_fb(fb); 37 38 if (plane >= ROCKCHIP_MAX_FB_BUFFER) 39 return NULL; 40 41 return rk_fb->obj[plane]; 42 } 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 int i; 48 49 for (i = 0; i < ROCKCHIP_MAX_FB_BUFFER; i++) 50 drm_gem_object_unreference_unlocked(rockchip_fb->obj[i]); 51 52 drm_framebuffer_cleanup(fb); 53 kfree(rockchip_fb); 54 } 55 56 static int rockchip_drm_fb_create_handle(struct drm_framebuffer *fb, 57 struct drm_file *file_priv, 58 unsigned int *handle) 59 { 60 struct rockchip_drm_fb *rockchip_fb = to_rockchip_fb(fb); 61 62 return drm_gem_handle_create(file_priv, 63 rockchip_fb->obj[0], handle); 64 } 65 66 static const struct drm_framebuffer_funcs rockchip_drm_fb_funcs = { 67 .destroy = rockchip_drm_fb_destroy, 68 .create_handle = rockchip_drm_fb_create_handle, 69 }; 70 71 static struct rockchip_drm_fb * 72 rockchip_fb_alloc(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cmd, 73 struct drm_gem_object **obj, unsigned int num_planes) 74 { 75 struct rockchip_drm_fb *rockchip_fb; 76 int ret; 77 int i; 78 79 rockchip_fb = kzalloc(sizeof(*rockchip_fb), GFP_KERNEL); 80 if (!rockchip_fb) 81 return ERR_PTR(-ENOMEM); 82 83 drm_helper_mode_fill_fb_struct(&rockchip_fb->fb, mode_cmd); 84 85 for (i = 0; i < num_planes; i++) 86 rockchip_fb->obj[i] = obj[i]; 87 88 ret = drm_framebuffer_init(dev, &rockchip_fb->fb, 89 &rockchip_drm_fb_funcs); 90 if (ret) { 91 dev_err(dev->dev, "Failed to initialize framebuffer: %d\n", 92 ret); 93 kfree(rockchip_fb); 94 return ERR_PTR(ret); 95 } 96 97 return rockchip_fb; 98 } 99 100 static struct drm_framebuffer * 101 rockchip_user_fb_create(struct drm_device *dev, struct drm_file *file_priv, 102 const struct drm_mode_fb_cmd2 *mode_cmd) 103 { 104 struct rockchip_drm_fb *rockchip_fb; 105 struct drm_gem_object *objs[ROCKCHIP_MAX_FB_BUFFER]; 106 struct drm_gem_object *obj; 107 unsigned int hsub; 108 unsigned int vsub; 109 int num_planes; 110 int ret; 111 int i; 112 113 hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format); 114 vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format); 115 num_planes = min(drm_format_num_planes(mode_cmd->pixel_format), 116 ROCKCHIP_MAX_FB_BUFFER); 117 118 for (i = 0; i < num_planes; i++) { 119 unsigned int width = mode_cmd->width / (i ? hsub : 1); 120 unsigned int height = mode_cmd->height / (i ? vsub : 1); 121 unsigned int min_size; 122 123 obj = drm_gem_object_lookup(file_priv, mode_cmd->handles[i]); 124 if (!obj) { 125 dev_err(dev->dev, "Failed to lookup GEM object\n"); 126 ret = -ENXIO; 127 goto err_gem_object_unreference; 128 } 129 130 min_size = (height - 1) * mode_cmd->pitches[i] + 131 mode_cmd->offsets[i] + 132 width * drm_format_plane_cpp(mode_cmd->pixel_format, i); 133 134 if (obj->size < min_size) { 135 drm_gem_object_unreference_unlocked(obj); 136 ret = -EINVAL; 137 goto err_gem_object_unreference; 138 } 139 objs[i] = obj; 140 } 141 142 rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, objs, i); 143 if (IS_ERR(rockchip_fb)) { 144 ret = PTR_ERR(rockchip_fb); 145 goto err_gem_object_unreference; 146 } 147 148 return &rockchip_fb->fb; 149 150 err_gem_object_unreference: 151 for (i--; i >= 0; i--) 152 drm_gem_object_unreference_unlocked(objs[i]); 153 return ERR_PTR(ret); 154 } 155 156 static void rockchip_drm_output_poll_changed(struct drm_device *dev) 157 { 158 struct rockchip_drm_private *private = dev->dev_private; 159 struct drm_fb_helper *fb_helper = &private->fbdev_helper; 160 161 if (fb_helper) 162 drm_fb_helper_hotplug_event(fb_helper); 163 } 164 165 static void rockchip_crtc_wait_for_update(struct drm_crtc *crtc) 166 { 167 struct rockchip_drm_private *priv = crtc->dev->dev_private; 168 int pipe = drm_crtc_index(crtc); 169 const struct rockchip_crtc_funcs *crtc_funcs = priv->crtc_funcs[pipe]; 170 171 if (crtc_funcs && crtc_funcs->wait_for_update) 172 crtc_funcs->wait_for_update(crtc); 173 } 174 175 /* 176 * We can't use drm_atomic_helper_wait_for_vblanks() because rk3288 and rk3066 177 * have hardware counters for neither vblanks nor scanlines, which results in 178 * a race where: 179 * | <-- HW vsync irq and reg take effect 180 * plane_commit --> | 181 * get_vblank and wait --> | 182 * | <-- handle_vblank, vblank->count + 1 183 * cleanup_fb --> | 184 * iommu crash --> | 185 * | <-- HW vsync irq and reg take effect 186 * 187 * This function is equivalent but uses rockchip_crtc_wait_for_update() instead 188 * of waiting for vblank_count to change. 189 */ 190 static void 191 rockchip_atomic_wait_for_complete(struct drm_device *dev, struct drm_atomic_state *old_state) 192 { 193 struct drm_crtc_state *old_crtc_state; 194 struct drm_crtc *crtc; 195 int i, ret; 196 197 for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { 198 /* No one cares about the old state, so abuse it for tracking 199 * and store whether we hold a vblank reference (and should do a 200 * vblank wait) in the ->enable boolean. 201 */ 202 old_crtc_state->enable = false; 203 204 if (!crtc->state->active) 205 continue; 206 207 if (!drm_atomic_helper_framebuffer_changed(dev, 208 old_state, crtc)) 209 continue; 210 211 ret = drm_crtc_vblank_get(crtc); 212 if (ret != 0) 213 continue; 214 215 old_crtc_state->enable = true; 216 } 217 218 for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) { 219 if (!old_crtc_state->enable) 220 continue; 221 222 rockchip_crtc_wait_for_update(crtc); 223 drm_crtc_vblank_put(crtc); 224 } 225 } 226 227 static void 228 rockchip_atomic_commit_tail(struct drm_atomic_state *state) 229 { 230 struct drm_device *dev = state->dev; 231 232 drm_atomic_helper_commit_modeset_disables(dev, state); 233 234 drm_atomic_helper_commit_modeset_enables(dev, state); 235 236 drm_atomic_helper_commit_planes(dev, state, true); 237 238 drm_atomic_helper_commit_hw_done(state); 239 240 rockchip_atomic_wait_for_complete(dev, state); 241 242 drm_atomic_helper_cleanup_planes(dev, state); 243 } 244 245 static struct drm_mode_config_helper_funcs rockchip_mode_config_helpers = { 246 .atomic_commit_tail = rockchip_atomic_commit_tail, 247 }; 248 249 static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = { 250 .fb_create = rockchip_user_fb_create, 251 .output_poll_changed = rockchip_drm_output_poll_changed, 252 .atomic_check = drm_atomic_helper_check, 253 .atomic_commit = drm_atomic_helper_commit, 254 }; 255 256 struct drm_framebuffer * 257 rockchip_drm_framebuffer_init(struct drm_device *dev, 258 const struct drm_mode_fb_cmd2 *mode_cmd, 259 struct drm_gem_object *obj) 260 { 261 struct rockchip_drm_fb *rockchip_fb; 262 263 rockchip_fb = rockchip_fb_alloc(dev, mode_cmd, &obj, 1); 264 if (IS_ERR(rockchip_fb)) 265 return NULL; 266 267 return &rockchip_fb->fb; 268 } 269 270 void rockchip_drm_mode_config_init(struct drm_device *dev) 271 { 272 dev->mode_config.min_width = 0; 273 dev->mode_config.min_height = 0; 274 275 /* 276 * set max width and height as default value(4096x4096). 277 * this value would be used to check framebuffer size limitation 278 * at drm_mode_addfb(). 279 */ 280 dev->mode_config.max_width = 4096; 281 dev->mode_config.max_height = 4096; 282 283 dev->mode_config.funcs = &rockchip_drm_mode_config_funcs; 284 dev->mode_config.helper_private = &rockchip_mode_config_helpers; 285 } 286