1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2016 Noralf Trønnes
6 #include <linux/module.h>
7 #include <linux/slab.h>
9 #include <drm/drm_atomic.h>
10 #include <drm/drm_atomic_helper.h>
11 #include <drm/drm_plane_helper.h>
12 #include <drm/drm_probe_helper.h>
13 #include <drm/drm_simple_kms_helper.h>
18 * This helper library provides helpers for drivers for simple display
21 * drm_simple_display_pipe_init() initializes a simple display pipeline
22 * which has only one full-screen scanout buffer feeding one output. The
23 * pipeline is represented by &struct drm_simple_display_pipe and binds
24 * together &drm_plane, &drm_crtc and &drm_encoder structures into one fixed
25 * entity. Some flexibility for code reuse is provided through a separately
26 * allocated &drm_connector object and supporting optional &drm_bridge
30 static const struct drm_encoder_funcs drm_simple_kms_encoder_funcs
= {
31 .destroy
= drm_encoder_cleanup
,
34 static enum drm_mode_status
35 drm_simple_kms_crtc_mode_valid(struct drm_crtc
*crtc
,
36 const struct drm_display_mode
*mode
)
38 struct drm_simple_display_pipe
*pipe
;
40 pipe
= container_of(crtc
, struct drm_simple_display_pipe
, crtc
);
41 if (!pipe
->funcs
|| !pipe
->funcs
->mode_valid
)
45 return pipe
->funcs
->mode_valid(crtc
, mode
);
48 static int drm_simple_kms_crtc_check(struct drm_crtc
*crtc
,
49 struct drm_crtc_state
*state
)
51 bool has_primary
= state
->plane_mask
&
52 drm_plane_mask(crtc
->primary
);
54 /* We always want to have an active plane with an active CRTC */
55 if (has_primary
!= state
->enable
)
58 return drm_atomic_add_affected_planes(state
->state
, crtc
);
61 static void drm_simple_kms_crtc_enable(struct drm_crtc
*crtc
,
62 struct drm_crtc_state
*old_state
)
64 struct drm_plane
*plane
;
65 struct drm_simple_display_pipe
*pipe
;
67 pipe
= container_of(crtc
, struct drm_simple_display_pipe
, crtc
);
68 if (!pipe
->funcs
|| !pipe
->funcs
->enable
)
72 pipe
->funcs
->enable(pipe
, crtc
->state
, plane
->state
);
75 static void drm_simple_kms_crtc_disable(struct drm_crtc
*crtc
,
76 struct drm_crtc_state
*old_state
)
78 struct drm_simple_display_pipe
*pipe
;
80 pipe
= container_of(crtc
, struct drm_simple_display_pipe
, crtc
);
81 if (!pipe
->funcs
|| !pipe
->funcs
->disable
)
84 pipe
->funcs
->disable(pipe
);
87 static const struct drm_crtc_helper_funcs drm_simple_kms_crtc_helper_funcs
= {
88 .mode_valid
= drm_simple_kms_crtc_mode_valid
,
89 .atomic_check
= drm_simple_kms_crtc_check
,
90 .atomic_enable
= drm_simple_kms_crtc_enable
,
91 .atomic_disable
= drm_simple_kms_crtc_disable
,
94 static int drm_simple_kms_crtc_enable_vblank(struct drm_crtc
*crtc
)
96 struct drm_simple_display_pipe
*pipe
;
98 pipe
= container_of(crtc
, struct drm_simple_display_pipe
, crtc
);
99 if (!pipe
->funcs
|| !pipe
->funcs
->enable_vblank
)
102 return pipe
->funcs
->enable_vblank(pipe
);
105 static void drm_simple_kms_crtc_disable_vblank(struct drm_crtc
*crtc
)
107 struct drm_simple_display_pipe
*pipe
;
109 pipe
= container_of(crtc
, struct drm_simple_display_pipe
, crtc
);
110 if (!pipe
->funcs
|| !pipe
->funcs
->disable_vblank
)
113 pipe
->funcs
->disable_vblank(pipe
);
116 static const struct drm_crtc_funcs drm_simple_kms_crtc_funcs
= {
117 .reset
= drm_atomic_helper_crtc_reset
,
118 .destroy
= drm_crtc_cleanup
,
119 .set_config
= drm_atomic_helper_set_config
,
120 .page_flip
= drm_atomic_helper_page_flip
,
121 .atomic_duplicate_state
= drm_atomic_helper_crtc_duplicate_state
,
122 .atomic_destroy_state
= drm_atomic_helper_crtc_destroy_state
,
123 .enable_vblank
= drm_simple_kms_crtc_enable_vblank
,
124 .disable_vblank
= drm_simple_kms_crtc_disable_vblank
,
127 static int drm_simple_kms_plane_atomic_check(struct drm_plane
*plane
,
128 struct drm_plane_state
*plane_state
)
130 struct drm_simple_display_pipe
*pipe
;
131 struct drm_crtc_state
*crtc_state
;
134 pipe
= container_of(plane
, struct drm_simple_display_pipe
, plane
);
135 crtc_state
= drm_atomic_get_new_crtc_state(plane_state
->state
,
138 ret
= drm_atomic_helper_check_plane_state(plane_state
, crtc_state
,
139 DRM_PLANE_HELPER_NO_SCALING
,
140 DRM_PLANE_HELPER_NO_SCALING
,
145 if (!plane_state
->visible
)
148 if (!pipe
->funcs
|| !pipe
->funcs
->check
)
151 return pipe
->funcs
->check(pipe
, plane_state
, crtc_state
);
154 static void drm_simple_kms_plane_atomic_update(struct drm_plane
*plane
,
155 struct drm_plane_state
*old_pstate
)
157 struct drm_simple_display_pipe
*pipe
;
159 pipe
= container_of(plane
, struct drm_simple_display_pipe
, plane
);
160 if (!pipe
->funcs
|| !pipe
->funcs
->update
)
163 pipe
->funcs
->update(pipe
, old_pstate
);
166 static int drm_simple_kms_plane_prepare_fb(struct drm_plane
*plane
,
167 struct drm_plane_state
*state
)
169 struct drm_simple_display_pipe
*pipe
;
171 pipe
= container_of(plane
, struct drm_simple_display_pipe
, plane
);
172 if (!pipe
->funcs
|| !pipe
->funcs
->prepare_fb
)
175 return pipe
->funcs
->prepare_fb(pipe
, state
);
178 static void drm_simple_kms_plane_cleanup_fb(struct drm_plane
*plane
,
179 struct drm_plane_state
*state
)
181 struct drm_simple_display_pipe
*pipe
;
183 pipe
= container_of(plane
, struct drm_simple_display_pipe
, plane
);
184 if (!pipe
->funcs
|| !pipe
->funcs
->cleanup_fb
)
187 pipe
->funcs
->cleanup_fb(pipe
, state
);
190 static bool drm_simple_kms_format_mod_supported(struct drm_plane
*plane
,
194 return modifier
== DRM_FORMAT_MOD_LINEAR
;
197 static const struct drm_plane_helper_funcs drm_simple_kms_plane_helper_funcs
= {
198 .prepare_fb
= drm_simple_kms_plane_prepare_fb
,
199 .cleanup_fb
= drm_simple_kms_plane_cleanup_fb
,
200 .atomic_check
= drm_simple_kms_plane_atomic_check
,
201 .atomic_update
= drm_simple_kms_plane_atomic_update
,
204 static const struct drm_plane_funcs drm_simple_kms_plane_funcs
= {
205 .update_plane
= drm_atomic_helper_update_plane
,
206 .disable_plane
= drm_atomic_helper_disable_plane
,
207 .destroy
= drm_plane_cleanup
,
208 .reset
= drm_atomic_helper_plane_reset
,
209 .atomic_duplicate_state
= drm_atomic_helper_plane_duplicate_state
,
210 .atomic_destroy_state
= drm_atomic_helper_plane_destroy_state
,
211 .format_mod_supported
= drm_simple_kms_format_mod_supported
,
215 * drm_simple_display_pipe_attach_bridge - Attach a bridge to the display pipe
216 * @pipe: simple display pipe object
217 * @bridge: bridge to attach
219 * Makes it possible to still use the drm_simple_display_pipe helpers when
220 * a DRM bridge has to be used.
222 * Note that you probably want to initialize the pipe by passing a NULL
223 * connector to drm_simple_display_pipe_init().
226 * Zero on success, negative error code on failure.
228 int drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe
*pipe
,
229 struct drm_bridge
*bridge
)
231 return drm_bridge_attach(&pipe
->encoder
, bridge
, NULL
);
233 EXPORT_SYMBOL(drm_simple_display_pipe_attach_bridge
);
236 * drm_simple_display_pipe_init - Initialize a simple display pipeline
238 * @pipe: simple display pipe object to initialize
239 * @funcs: callbacks for the display pipe (optional)
240 * @formats: array of supported formats (DRM_FORMAT\_\*)
241 * @format_count: number of elements in @formats
242 * @format_modifiers: array of formats modifiers
243 * @connector: connector to attach and register (optional)
245 * Sets up a display pipeline which consist of a really simple
246 * plane-crtc-encoder pipe.
248 * If a connector is supplied, the pipe will be coupled with the provided
249 * connector. You may supply a NULL connector when using drm bridges, that
250 * handle connectors themselves (see drm_simple_display_pipe_attach_bridge()).
252 * Teardown of a simple display pipe is all handled automatically by the drm
253 * core through calling drm_mode_config_cleanup(). Drivers afterwards need to
254 * release the memory for the structure themselves.
257 * Zero on success, negative error code on failure.
259 int drm_simple_display_pipe_init(struct drm_device
*dev
,
260 struct drm_simple_display_pipe
*pipe
,
261 const struct drm_simple_display_pipe_funcs
*funcs
,
262 const uint32_t *formats
, unsigned int format_count
,
263 const uint64_t *format_modifiers
,
264 struct drm_connector
*connector
)
266 struct drm_encoder
*encoder
= &pipe
->encoder
;
267 struct drm_plane
*plane
= &pipe
->plane
;
268 struct drm_crtc
*crtc
= &pipe
->crtc
;
271 pipe
->connector
= connector
;
274 drm_plane_helper_add(plane
, &drm_simple_kms_plane_helper_funcs
);
275 ret
= drm_universal_plane_init(dev
, plane
, 0,
276 &drm_simple_kms_plane_funcs
,
277 formats
, format_count
,
279 DRM_PLANE_TYPE_PRIMARY
, NULL
);
283 drm_crtc_helper_add(crtc
, &drm_simple_kms_crtc_helper_funcs
);
284 ret
= drm_crtc_init_with_planes(dev
, crtc
, plane
, NULL
,
285 &drm_simple_kms_crtc_funcs
, NULL
);
289 encoder
->possible_crtcs
= drm_crtc_mask(crtc
);
290 ret
= drm_encoder_init(dev
, encoder
, &drm_simple_kms_encoder_funcs
,
291 DRM_MODE_ENCODER_NONE
, NULL
);
292 if (ret
|| !connector
)
295 return drm_connector_attach_encoder(connector
, encoder
);
297 EXPORT_SYMBOL(drm_simple_display_pipe_init
);
299 MODULE_LICENSE("GPL");