linux/drivers/gpu/drm/drm_simple_kms_helper.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright (C) 2016 Noralf Trønnes
   4 */
   5
   6#include <linux/module.h>
   7#include <linux/slab.h>
   8
   9#include <drm/drm_atomic.h>
  10#include <drm/drm_atomic_helper.h>
  11#include <drm/drm_bridge.h>
  12#include <drm/drm_drv.h>
  13#include <drm/drm_gem_atomic_helper.h>
  14#include <drm/drm_managed.h>
  15#include <drm/drm_plane_helper.h>
  16#include <drm/drm_probe_helper.h>
  17#include <drm/drm_simple_kms_helper.h>
  18
  19/**
  20 * DOC: overview
  21 *
  22 * This helper library provides helpers for drivers for simple display
  23 * hardware.
  24 *
  25 * drm_simple_display_pipe_init() initializes a simple display pipeline
  26 * which has only one full-screen scanout buffer feeding one output. The
  27 * pipeline is represented by &struct drm_simple_display_pipe and binds
  28 * together &drm_plane, &drm_crtc and &drm_encoder structures into one fixed
  29 * entity. Some flexibility for code reuse is provided through a separately
  30 * allocated &drm_connector object and supporting optional &drm_bridge
  31 * encoder drivers.
  32 *
  33 * Many drivers require only a very simple encoder that fulfills the minimum
  34 * requirements of the display pipeline and does not add additional
  35 * functionality. The function drm_simple_encoder_init() provides an
  36 * implementation of such an encoder.
  37 */
  38
  39static const struct drm_encoder_funcs drm_simple_encoder_funcs_cleanup = {
  40        .destroy = drm_encoder_cleanup,
  41};
  42
  43/**
  44 * drm_simple_encoder_init - Initialize a preallocated encoder with
  45 *                           basic functionality.
  46 * @dev: drm device
  47 * @encoder: the encoder to initialize
  48 * @encoder_type: user visible type of the encoder
  49 *
  50 * Initialises a preallocated encoder that has no further functionality.
  51 * Settings for possible CRTC and clones are left to their initial values.
  52 * The encoder will be cleaned up automatically as part of the mode-setting
  53 * cleanup.
  54 *
  55 * The caller of drm_simple_encoder_init() is responsible for freeing
  56 * the encoder's memory after the encoder has been cleaned up. At the
  57 * moment this only works reliably if the encoder data structure is
  58 * stored in the device structure. Free the encoder's memory as part of
  59 * the device release function.
  60 *
  61 * Note: consider using drmm_simple_encoder_alloc() instead of
  62 * drm_simple_encoder_init() to let the DRM managed resource infrastructure
  63 * take care of cleanup and deallocation.
  64 *
  65 * Returns:
  66 * Zero on success, error code on failure.
  67 */
  68int drm_simple_encoder_init(struct drm_device *dev,
  69                            struct drm_encoder *encoder,
  70                            int encoder_type)
  71{
  72        return drm_encoder_init(dev, encoder,
  73                                &drm_simple_encoder_funcs_cleanup,
  74                                encoder_type, NULL);
  75}
  76EXPORT_SYMBOL(drm_simple_encoder_init);
  77
  78void *__drmm_simple_encoder_alloc(struct drm_device *dev, size_t size,
  79                                  size_t offset, int encoder_type)
  80{
  81        return __drmm_encoder_alloc(dev, size, offset, NULL, encoder_type,
  82                                    NULL);
  83}
  84EXPORT_SYMBOL(__drmm_simple_encoder_alloc);
  85
  86static enum drm_mode_status
  87drm_simple_kms_crtc_mode_valid(struct drm_crtc *crtc,
  88                               const struct drm_display_mode *mode)
  89{
  90        struct drm_simple_display_pipe *pipe;
  91
  92        pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
  93        if (!pipe->funcs || !pipe->funcs->mode_valid)
  94                /* Anything goes */
  95                return MODE_OK;
  96
  97        return pipe->funcs->mode_valid(pipe, mode);
  98}
  99
 100static int drm_simple_kms_crtc_check(struct drm_crtc *crtc,
 101                                     struct drm_atomic_state *state)
 102{
 103        struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
 104                                                                          crtc);
 105        bool has_primary = crtc_state->plane_mask &
 106                           drm_plane_mask(crtc->primary);
 107
 108        /* We always want to have an active plane with an active CRTC */
 109        if (has_primary != crtc_state->enable)
 110                return -EINVAL;
 111
 112        return drm_atomic_add_affected_planes(state, crtc);
 113}
 114
 115static void drm_simple_kms_crtc_enable(struct drm_crtc *crtc,
 116                                       struct drm_atomic_state *state)
 117{
 118        struct drm_plane *plane;
 119        struct drm_simple_display_pipe *pipe;
 120
 121        pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
 122        if (!pipe->funcs || !pipe->funcs->enable)
 123                return;
 124
 125        plane = &pipe->plane;
 126        pipe->funcs->enable(pipe, crtc->state, plane->state);
 127}
 128
 129static void drm_simple_kms_crtc_disable(struct drm_crtc *crtc,
 130                                        struct drm_atomic_state *state)
 131{
 132        struct drm_simple_display_pipe *pipe;
 133
 134        pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
 135        if (!pipe->funcs || !pipe->funcs->disable)
 136                return;
 137
 138        pipe->funcs->disable(pipe);
 139}
 140
 141static const struct drm_crtc_helper_funcs drm_simple_kms_crtc_helper_funcs = {
 142        .mode_valid = drm_simple_kms_crtc_mode_valid,
 143        .atomic_check = drm_simple_kms_crtc_check,
 144        .atomic_enable = drm_simple_kms_crtc_enable,
 145        .atomic_disable = drm_simple_kms_crtc_disable,
 146};
 147
 148static void drm_simple_kms_crtc_reset(struct drm_crtc *crtc)
 149{
 150        struct drm_simple_display_pipe *pipe;
 151
 152        pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
 153        if (!pipe->funcs || !pipe->funcs->reset_crtc)
 154                return drm_atomic_helper_crtc_reset(crtc);
 155
 156        return pipe->funcs->reset_crtc(pipe);
 157}
 158
 159static struct drm_crtc_state *drm_simple_kms_crtc_duplicate_state(struct drm_crtc *crtc)
 160{
 161        struct drm_simple_display_pipe *pipe;
 162
 163        pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
 164        if (!pipe->funcs || !pipe->funcs->duplicate_crtc_state)
 165                return drm_atomic_helper_crtc_duplicate_state(crtc);
 166
 167        return pipe->funcs->duplicate_crtc_state(pipe);
 168}
 169
 170static void drm_simple_kms_crtc_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *state)
 171{
 172        struct drm_simple_display_pipe *pipe;
 173
 174        pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
 175        if (!pipe->funcs || !pipe->funcs->destroy_crtc_state)
 176                drm_atomic_helper_crtc_destroy_state(crtc, state);
 177        else
 178                pipe->funcs->destroy_crtc_state(pipe, state);
 179}
 180
 181static int drm_simple_kms_crtc_enable_vblank(struct drm_crtc *crtc)
 182{
 183        struct drm_simple_display_pipe *pipe;
 184
 185        pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
 186        if (!pipe->funcs || !pipe->funcs->enable_vblank)
 187                return 0;
 188
 189        return pipe->funcs->enable_vblank(pipe);
 190}
 191
 192static void drm_simple_kms_crtc_disable_vblank(struct drm_crtc *crtc)
 193{
 194        struct drm_simple_display_pipe *pipe;
 195
 196        pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
 197        if (!pipe->funcs || !pipe->funcs->disable_vblank)
 198                return;
 199
 200        pipe->funcs->disable_vblank(pipe);
 201}
 202
 203static const struct drm_crtc_funcs drm_simple_kms_crtc_funcs = {
 204        .reset = drm_simple_kms_crtc_reset,
 205        .destroy = drm_crtc_cleanup,
 206        .set_config = drm_atomic_helper_set_config,
 207        .page_flip = drm_atomic_helper_page_flip,
 208        .atomic_duplicate_state = drm_simple_kms_crtc_duplicate_state,
 209        .atomic_destroy_state = drm_simple_kms_crtc_destroy_state,
 210        .enable_vblank = drm_simple_kms_crtc_enable_vblank,
 211        .disable_vblank = drm_simple_kms_crtc_disable_vblank,
 212};
 213
 214static int drm_simple_kms_plane_atomic_check(struct drm_plane *plane,
 215                                        struct drm_atomic_state *state)
 216{
 217        struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state,
 218                                                                             plane);
 219        struct drm_simple_display_pipe *pipe;
 220        struct drm_crtc_state *crtc_state;
 221        int ret;
 222
 223        pipe = container_of(plane, struct drm_simple_display_pipe, plane);
 224        crtc_state = drm_atomic_get_new_crtc_state(state,
 225                                                   &pipe->crtc);
 226
 227        ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state,
 228                                                  DRM_PLANE_HELPER_NO_SCALING,
 229                                                  DRM_PLANE_HELPER_NO_SCALING,
 230                                                  false, true);
 231        if (ret)
 232                return ret;
 233
 234        if (!plane_state->visible)
 235                return 0;
 236
 237        if (!pipe->funcs || !pipe->funcs->check)
 238                return 0;
 239
 240        return pipe->funcs->check(pipe, plane_state, crtc_state);
 241}
 242
 243static void drm_simple_kms_plane_atomic_update(struct drm_plane *plane,
 244                                        struct drm_atomic_state *state)
 245{
 246        struct drm_plane_state *old_pstate = drm_atomic_get_old_plane_state(state,
 247                                                                            plane);
 248        struct drm_simple_display_pipe *pipe;
 249
 250        pipe = container_of(plane, struct drm_simple_display_pipe, plane);
 251        if (!pipe->funcs || !pipe->funcs->update)
 252                return;
 253
 254        pipe->funcs->update(pipe, old_pstate);
 255}
 256
 257static int drm_simple_kms_plane_prepare_fb(struct drm_plane *plane,
 258                                           struct drm_plane_state *state)
 259{
 260        struct drm_simple_display_pipe *pipe;
 261
 262        pipe = container_of(plane, struct drm_simple_display_pipe, plane);
 263        if (!pipe->funcs || !pipe->funcs->prepare_fb) {
 264                if (WARN_ON_ONCE(!drm_core_check_feature(plane->dev, DRIVER_GEM)))
 265                        return 0;
 266
 267                WARN_ON_ONCE(pipe->funcs && pipe->funcs->cleanup_fb);
 268
 269                return drm_gem_simple_display_pipe_prepare_fb(pipe, state);
 270        }
 271
 272        return pipe->funcs->prepare_fb(pipe, state);
 273}
 274
 275static void drm_simple_kms_plane_cleanup_fb(struct drm_plane *plane,
 276                                            struct drm_plane_state *state)
 277{
 278        struct drm_simple_display_pipe *pipe;
 279
 280        pipe = container_of(plane, struct drm_simple_display_pipe, plane);
 281        if (!pipe->funcs || !pipe->funcs->cleanup_fb)
 282                return;
 283
 284        pipe->funcs->cleanup_fb(pipe, state);
 285}
 286
 287static bool drm_simple_kms_format_mod_supported(struct drm_plane *plane,
 288                                                uint32_t format,
 289                                                uint64_t modifier)
 290{
 291        return modifier == DRM_FORMAT_MOD_LINEAR;
 292}
 293
 294static const struct drm_plane_helper_funcs drm_simple_kms_plane_helper_funcs = {
 295        .prepare_fb = drm_simple_kms_plane_prepare_fb,
 296        .cleanup_fb = drm_simple_kms_plane_cleanup_fb,
 297        .atomic_check = drm_simple_kms_plane_atomic_check,
 298        .atomic_update = drm_simple_kms_plane_atomic_update,
 299};
 300
 301static void drm_simple_kms_plane_reset(struct drm_plane *plane)
 302{
 303        struct drm_simple_display_pipe *pipe;
 304
 305        pipe = container_of(plane, struct drm_simple_display_pipe, plane);
 306        if (!pipe->funcs || !pipe->funcs->reset_plane)
 307                return drm_atomic_helper_plane_reset(plane);
 308
 309        return pipe->funcs->reset_plane(pipe);
 310}
 311
 312static struct drm_plane_state *drm_simple_kms_plane_duplicate_state(struct drm_plane *plane)
 313{
 314        struct drm_simple_display_pipe *pipe;
 315
 316        pipe = container_of(plane, struct drm_simple_display_pipe, plane);
 317        if (!pipe->funcs || !pipe->funcs->duplicate_plane_state)
 318                return drm_atomic_helper_plane_duplicate_state(plane);
 319
 320        return pipe->funcs->duplicate_plane_state(pipe);
 321}
 322
 323static void drm_simple_kms_plane_destroy_state(struct drm_plane *plane,
 324                                               struct drm_plane_state *state)
 325{
 326        struct drm_simple_display_pipe *pipe;
 327
 328        pipe = container_of(plane, struct drm_simple_display_pipe, plane);
 329        if (!pipe->funcs || !pipe->funcs->destroy_plane_state)
 330                drm_atomic_helper_plane_destroy_state(plane, state);
 331        else
 332                pipe->funcs->destroy_plane_state(pipe, state);
 333}
 334
 335static const struct drm_plane_funcs drm_simple_kms_plane_funcs = {
 336        .update_plane           = drm_atomic_helper_update_plane,
 337        .disable_plane          = drm_atomic_helper_disable_plane,
 338        .destroy                = drm_plane_cleanup,
 339        .reset                  = drm_simple_kms_plane_reset,
 340        .atomic_duplicate_state = drm_simple_kms_plane_duplicate_state,
 341        .atomic_destroy_state   = drm_simple_kms_plane_destroy_state,
 342        .format_mod_supported   = drm_simple_kms_format_mod_supported,
 343};
 344
 345/**
 346 * drm_simple_display_pipe_attach_bridge - Attach a bridge to the display pipe
 347 * @pipe: simple display pipe object
 348 * @bridge: bridge to attach
 349 *
 350 * Makes it possible to still use the drm_simple_display_pipe helpers when
 351 * a DRM bridge has to be used.
 352 *
 353 * Note that you probably want to initialize the pipe by passing a NULL
 354 * connector to drm_simple_display_pipe_init().
 355 *
 356 * Returns:
 357 * Zero on success, negative error code on failure.
 358 */
 359int drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe *pipe,
 360                                          struct drm_bridge *bridge)
 361{
 362        return drm_bridge_attach(&pipe->encoder, bridge, NULL, 0);
 363}
 364EXPORT_SYMBOL(drm_simple_display_pipe_attach_bridge);
 365
 366/**
 367 * drm_simple_display_pipe_init - Initialize a simple display pipeline
 368 * @dev: DRM device
 369 * @pipe: simple display pipe object to initialize
 370 * @funcs: callbacks for the display pipe (optional)
 371 * @formats: array of supported formats (DRM_FORMAT\_\*)
 372 * @format_count: number of elements in @formats
 373 * @format_modifiers: array of formats modifiers
 374 * @connector: connector to attach and register (optional)
 375 *
 376 * Sets up a display pipeline which consist of a really simple
 377 * plane-crtc-encoder pipe.
 378 *
 379 * If a connector is supplied, the pipe will be coupled with the provided
 380 * connector. You may supply a NULL connector when using drm bridges, that
 381 * handle connectors themselves (see drm_simple_display_pipe_attach_bridge()).
 382 *
 383 * Teardown of a simple display pipe is all handled automatically by the drm
 384 * core through calling drm_mode_config_cleanup(). Drivers afterwards need to
 385 * release the memory for the structure themselves.
 386 *
 387 * Returns:
 388 * Zero on success, negative error code on failure.
 389 */
 390int drm_simple_display_pipe_init(struct drm_device *dev,
 391                        struct drm_simple_display_pipe *pipe,
 392                        const struct drm_simple_display_pipe_funcs *funcs,
 393                        const uint32_t *formats, unsigned int format_count,
 394                        const uint64_t *format_modifiers,
 395                        struct drm_connector *connector)
 396{
 397        struct drm_encoder *encoder = &pipe->encoder;
 398        struct drm_plane *plane = &pipe->plane;
 399        struct drm_crtc *crtc = &pipe->crtc;
 400        int ret;
 401
 402        pipe->connector = connector;
 403        pipe->funcs = funcs;
 404
 405        drm_plane_helper_add(plane, &drm_simple_kms_plane_helper_funcs);
 406        ret = drm_universal_plane_init(dev, plane, 0,
 407                                       &drm_simple_kms_plane_funcs,
 408                                       formats, format_count,
 409                                       format_modifiers,
 410                                       DRM_PLANE_TYPE_PRIMARY, NULL);
 411        if (ret)
 412                return ret;
 413
 414        drm_crtc_helper_add(crtc, &drm_simple_kms_crtc_helper_funcs);
 415        ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
 416                                        &drm_simple_kms_crtc_funcs, NULL);
 417        if (ret)
 418                return ret;
 419
 420        encoder->possible_crtcs = drm_crtc_mask(crtc);
 421        ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_NONE);
 422        if (ret || !connector)
 423                return ret;
 424
 425        return drm_connector_attach_encoder(connector, encoder);
 426}
 427EXPORT_SYMBOL(drm_simple_display_pipe_init);
 428
 429MODULE_LICENSE("GPL");
 430