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