linux/drivers/gpu/drm/tinydrm/core/tinydrm-core.c
<<
>>
Prefs
   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");
 362