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