xref: /OK3568_Linux_fs/kernel/drivers/gpu/drm/drm_simple_kms_helper.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright (C) 2016 Noralf Trønnes
4*4882a593Smuzhiyun  */
5*4882a593Smuzhiyun 
6*4882a593Smuzhiyun #include <linux/module.h>
7*4882a593Smuzhiyun #include <linux/slab.h>
8*4882a593Smuzhiyun 
9*4882a593Smuzhiyun #include <drm/drm_atomic.h>
10*4882a593Smuzhiyun #include <drm/drm_atomic_helper.h>
11*4882a593Smuzhiyun #include <drm/drm_bridge.h>
12*4882a593Smuzhiyun #include <drm/drm_plane_helper.h>
13*4882a593Smuzhiyun #include <drm/drm_probe_helper.h>
14*4882a593Smuzhiyun #include <drm/drm_simple_kms_helper.h>
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun /**
17*4882a593Smuzhiyun  * DOC: overview
18*4882a593Smuzhiyun  *
19*4882a593Smuzhiyun  * This helper library provides helpers for drivers for simple display
20*4882a593Smuzhiyun  * hardware.
21*4882a593Smuzhiyun  *
22*4882a593Smuzhiyun  * drm_simple_display_pipe_init() initializes a simple display pipeline
23*4882a593Smuzhiyun  * which has only one full-screen scanout buffer feeding one output. The
24*4882a593Smuzhiyun  * pipeline is represented by &struct drm_simple_display_pipe and binds
25*4882a593Smuzhiyun  * together &drm_plane, &drm_crtc and &drm_encoder structures into one fixed
26*4882a593Smuzhiyun  * entity. Some flexibility for code reuse is provided through a separately
27*4882a593Smuzhiyun  * allocated &drm_connector object and supporting optional &drm_bridge
28*4882a593Smuzhiyun  * encoder drivers.
29*4882a593Smuzhiyun  *
30*4882a593Smuzhiyun  * Many drivers require only a very simple encoder that fulfills the minimum
31*4882a593Smuzhiyun  * requirements of the display pipeline and does not add additional
32*4882a593Smuzhiyun  * functionality. The function drm_simple_encoder_init() provides an
33*4882a593Smuzhiyun  * implementation of such an encoder.
34*4882a593Smuzhiyun  */
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun static const struct drm_encoder_funcs drm_simple_encoder_funcs_cleanup = {
37*4882a593Smuzhiyun 	.destroy = drm_encoder_cleanup,
38*4882a593Smuzhiyun };
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun /**
41*4882a593Smuzhiyun  * drm_simple_encoder_init - Initialize a preallocated encoder with
42*4882a593Smuzhiyun  *                           basic functionality.
43*4882a593Smuzhiyun  * @dev: drm device
44*4882a593Smuzhiyun  * @encoder: the encoder to initialize
45*4882a593Smuzhiyun  * @encoder_type: user visible type of the encoder
46*4882a593Smuzhiyun  *
47*4882a593Smuzhiyun  * Initialises a preallocated encoder that has no further functionality.
48*4882a593Smuzhiyun  * Settings for possible CRTC and clones are left to their initial values.
49*4882a593Smuzhiyun  * The encoder will be cleaned up automatically as part of the mode-setting
50*4882a593Smuzhiyun  * cleanup.
51*4882a593Smuzhiyun  *
52*4882a593Smuzhiyun  * The caller of drm_simple_encoder_init() is responsible for freeing
53*4882a593Smuzhiyun  * the encoder's memory after the encoder has been cleaned up. At the
54*4882a593Smuzhiyun  * moment this only works reliably if the encoder data structure is
55*4882a593Smuzhiyun  * stored in the device structure. Free the encoder's memory as part of
56*4882a593Smuzhiyun  * the device release function.
57*4882a593Smuzhiyun  *
58*4882a593Smuzhiyun  * FIXME: Later improvements to DRM's resource management may allow for
59*4882a593Smuzhiyun  *        an automated kfree() of the encoder's memory.
60*4882a593Smuzhiyun  *
61*4882a593Smuzhiyun  * Returns:
62*4882a593Smuzhiyun  * Zero on success, error code on failure.
63*4882a593Smuzhiyun  */
drm_simple_encoder_init(struct drm_device * dev,struct drm_encoder * encoder,int encoder_type)64*4882a593Smuzhiyun int drm_simple_encoder_init(struct drm_device *dev,
65*4882a593Smuzhiyun 			    struct drm_encoder *encoder,
66*4882a593Smuzhiyun 			    int encoder_type)
67*4882a593Smuzhiyun {
68*4882a593Smuzhiyun 	return drm_encoder_init(dev, encoder,
69*4882a593Smuzhiyun 				&drm_simple_encoder_funcs_cleanup,
70*4882a593Smuzhiyun 				encoder_type, NULL);
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun EXPORT_SYMBOL(drm_simple_encoder_init);
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun static enum drm_mode_status
drm_simple_kms_crtc_mode_valid(struct drm_crtc * crtc,const struct drm_display_mode * mode)75*4882a593Smuzhiyun drm_simple_kms_crtc_mode_valid(struct drm_crtc *crtc,
76*4882a593Smuzhiyun 			       const struct drm_display_mode *mode)
77*4882a593Smuzhiyun {
78*4882a593Smuzhiyun 	struct drm_simple_display_pipe *pipe;
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun 	pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
81*4882a593Smuzhiyun 	if (!pipe->funcs || !pipe->funcs->mode_valid)
82*4882a593Smuzhiyun 		/* Anything goes */
83*4882a593Smuzhiyun 		return MODE_OK;
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	return pipe->funcs->mode_valid(pipe, mode);
86*4882a593Smuzhiyun }
87*4882a593Smuzhiyun 
drm_simple_kms_crtc_check(struct drm_crtc * crtc,struct drm_crtc_state * state)88*4882a593Smuzhiyun static int drm_simple_kms_crtc_check(struct drm_crtc *crtc,
89*4882a593Smuzhiyun 				     struct drm_crtc_state *state)
90*4882a593Smuzhiyun {
91*4882a593Smuzhiyun 	bool has_primary = state->plane_mask &
92*4882a593Smuzhiyun 			   drm_plane_mask(crtc->primary);
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 	/* We always want to have an active plane with an active CRTC */
95*4882a593Smuzhiyun 	if (has_primary != state->enable)
96*4882a593Smuzhiyun 		return -EINVAL;
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun 	return drm_atomic_add_affected_planes(state->state, crtc);
99*4882a593Smuzhiyun }
100*4882a593Smuzhiyun 
drm_simple_kms_crtc_enable(struct drm_crtc * crtc,struct drm_crtc_state * old_state)101*4882a593Smuzhiyun static void drm_simple_kms_crtc_enable(struct drm_crtc *crtc,
102*4882a593Smuzhiyun 				       struct drm_crtc_state *old_state)
103*4882a593Smuzhiyun {
104*4882a593Smuzhiyun 	struct drm_plane *plane;
105*4882a593Smuzhiyun 	struct drm_simple_display_pipe *pipe;
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun 	pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
108*4882a593Smuzhiyun 	if (!pipe->funcs || !pipe->funcs->enable)
109*4882a593Smuzhiyun 		return;
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun 	plane = &pipe->plane;
112*4882a593Smuzhiyun 	pipe->funcs->enable(pipe, crtc->state, plane->state);
113*4882a593Smuzhiyun }
114*4882a593Smuzhiyun 
drm_simple_kms_crtc_disable(struct drm_crtc * crtc,struct drm_crtc_state * old_state)115*4882a593Smuzhiyun static void drm_simple_kms_crtc_disable(struct drm_crtc *crtc,
116*4882a593Smuzhiyun 					struct drm_crtc_state *old_state)
117*4882a593Smuzhiyun {
118*4882a593Smuzhiyun 	struct drm_simple_display_pipe *pipe;
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun 	pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
121*4882a593Smuzhiyun 	if (!pipe->funcs || !pipe->funcs->disable)
122*4882a593Smuzhiyun 		return;
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun 	pipe->funcs->disable(pipe);
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun static const struct drm_crtc_helper_funcs drm_simple_kms_crtc_helper_funcs = {
128*4882a593Smuzhiyun 	.mode_valid = drm_simple_kms_crtc_mode_valid,
129*4882a593Smuzhiyun 	.atomic_check = drm_simple_kms_crtc_check,
130*4882a593Smuzhiyun 	.atomic_enable = drm_simple_kms_crtc_enable,
131*4882a593Smuzhiyun 	.atomic_disable = drm_simple_kms_crtc_disable,
132*4882a593Smuzhiyun };
133*4882a593Smuzhiyun 
drm_simple_kms_crtc_enable_vblank(struct drm_crtc * crtc)134*4882a593Smuzhiyun static int drm_simple_kms_crtc_enable_vblank(struct drm_crtc *crtc)
135*4882a593Smuzhiyun {
136*4882a593Smuzhiyun 	struct drm_simple_display_pipe *pipe;
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun 	pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
139*4882a593Smuzhiyun 	if (!pipe->funcs || !pipe->funcs->enable_vblank)
140*4882a593Smuzhiyun 		return 0;
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun 	return pipe->funcs->enable_vblank(pipe);
143*4882a593Smuzhiyun }
144*4882a593Smuzhiyun 
drm_simple_kms_crtc_disable_vblank(struct drm_crtc * crtc)145*4882a593Smuzhiyun static void drm_simple_kms_crtc_disable_vblank(struct drm_crtc *crtc)
146*4882a593Smuzhiyun {
147*4882a593Smuzhiyun 	struct drm_simple_display_pipe *pipe;
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun 	pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
150*4882a593Smuzhiyun 	if (!pipe->funcs || !pipe->funcs->disable_vblank)
151*4882a593Smuzhiyun 		return;
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	pipe->funcs->disable_vblank(pipe);
154*4882a593Smuzhiyun }
155*4882a593Smuzhiyun 
156*4882a593Smuzhiyun static const struct drm_crtc_funcs drm_simple_kms_crtc_funcs = {
157*4882a593Smuzhiyun 	.reset = drm_atomic_helper_crtc_reset,
158*4882a593Smuzhiyun 	.destroy = drm_crtc_cleanup,
159*4882a593Smuzhiyun 	.set_config = drm_atomic_helper_set_config,
160*4882a593Smuzhiyun 	.page_flip = drm_atomic_helper_page_flip,
161*4882a593Smuzhiyun 	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
162*4882a593Smuzhiyun 	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
163*4882a593Smuzhiyun 	.enable_vblank = drm_simple_kms_crtc_enable_vblank,
164*4882a593Smuzhiyun 	.disable_vblank = drm_simple_kms_crtc_disable_vblank,
165*4882a593Smuzhiyun };
166*4882a593Smuzhiyun 
drm_simple_kms_plane_atomic_check(struct drm_plane * plane,struct drm_plane_state * plane_state)167*4882a593Smuzhiyun static int drm_simple_kms_plane_atomic_check(struct drm_plane *plane,
168*4882a593Smuzhiyun 					struct drm_plane_state *plane_state)
169*4882a593Smuzhiyun {
170*4882a593Smuzhiyun 	struct drm_simple_display_pipe *pipe;
171*4882a593Smuzhiyun 	struct drm_crtc_state *crtc_state;
172*4882a593Smuzhiyun 	int ret;
173*4882a593Smuzhiyun 
174*4882a593Smuzhiyun 	pipe = container_of(plane, struct drm_simple_display_pipe, plane);
175*4882a593Smuzhiyun 	crtc_state = drm_atomic_get_new_crtc_state(plane_state->state,
176*4882a593Smuzhiyun 						   &pipe->crtc);
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 	ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state,
179*4882a593Smuzhiyun 						  DRM_PLANE_HELPER_NO_SCALING,
180*4882a593Smuzhiyun 						  DRM_PLANE_HELPER_NO_SCALING,
181*4882a593Smuzhiyun 						  false, true);
182*4882a593Smuzhiyun 	if (ret)
183*4882a593Smuzhiyun 		return ret;
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun 	if (!plane_state->visible)
186*4882a593Smuzhiyun 		return 0;
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 	if (!pipe->funcs || !pipe->funcs->check)
189*4882a593Smuzhiyun 		return 0;
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun 	return pipe->funcs->check(pipe, plane_state, crtc_state);
192*4882a593Smuzhiyun }
193*4882a593Smuzhiyun 
drm_simple_kms_plane_atomic_update(struct drm_plane * plane,struct drm_plane_state * old_pstate)194*4882a593Smuzhiyun static void drm_simple_kms_plane_atomic_update(struct drm_plane *plane,
195*4882a593Smuzhiyun 					struct drm_plane_state *old_pstate)
196*4882a593Smuzhiyun {
197*4882a593Smuzhiyun 	struct drm_simple_display_pipe *pipe;
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun 	pipe = container_of(plane, struct drm_simple_display_pipe, plane);
200*4882a593Smuzhiyun 	if (!pipe->funcs || !pipe->funcs->update)
201*4882a593Smuzhiyun 		return;
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	pipe->funcs->update(pipe, old_pstate);
204*4882a593Smuzhiyun }
205*4882a593Smuzhiyun 
drm_simple_kms_plane_prepare_fb(struct drm_plane * plane,struct drm_plane_state * state)206*4882a593Smuzhiyun static int drm_simple_kms_plane_prepare_fb(struct drm_plane *plane,
207*4882a593Smuzhiyun 					   struct drm_plane_state *state)
208*4882a593Smuzhiyun {
209*4882a593Smuzhiyun 	struct drm_simple_display_pipe *pipe;
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun 	pipe = container_of(plane, struct drm_simple_display_pipe, plane);
212*4882a593Smuzhiyun 	if (!pipe->funcs || !pipe->funcs->prepare_fb)
213*4882a593Smuzhiyun 		return 0;
214*4882a593Smuzhiyun 
215*4882a593Smuzhiyun 	return pipe->funcs->prepare_fb(pipe, state);
216*4882a593Smuzhiyun }
217*4882a593Smuzhiyun 
drm_simple_kms_plane_cleanup_fb(struct drm_plane * plane,struct drm_plane_state * state)218*4882a593Smuzhiyun static void drm_simple_kms_plane_cleanup_fb(struct drm_plane *plane,
219*4882a593Smuzhiyun 					    struct drm_plane_state *state)
220*4882a593Smuzhiyun {
221*4882a593Smuzhiyun 	struct drm_simple_display_pipe *pipe;
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun 	pipe = container_of(plane, struct drm_simple_display_pipe, plane);
224*4882a593Smuzhiyun 	if (!pipe->funcs || !pipe->funcs->cleanup_fb)
225*4882a593Smuzhiyun 		return;
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 	pipe->funcs->cleanup_fb(pipe, state);
228*4882a593Smuzhiyun }
229*4882a593Smuzhiyun 
drm_simple_kms_format_mod_supported(struct drm_plane * plane,uint32_t format,uint64_t modifier)230*4882a593Smuzhiyun static bool drm_simple_kms_format_mod_supported(struct drm_plane *plane,
231*4882a593Smuzhiyun 						uint32_t format,
232*4882a593Smuzhiyun 						uint64_t modifier)
233*4882a593Smuzhiyun {
234*4882a593Smuzhiyun 	return modifier == DRM_FORMAT_MOD_LINEAR;
235*4882a593Smuzhiyun }
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun static const struct drm_plane_helper_funcs drm_simple_kms_plane_helper_funcs = {
238*4882a593Smuzhiyun 	.prepare_fb = drm_simple_kms_plane_prepare_fb,
239*4882a593Smuzhiyun 	.cleanup_fb = drm_simple_kms_plane_cleanup_fb,
240*4882a593Smuzhiyun 	.atomic_check = drm_simple_kms_plane_atomic_check,
241*4882a593Smuzhiyun 	.atomic_update = drm_simple_kms_plane_atomic_update,
242*4882a593Smuzhiyun };
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun static const struct drm_plane_funcs drm_simple_kms_plane_funcs = {
245*4882a593Smuzhiyun 	.update_plane		= drm_atomic_helper_update_plane,
246*4882a593Smuzhiyun 	.disable_plane		= drm_atomic_helper_disable_plane,
247*4882a593Smuzhiyun 	.destroy		= drm_plane_cleanup,
248*4882a593Smuzhiyun 	.reset			= drm_atomic_helper_plane_reset,
249*4882a593Smuzhiyun 	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
250*4882a593Smuzhiyun 	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
251*4882a593Smuzhiyun 	.format_mod_supported   = drm_simple_kms_format_mod_supported,
252*4882a593Smuzhiyun };
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun /**
255*4882a593Smuzhiyun  * drm_simple_display_pipe_attach_bridge - Attach a bridge to the display pipe
256*4882a593Smuzhiyun  * @pipe: simple display pipe object
257*4882a593Smuzhiyun  * @bridge: bridge to attach
258*4882a593Smuzhiyun  *
259*4882a593Smuzhiyun  * Makes it possible to still use the drm_simple_display_pipe helpers when
260*4882a593Smuzhiyun  * a DRM bridge has to be used.
261*4882a593Smuzhiyun  *
262*4882a593Smuzhiyun  * Note that you probably want to initialize the pipe by passing a NULL
263*4882a593Smuzhiyun  * connector to drm_simple_display_pipe_init().
264*4882a593Smuzhiyun  *
265*4882a593Smuzhiyun  * Returns:
266*4882a593Smuzhiyun  * Zero on success, negative error code on failure.
267*4882a593Smuzhiyun  */
drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe * pipe,struct drm_bridge * bridge)268*4882a593Smuzhiyun int drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe *pipe,
269*4882a593Smuzhiyun 					  struct drm_bridge *bridge)
270*4882a593Smuzhiyun {
271*4882a593Smuzhiyun 	return drm_bridge_attach(&pipe->encoder, bridge, NULL, 0);
272*4882a593Smuzhiyun }
273*4882a593Smuzhiyun EXPORT_SYMBOL(drm_simple_display_pipe_attach_bridge);
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun /**
276*4882a593Smuzhiyun  * drm_simple_display_pipe_init - Initialize a simple display pipeline
277*4882a593Smuzhiyun  * @dev: DRM device
278*4882a593Smuzhiyun  * @pipe: simple display pipe object to initialize
279*4882a593Smuzhiyun  * @funcs: callbacks for the display pipe (optional)
280*4882a593Smuzhiyun  * @formats: array of supported formats (DRM_FORMAT\_\*)
281*4882a593Smuzhiyun  * @format_count: number of elements in @formats
282*4882a593Smuzhiyun  * @format_modifiers: array of formats modifiers
283*4882a593Smuzhiyun  * @connector: connector to attach and register (optional)
284*4882a593Smuzhiyun  *
285*4882a593Smuzhiyun  * Sets up a display pipeline which consist of a really simple
286*4882a593Smuzhiyun  * plane-crtc-encoder pipe.
287*4882a593Smuzhiyun  *
288*4882a593Smuzhiyun  * If a connector is supplied, the pipe will be coupled with the provided
289*4882a593Smuzhiyun  * connector. You may supply a NULL connector when using drm bridges, that
290*4882a593Smuzhiyun  * handle connectors themselves (see drm_simple_display_pipe_attach_bridge()).
291*4882a593Smuzhiyun  *
292*4882a593Smuzhiyun  * Teardown of a simple display pipe is all handled automatically by the drm
293*4882a593Smuzhiyun  * core through calling drm_mode_config_cleanup(). Drivers afterwards need to
294*4882a593Smuzhiyun  * release the memory for the structure themselves.
295*4882a593Smuzhiyun  *
296*4882a593Smuzhiyun  * Returns:
297*4882a593Smuzhiyun  * Zero on success, negative error code on failure.
298*4882a593Smuzhiyun  */
drm_simple_display_pipe_init(struct drm_device * dev,struct drm_simple_display_pipe * pipe,const struct drm_simple_display_pipe_funcs * funcs,const uint32_t * formats,unsigned int format_count,const uint64_t * format_modifiers,struct drm_connector * connector)299*4882a593Smuzhiyun int drm_simple_display_pipe_init(struct drm_device *dev,
300*4882a593Smuzhiyun 			struct drm_simple_display_pipe *pipe,
301*4882a593Smuzhiyun 			const struct drm_simple_display_pipe_funcs *funcs,
302*4882a593Smuzhiyun 			const uint32_t *formats, unsigned int format_count,
303*4882a593Smuzhiyun 			const uint64_t *format_modifiers,
304*4882a593Smuzhiyun 			struct drm_connector *connector)
305*4882a593Smuzhiyun {
306*4882a593Smuzhiyun 	struct drm_encoder *encoder = &pipe->encoder;
307*4882a593Smuzhiyun 	struct drm_plane *plane = &pipe->plane;
308*4882a593Smuzhiyun 	struct drm_crtc *crtc = &pipe->crtc;
309*4882a593Smuzhiyun 	int ret;
310*4882a593Smuzhiyun 
311*4882a593Smuzhiyun 	pipe->connector = connector;
312*4882a593Smuzhiyun 	pipe->funcs = funcs;
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 	drm_plane_helper_add(plane, &drm_simple_kms_plane_helper_funcs);
315*4882a593Smuzhiyun 	ret = drm_universal_plane_init(dev, plane, 0,
316*4882a593Smuzhiyun 				       &drm_simple_kms_plane_funcs,
317*4882a593Smuzhiyun 				       formats, format_count,
318*4882a593Smuzhiyun 				       format_modifiers,
319*4882a593Smuzhiyun 				       DRM_PLANE_TYPE_PRIMARY, NULL);
320*4882a593Smuzhiyun 	if (ret)
321*4882a593Smuzhiyun 		return ret;
322*4882a593Smuzhiyun 
323*4882a593Smuzhiyun 	drm_crtc_helper_add(crtc, &drm_simple_kms_crtc_helper_funcs);
324*4882a593Smuzhiyun 	ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
325*4882a593Smuzhiyun 					&drm_simple_kms_crtc_funcs, NULL);
326*4882a593Smuzhiyun 	if (ret)
327*4882a593Smuzhiyun 		return ret;
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun 	encoder->possible_crtcs = drm_crtc_mask(crtc);
330*4882a593Smuzhiyun 	ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_NONE);
331*4882a593Smuzhiyun 	if (ret || !connector)
332*4882a593Smuzhiyun 		return ret;
333*4882a593Smuzhiyun 
334*4882a593Smuzhiyun 	return drm_connector_attach_encoder(connector, encoder);
335*4882a593Smuzhiyun }
336*4882a593Smuzhiyun EXPORT_SYMBOL(drm_simple_display_pipe_init);
337*4882a593Smuzhiyun 
338*4882a593Smuzhiyun MODULE_LICENSE("GPL");
339