1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2016 Noralf Trønnes
7 #include <drm/drm_atomic.h>
8 #include <drm/drm_atomic_helper.h>
9 #include <drm/drm_plane_helper.h>
10 #include <drm/drm_probe_helper.h>
11 #include <drm/drm_simple_kms_helper.h>
12 #include <linux/slab.h>
17 * This helper library provides helpers for drivers for simple display
20 * drm_simple_display_pipe_init() initializes a simple display pipeline
21 * which has only one full-screen scanout buffer feeding one output. The
22 * pipeline is represented by &struct drm_simple_display_pipe and binds
23 * together &drm_plane, &drm_crtc and &drm_encoder structures into one fixed
24 * entity. Some flexibility for code reuse is provided through a separately
25 * allocated &drm_connector object and supporting optional &drm_bridge
29 static const struct drm_encoder_funcs drm_simple_kms_encoder_funcs
= {
30 .destroy
= drm_encoder_cleanup
,
33 static enum drm_mode_status
34 drm_simple_kms_crtc_mode_valid(struct drm_crtc
*crtc
,
35 const struct drm_display_mode
*mode
)
37 struct drm_simple_display_pipe
*pipe
;
39 pipe
= container_of(crtc
, struct drm_simple_display_pipe
, crtc
);
40 if (!pipe
->funcs
|| !pipe
->funcs
->mode_valid
)
44 return pipe
->funcs
->mode_valid(crtc
, mode
);
47 static int drm_simple_kms_crtc_check(struct drm_crtc
*crtc
,
48 struct drm_crtc_state
*state
)
50 bool has_primary
= state
->plane_mask
&
51 drm_plane_mask(crtc
->primary
);
53 /* We always want to have an active plane with an active CRTC */
54 if (has_primary
!= state
->enable
)
57 return drm_atomic_add_affected_planes(state
->state
, crtc
);
60 static void drm_simple_kms_crtc_enable(struct drm_crtc
*crtc
,
61 struct drm_crtc_state
*old_state
)
63 struct drm_plane
*plane
;
64 struct drm_simple_display_pipe
*pipe
;
66 pipe
= container_of(crtc
, struct drm_simple_display_pipe
, crtc
);
67 if (!pipe
->funcs
|| !pipe
->funcs
->enable
)
71 pipe
->funcs
->enable(pipe
, crtc
->state
, plane
->state
);
74 static void drm_simple_kms_crtc_disable(struct drm_crtc
*crtc
,
75 struct drm_crtc_state
*old_state
)
77 struct drm_simple_display_pipe
*pipe
;
79 pipe
= container_of(crtc
, struct drm_simple_display_pipe
, crtc
);
80 if (!pipe
->funcs
|| !pipe
->funcs
->disable
)
83 pipe
->funcs
->disable(pipe
);
86 static const struct drm_crtc_helper_funcs drm_simple_kms_crtc_helper_funcs
= {
87 .mode_valid
= drm_simple_kms_crtc_mode_valid
,
88 .atomic_check
= drm_simple_kms_crtc_check
,
89 .atomic_enable
= drm_simple_kms_crtc_enable
,
90 .atomic_disable
= drm_simple_kms_crtc_disable
,
93 static int drm_simple_kms_crtc_enable_vblank(struct drm_crtc
*crtc
)
95 struct drm_simple_display_pipe
*pipe
;
97 pipe
= container_of(crtc
, struct drm_simple_display_pipe
, crtc
);
98 if (!pipe
->funcs
|| !pipe
->funcs
->enable_vblank
)
101 return pipe
->funcs
->enable_vblank(pipe
);
104 static void drm_simple_kms_crtc_disable_vblank(struct drm_crtc
*crtc
)
106 struct drm_simple_display_pipe
*pipe
;
108 pipe
= container_of(crtc
, struct drm_simple_display_pipe
, crtc
);
109 if (!pipe
->funcs
|| !pipe
->funcs
->disable_vblank
)
112 pipe
->funcs
->disable_vblank(pipe
);
115 static const struct drm_crtc_funcs drm_simple_kms_crtc_funcs
= {
116 .reset
= drm_atomic_helper_crtc_reset
,
117 .destroy
= drm_crtc_cleanup
,
118 .set_config
= drm_atomic_helper_set_config
,
119 .page_flip
= drm_atomic_helper_page_flip
,
120 .atomic_duplicate_state
= drm_atomic_helper_crtc_duplicate_state
,
121 .atomic_destroy_state
= drm_atomic_helper_crtc_destroy_state
,
122 .enable_vblank
= drm_simple_kms_crtc_enable_vblank
,
123 .disable_vblank
= drm_simple_kms_crtc_disable_vblank
,
126 static int drm_simple_kms_plane_atomic_check(struct drm_plane
*plane
,
127 struct drm_plane_state
*plane_state
)
129 struct drm_simple_display_pipe
*pipe
;
130 struct drm_crtc_state
*crtc_state
;
133 pipe
= container_of(plane
, struct drm_simple_display_pipe
, plane
);
134 crtc_state
= drm_atomic_get_new_crtc_state(plane_state
->state
,
137 ret
= drm_atomic_helper_check_plane_state(plane_state
, crtc_state
,
138 DRM_PLANE_HELPER_NO_SCALING
,
139 DRM_PLANE_HELPER_NO_SCALING
,
144 if (!plane_state
->visible
)
147 if (!pipe
->funcs
|| !pipe
->funcs
->check
)
150 return pipe
->funcs
->check(pipe
, plane_state
, crtc_state
);
153 static void drm_simple_kms_plane_atomic_update(struct drm_plane
*plane
,
154 struct drm_plane_state
*old_pstate
)
156 struct drm_simple_display_pipe
*pipe
;
158 pipe
= container_of(plane
, struct drm_simple_display_pipe
, plane
);
159 if (!pipe
->funcs
|| !pipe
->funcs
->update
)
162 pipe
->funcs
->update(pipe
, old_pstate
);
165 static int drm_simple_kms_plane_prepare_fb(struct drm_plane
*plane
,
166 struct drm_plane_state
*state
)
168 struct drm_simple_display_pipe
*pipe
;
170 pipe
= container_of(plane
, struct drm_simple_display_pipe
, plane
);
171 if (!pipe
->funcs
|| !pipe
->funcs
->prepare_fb
)
174 return pipe
->funcs
->prepare_fb(pipe
, state
);
177 static void drm_simple_kms_plane_cleanup_fb(struct drm_plane
*plane
,
178 struct drm_plane_state
*state
)
180 struct drm_simple_display_pipe
*pipe
;
182 pipe
= container_of(plane
, struct drm_simple_display_pipe
, plane
);
183 if (!pipe
->funcs
|| !pipe
->funcs
->cleanup_fb
)
186 pipe
->funcs
->cleanup_fb(pipe
, state
);
189 static bool drm_simple_kms_format_mod_supported(struct drm_plane
*plane
,
193 return modifier
== DRM_FORMAT_MOD_LINEAR
;
196 static const struct drm_plane_helper_funcs drm_simple_kms_plane_helper_funcs
= {
197 .prepare_fb
= drm_simple_kms_plane_prepare_fb
,
198 .cleanup_fb
= drm_simple_kms_plane_cleanup_fb
,
199 .atomic_check
= drm_simple_kms_plane_atomic_check
,
200 .atomic_update
= drm_simple_kms_plane_atomic_update
,
203 static const struct drm_plane_funcs drm_simple_kms_plane_funcs
= {
204 .update_plane
= drm_atomic_helper_update_plane
,
205 .disable_plane
= drm_atomic_helper_disable_plane
,
206 .destroy
= drm_plane_cleanup
,
207 .reset
= drm_atomic_helper_plane_reset
,
208 .atomic_duplicate_state
= drm_atomic_helper_plane_duplicate_state
,
209 .atomic_destroy_state
= drm_atomic_helper_plane_destroy_state
,
210 .format_mod_supported
= drm_simple_kms_format_mod_supported
,
214 * drm_simple_display_pipe_attach_bridge - Attach a bridge to the display pipe
215 * @pipe: simple display pipe object
216 * @bridge: bridge to attach
218 * Makes it possible to still use the drm_simple_display_pipe helpers when
219 * a DRM bridge has to be used.
221 * Note that you probably want to initialize the pipe by passing a NULL
222 * connector to drm_simple_display_pipe_init().
225 * Zero on success, negative error code on failure.
227 int drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe
*pipe
,
228 struct drm_bridge
*bridge
)
230 return drm_bridge_attach(&pipe
->encoder
, bridge
, NULL
);
232 EXPORT_SYMBOL(drm_simple_display_pipe_attach_bridge
);
235 * drm_simple_display_pipe_init - Initialize a simple display pipeline
237 * @pipe: simple display pipe object to initialize
238 * @funcs: callbacks for the display pipe (optional)
239 * @formats: array of supported formats (DRM_FORMAT\_\*)
240 * @format_count: number of elements in @formats
241 * @format_modifiers: array of formats modifiers
242 * @connector: connector to attach and register (optional)
244 * Sets up a display pipeline which consist of a really simple
245 * plane-crtc-encoder pipe.
247 * If a connector is supplied, the pipe will be coupled with the provided
248 * connector. You may supply a NULL connector when using drm bridges, that
249 * handle connectors themselves (see drm_simple_display_pipe_attach_bridge()).
251 * Teardown of a simple display pipe is all handled automatically by the drm
252 * core through calling drm_mode_config_cleanup(). Drivers afterwards need to
253 * release the memory for the structure themselves.
256 * Zero on success, negative error code on failure.
258 int drm_simple_display_pipe_init(struct drm_device
*dev
,
259 struct drm_simple_display_pipe
*pipe
,
260 const struct drm_simple_display_pipe_funcs
*funcs
,
261 const uint32_t *formats
, unsigned int format_count
,
262 const uint64_t *format_modifiers
,
263 struct drm_connector
*connector
)
265 struct drm_encoder
*encoder
= &pipe
->encoder
;
266 struct drm_plane
*plane
= &pipe
->plane
;
267 struct drm_crtc
*crtc
= &pipe
->crtc
;
270 pipe
->connector
= connector
;
273 drm_plane_helper_add(plane
, &drm_simple_kms_plane_helper_funcs
);
274 ret
= drm_universal_plane_init(dev
, plane
, 0,
275 &drm_simple_kms_plane_funcs
,
276 formats
, format_count
,
278 DRM_PLANE_TYPE_PRIMARY
, NULL
);
282 drm_crtc_helper_add(crtc
, &drm_simple_kms_crtc_helper_funcs
);
283 ret
= drm_crtc_init_with_planes(dev
, crtc
, plane
, NULL
,
284 &drm_simple_kms_crtc_funcs
, NULL
);
288 encoder
->possible_crtcs
= drm_crtc_mask(crtc
);
289 ret
= drm_encoder_init(dev
, encoder
, &drm_simple_kms_encoder_funcs
,
290 DRM_MODE_ENCODER_NONE
, NULL
);
291 if (ret
|| !connector
)
294 return drm_connector_attach_encoder(connector
, encoder
);
296 EXPORT_SYMBOL(drm_simple_display_pipe_init
);
298 MODULE_LICENSE("GPL");