File Index
Files and directories
Sign in
FILE

drm_simple_kms_helper.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/drm_simple_kms_helper.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Copyright (C) 2016 Noralf Trønnes
  4 */
  5
  6#include <linux/export.h>
  7#include <linux/module.h>
  8#include <linux/slab.h>
  9
 10#include <drm/drm_atomic.h>
 11#include <drm/drm_atomic_helper.h>
 12#include <drm/drm_bridge.h>
 13#include <drm/drm_drv.h>
 14#include <drm/drm_gem_atomic_helper.h>
 15#include <drm/drm_managed.h>
 16#include <drm/drm_probe_helper.h>
 17#include <drm/drm_simple_kms_helper.h>
 18
 19/**
 20 * DOC: overview
 21 *
 22 * This helper library provides helpers for drivers for simple display
 23 * hardware.
 24 *
 25 * drm_simple_display_pipe_init() initializes a simple display pipeline
 26 * which has only one full-screen scanout buffer feeding one output. The
 27 * pipeline is represented by &struct drm_simple_display_pipe and binds
 28 * together &drm_plane, &drm_crtc and &drm_encoder structures into one fixed
 29 * entity. Some flexibility for code reuse is provided through a separately
 30 * allocated &drm_connector object and supporting optional &drm_bridge
 31 * encoder drivers.
 32 *
 33 * Many drivers require only a very simple encoder that fulfills the minimum
 34 * requirements of the display pipeline and does not add additional
 35 * functionality. The function drm_simple_encoder_init() provides an
 36 * implementation of such an encoder.
 37 */
 38
 39static const struct drm_encoder_funcs drm_simple_encoder_funcs_cleanup = {
 40	.destroy = drm_encoder_cleanup,
 41};
 42
 43/**
 44 * drm_simple_encoder_init - Initialize a preallocated encoder with
 45 *                           basic functionality.
 46 * @dev: drm device
 47 * @encoder: the encoder to initialize
 48 * @encoder_type: user visible type of the encoder
 49 *
 50 * Initialises a preallocated encoder that has no further functionality.
 51 * Settings for possible CRTC and clones are left to their initial values.
 52 * The encoder will be cleaned up automatically as part of the mode-setting
 53 * cleanup.
 54 *
 55 * The caller of drm_simple_encoder_init() is responsible for freeing
 56 * the encoder's memory after the encoder has been cleaned up. At the
 57 * moment this only works reliably if the encoder data structure is
 58 * stored in the device structure. Free the encoder's memory as part of
 59 * the device release function.
 60 *
 61 * Note: consider using drmm_simple_encoder_alloc() instead of
 62 * drm_simple_encoder_init() to let the DRM managed resource infrastructure
 63 * take care of cleanup and deallocation.
 64 *
 65 * Returns:
 66 * Zero on success, error code on failure.
 67 */
 68int drm_simple_encoder_init(struct drm_device *dev,
 69			    struct drm_encoder *encoder,
 70			    int encoder_type)
 71{
 72	return drm_encoder_init(dev, encoder,
 73				&drm_simple_encoder_funcs_cleanup,
 74				encoder_type, NULL);
 75}
 76EXPORT_SYMBOL(drm_simple_encoder_init);
 77
 78void *__drmm_simple_encoder_alloc(struct drm_device *dev, size_t size,
 79				  size_t offset, int encoder_type)
 80{
 81	return __drmm_encoder_alloc(dev, size, offset, NULL, encoder_type,
 82				    NULL);
 83}
 84EXPORT_SYMBOL(__drmm_simple_encoder_alloc);
 85
 86static enum drm_mode_status
 87drm_simple_kms_crtc_mode_valid(struct drm_crtc *crtc,
 88			       const struct drm_display_mode *mode)
 89{
 90	struct drm_simple_display_pipe *pipe;
 91
 92	pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
 93	if (!pipe->funcs || !pipe->funcs->mode_valid)
 94		/* Anything goes */
 95		return MODE_OK;
 96
 97	return pipe->funcs->mode_valid(pipe, mode);
 98}
 99
