blob: 7a00455ca568b0e82f35f6a6a5d972f26d41b204 [file] [log] [blame]
Noralf Trønnes5b809072016-06-10 16:55:59 +02001/*
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
Daniel Vetterea0dd852016-12-29 21:48:26 +010026 * pipeline is represented by &struct drm_simple_display_pipe and binds
Noralf Trønnes5b809072016-06-10 16:55:59 +020027 * 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
Linus Walleij40275dc2018-02-20 08:28:59 +010037static enum drm_mode_status
38drm_simple_kms_crtc_mode_valid(struct drm_crtc *crtc,
39 const struct drm_display_mode *mode)
40{
41 struct drm_simple_display_pipe *pipe;
42
43 pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
44 if (!pipe->funcs || !pipe->funcs->mode_valid)
45 /* Anything goes */
46 return MODE_OK;
47
48 return pipe->funcs->mode_valid(crtc, mode);
49}
50
Daniel Vetter6dcf0de2016-08-23 08:25:40 +020051static int drm_simple_kms_crtc_check(struct drm_crtc *crtc,
52 struct drm_crtc_state *state)
53{
Maarten Lankhorst765831d2017-07-12 10:13:29 +020054 bool has_primary = state->plane_mask &
55 BIT(drm_plane_index(crtc->primary));
56
57 /* We always want to have an active plane with an active CRTC */
58 if (has_primary != state->enable)
59 return -EINVAL;
60
Daniel Vetter6dcf0de2016-08-23 08:25:40 +020061 return drm_atomic_add_affected_planes(state->state, crtc);
62}
63
Laurent Pinchart0b20a0f2017-06-30 12:36:44 +030064static void drm_simple_kms_crtc_enable(struct drm_crtc *crtc,
65 struct drm_crtc_state *old_state)
Noralf Trønnes5b809072016-06-10 16:55:59 +020066{
Ville Syrjälä0c9c7fd2018-03-22 22:27:37 +020067 struct drm_plane *plane;
Noralf Trønnes5b809072016-06-10 16:55:59 +020068 struct drm_simple_display_pipe *pipe;
69
70 pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
71 if (!pipe->funcs || !pipe->funcs->enable)
72 return;
73
Ville Syrjälä0c9c7fd2018-03-22 22:27:37 +020074 plane = &pipe->plane;
75 pipe->funcs->enable(pipe, crtc->state, plane->state);
Noralf Trønnes5b809072016-06-10 16:55:59 +020076}
77
Laurent Pinchart64581712017-06-30 12:36:45 +030078static void drm_simple_kms_crtc_disable(struct drm_crtc *crtc,
79 struct drm_crtc_state *old_state)
Noralf Trønnes5b809072016-06-10 16:55:59 +020080{
81 struct drm_simple_display_pipe *pipe;
82
83 pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
84 if (!pipe->funcs || !pipe->funcs->disable)
85 return;
86
87 pipe->funcs->disable(pipe);
88}
89
90static const struct drm_crtc_helper_funcs drm_simple_kms_crtc_helper_funcs = {
Linus Walleij40275dc2018-02-20 08:28:59 +010091 .mode_valid = drm_simple_kms_crtc_mode_valid,
Daniel Vetter6dcf0de2016-08-23 08:25:40 +020092 .atomic_check = drm_simple_kms_crtc_check,
Laurent Pinchart0b20a0f2017-06-30 12:36:44 +030093 .atomic_enable = drm_simple_kms_crtc_enable,
Laurent Pinchart64581712017-06-30 12:36:45 +030094 .atomic_disable = drm_simple_kms_crtc_disable,
Noralf Trønnes5b809072016-06-10 16:55:59 +020095};
96
Oleksandr Andrushchenkoac86cba2018-02-12 10:52:51 +020097static int drm_simple_kms_crtc_enable_vblank(struct drm_crtc *crtc)
98{
99 struct drm_simple_display_pipe *pipe;
100
101 pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
102 if (!pipe->funcs || !pipe->funcs->enable_vblank)
103 return 0;
104
105 return pipe->funcs->enable_vblank(pipe);
106}
107
108static void drm_simple_kms_crtc_disable_vblank(struct drm_crtc *crtc)
109{
110 struct drm_simple_display_pipe *pipe;
111
112 pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
113 if (!pipe->funcs || !pipe->funcs->disable_vblank)
114 return;
115
116 pipe->funcs->disable_vblank(pipe);
117}
118
Noralf Trønnes5b809072016-06-10 16:55:59 +0200119static const struct drm_crtc_funcs drm_simple_kms_crtc_funcs = {
120 .reset = drm_atomic_helper_crtc_reset,
121 .destroy = drm_crtc_cleanup,
122 .set_config = drm_atomic_helper_set_config,
123 .page_flip = drm_atomic_helper_page_flip,
124 .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
125 .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
Oleksandr Andrushchenkoac86cba2018-02-12 10:52:51 +0200126 .enable_vblank = drm_simple_kms_crtc_enable_vblank,
127 .disable_vblank = drm_simple_kms_crtc_disable_vblank,
Noralf Trønnes5b809072016-06-10 16:55:59 +0200128};
129
130static int drm_simple_kms_plane_atomic_check(struct drm_plane *plane,
131 struct drm_plane_state *plane_state)
132{
Noralf Trønnes5b809072016-06-10 16:55:59 +0200133 struct drm_simple_display_pipe *pipe;
134 struct drm_crtc_state *crtc_state;
Noralf Trønnes5b809072016-06-10 16:55:59 +0200135 int ret;
136
137 pipe = container_of(plane, struct drm_simple_display_pipe, plane);
Maarten Lankhorstb4d93672017-03-01 10:22:10 +0100138 crtc_state = drm_atomic_get_new_crtc_state(plane_state->state,
139 &pipe->crtc);
Ville Syrjälä4be12cc2016-07-26 19:07:04 +0300140
Ville Syrjäläa01cb8b2017-11-01 22:16:19 +0200141 ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state,
Ville Syrjäläa01cb8b2017-11-01 22:16:19 +0200142 DRM_PLANE_HELPER_NO_SCALING,
143 DRM_PLANE_HELPER_NO_SCALING,
144 false, true);
Noralf Trønnes5b809072016-06-10 16:55:59 +0200145 if (ret)
146 return ret;
147
Ville Syrjälä4be12cc2016-07-26 19:07:04 +0300148 if (!plane_state->visible)
Oleksandr Andrushchenko4751cf72018-02-22 08:09:19 +0200149 return 0;
150
Noralf Trønnes5b809072016-06-10 16:55:59 +0200151 if (!pipe->funcs || !pipe->funcs->check)
152 return 0;
153
154 return pipe->funcs->check(pipe, plane_state, crtc_state);
155}
156
157static void drm_simple_kms_plane_atomic_update(struct drm_plane *plane,
Eric Anholtbcd2ba02017-03-20 16:36:15 -0700158 struct drm_plane_state *old_pstate)
Noralf Trønnes5b809072016-06-10 16:55:59 +0200159{
160 struct drm_simple_display_pipe *pipe;
161
162 pipe = container_of(plane, struct drm_simple_display_pipe, plane);
163 if (!pipe->funcs || !pipe->funcs->update)
164 return;
165
Eric Anholtbcd2ba02017-03-20 16:36:15 -0700166 pipe->funcs->update(pipe, old_pstate);
Noralf Trønnes5b809072016-06-10 16:55:59 +0200167}
168
Marek Vasut7d83a152016-10-02 19:01:24 +0200169static int drm_simple_kms_plane_prepare_fb(struct drm_plane *plane,
170 struct drm_plane_state *state)
171{
172 struct drm_simple_display_pipe *pipe;
173
174 pipe = container_of(plane, struct drm_simple_display_pipe, plane);
175 if (!pipe->funcs || !pipe->funcs->prepare_fb)
176 return 0;
177
178 return pipe->funcs->prepare_fb(pipe, state);
179}
180
181static void drm_simple_kms_plane_cleanup_fb(struct drm_plane *plane,
182 struct drm_plane_state *state)
183{
184 struct drm_simple_display_pipe *pipe;
185
186 pipe = container_of(plane, struct drm_simple_display_pipe, plane);
187 if (!pipe->funcs || !pipe->funcs->cleanup_fb)
188 return;
189
190 pipe->funcs->cleanup_fb(pipe, state);
191}
192
Noralf Trønnes5b809072016-06-10 16:55:59 +0200193static const struct drm_plane_helper_funcs drm_simple_kms_plane_helper_funcs = {
Marek Vasut7d83a152016-10-02 19:01:24 +0200194 .prepare_fb = drm_simple_kms_plane_prepare_fb,
195 .cleanup_fb = drm_simple_kms_plane_cleanup_fb,
Noralf Trønnes5b809072016-06-10 16:55:59 +0200196 .atomic_check = drm_simple_kms_plane_atomic_check,
197 .atomic_update = drm_simple_kms_plane_atomic_update,
198};
199
200static const struct drm_plane_funcs drm_simple_kms_plane_funcs = {
201 .update_plane = drm_atomic_helper_update_plane,
202 .disable_plane = drm_atomic_helper_disable_plane,
203 .destroy = drm_plane_cleanup,
204 .reset = drm_atomic_helper_plane_reset,
205 .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
206 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
207};
208
209/**
Andrea Merello315486c2016-08-25 11:04:34 +0200210 * drm_simple_display_pipe_attach_bridge - Attach a bridge to the display pipe
211 * @pipe: simple display pipe object
212 * @bridge: bridge to attach
213 *
214 * Makes it possible to still use the drm_simple_display_pipe helpers when
215 * a DRM bridge has to be used.
216 *
217 * Note that you probably want to initialize the pipe by passing a NULL
218 * connector to drm_simple_display_pipe_init().
219 *
220 * Returns:
221 * Zero on success, negative error code on failure.
222 */
223int drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe *pipe,
224 struct drm_bridge *bridge)
225{
Laurent Pinchart3bb80f22016-11-28 17:59:08 +0200226 return drm_bridge_attach(&pipe->encoder, bridge, NULL);
Andrea Merello315486c2016-08-25 11:04:34 +0200227}
228EXPORT_SYMBOL(drm_simple_display_pipe_attach_bridge);
229
230/**
Noralf Trønnes5b809072016-06-10 16:55:59 +0200231 * drm_simple_display_pipe_init - Initialize a simple display pipeline
232 * @dev: DRM device
233 * @pipe: simple display pipe object to initialize
234 * @funcs: callbacks for the display pipe (optional)
Daniel Vetter62cacc72016-08-12 22:48:37 +0200235 * @formats: array of supported formats (DRM_FORMAT\_\*)
Noralf Trønnes5b809072016-06-10 16:55:59 +0200236 * @format_count: number of elements in @formats
Ben Widawskye6fc3b62017-07-23 20:46:38 -0700237 * @format_modifiers: array of formats modifiers
Andrea Merello4f993972016-08-25 11:04:33 +0200238 * @connector: connector to attach and register (optional)
Noralf Trønnes5b809072016-06-10 16:55:59 +0200239 *
240 * Sets up a display pipeline which consist of a really simple
Andrea Merello4f993972016-08-25 11:04:33 +0200241 * plane-crtc-encoder pipe.
242 *
243 * If a connector is supplied, the pipe will be coupled with the provided
244 * connector. You may supply a NULL connector when using drm bridges, that
245 * handle connectors themselves (see drm_simple_display_pipe_attach_bridge()).
246 *
Noralf Trønnes5b809072016-06-10 16:55:59 +0200247 * Teardown of a simple display pipe is all handled automatically by the drm
248 * core through calling drm_mode_config_cleanup(). Drivers afterwards need to
249 * release the memory for the structure themselves.
250 *
251 * Returns:
252 * Zero on success, negative error code on failure.
253 */
254int drm_simple_display_pipe_init(struct drm_device *dev,
255 struct drm_simple_display_pipe *pipe,
256 const struct drm_simple_display_pipe_funcs *funcs,
257 const uint32_t *formats, unsigned int format_count,
Ben Widawskye6fc3b62017-07-23 20:46:38 -0700258 const uint64_t *format_modifiers,
Noralf Trønnes5b809072016-06-10 16:55:59 +0200259 struct drm_connector *connector)
260{
261 struct drm_encoder *encoder = &pipe->encoder;
262 struct drm_plane *plane = &pipe->plane;
263 struct drm_crtc *crtc = &pipe->crtc;
264 int ret;
265
266 pipe->connector = connector;
267 pipe->funcs = funcs;
268
269 drm_plane_helper_add(plane, &drm_simple_kms_plane_helper_funcs);
270 ret = drm_universal_plane_init(dev, plane, 0,
271 &drm_simple_kms_plane_funcs,
272 formats, format_count,
Ben Widawskye6fc3b62017-07-23 20:46:38 -0700273 format_modifiers,
Noralf Trønnes5b809072016-06-10 16:55:59 +0200274 DRM_PLANE_TYPE_PRIMARY, NULL);
275 if (ret)
276 return ret;
277
278 drm_crtc_helper_add(crtc, &drm_simple_kms_crtc_helper_funcs);
279 ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
280 &drm_simple_kms_crtc_funcs, NULL);
281 if (ret)
282 return ret;
283
284 encoder->possible_crtcs = 1 << drm_crtc_index(crtc);
285 ret = drm_encoder_init(dev, encoder, &drm_simple_kms_encoder_funcs,
286 DRM_MODE_ENCODER_NONE, NULL);
Andrea Merello4f993972016-08-25 11:04:33 +0200287 if (ret || !connector)
Noralf Trønnes5b809072016-06-10 16:55:59 +0200288 return ret;
289
290 return drm_mode_connector_attach_encoder(connector, encoder);
291}
292EXPORT_SYMBOL(drm_simple_display_pipe_init);
293
294MODULE_LICENSE("GPL");