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 <linux/module.h>
11 #include <linux/slab.h>
12 
13 #include <drm/drm_atomic.h>
14 #include <drm/drm_atomic_helper.h>
15 #include <drm/drm_plane_helper.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 
34 static const struct drm_encoder_funcs drm_simple_kms_encoder_funcs = {
35 	.destroy = drm_encoder_cleanup,
36 };
37 
38 static enum drm_mode_status
39 drm_simple_kms_crtc_mode_valid(struct drm_crtc *crtc,
40 			       const struct drm_display_mode *mode)
41 {
42 	struct drm_simple_display_pipe *pipe;
43 
44 	pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
45 	if (!pipe->funcs || !pipe->funcs->mode_valid)
46 		/* Anything goes */
47 		return MODE_OK;
48 
49 	return pipe->funcs->mode_valid(crtc, mode);
50 }
51 
52 static int drm_simple_kms_crtc_check(struct drm_crtc *crtc,
53 				     struct drm_crtc_state *state)
54 {
55 	bool has_primary = state->plane_mask &
56 			   drm_plane_mask(crtc->primary);
57 
58 	/* We always want to have an active plane with an active CRTC */
59 	if (has_primary != state->enable)
60 		return -EINVAL;
61 
62 	return drm_atomic_add_affected_planes(state->state, crtc);
63 }
64 
65 static void drm_simple_kms_crtc_enable(struct drm_crtc *crtc,
66 				       struct drm_crtc_state *old_state)
67 {
68 	struct drm_plane *plane;
69 	struct drm_simple_display_pipe *pipe;
70 
71 	pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
72 	if (!pipe->funcs || !pipe->funcs->enable)
73 		return;
74 
75 	plane = &pipe->plane;
76 	pipe->funcs->enable(pipe, crtc->state, plane->state);
77 }
78 
79 static void drm_simple_kms_crtc_disable(struct drm_crtc *crtc,
80 					struct drm_crtc_state *old_state)
81 {
82 	struct drm_simple_display_pipe *pipe;
83 
84 	pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
85 	if (!pipe->funcs || !pipe->funcs->disable)
86 		return;
87 
88 	pipe->funcs->disable(pipe);
89 }
90 
91 static const struct drm_crtc_helper_funcs drm_simple_kms_crtc_helper_funcs = {
92 	.mode_valid = drm_simple_kms_crtc_mode_valid,
93 	.atomic_check = drm_simple_kms_crtc_check,
94 	.atomic_enable = drm_simple_kms_crtc_enable,
95 	.atomic_disable = drm_simple_kms_crtc_disable,
96 };
97 
98 static int drm_simple_kms_crtc_enable_vblank(struct drm_crtc *crtc)
99 {
100 	struct drm_simple_display_pipe *pipe;
101 
102 	pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
103 	if (!pipe->funcs || !pipe->funcs->enable_vblank)
104 		return 0;
105 
106 	return pipe->funcs->enable_vblank(pipe);
107 }
108 
109 static void drm_simple_kms_crtc_disable_vblank(struct drm_crtc *crtc)
110 {
111 	struct drm_simple_display_pipe *pipe;
112 
113 	pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
114 	if (!pipe->funcs || !pipe->funcs->disable_vblank)
115 		return;
116 
117 	pipe->funcs->disable_vblank(pipe);
118 }
119 
120 static const struct drm_crtc_funcs drm_simple_kms_crtc_funcs = {
121 	.reset = drm_atomic_helper_crtc_reset,
122 	.destroy = drm_crtc_cleanup,
123 	.set_config = drm_atomic_helper_set_config,
124 	.page_flip = drm_atomic_helper_page_flip,
125 	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
126 	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
127 	.enable_vblank = drm_simple_kms_crtc_enable_vblank,
128 	.disable_vblank = drm_simple_kms_crtc_disable_vblank,
129 };
130 
131 static int drm_simple_kms_plane_atomic_check(struct drm_plane *plane,
132 					struct drm_plane_state *plane_state)
133 {
134 	struct drm_simple_display_pipe *pipe;
135 	struct drm_crtc_state *crtc_state;
136 	int ret;
137 
138 	pipe = container_of(plane, struct drm_simple_display_pipe, plane);
139 	crtc_state = drm_atomic_get_new_crtc_state(plane_state->state,
140 						   &pipe->crtc);
141 
142 	ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state,
143 						  DRM_PLANE_HELPER_NO_SCALING,
144 						  DRM_PLANE_HELPER_NO_SCALING,
145 						  false, true);
146 	if (ret)
147 		return ret;
148 
149 	if (!plane_state->visible)
150 		return 0;
151 
152 	if (!pipe->funcs || !pipe->funcs->check)
153 		return 0;
154 
155 	return pipe->funcs->check(pipe, plane_state, crtc_state);
156 }
157 
158 static void drm_simple_kms_plane_atomic_update(struct drm_plane *plane,
159 					struct drm_plane_state *old_pstate)
160 {
161 	struct drm_simple_display_pipe *pipe;
162 
163 	pipe = container_of(plane, struct drm_simple_display_pipe, plane);
164 	if (!pipe->funcs || !pipe->funcs->update)
165 		return;
166 
167 	pipe->funcs->update(pipe, old_pstate);
168 }
169 
170 static int drm_simple_kms_plane_prepare_fb(struct drm_plane *plane,
171 					   struct drm_plane_state *state)
172 {
173 	struct drm_simple_display_pipe *pipe;
174 
175 	pipe = container_of(plane, struct drm_simple_display_pipe, plane);
176 	if (!pipe->funcs || !pipe->funcs->prepare_fb)
177 		return 0;
178 
179 	return pipe->funcs->prepare_fb(pipe, state);
180 }
181 
182 static void drm_simple_kms_plane_cleanup_fb(struct drm_plane *plane,
183 					    struct drm_plane_state *state)
184 {
185 	struct drm_simple_display_pipe *pipe;
186 
187 	pipe = container_of(plane, struct drm_simple_display_pipe, plane);
188 	if (!pipe->funcs || !pipe->funcs->cleanup_fb)
189 		return;
190 
191 	pipe->funcs->cleanup_fb(pipe, state);
192 }
193 
194 static bool drm_simple_kms_format_mod_supported(struct drm_plane *plane,
195 						uint32_t format,
196 						uint64_t modifier)
197 {
198 	return modifier == DRM_FORMAT_MOD_LINEAR;
199 }
200 
201 static const struct drm_plane_helper_funcs drm_simple_kms_plane_helper_funcs = {
202 	.prepare_fb = drm_simple_kms_plane_prepare_fb,
203 	.cleanup_fb = drm_simple_kms_plane_cleanup_fb,
204 	.atomic_check = drm_simple_kms_plane_atomic_check,
205 	.atomic_update = drm_simple_kms_plane_atomic_update,
206 };
207 
208 static const struct drm_plane_funcs drm_simple_kms_plane_funcs = {
209 	.update_plane		= drm_atomic_helper_update_plane,
210 	.disable_plane		= drm_atomic_helper_disable_plane,
211 	.destroy		= drm_plane_cleanup,
212 	.reset			= drm_atomic_helper_plane_reset,
213 	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
214 	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
215 	.format_mod_supported   = drm_simple_kms_format_mod_supported,
216 };
217 
218 /**
219  * drm_simple_display_pipe_attach_bridge - Attach a bridge to the display pipe
220  * @pipe: simple display pipe object
221  * @bridge: bridge to attach
222  *
223  * Makes it possible to still use the drm_simple_display_pipe helpers when
224  * a DRM bridge has to be used.
225  *
226  * Note that you probably want to initialize the pipe by passing a NULL
227  * connector to drm_simple_display_pipe_init().
228  *
229  * Returns:
230  * Zero on success, negative error code on failure.
231  */
232 int drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe *pipe,
233 					  struct drm_bridge *bridge)
234 {
235 	return drm_bridge_attach(&pipe->encoder, bridge, NULL);
236 }
237 EXPORT_SYMBOL(drm_simple_display_pipe_attach_bridge);
238 
239 /**
240  * drm_simple_display_pipe_init - Initialize a simple display pipeline
241  * @dev: DRM device
242  * @pipe: simple display pipe object to initialize
243  * @funcs: callbacks for the display pipe (optional)
244  * @formats: array of supported formats (DRM_FORMAT\_\*)
245  * @format_count: number of elements in @formats
246  * @format_modifiers: array of formats modifiers
247  * @connector: connector to attach and register (optional)
248  *
249  * Sets up a display pipeline which consist of a really simple
250  * plane-crtc-encoder pipe.
251  *
252  * If a connector is supplied, the pipe will be coupled with the provided
253  * connector. You may supply a NULL connector when using drm bridges, that
254  * handle connectors themselves (see drm_simple_display_pipe_attach_bridge()).
255  *
256  * Teardown of a simple display pipe is all handled automatically by the drm
257  * core through calling drm_mode_config_cleanup(). Drivers afterwards need to
258  * release the memory for the structure themselves.
259  *
260  * Returns:
261  * Zero on success, negative error code on failure.
262  */
263 int drm_simple_display_pipe_init(struct drm_device *dev,
264 			struct drm_simple_display_pipe *pipe,
265 			const struct drm_simple_display_pipe_funcs *funcs,
266 			const uint32_t *formats, unsigned int format_count,
267 			const uint64_t *format_modifiers,
268 			struct drm_connector *connector)
269 {
270 	struct drm_encoder *encoder = &pipe->encoder;
271 	struct drm_plane *plane = &pipe->plane;
272 	struct drm_crtc *crtc = &pipe->crtc;
273 	int ret;
274 
275 	pipe->connector = connector;
276 	pipe->funcs = funcs;
277 
278 	drm_plane_helper_add(plane, &drm_simple_kms_plane_helper_funcs);
279 	ret = drm_universal_plane_init(dev, plane, 0,
280 				       &drm_simple_kms_plane_funcs,
281 				       formats, format_count,
282 				       format_modifiers,
283 				       DRM_PLANE_TYPE_PRIMARY, NULL);
284 	if (ret)
285 		return ret;
286 
287 	drm_crtc_helper_add(crtc, &drm_simple_kms_crtc_helper_funcs);
288 	ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
289 					&drm_simple_kms_crtc_funcs, NULL);
290 	if (ret)
291 		return ret;
292 
293 	encoder->possible_crtcs = drm_crtc_mask(crtc);
294 	ret = drm_encoder_init(dev, encoder, &drm_simple_kms_encoder_funcs,
295 			       DRM_MODE_ENCODER_NONE, NULL);
296 	if (ret || !connector)
297 		return ret;
298 
299 	return drm_connector_attach_encoder(connector, encoder);
300 }
301 EXPORT_SYMBOL(drm_simple_display_pipe_init);
302 
303 MODULE_LICENSE("GPL");
304