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_plane_helper.h>
  13#include <drm/drm_probe_helper.h>
  14#include <drm/drm_simple_kms_helper.h>
  15
  16/**
  17 * DOC: overview
  18 *
  19 * This helper library provides helpers for drivers for simple display
  20 * hardware.
  21 *
  22 * drm_simple_display_pipe_init() initializes a simple display pipeline
  23 * which has only one full-screen scanout buffer feeding one output. The
  24 * pipeline is represented by &struct drm_simple_display_pipe and binds
  25 * together &drm_plane, &drm_crtc and &drm_encoder structures into one fixed
  26 * entity. Some flexibility for code reuse is provided through a separately
  27 * allocated &drm_connector object and supporting optional &drm_bridge
  28 * encoder drivers.
  29 */
  30
  31static const struct drm_encoder_funcs drm_simple_kms_encoder_funcs = {
  32        .destroy = drm_encoder_cleanup,
  33};
  34
  35static enum drm_mode_status
  36drm_simple_kms_crtc_mode_valid(struct drm_crtc *crtc,
  37                               const struct drm_display_mode *mode)
  38{
  39        struct drm_simple_display_pipe *pipe;
  40
  41        pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
  42        if (!pipe->funcs || !pipe->funcs->mode_valid)
  43                /* Anything goes */
  44                return MODE_OK;
  45
  46        return pipe->funcs->mode_valid(pipe, mode);
  47}
  48
  49static int drm_simple_kms_crtc_check(struct drm_crtc *crtc,
  50                                     struct drm_crtc_state *state)
  51{
  52        bool has_primary = state->plane_mask &
  53                           drm_plane_mask(crtc->primary);
  54
  55        /* We always want to have an active plane with an active CRTC */
  56        if (has_primary != state->enable)
  57                return -EINVAL;
  58
  59        return drm_atomic_add_affected_planes(state->state, crtc);
  60}
  61
  62static void drm_simple_kms_crtc_enable(struct drm_crtc *crtc,
  63                                       struct drm_crtc_state *old_state)
  64{
  65        struct drm_plane *plane;
  66        struct drm_simple_display_pipe *pipe;
  67
  68        pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
  69        if (!pipe->funcs || !pipe->funcs->enable)
  70                return;
  71
  72        plane = &pipe->plane;
  73        pipe->funcs->enable(pipe, crtc->state, plane->state);
  74}
  75
  76static void drm_simple_kms_crtc_disable(struct drm_crtc *crtc,
  77                                        struct drm_crtc_state *old_state)
  78{
  79        struct drm_simple_display_pipe *pipe;
  80
  81        pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
  82        if (!pipe->funcs || !pipe->funcs->disable)
  83                return;
  84
  85        pipe->funcs->disable(pipe);
  86}
  87
  88static const struct drm_crtc_helper_funcs drm_simple_kms_crtc_helper_funcs = {
  89        .mode_valid = drm_simple_kms_crtc_mode_valid,
  90        .atomic_check = drm_simple_kms_crtc_check,
  91        .atomic_enable = drm_simple_kms_crtc_enable,
  92        .atomic_disable = drm_simple_kms_crtc_disable,
  93};
  94
  95static int drm_simple_kms_crtc_enable_vblank(struct drm_crtc *crtc)
  96{
  97        struct drm_simple_display_pipe *pipe;
  98
  99        pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
 100        if (!pipe->funcs || !pipe->funcs->enable_vblank)
 101                return 0;
 102
 103        return pipe->funcs->enable_vblank(pipe);
 104}
 105
 106static void drm_simple_kms_crtc_disable_vblank(struct drm_crtc *crtc)
 107{
 108        struct drm_simple_display_pipe *pipe;
 109
 110        pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
 111        if (!pipe->funcs || !pipe->funcs->disable_vblank)
 112                return;
 113
 114        pipe->funcs->disable_vblank(pipe);
 115}
 116
 117static const struct drm_crtc_funcs drm_simple_kms_crtc_funcs = {
 118        .reset = drm_atomic_helper_crtc_reset,
 119        .destroy = drm_crtc_cleanup,
 120        .set_config = drm_atomic_helper_set_config,
 121        .page_flip = drm_atomic_helper_page_flip,
 122        .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
 123        .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
 124        .enable_vblank = drm_simple_kms_crtc_enable_vblank,
 125        .disable_vblank = drm_simple_kms_crtc_disable_vblank,
 126};
 127
 128static int drm_simple_kms_plane_atomic_check(struct drm_plane *plane,
 129                                        struct drm_plane_state *plane_state)
 130{
 131        struct drm_simple_display_pipe *pipe;
 132        struct drm_crtc_state *crtc_state;
 133        int ret;
 134
 135        pipe = container_of(plane, struct drm_simple_display_pipe, plane);
 136        crtc_state = drm_atomic_get_new_crtc_state(plane_state->state,
 137                                                   &pipe->crtc);
 138
 139        ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state,
 140                                                  DRM_PLANE_HELPER_NO_SCALING,
 141                                                  DRM_PLANE_HELPER_NO_SCALING,
 142                                                  false, true);
 143        if (ret)
 144                return ret;
 145
 146        if (!plane_state->visible)
 147                return 0;
 148
 149        if (!pipe->funcs || !pipe->funcs->check)
 150                return 0;
 151
 152        return pipe->funcs->check(pipe, plane_state, crtc_state);
 153}
 154
 155static void drm_simple_kms_plane_atomic_update(struct drm_plane *plane,
 156                                        struct drm_plane_state *old_pstate)
 157{
 158        struct drm_simple_display_pipe *pipe;
 159
 160        pipe = container_of(plane, struct drm_simple_display_pipe, plane);
 161        if (!pipe->funcs || !pipe->funcs->update)
 162                return;
 163
 164        pipe->funcs->update(pipe, old_pstate);
 165}
 166
 167static int drm_simple_kms_plane_prepare_fb(struct drm_plane *plane,
 168                                           struct drm_plane_state *state)
 169{
 170        struct drm_simple_display_pipe *pipe;
 171
 172        pipe = container_of(plane, struct drm_simple_display_pipe, plane);
 173        if (!pipe->funcs || !pipe->funcs->prepare_fb)
 174                return 0;
 175
 176        return pipe->funcs->prepare_fb(pipe, state);
 177}
 178
 179static void drm_simple_kms_plane_cleanup_fb(struct drm_plane *plane,
 180                                            struct drm_plane_state *state)
 181{
 182        struct drm_simple_display_pipe *pipe;
 183
 184        pipe = container_of(plane, struct drm_simple_display_pipe, plane);
 185        if (!pipe->funcs || !pipe->funcs->cleanup_fb)
 186                return;
 187
 188        pipe->funcs->cleanup_fb(pipe, state);
 189}
 190
 191static bool drm_simple_kms_format_mod_supported(struct drm_plane *plane,
 192                                                uint32_t format,
 193                                                uint64_t modifier)
 194{
 195        return modifier == DRM_FORMAT_MOD_LINEAR;
 196}
 197
 198static const struct drm_plane_helper_funcs drm_simple_kms_plane_helper_funcs = {
 199        .prepare_fb = drm_simple_kms_plane_prepare_fb,
 200        .cleanup_fb = drm_simple_kms_plane_cleanup_fb,
 201        .atomic_check = drm_simple_kms_plane_atomic_check,
 202        .atomic_update = drm_simple_kms_plane_atomic_update,
 203};
 204
 205static const struct drm_plane_funcs drm_simple_kms_plane_funcs = {
 206        .update_plane           = drm_atomic_helper_update_plane,
 207        .disable_plane          = drm_atomic_helper_disable_plane,
 208        .destroy                = drm_plane_cleanup,
 209        .reset                  = drm_atomic_helper_plane_reset,
 210        .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
 211        .atomic_destroy_state   = drm_atomic_helper_plane_destroy_state,
 212        .format_mod_supported   = drm_simple_kms_format_mod_supported,
 213};
 214
 215/**
 216 * drm_simple_display_pipe_attach_bridge - Attach a bridge to the display pipe
 217 * @pipe: simple display pipe object
 218 * @bridge: bridge to attach
 219 *
 220 * Makes it possible to still use the drm_simple_display_pipe helpers when
 221 * a DRM bridge has to be used.
 222 *
 223 * Note that you probably want to initialize the pipe by passing a NULL
 224 * connector to drm_simple_display_pipe_init().
 225 *
 226 * Returns:
 227 * Zero on success, negative error code on failure.
 228 */
 229int drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe *pipe,
 230                                          struct drm_bridge *bridge)
 231{
 232        return drm_bridge_attach(&pipe->encoder, bridge, NULL);
 233}
 234EXPORT_SYMBOL(drm_simple_display_pipe_attach_bridge);
 235
 236/**
 237 * drm_simple_display_pipe_init - Initialize a simple display pipeline
 238 * @dev: DRM device
 239 * @pipe: simple display pipe object to initialize
 240 * @funcs: callbacks for the display pipe (optional)
 241 * @formats: array of supported formats (DRM_FORMAT\_\*)
 242 * @format_count: number of elements in @formats
 243 * @format_modifiers: array of formats modifiers
 244 * @connector: connector to attach and register (optional)
 245 *
 246 * Sets up a display pipeline which consist of a really simple
 247 * plane-crtc-encoder pipe.
 248 *
 249 * If a connector is supplied, the pipe will be coupled with the provided
 250 * connector. You may supply a NULL connector when using drm bridges, that
 251 * handle connectors themselves (see drm_simple_display_pipe_attach_bridge()).
 252 *
 253 * Teardown of a simple display pipe is all handled automatically by the drm
 254 * core through calling drm_mode_config_cleanup(). Drivers afterwards need to
 255 * release the memory for the structure themselves.
 256 *
 257 * Returns:
 258 * Zero on success, negative error code on failure.
 259 */
 260int drm_simple_display_pipe_init(struct drm_device *dev,
 261                        struct drm_simple_display_pipe *pipe,
 262                        const struct drm_simple_display_pipe_funcs *funcs,
 263                        const uint32_t *formats, unsigned int format_count,
 264                        const uint64_t *format_modifiers,
 265                        struct drm_connector *connector)
 266{
 267        struct drm_encoder *encoder = &pipe->encoder;
 268        struct drm_plane *plane = &pipe->plane;
 269        struct drm_crtc *crtc = &pipe->crtc;
 270        int ret;
 271
 272        pipe->connector = connector;
 273        pipe->funcs = funcs;
 274
 275        drm_plane_helper_add(plane, &drm_simple_kms_plane_helper_funcs);
 276        ret = drm_universal_plane_init(dev, plane, 0,
 277                                       &drm_simple_kms_plane_funcs,
 278                                       formats, format_count,
 279                                       format_modifiers,
 280                                       DRM_PLANE_TYPE_PRIMARY, NULL);
 281        if (ret)
 282                return ret;
 283
 284        drm_crtc_helper_add(crtc, &drm_simple_kms_crtc_helper_funcs);
 285        ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
 286                                        &drm_simple_kms_crtc_funcs, NULL);
 287        if (ret)
 288                return ret;
 289
 290        encoder->possible_crtcs = drm_crtc_mask(crtc);
 291        ret = drm_encoder_init(dev, encoder, &drm_simple_kms_encoder_funcs,
 292                               DRM_MODE_ENCODER_NONE, NULL);
 293        if (ret || !connector)
 294                return ret;
 295
 296        return drm_connector_attach_encoder(connector, encoder);
 297}
 298EXPORT_SYMBOL(drm_simple_display_pipe_init);
 299
 300MODULE_LICENSE("GPL");
 301