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 * Many drivers require only a very simple encoder that fulfills the minimum
  31 * requirements of the display pipeline and does not add additional
  32 * functionality. The function drm_simple_encoder_init() provides an
  33 * implementation of such an encoder.
  34 */
  35
  36static const struct drm_encoder_funcs drm_simple_encoder_funcs_cleanup = {
  37        .destroy = drm_encoder_cleanup,
  38};
  39
  40/**
  41 * drm_simple_encoder_init - Initialize a preallocated encoder with
  42 *                           basic functionality.
  43 * @dev: drm device
  44 * @encoder: the encoder to initialize
  45 * @encoder_type: user visible type of the encoder
  46 *
  47 * Initialises a preallocated encoder that has no further functionality.
  48 * Settings for possible CRTC and clones are left to their initial values.
  49 * The encoder will be cleaned up automatically as part of the mode-setting
  50 * cleanup.
  51 *
  52 * The caller of drm_simple_encoder_init() is responsible for freeing
  53 * the encoder's memory after the encoder has been cleaned up. At the
  54 * moment this only works reliably if the encoder data structure is
  55 * stored in the device structure. Free the encoder's memory as part of
  56 * the device release function.
  57 *
  58 * FIXME: Later improvements to DRM's resource management may allow for
  59 *        an automated kfree() of the encoder's memory.
  60 *
  61 * Returns:
  62 * Zero on success, error code on failure.
  63 */
  64int drm_simple_encoder_init(struct drm_device *dev,
  65                            struct drm_encoder *encoder,
  66                            int encoder_type)
  67{
  68        return drm_encoder_init(dev, encoder,
  69                                &drm_simple_encoder_funcs_cleanup,
  70                                encoder_type, NULL);
  71}
  72EXPORT_SYMBOL(drm_simple_encoder_init);
  73
  74static enum drm_mode_status
  75drm_simple_kms_crtc_mode_valid(struct drm_crtc *crtc,
  76                               const struct drm_display_mode *mode)
  77{
  78        struct drm_simple_display_pipe *pipe;
  79
  80        pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
  81        if (!pipe->funcs || !pipe->funcs->mode_valid)
  82                /* Anything goes */
  83                return MODE_OK;
  84
  85        return pipe->funcs->mode_valid(pipe, mode);
  86}
  87
  88static int drm_simple_kms_crtc_check(struct drm_crtc *crtc,
  89                                     struct drm_atomic_state *state)
  90{
  91        struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
  92                                                                          crtc);
  93        bool has_primary = crtc_state->plane_mask &
  94                           drm_plane_mask(crtc->primary);
  95
  96        /* We always want to have an active plane with an active CRTC */
  97        if (has_primary != crtc_state->enable)
  98                return -EINVAL;
  99
 100        return drm_atomic_add_affected_planes(state, crtc);
 101}
 102
 103static void drm_simple_kms_crtc_enable(struct drm_crtc *crtc,
 104                                       struct drm_atomic_state *state)
 105{
 106        struct drm_plane *plane;
 107        struct drm_simple_display_pipe *pipe;
 108
 109        pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
 110        if (!pipe->funcs || !pipe->funcs->enable)
 111                return;
 112
 113        plane = &pipe->plane;
 114        pipe->funcs->enable(pipe, crtc->state, plane->state);
 115}
 116
 117static void drm_simple_kms_crtc_disable(struct drm_crtc *crtc,
 118                                        struct drm_atomic_state *state)
 119{
 120        struct drm_simple_display_pipe *pipe;
 121
 122        pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
 123        if (!pipe->funcs || !pipe->funcs->disable)
 124                return;
 125
 126        pipe->funcs->disable(pipe);
 127}
 128
 129static const struct drm_crtc_helper_funcs drm_simple_kms_crtc_helper_funcs = {
 130        .mode_valid = drm_simple_kms_crtc_mode_valid,
 131        .atomic_check = drm_simple_kms_crtc_check,
 132        .atomic_enable = drm_simple_kms_crtc_enable,
 133        .atomic_disable = drm_simple_kms_crtc_disable,
 134};
 135
 136static int drm_simple_kms_crtc_enable_vblank(struct drm_crtc *crtc)
 137{
 138        struct drm_simple_display_pipe *pipe;
 139
 140        pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
 141        if (!pipe->funcs || !pipe->funcs->enable_vblank)
 142                return 0;
 143
 144        return pipe->funcs->enable_vblank(pipe);
 145}
 146
 147static void drm_simple_kms_crtc_disable_vblank(struct drm_crtc *crtc)
 148{
 149        struct drm_simple_display_pipe *pipe;
 150
 151        pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
 152        if (!pipe->funcs || !pipe->funcs->disable_vblank)
 153                return;
 154
 155        pipe->funcs->disable_vblank(pipe);
 156}
 157
 158static const struct drm_crtc_funcs drm_simple_kms_crtc_funcs = {
 159        .reset = drm_atomic_helper_crtc_reset,
 160        .destroy = drm_crtc_cleanup,
 161        .set_config = drm_atomic_helper_set_config,
 162        .page_flip = drm_atomic_helper_page_flip,
 163        .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
 164        .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
 165        .enable_vblank = drm_simple_kms_crtc_enable_vblank,
 166        .disable_vblank = drm_simple_kms_crtc_disable_vblank,
 167};
 168
 169static int drm_simple_kms_plane_atomic_check(struct drm_plane *plane,
 170                                        struct drm_plane_state *plane_state)
 171{
 172        struct drm_simple_display_pipe *pipe;
 173        struct drm_crtc_state *crtc_state;
 174        int ret;
 175
 176        pipe = container_of(plane, struct drm_simple_display_pipe, plane);
 177        crtc_state = drm_atomic_get_new_crtc_state(plane_state->state,
 178                                                   &pipe->crtc);
 179
 180        ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state,
 181                                                  DRM_PLANE_HELPER_NO_SCALING,
 182                                                  DRM_PLANE_HELPER_NO_SCALING,
 183                                                  false, true);
 184        if (ret)
 185                return ret;
 186
 187        if (!plane_state->visible)
 188                return 0;
 189
 190        if (!pipe->funcs || !pipe->funcs->check)
 191                return 0;
 192
 193        return pipe->funcs->check(pipe, plane_state, crtc_state);
 194}
 195
 196static void drm_simple_kms_plane_atomic_update(struct drm_plane *plane,
 197                                        struct drm_plane_state *old_pstate)
 198{
 199        struct drm_simple_display_pipe *pipe;
 200
 201        pipe = container_of(plane, struct drm_simple_display_pipe, plane);
 202        if (!pipe->funcs || !pipe->funcs->update)
 203                return;
 204
 205        pipe->funcs->update(pipe, old_pstate);
 206}
 207
 208static int drm_simple_kms_plane_prepare_fb(struct drm_plane *plane,
 209                                           struct drm_plane_state *state)
 210{
 211        struct drm_simple_display_pipe *pipe;
 212
 213        pipe = container_of(plane, struct drm_simple_display_pipe, plane);
 214        if (!pipe->funcs || !pipe->funcs->prepare_fb)
 215                return 0;
 216
 217        return pipe->funcs->prepare_fb(pipe, state);
 218}
 219
 220static void drm_simple_kms_plane_cleanup_fb(struct drm_plane *plane,
 221                                            struct drm_plane_state *state)
 222{
 223        struct drm_simple_display_pipe *pipe;
 224
 225        pipe = container_of(plane, struct drm_simple_display_pipe, plane);
 226        if (!pipe->funcs || !pipe->funcs->cleanup_fb)
 227                return;
 228
 229        pipe->funcs->cleanup_fb(pipe, state);
 230}
 231
 232static bool drm_simple_kms_format_mod_supported(struct drm_plane *plane,
 233                                                uint32_t format,
 234                                                uint64_t modifier)
 235{
 236        return modifier == DRM_FORMAT_MOD_LINEAR;
 237}
 238
 239static const struct drm_plane_helper_funcs drm_simple_kms_plane_helper_funcs = {
 240        .prepare_fb = drm_simple_kms_plane_prepare_fb,
 241        .cleanup_fb = drm_simple_kms_plane_cleanup_fb,
 242        .atomic_check = drm_simple_kms_plane_atomic_check,
 243        .atomic_update = drm_simple_kms_plane_atomic_update,
 244};
 245
 246static const struct drm_plane_funcs drm_simple_kms_plane_funcs = {
 247        .update_plane           = drm_atomic_helper_update_plane,
 248        .disable_plane          = drm_atomic_helper_disable_plane,
 249        .destroy                = drm_plane_cleanup,
 250        .reset                  = drm_atomic_helper_plane_reset,
 251        .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
 252        .atomic_destroy_state   = drm_atomic_helper_plane_destroy_state,
 253        .format_mod_supported   = drm_simple_kms_format_mod_supported,
 254};
 255
 256/**
 257 * drm_simple_display_pipe_attach_bridge - Attach a bridge to the display pipe
 258 * @pipe: simple display pipe object
 259 * @bridge: bridge to attach
 260 *
 261 * Makes it possible to still use the drm_simple_display_pipe helpers when
 262 * a DRM bridge has to be used.
 263 *
 264 * Note that you probably want to initialize the pipe by passing a NULL
 265 * connector to drm_simple_display_pipe_init().
 266 *
 267 * Returns:
 268 * Zero on success, negative error code on failure.
 269 */
 270int drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe *pipe,
 271                                          struct drm_bridge *bridge)
 272{
 273        return drm_bridge_attach(&pipe->encoder, bridge, NULL, 0);
 274}
 275EXPORT_SYMBOL(drm_simple_display_pipe_attach_bridge);
 276
 277/**
 278 * drm_simple_display_pipe_init - Initialize a simple display pipeline
 279 * @dev: DRM device
 280 * @pipe: simple display pipe object to initialize
 281 * @funcs: callbacks for the display pipe (optional)
 282 * @formats: array of supported formats (DRM_FORMAT\_\*)
 283 * @format_count: number of elements in @formats
 284 * @format_modifiers: array of formats modifiers
 285 * @connector: connector to attach and register (optional)
 286 *
 287 * Sets up a display pipeline which consist of a really simple
 288 * plane-crtc-encoder pipe.
 289 *
 290 * If a connector is supplied, the pipe will be coupled with the provided
 291 * connector. You may supply a NULL connector when using drm bridges, that
 292 * handle connectors themselves (see drm_simple_display_pipe_attach_bridge()).
 293 *
 294 * Teardown of a simple display pipe is all handled automatically by the drm
 295 * core through calling drm_mode_config_cleanup(). Drivers afterwards need to
 296 * release the memory for the structure themselves.
 297 *
 298 * Returns:
 299 * Zero on success, negative error code on failure.
 300 */
 301int drm_simple_display_pipe_init(struct drm_device *dev,
 302                        struct drm_simple_display_pipe *pipe,
 303                        const struct drm_simple_display_pipe_funcs *funcs,
 304                        const uint32_t *formats, unsigned int format_count,
 305                        const uint64_t *format_modifiers,
 306                        struct drm_connector *connector)
 307{
 308        struct drm_encoder *encoder = &pipe->encoder;
 309        struct drm_plane *plane = &pipe->plane;
 310        struct drm_crtc *crtc = &pipe->crtc;
 311        int ret;
 312
 313        pipe->connector = connector;
 314        pipe->funcs = funcs;
 315
 316        drm_plane_helper_add(plane, &drm_simple_kms_plane_helper_funcs);
 317        ret = drm_universal_plane_init(dev, plane, 0,
 318                                       &drm_simple_kms_plane_funcs,
 319                                       formats, format_count,
 320                                       format_modifiers,
 321                                       DRM_PLANE_TYPE_PRIMARY, NULL);
 322        if (ret)
 323                return ret;
 324
 325        drm_crtc_helper_add(crtc, &drm_simple_kms_crtc_helper_funcs);
 326        ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
 327                                        &drm_simple_kms_crtc_funcs, NULL);
 328        if (ret)
 329                return ret;
 330
 331        encoder->possible_crtcs = drm_crtc_mask(crtc);
 332        ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_NONE);
 333        if (ret || !connector)
 334                return ret;
 335
 336        return drm_connector_attach_encoder(connector, encoder);
 337}
 338EXPORT_SYMBOL(drm_simple_display_pipe_init);
 339
 340MODULE_LICENSE("GPL");
 341