at v4.13 361 lines 9.6 kB view raw
1/* 2 * Copyright (C) 2016 Noralf Trønnes 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 */ 9 10#include <drm/drm_atomic.h> 11#include <drm/drm_atomic_helper.h> 12#include <drm/drm_crtc_helper.h> 13#include <drm/tinydrm/tinydrm.h> 14#include <linux/device.h> 15#include <linux/dma-buf.h> 16 17/** 18 * DOC: overview 19 * 20 * This library provides driver helpers for very simple display hardware. 21 * 22 * It is based on &drm_simple_display_pipe coupled with a &drm_connector which 23 * has only one fixed &drm_display_mode. The framebuffers are backed by the 24 * cma helper and have support for framebuffer flushing (dirty). 25 * fbdev support is also included. 26 * 27 */ 28 29/** 30 * DOC: core 31 * 32 * The driver allocates &tinydrm_device, initializes it using 33 * devm_tinydrm_init(), sets up the pipeline using tinydrm_display_pipe_init() 34 * and registers the DRM device using devm_tinydrm_register(). 35 */ 36 37/** 38 * tinydrm_lastclose - DRM lastclose helper 39 * @drm: DRM device 40 * 41 * This function ensures that fbdev is restored when drm_lastclose() is called 42 * on the last drm_release(). Drivers can use this as their 43 * &drm_driver->lastclose callback. 44 */ 45void tinydrm_lastclose(struct drm_device *drm) 46{ 47 struct tinydrm_device *tdev = drm->dev_private; 48 49 DRM_DEBUG_KMS("\n"); 50 drm_fbdev_cma_restore_mode(tdev->fbdev_cma); 51} 52EXPORT_SYMBOL(tinydrm_lastclose); 53 54/** 55 * tinydrm_gem_cma_prime_import_sg_table - Produce a CMA GEM object from 56 * another driver's scatter/gather table of pinned pages 57 * @drm: DRM device to import into 58 * @attach: DMA-BUF attachment 59 * @sgt: Scatter/gather table of pinned pages 60 * 61 * This function imports a scatter/gather table exported via DMA-BUF by 62 * another driver using drm_gem_cma_prime_import_sg_table(). It sets the 63 * kernel virtual address on the CMA object. Drivers should use this as their 64 * &drm_driver->gem_prime_import_sg_table callback if they need the virtual 65 * address. tinydrm_gem_cma_free_object() should be used in combination with 66 * this function. 67 * 68 * Returns: 69 * A pointer to a newly created GEM object or an ERR_PTR-encoded negative 70 * error code on failure. 71 */ 72struct drm_gem_object * 73tinydrm_gem_cma_prime_import_sg_table(struct drm_device *drm, 74 struct dma_buf_attachment *attach, 75 struct sg_table *sgt) 76{ 77 struct drm_gem_cma_object *cma_obj; 78 struct drm_gem_object *obj; 79 void *vaddr; 80 81 vaddr = dma_buf_vmap(attach->dmabuf); 82 if (!vaddr) { 83 DRM_ERROR("Failed to vmap PRIME buffer\n"); 84 return ERR_PTR(-ENOMEM); 85 } 86 87 obj = drm_gem_cma_prime_import_sg_table(drm, attach, sgt); 88 if (IS_ERR(obj)) { 89 dma_buf_vunmap(attach->dmabuf, vaddr); 90 return obj; 91 } 92 93 cma_obj = to_drm_gem_cma_obj(obj); 94 cma_obj->vaddr = vaddr; 95 96 return obj; 97} 98EXPORT_SYMBOL(tinydrm_gem_cma_prime_import_sg_table); 99 100/** 101 * tinydrm_gem_cma_free_object - Free resources associated with a CMA GEM 102 * object 103 * @gem_obj: GEM object to free 104 * 105 * This function frees the backing memory of the CMA GEM object, cleans up the 106 * GEM object state and frees the memory used to store the object itself using 107 * drm_gem_cma_free_object(). It also handles PRIME buffers which has the kernel 108 * virtual address set by tinydrm_gem_cma_prime_import_sg_table(). Drivers 109 * can use this as their &drm_driver->gem_free_object callback. 110 */ 111void tinydrm_gem_cma_free_object(struct drm_gem_object *gem_obj) 112{ 113 if (gem_obj->import_attach) { 114 struct drm_gem_cma_object *cma_obj; 115 116 cma_obj = to_drm_gem_cma_obj(gem_obj); 117 dma_buf_vunmap(gem_obj->import_attach->dmabuf, cma_obj->vaddr); 118 cma_obj->vaddr = NULL; 119 } 120 121 drm_gem_cma_free_object(gem_obj); 122} 123EXPORT_SYMBOL_GPL(tinydrm_gem_cma_free_object); 124 125static struct drm_framebuffer * 126tinydrm_fb_create(struct drm_device *drm, struct drm_file *file_priv, 127 const struct drm_mode_fb_cmd2 *mode_cmd) 128{ 129 struct tinydrm_device *tdev = drm->dev_private; 130 131 return drm_fb_cma_create_with_funcs(drm, file_priv, mode_cmd, 132 tdev->fb_funcs); 133} 134 135static const struct drm_mode_config_funcs tinydrm_mode_config_funcs = { 136 .fb_create = tinydrm_fb_create, 137 .atomic_check = drm_atomic_helper_check, 138 .atomic_commit = drm_atomic_helper_commit, 139}; 140 141static int tinydrm_init(struct device *parent, struct tinydrm_device *tdev, 142 const struct drm_framebuffer_funcs *fb_funcs, 143 struct drm_driver *driver) 144{ 145 struct drm_device *drm; 146 147 mutex_init(&tdev->dirty_lock); 148 tdev->fb_funcs = fb_funcs; 149 150 /* 151 * We don't embed drm_device, because that prevent us from using 152 * devm_kzalloc() to allocate tinydrm_device in the driver since 153 * drm_dev_unref() frees the structure. The devm_ functions provide 154 * for easy error handling. 155 */ 156 drm = drm_dev_alloc(driver, parent); 157 if (IS_ERR(drm)) 158 return PTR_ERR(drm); 159 160 tdev->drm = drm; 161 drm->dev_private = tdev; 162 drm_mode_config_init(drm); 163 drm->mode_config.funcs = &tinydrm_mode_config_funcs; 164 165 return 0; 166} 167 168static void tinydrm_fini(struct tinydrm_device *tdev) 169{ 170 drm_mode_config_cleanup(tdev->drm); 171 mutex_destroy(&tdev->dirty_lock); 172 tdev->drm->dev_private = NULL; 173 drm_dev_unref(tdev->drm); 174} 175 176static void devm_tinydrm_release(void *data) 177{ 178 tinydrm_fini(data); 179} 180 181/** 182 * devm_tinydrm_init - Initialize tinydrm device 183 * @parent: Parent device object 184 * @tdev: tinydrm device 185 * @fb_funcs: Framebuffer functions 186 * @driver: DRM driver 187 * 188 * This function initializes @tdev, the underlying DRM device and it's 189 * mode_config. Resources will be automatically freed on driver detach (devres) 190 * using drm_mode_config_cleanup() and drm_dev_unref(). 191 * 192 * Returns: 193 * Zero on success, negative error code on failure. 194 */ 195int devm_tinydrm_init(struct device *parent, struct tinydrm_device *tdev, 196 const struct drm_framebuffer_funcs *fb_funcs, 197 struct drm_driver *driver) 198{ 199 int ret; 200 201 ret = tinydrm_init(parent, tdev, fb_funcs, driver); 202 if (ret) 203 return ret; 204 205 ret = devm_add_action(parent, devm_tinydrm_release, tdev); 206 if (ret) 207 tinydrm_fini(tdev); 208 209 return ret; 210} 211EXPORT_SYMBOL(devm_tinydrm_init); 212 213static int tinydrm_register(struct tinydrm_device *tdev) 214{ 215 struct drm_device *drm = tdev->drm; 216 int bpp = drm->mode_config.preferred_depth; 217 struct drm_fbdev_cma *fbdev; 218 int ret; 219 220 ret = drm_dev_register(tdev->drm, 0); 221 if (ret) 222 return ret; 223 224 fbdev = drm_fbdev_cma_init_with_funcs(drm, bpp ? bpp : 32, 225 drm->mode_config.num_connector, 226 tdev->fb_funcs); 227 if (IS_ERR(fbdev)) 228 DRM_ERROR("Failed to initialize fbdev: %ld\n", PTR_ERR(fbdev)); 229 else 230 tdev->fbdev_cma = fbdev; 231 232 return 0; 233} 234 235static void tinydrm_unregister(struct tinydrm_device *tdev) 236{ 237 struct drm_fbdev_cma *fbdev_cma = tdev->fbdev_cma; 238 239 drm_atomic_helper_shutdown(tdev->drm); 240 /* don't restore fbdev in lastclose, keep pipeline disabled */ 241 tdev->fbdev_cma = NULL; 242 drm_dev_unregister(tdev->drm); 243 if (fbdev_cma) 244 drm_fbdev_cma_fini(fbdev_cma); 245} 246 247static void devm_tinydrm_register_release(void *data) 248{ 249 tinydrm_unregister(data); 250} 251 252/** 253 * devm_tinydrm_register - Register tinydrm device 254 * @tdev: tinydrm device 255 * 256 * This function registers the underlying DRM device and fbdev. 257 * These resources will be automatically unregistered on driver detach (devres) 258 * and the display pipeline will be disabled. 259 * 260 * Returns: 261 * Zero on success, negative error code on failure. 262 */ 263int devm_tinydrm_register(struct tinydrm_device *tdev) 264{ 265 struct device *dev = tdev->drm->dev; 266 int ret; 267 268 ret = tinydrm_register(tdev); 269 if (ret) 270 return ret; 271 272 ret = devm_add_action(dev, devm_tinydrm_register_release, tdev); 273 if (ret) 274 tinydrm_unregister(tdev); 275 276 return ret; 277} 278EXPORT_SYMBOL(devm_tinydrm_register); 279 280/** 281 * tinydrm_shutdown - Shutdown tinydrm 282 * @tdev: tinydrm device 283 * 284 * This function makes sure that the display pipeline is disabled. 285 * Used by drivers in their shutdown callback to turn off the display 286 * on machine shutdown and reboot. 287 */ 288void tinydrm_shutdown(struct tinydrm_device *tdev) 289{ 290 drm_atomic_helper_shutdown(tdev->drm); 291} 292EXPORT_SYMBOL(tinydrm_shutdown); 293 294/** 295 * tinydrm_suspend - Suspend tinydrm 296 * @tdev: tinydrm device 297 * 298 * Used in driver PM operations to suspend tinydrm. 299 * Suspends fbdev and DRM. 300 * Resume with tinydrm_resume(). 301 * 302 * Returns: 303 * Zero on success, negative error code on failure. 304 */ 305int tinydrm_suspend(struct tinydrm_device *tdev) 306{ 307 struct drm_atomic_state *state; 308 309 if (tdev->suspend_state) { 310 DRM_ERROR("Failed to suspend: state already set\n"); 311 return -EINVAL; 312 } 313 314 drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 1); 315 state = drm_atomic_helper_suspend(tdev->drm); 316 if (IS_ERR(state)) { 317 drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 0); 318 return PTR_ERR(state); 319 } 320 321 tdev->suspend_state = state; 322 323 return 0; 324} 325EXPORT_SYMBOL(tinydrm_suspend); 326 327/** 328 * tinydrm_resume - Resume tinydrm 329 * @tdev: tinydrm device 330 * 331 * Used in driver PM operations to resume tinydrm. 332 * Suspend with tinydrm_suspend(). 333 * 334 * Returns: 335 * Zero on success, negative error code on failure. 336 */ 337int tinydrm_resume(struct tinydrm_device *tdev) 338{ 339 struct drm_atomic_state *state = tdev->suspend_state; 340 int ret; 341 342 if (!state) { 343 DRM_ERROR("Failed to resume: state is not set\n"); 344 return -EINVAL; 345 } 346 347 tdev->suspend_state = NULL; 348 349 ret = drm_atomic_helper_resume(tdev->drm, state); 350 if (ret) { 351 DRM_ERROR("Error resuming state: %d\n", ret); 352 return ret; 353 } 354 355 drm_fbdev_cma_set_suspend_unlocked(tdev->fbdev_cma, 0); 356 357 return 0; 358} 359EXPORT_SYMBOL(tinydrm_resume); 360 361MODULE_LICENSE("GPL");