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