100static int drm_simple_kms_crtc_check(struct drm_crtc *crtc,
101				     struct drm_atomic_state *state)
102{
103	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
104	int ret;
105
106	if (!crtc_state->enable)
107		goto out;
108
109	ret = drm_atomic_helper_check_crtc_primary_plane(crtc_state);
110	if (ret)
111		return ret;
112
113out:
114	return drm_atomic_add_affected_planes(state, crtc);
115}
116
117static void drm_simple_kms_crtc_enable(struct drm_crtc *crtc,
118				       struct drm_atomic_state *state)
119{
120	struct drm_plane *plane;
121	struct drm_simple_display_pipe *pipe;
122
123	pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
124	if (!pipe->funcs || !pipe->funcs->enable)
125		return;
126
127	plane = &pipe->plane;
128	pipe->funcs->enable(pipe, crtc->state, plane->state);
129}
130
131static void drm_simple_kms_crtc_disable(struct drm_crtc *crtc,
132					struct drm_atomic_state *state)
133{
134	struct drm_simple_display_pipe *pipe;
135
136	pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
137	if (!pipe->funcs || !pipe->funcs->disable)
138		return;
139
140	pipe->funcs->disable(pipe);
141}
142
143static const struct drm_crtc_helper_funcs drm_simple_kms_crtc_helper_funcs = {
144	.mode_valid = drm_simple_kms_crtc_mode_valid,
145	.atomic_check = drm_simple_kms_crtc_check,
146	.atomic_enable = drm_simple_kms_crtc_enable,
147	.atomic_disable = drm_simple_kms_crtc_disable,
148};
149
150static void drm_simple_kms_crtc_reset(struct drm_crtc *crtc)
151{
152	struct drm_simple_display_pipe *pipe;
153
154	pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
155	if (!pipe->funcs || !pipe->funcs->reset_crtc)
156		return drm_atomic_helper_crtc_reset(crtc);
157
158	return pipe->funcs->reset_crtc(pipe);
159}
160
161static struct drm_crtc_state *drm_simple_kms_crtc_duplicate_state(struct drm_crtc *crtc)
162{
163	struct drm_simple_display_pipe *pipe;
164
165	pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
166	if (!pipe->funcs || !pipe->funcs->duplicate_crtc_state)
167		return drm_atomic_helper_crtc_duplicate_state(crtc);
168
169	return pipe->funcs->duplicate_crtc_state(pipe);
170}
171
172static void drm_simple_kms_crtc_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *state)
173{
174	struct drm_simple_display_pipe *pipe;
175
176	pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
177	if (!pipe->funcs || !pipe->funcs->destroy_crtc_state)
178		drm_atomic_helper_crtc_destroy_state(crtc, state);
179	else
180		pipe->funcs->destroy_crtc_state(pipe, state);
181}
182
183static int drm_simple_kms_crtc_enable_vblank(struct drm_crtc *crtc)
184{
185	struct drm_simple_display_pipe *pipe;
186
187	pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
188	if (!pipe->funcs || !pipe->funcs->enable_vblank)
189		return 0;
190
191	return pipe->funcs->enable_vblank(pipe);
192}
193
194static void drm_simple_kms_crtc_disable_vblank(struct drm_crtc *crtc)
195{
196	struct drm_simple_display_pipe *pipe;
197
198	pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
199	if (!pipe->funcs || !pipe->funcs->disable_vblank)
200		return;
201
202	pipe->funcs->disable_vblank(pipe);
203}
204
205static const struct drm_crtc_funcs drm_simple_kms_crtc_funcs = {
206	.reset = drm_simple_kms_crtc_reset,
207	.destroy = drm_crtc_cleanup,
208	.set_config = drm_atomic_helper_set_config,
209	.page_flip = drm_atomic_helper_page_flip,
210	.atomic_duplicate_state = drm_simple_kms_crtc_duplicate_state,
211	.atomic_destroy_state = drm_simple_kms_crtc_destroy_state,
212	.enable_vblank = drm_simple_kms_crtc_enable_vblank,
213	.disable_vblank = drm_simple_kms_crtc_disable_vblank,
214};
215
216static int drm_simple_kms_plane_atomic_check(struct drm_plane *plane,
217					struct drm_atomic_state *state)
218{
219	struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state,
220									     plane);
221	struct drm_simple_display_pipe *pipe;
222	struct drm_crtc_state *crtc_state;
223	int ret;
224
225	pipe = container_of(plane, struct drm_simple_display_pipe, plane);
226	crtc_state = drm_atomic_get_new_crtc_state(state,
227						   &pipe->crtc);
228
229	ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state,
230						  DRM_PLANE_NO_SCALING,
231						  DRM_PLANE_NO_SCALING,
232						  false, false);
233	if (ret)
234		return ret;
235
236	if (!plane_state->visible)
237		return 0;
238
239	if (!pipe->funcs || !pipe->funcs->check)
240		return 0;
241
242	return pipe->funcs->check(pipe, plane_state, crtc_state);
243}
244
245static void drm_simple_kms_plane_atomic_update(struct drm_plane *plane,
246					struct drm_atomic_state *state)
247{
248	struct drm_plane_state *old_pstate = drm_atomic_get_old_plane_state(state,
249									    plane);
250	struct drm_simple_display_pipe *pipe;
251
252	pipe = container_of(plane, struct drm_simple_display_pipe, plane);
253	if (!pipe->funcs || !pipe->funcs->update)
254		return;
255
256	pipe->funcs->update(pipe, old_pstate);
257}
258
259static int drm_simple_kms_plane_prepare_fb(struct drm_plane *plane,
260					   struct drm_plane_state *state)
261{
262	struct drm_simple_display_pipe *pipe;
263
264	pipe = container_of(plane, struct drm_simple_display_pipe, plane);
265	if (!pipe->funcs || !pipe->funcs->prepare_fb) {
266		if (WARN_ON_ONCE(!drm_core_check_feature(plane->dev, DRIVER_GEM)))
267			return 0;
268
269		WARN_ON_ONCE(pipe->funcs && pipe->funcs->cleanup_fb);
270
271		return drm_gem_plane_helper_prepare_fb(plane, state);
272	}
273
274	return pipe->funcs->prepare_fb(pipe, state);
275}
276
277static void drm_simple_kms_plane_cleanup_fb(struct drm_plane *plane,
278					    struct drm_plane_state *state)
279{
280	struct drm_simple_display_pipe *pipe;
281
282	pipe = container_of(plane, struct drm_simple_display_pipe, plane);
283	if (!pipe->funcs || !pipe->funcs->cleanup_fb)
284		return;
285
286	pipe->funcs->cleanup_fb(pipe, state);
287}
288
289static int drm_simple_kms_plane_begin_fb_access(struct drm_plane *plane,
290						struct drm_plane_state *new_plane_state)
291{
292	struct drm_simple_display_pipe *pipe;
293
294	pipe = container_of(plane, struct drm_simple_display_pipe, plane);
295	if (!pipe->funcs || !pipe->funcs->begin_fb_access)
296		return 0;
297
298	return pipe->funcs->begin_fb_access(pipe, new_plane_state);
299}
300
301static void drm_simple_kms_plane_end_fb_access(struct drm_plane *plane,
302					       struct drm_plane_state *new_plane_state)
303{
304	struct drm_simple_display_pipe *pipe;
305
306	pipe = container_of(plane, struct drm_simple_display_pipe, plane);
307	if (!pipe->funcs || !pipe->funcs->end_fb_access)
308		return;
309
310	pipe->funcs->end_fb_access(pipe, new_plane_state);
311}
312
313static bool drm_simple_kms_format_mod_supported(struct drm_plane *plane,
314						uint32_t format,
315						uint64_t modifier)
316{
317	return modifier == DRM_FORMAT_MOD_LINEAR;
318}
319
320static const struct drm_plane_helper_funcs drm_simple_kms_plane_helper_funcs = {
321	.prepare_fb = drm_simple_kms_plane_prepare_fb,
322	.cleanup_fb = drm_simple_kms_plane_cleanup_fb,
323	.begin_fb_access = drm_simple_kms_plane_begin_fb_access,
324	.end_fb_access = drm_simple_kms_plane_end_fb_access,
325	.atomic_check = drm_simple_kms_plane_atomic_check,
326	.atomic_update = drm_simple_kms_plane_atomic_update,
327};
328
329static void drm_simple_kms_plane_reset(struct drm_plane *plane)
330{
331	struct drm_simple_display_pipe *pipe;
332
333	pipe = container_of(plane, struct drm_simple_display_pipe, plane);
334	if (!pipe->funcs || !pipe->funcs->reset_plane)
335		return drm_atomic_helper_plane_reset(plane);
336
337	return pipe->funcs->reset_plane(pipe);
338}
339
340static struct drm_plane_state *drm_simple_kms_plane_duplicate_state(struct drm_plane *plane)
341{
342	struct drm_simple_display_pipe *pipe;
343
344	pipe = container_of(plane, struct drm_simple_display_pipe, plane);
345	if (!pipe->funcs || !pipe->funcs->duplicate_plane_state)
346		return drm_atomic_helper_plane_duplicate_state(plane);
347
348	return pipe->funcs->duplicate_plane_state(pipe);
349}
350
351static void drm_simple_kms_plane_destroy_state(struct drm_plane *plane,
352					       struct drm_plane_state *state)
353{
354	struct drm_simple_display_pipe *pipe;
355
356	pipe = container_of(plane, struct drm_simple_display_pipe, plane);
357	if (!pipe->funcs || !pipe->funcs->destroy_plane_state)
358		drm_atomic_helper_plane_destroy_state(plane, state);
359	else
360		pipe->funcs->destroy_plane_state(pipe, state);
361}
362
363static const struct drm_plane_funcs drm_simple_kms_plane_funcs = {
364	.update_plane		= drm_atomic_helper_update_plane,
365	.disable_plane		= drm_atomic_helper_disable_plane,
366	.destroy		= drm_plane_cleanup,
367	.reset			= drm_simple_kms_plane_reset,
368	.atomic_duplicate_state	= drm_simple_kms_plane_duplicate_state,
369	.atomic_destroy_state	= drm_simple_kms_plane_destroy_state,
370	.format_mod_supported   = drm_simple_kms_format_mod_supported,
371};
372
373/**
374 * drm_simple_display_pipe_attach_bridge - Attach a bridge to the display pipe
375 * @pipe: simple display pipe object
376 * @bridge: bridge to attach
377 *
378 * Makes it possible to still use the drm_simple_display_pipe helpers when
379 * a DRM bridge has to be used.
380 *
381 * Note that you probably want to initialize the pipe by passing a NULL
382 * connector to drm_simple_display_pipe_init().
383 *
384 * Returns:
385 * Zero on success, negative error code on failure.
386 */
387int drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe *pipe,
388					  struct drm_bridge *bridge)
389{
390	return drm_bridge_attach(&pipe->encoder, bridge, NULL, 0);
391}
392EXPORT_SYMBOL(drm_simple_display_pipe_attach_bridge);
393
394/**
395 * drm_simple_display_pipe_init - Initialize a simple display pipeline
396 * @dev: DRM device
397 * @pipe: simple display pipe object to initialize
398 * @funcs: callbacks for the display pipe (optional)
399 * @formats: array of supported formats (DRM_FORMAT\_\*)
400 * @format_count: number of elements in @formats
401 * @format_modifiers: array of formats modifiers
402 * @connector: connector to attach and register (optional)
403 *
404 * Sets up a display pipeline which consist of a really simple
405 * plane-crtc-encoder pipe.
406 *
407 * If a connector is supplied, the pipe will be coupled with the provided
408 * connector. You may supply a NULL connector when using drm bridges, that
409 * handle connectors themselves (see drm_simple_display_pipe_attach_bridge()).
410 *
411 * Teardown of a simple display pipe is all handled automatically by the drm
412 * core through calling drm_mode_config_cleanup(). Drivers afterwards need to
413 * release the memory for the structure themselves.
414 *
415 * Returns:
416 * Zero on success, negative error code on failure.
417 */
418int drm_simple_display_pipe_init(struct drm_device *dev,
419			struct drm_simple_display_pipe *pipe,
420			const struct drm_simple_display_pipe_funcs *funcs,
421			const uint32_t *formats, unsigned int format_count,
422			const uint64_t *format_modifiers,
423			struct drm_connector *connector)
424{
425	struct drm_encoder *encoder = &pipe->encoder;
426	struct drm_plane *plane = &pipe->plane;
427	struct drm_crtc *crtc = &pipe->crtc;
428	int ret;
429
430	pipe->connector = connector;
431	pipe->funcs = funcs;
432
433	drm_plane_helper_add(plane, &drm_simple_kms_plane_helper_funcs);
434	ret = drm_universal_plane_init(dev, plane, 0,
435				       &drm_simple_kms_plane_funcs,
436				       formats, format_count,
437				       format_modifiers,
438				       DRM_PLANE_TYPE_PRIMARY, NULL);
439	if (ret)
440		return ret;
441
442	drm_crtc_helper_add(crtc, &drm_simple_kms_crtc_helper_funcs);
443	ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
444					&drm_simple_kms_crtc_funcs, NULL);
445	if (ret)
446		return ret;
447
448	encoder->possible_crtcs = drm_crtc_mask(crtc);
449	ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_NONE);
450	if (ret || !connector)
451		return ret;
452
453	return drm_connector_attach_encoder(connector, encoder);
454}
455EXPORT_SYMBOL(drm_simple_display_pipe_init);
456
457MODULE_DESCRIPTION("Helpers for drivers for simple display hardware");
458MODULE_LICENSE("GPL");
~/ Read-only file preview.