xref: /linux/drivers/gpu/drm/drm_simple_kms_helper.c (revision 29b77ad7b9ca8c87152a1a9e8188970fb2a93df4)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
25b809074SNoralf Trønnes /*
35b809074SNoralf Trønnes  * Copyright (C) 2016 Noralf Trønnes
45b809074SNoralf Trønnes  */
55b809074SNoralf Trønnes 
60500c04eSSam Ravnborg #include <linux/module.h>
70500c04eSSam Ravnborg #include <linux/slab.h>
80500c04eSSam Ravnborg 
95b809074SNoralf Trønnes #include <drm/drm_atomic.h>
105b809074SNoralf Trønnes #include <drm/drm_atomic_helper.h>
11ee68c743SBoris Brezillon #include <drm/drm_bridge.h>
125b809074SNoralf Trønnes #include <drm/drm_plane_helper.h>
13fcd70cd3SDaniel Vetter #include <drm/drm_probe_helper.h>
145b809074SNoralf Trønnes #include <drm/drm_simple_kms_helper.h>
155b809074SNoralf Trønnes 
165b809074SNoralf Trønnes /**
175b809074SNoralf Trønnes  * DOC: overview
185b809074SNoralf Trønnes  *
195b809074SNoralf Trønnes  * This helper library provides helpers for drivers for simple display
205b809074SNoralf Trønnes  * hardware.
215b809074SNoralf Trønnes  *
225b809074SNoralf Trønnes  * drm_simple_display_pipe_init() initializes a simple display pipeline
235b809074SNoralf Trønnes  * which has only one full-screen scanout buffer feeding one output. The
24ea0dd85aSDaniel Vetter  * pipeline is represented by &struct drm_simple_display_pipe and binds
255b809074SNoralf Trønnes  * together &drm_plane, &drm_crtc and &drm_encoder structures into one fixed
265b809074SNoralf Trønnes  * entity. Some flexibility for code reuse is provided through a separately
275b809074SNoralf Trønnes  * allocated &drm_connector object and supporting optional &drm_bridge
285b809074SNoralf Trønnes  * encoder drivers.
2963170ac6SThomas Zimmermann  *
3063170ac6SThomas Zimmermann  * Many drivers require only a very simple encoder that fulfills the minimum
3163170ac6SThomas Zimmermann  * requirements of the display pipeline and does not add additional
3263170ac6SThomas Zimmermann  * functionality. The function drm_simple_encoder_init() provides an
3363170ac6SThomas Zimmermann  * implementation of such an encoder.
345b809074SNoralf Trønnes  */
355b809074SNoralf Trønnes 
3663170ac6SThomas Zimmermann static const struct drm_encoder_funcs drm_simple_encoder_funcs_cleanup = {
375b809074SNoralf Trønnes 	.destroy = drm_encoder_cleanup,
385b809074SNoralf Trønnes };
395b809074SNoralf Trønnes 
4063170ac6SThomas Zimmermann /**
412cb5974dSThomas Zimmermann  * drm_simple_encoder_init - Initialize a preallocated encoder with
422cb5974dSThomas Zimmermann  *                           basic functionality.
4363170ac6SThomas Zimmermann  * @dev: drm device
442cb5974dSThomas Zimmermann  * @encoder: the encoder to initialize
4563170ac6SThomas Zimmermann  * @encoder_type: user visible type of the encoder
4663170ac6SThomas Zimmermann  *
4763170ac6SThomas Zimmermann  * Initialises a preallocated encoder that has no further functionality.
4863170ac6SThomas Zimmermann  * Settings for possible CRTC and clones are left to their initial values.
4963170ac6SThomas Zimmermann  * The encoder will be cleaned up automatically as part of the mode-setting
5063170ac6SThomas Zimmermann  * cleanup.
5163170ac6SThomas Zimmermann  *
522cb5974dSThomas Zimmermann  * The caller of drm_simple_encoder_init() is responsible for freeing
532cb5974dSThomas Zimmermann  * the encoder's memory after the encoder has been cleaned up. At the
542cb5974dSThomas Zimmermann  * moment this only works reliably if the encoder data structure is
552cb5974dSThomas Zimmermann  * stored in the device structure. Free the encoder's memory as part of
562cb5974dSThomas Zimmermann  * the device release function.
572cb5974dSThomas Zimmermann  *
582cb5974dSThomas Zimmermann  * FIXME: Later improvements to DRM's resource management may allow for
592cb5974dSThomas Zimmermann  *        an automated kfree() of the encoder's memory.
602cb5974dSThomas Zimmermann  *
6163170ac6SThomas Zimmermann  * Returns:
6263170ac6SThomas Zimmermann  * Zero on success, error code on failure.
6363170ac6SThomas Zimmermann  */
6463170ac6SThomas Zimmermann int drm_simple_encoder_init(struct drm_device *dev,
6563170ac6SThomas Zimmermann 			    struct drm_encoder *encoder,
6663170ac6SThomas Zimmermann 			    int encoder_type)
6763170ac6SThomas Zimmermann {
6863170ac6SThomas Zimmermann 	return drm_encoder_init(dev, encoder,
6963170ac6SThomas Zimmermann 				&drm_simple_encoder_funcs_cleanup,
7063170ac6SThomas Zimmermann 				encoder_type, NULL);
7163170ac6SThomas Zimmermann }
7263170ac6SThomas Zimmermann EXPORT_SYMBOL(drm_simple_encoder_init);
7363170ac6SThomas Zimmermann 
7440275dc4SLinus Walleij static enum drm_mode_status
7540275dc4SLinus Walleij drm_simple_kms_crtc_mode_valid(struct drm_crtc *crtc,
7640275dc4SLinus Walleij 			       const struct drm_display_mode *mode)
7740275dc4SLinus Walleij {
7840275dc4SLinus Walleij 	struct drm_simple_display_pipe *pipe;
7940275dc4SLinus Walleij 
8040275dc4SLinus Walleij 	pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
8140275dc4SLinus Walleij 	if (!pipe->funcs || !pipe->funcs->mode_valid)
8240275dc4SLinus Walleij 		/* Anything goes */
8340275dc4SLinus Walleij 		return MODE_OK;
8440275dc4SLinus Walleij 
8562db7d1eSDaniel Vetter 	return pipe->funcs->mode_valid(pipe, mode);
8640275dc4SLinus Walleij }
8740275dc4SLinus Walleij 
886dcf0de7SDaniel Vetter static int drm_simple_kms_crtc_check(struct drm_crtc *crtc,
89*29b77ad7SMaxime Ripard 				     struct drm_atomic_state *state)
906dcf0de7SDaniel Vetter {
91*29b77ad7SMaxime Ripard 	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
92*29b77ad7SMaxime Ripard 									  crtc);
93*29b77ad7SMaxime Ripard 	bool has_primary = crtc_state->plane_mask &
9462f77ad0SVille Syrjälä 			   drm_plane_mask(crtc->primary);
95765831dcSMaarten Lankhorst 
96765831dcSMaarten Lankhorst 	/* We always want to have an active plane with an active CRTC */
97*29b77ad7SMaxime Ripard 	if (has_primary != crtc_state->enable)
98765831dcSMaarten Lankhorst 		return -EINVAL;
99765831dcSMaarten Lankhorst 
100*29b77ad7SMaxime Ripard 	return drm_atomic_add_affected_planes(crtc_state->state, crtc);
1016dcf0de7SDaniel Vetter }
1026dcf0de7SDaniel Vetter 
1030b20a0f8SLaurent Pinchart static void drm_simple_kms_crtc_enable(struct drm_crtc *crtc,
104351f950dSMaxime Ripard 				       struct drm_atomic_state *state)
1055b809074SNoralf Trønnes {
1060c9c7fd0SVille Syrjälä 	struct drm_plane *plane;
1075b809074SNoralf Trønnes 	struct drm_simple_display_pipe *pipe;
1085b809074SNoralf Trønnes 
1095b809074SNoralf Trønnes 	pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
1105b809074SNoralf Trønnes 	if (!pipe->funcs || !pipe->funcs->enable)
1115b809074SNoralf Trønnes 		return;
1125b809074SNoralf Trønnes 
1130c9c7fd0SVille Syrjälä 	plane = &pipe->plane;
1140c9c7fd0SVille Syrjälä 	pipe->funcs->enable(pipe, crtc->state, plane->state);
1155b809074SNoralf Trønnes }
1165b809074SNoralf Trønnes 
11764581714SLaurent Pinchart static void drm_simple_kms_crtc_disable(struct drm_crtc *crtc,
118351f950dSMaxime Ripard 					struct drm_atomic_state *state)
1195b809074SNoralf Trønnes {
1205b809074SNoralf Trønnes 	struct drm_simple_display_pipe *pipe;
1215b809074SNoralf Trønnes 
1225b809074SNoralf Trønnes 	pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
1235b809074SNoralf Trønnes 	if (!pipe->funcs || !pipe->funcs->disable)
1245b809074SNoralf Trønnes 		return;
1255b809074SNoralf Trønnes 
1265b809074SNoralf Trønnes 	pipe->funcs->disable(pipe);
1275b809074SNoralf Trønnes }
1285b809074SNoralf Trønnes 
1295b809074SNoralf Trønnes static const struct drm_crtc_helper_funcs drm_simple_kms_crtc_helper_funcs = {
13040275dc4SLinus Walleij 	.mode_valid = drm_simple_kms_crtc_mode_valid,
1316dcf0de7SDaniel Vetter 	.atomic_check = drm_simple_kms_crtc_check,
1320b20a0f8SLaurent Pinchart 	.atomic_enable = drm_simple_kms_crtc_enable,
13364581714SLaurent Pinchart 	.atomic_disable = drm_simple_kms_crtc_disable,
1345b809074SNoralf Trønnes };
1355b809074SNoralf Trønnes 
136ac86cba9SOleksandr Andrushchenko static int drm_simple_kms_crtc_enable_vblank(struct drm_crtc *crtc)
137ac86cba9SOleksandr Andrushchenko {
138ac86cba9SOleksandr Andrushchenko 	struct drm_simple_display_pipe *pipe;
139ac86cba9SOleksandr Andrushchenko 
140ac86cba9SOleksandr Andrushchenko 	pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
141ac86cba9SOleksandr Andrushchenko 	if (!pipe->funcs || !pipe->funcs->enable_vblank)
142ac86cba9SOleksandr Andrushchenko 		return 0;
143ac86cba9SOleksandr Andrushchenko 
144ac86cba9SOleksandr Andrushchenko 	return pipe->funcs->enable_vblank(pipe);
145ac86cba9SOleksandr Andrushchenko }
146ac86cba9SOleksandr Andrushchenko 
147ac86cba9SOleksandr Andrushchenko static void drm_simple_kms_crtc_disable_vblank(struct drm_crtc *crtc)
148ac86cba9SOleksandr Andrushchenko {
149ac86cba9SOleksandr Andrushchenko 	struct drm_simple_display_pipe *pipe;
150ac86cba9SOleksandr Andrushchenko 
151ac86cba9SOleksandr Andrushchenko 	pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
152ac86cba9SOleksandr Andrushchenko 	if (!pipe->funcs || !pipe->funcs->disable_vblank)
153ac86cba9SOleksandr Andrushchenko 		return;
154ac86cba9SOleksandr Andrushchenko 
155ac86cba9SOleksandr Andrushchenko 	pipe->funcs->disable_vblank(pipe);
156ac86cba9SOleksandr Andrushchenko }
157ac86cba9SOleksandr Andrushchenko 
1585b809074SNoralf Trønnes static const struct drm_crtc_funcs drm_simple_kms_crtc_funcs = {
1595b809074SNoralf Trønnes 	.reset = drm_atomic_helper_crtc_reset,
1605b809074SNoralf Trønnes 	.destroy = drm_crtc_cleanup,
1615b809074SNoralf Trønnes 	.set_config = drm_atomic_helper_set_config,
1625b809074SNoralf Trønnes 	.page_flip = drm_atomic_helper_page_flip,
1635b809074SNoralf Trønnes 	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
1645b809074SNoralf Trønnes 	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
165ac86cba9SOleksandr Andrushchenko 	.enable_vblank = drm_simple_kms_crtc_enable_vblank,
166ac86cba9SOleksandr Andrushchenko 	.disable_vblank = drm_simple_kms_crtc_disable_vblank,
1675b809074SNoralf Trønnes };
1685b809074SNoralf Trønnes 
1695b809074SNoralf Trønnes static int drm_simple_kms_plane_atomic_check(struct drm_plane *plane,
1705b809074SNoralf Trønnes 					struct drm_plane_state *plane_state)
1715b809074SNoralf Trønnes {
1725b809074SNoralf Trønnes 	struct drm_simple_display_pipe *pipe;
1735b809074SNoralf Trønnes 	struct drm_crtc_state *crtc_state;
1745b809074SNoralf Trønnes 	int ret;
1755b809074SNoralf Trønnes 
1765b809074SNoralf Trønnes 	pipe = container_of(plane, struct drm_simple_display_pipe, plane);
177b4d93679SMaarten Lankhorst 	crtc_state = drm_atomic_get_new_crtc_state(plane_state->state,
1785b809074SNoralf Trønnes 						   &pipe->crtc);
1794be12cc2SVille Syrjälä 
180a01cb8baSVille Syrjälä 	ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state,
1815b809074SNoralf Trønnes 						  DRM_PLANE_HELPER_NO_SCALING,
1825b809074SNoralf Trønnes 						  DRM_PLANE_HELPER_NO_SCALING,
1834be12cc2SVille Syrjälä 						  false, true);
1845b809074SNoralf Trønnes 	if (ret)
1855b809074SNoralf Trønnes 		return ret;
1865b809074SNoralf Trønnes 
1874be12cc2SVille Syrjälä 	if (!plane_state->visible)
1884751cf73SOleksandr Andrushchenko 		return 0;
1894751cf73SOleksandr Andrushchenko 
1905b809074SNoralf Trønnes 	if (!pipe->funcs || !pipe->funcs->check)
1915b809074SNoralf Trønnes 		return 0;
1925b809074SNoralf Trønnes 
1935b809074SNoralf Trønnes 	return pipe->funcs->check(pipe, plane_state, crtc_state);
1945b809074SNoralf Trønnes }
1955b809074SNoralf Trønnes 
1965b809074SNoralf Trønnes static void drm_simple_kms_plane_atomic_update(struct drm_plane *plane,
197bcd2ba02SEric Anholt 					struct drm_plane_state *old_pstate)
1985b809074SNoralf Trønnes {
1995b809074SNoralf Trønnes 	struct drm_simple_display_pipe *pipe;
2005b809074SNoralf Trønnes 
2015b809074SNoralf Trønnes 	pipe = container_of(plane, struct drm_simple_display_pipe, plane);
2025b809074SNoralf Trønnes 	if (!pipe->funcs || !pipe->funcs->update)
2035b809074SNoralf Trønnes 		return;
2045b809074SNoralf Trønnes 
205bcd2ba02SEric Anholt 	pipe->funcs->update(pipe, old_pstate);
2065b809074SNoralf Trønnes }
2075b809074SNoralf Trønnes 
2087d83a155SMarek Vasut static int drm_simple_kms_plane_prepare_fb(struct drm_plane *plane,
2097d83a155SMarek Vasut 					   struct drm_plane_state *state)
2107d83a155SMarek Vasut {
2117d83a155SMarek Vasut 	struct drm_simple_display_pipe *pipe;
2127d83a155SMarek Vasut 
2137d83a155SMarek Vasut 	pipe = container_of(plane, struct drm_simple_display_pipe, plane);
2147d83a155SMarek Vasut 	if (!pipe->funcs || !pipe->funcs->prepare_fb)
2157d83a155SMarek Vasut 		return 0;
2167d83a155SMarek Vasut 
2177d83a155SMarek Vasut 	return pipe->funcs->prepare_fb(pipe, state);
2187d83a155SMarek Vasut }
2197d83a155SMarek Vasut 
2207d83a155SMarek Vasut static void drm_simple_kms_plane_cleanup_fb(struct drm_plane *plane,
2217d83a155SMarek Vasut 					    struct drm_plane_state *state)
2227d83a155SMarek Vasut {
2237d83a155SMarek Vasut 	struct drm_simple_display_pipe *pipe;
2247d83a155SMarek Vasut 
2257d83a155SMarek Vasut 	pipe = container_of(plane, struct drm_simple_display_pipe, plane);
2267d83a155SMarek Vasut 	if (!pipe->funcs || !pipe->funcs->cleanup_fb)
2277d83a155SMarek Vasut 		return;
2287d83a155SMarek Vasut 
2297d83a155SMarek Vasut 	pipe->funcs->cleanup_fb(pipe, state);
2307d83a155SMarek Vasut }
2317d83a155SMarek Vasut 
232dff906c3SEric Anholt static bool drm_simple_kms_format_mod_supported(struct drm_plane *plane,
233dff906c3SEric Anholt 						uint32_t format,
234dff906c3SEric Anholt 						uint64_t modifier)
235dff906c3SEric Anholt {
236dff906c3SEric Anholt 	return modifier == DRM_FORMAT_MOD_LINEAR;
237dff906c3SEric Anholt }
238dff906c3SEric Anholt 
2395b809074SNoralf Trønnes static const struct drm_plane_helper_funcs drm_simple_kms_plane_helper_funcs = {
2407d83a155SMarek Vasut 	.prepare_fb = drm_simple_kms_plane_prepare_fb,
2417d83a155SMarek Vasut 	.cleanup_fb = drm_simple_kms_plane_cleanup_fb,
2425b809074SNoralf Trønnes 	.atomic_check = drm_simple_kms_plane_atomic_check,
2435b809074SNoralf Trønnes 	.atomic_update = drm_simple_kms_plane_atomic_update,
2445b809074SNoralf Trønnes };
2455b809074SNoralf Trønnes 
2465b809074SNoralf Trønnes static const struct drm_plane_funcs drm_simple_kms_plane_funcs = {
2475b809074SNoralf Trønnes 	.update_plane		= drm_atomic_helper_update_plane,
2485b809074SNoralf Trønnes 	.disable_plane		= drm_atomic_helper_disable_plane,
2495b809074SNoralf Trønnes 	.destroy		= drm_plane_cleanup,
2505b809074SNoralf Trønnes 	.reset			= drm_atomic_helper_plane_reset,
2515b809074SNoralf Trønnes 	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
2525b809074SNoralf Trønnes 	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
253dff906c3SEric Anholt 	.format_mod_supported   = drm_simple_kms_format_mod_supported,
2545b809074SNoralf Trønnes };
2555b809074SNoralf Trønnes 
2565b809074SNoralf Trønnes /**
257315486c6SAndrea Merello  * drm_simple_display_pipe_attach_bridge - Attach a bridge to the display pipe
258315486c6SAndrea Merello  * @pipe: simple display pipe object
259315486c6SAndrea Merello  * @bridge: bridge to attach
260315486c6SAndrea Merello  *
261315486c6SAndrea Merello  * Makes it possible to still use the drm_simple_display_pipe helpers when
262315486c6SAndrea Merello  * a DRM bridge has to be used.
263315486c6SAndrea Merello  *
264315486c6SAndrea Merello  * Note that you probably want to initialize the pipe by passing a NULL
265315486c6SAndrea Merello  * connector to drm_simple_display_pipe_init().
266315486c6SAndrea Merello  *
267315486c6SAndrea Merello  * Returns:
268315486c6SAndrea Merello  * Zero on success, negative error code on failure.
269315486c6SAndrea Merello  */
270315486c6SAndrea Merello int drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe *pipe,
271315486c6SAndrea Merello 					  struct drm_bridge *bridge)
272315486c6SAndrea Merello {
273a25b988fSLaurent Pinchart 	return drm_bridge_attach(&pipe->encoder, bridge, NULL, 0);
274315486c6SAndrea Merello }
275315486c6SAndrea Merello EXPORT_SYMBOL(drm_simple_display_pipe_attach_bridge);
276315486c6SAndrea Merello 
277315486c6SAndrea Merello /**
2785b809074SNoralf Trønnes  * drm_simple_display_pipe_init - Initialize a simple display pipeline
2795b809074SNoralf Trønnes  * @dev: DRM device
2805b809074SNoralf Trønnes  * @pipe: simple display pipe object to initialize
2815b809074SNoralf Trønnes  * @funcs: callbacks for the display pipe (optional)
28262cacc79SDaniel Vetter  * @formats: array of supported formats (DRM_FORMAT\_\*)
2835b809074SNoralf Trønnes  * @format_count: number of elements in @formats
284e6fc3b68SBen Widawsky  * @format_modifiers: array of formats modifiers
2854f993973SAndrea Merello  * @connector: connector to attach and register (optional)
2865b809074SNoralf Trønnes  *
2875b809074SNoralf Trønnes  * Sets up a display pipeline which consist of a really simple
2884f993973SAndrea Merello  * plane-crtc-encoder pipe.
2894f993973SAndrea Merello  *
2904f993973SAndrea Merello  * If a connector is supplied, the pipe will be coupled with the provided
2914f993973SAndrea Merello  * connector. You may supply a NULL connector when using drm bridges, that
2924f993973SAndrea Merello  * handle connectors themselves (see drm_simple_display_pipe_attach_bridge()).
2934f993973SAndrea Merello  *
2945b809074SNoralf Trønnes  * Teardown of a simple display pipe is all handled automatically by the drm
2955b809074SNoralf Trønnes  * core through calling drm_mode_config_cleanup(). Drivers afterwards need to
2965b809074SNoralf Trønnes  * release the memory for the structure themselves.
2975b809074SNoralf Trønnes  *
2985b809074SNoralf Trønnes  * Returns:
2995b809074SNoralf Trønnes  * Zero on success, negative error code on failure.
3005b809074SNoralf Trønnes  */
3015b809074SNoralf Trønnes int drm_simple_display_pipe_init(struct drm_device *dev,
3025b809074SNoralf Trønnes 			struct drm_simple_display_pipe *pipe,
3035b809074SNoralf Trønnes 			const struct drm_simple_display_pipe_funcs *funcs,
3045b809074SNoralf Trønnes 			const uint32_t *formats, unsigned int format_count,
305e6fc3b68SBen Widawsky 			const uint64_t *format_modifiers,
3065b809074SNoralf Trønnes 			struct drm_connector *connector)
3075b809074SNoralf Trønnes {
3085b809074SNoralf Trønnes 	struct drm_encoder *encoder = &pipe->encoder;
3095b809074SNoralf Trønnes 	struct drm_plane *plane = &pipe->plane;
3105b809074SNoralf Trønnes 	struct drm_crtc *crtc = &pipe->crtc;
3115b809074SNoralf Trønnes 	int ret;
3125b809074SNoralf Trønnes 
3135b809074SNoralf Trønnes 	pipe->connector = connector;
3145b809074SNoralf Trønnes 	pipe->funcs = funcs;
3155b809074SNoralf Trønnes 
3165b809074SNoralf Trønnes 	drm_plane_helper_add(plane, &drm_simple_kms_plane_helper_funcs);
3175b809074SNoralf Trønnes 	ret = drm_universal_plane_init(dev, plane, 0,
3185b809074SNoralf Trønnes 				       &drm_simple_kms_plane_funcs,
3195b809074SNoralf Trønnes 				       formats, format_count,
320e6fc3b68SBen Widawsky 				       format_modifiers,
3215b809074SNoralf Trønnes 				       DRM_PLANE_TYPE_PRIMARY, NULL);
3225b809074SNoralf Trønnes 	if (ret)
3235b809074SNoralf Trønnes 		return ret;
3245b809074SNoralf Trønnes 
3255b809074SNoralf Trønnes 	drm_crtc_helper_add(crtc, &drm_simple_kms_crtc_helper_funcs);
3265b809074SNoralf Trønnes 	ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
3275b809074SNoralf Trønnes 					&drm_simple_kms_crtc_funcs, NULL);
3285b809074SNoralf Trønnes 	if (ret)
3295b809074SNoralf Trønnes 		return ret;
3305b809074SNoralf Trønnes 
3316a52193bSVille Syrjälä 	encoder->possible_crtcs = drm_crtc_mask(crtc);
33263170ac6SThomas Zimmermann 	ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_NONE);
3334f993973SAndrea Merello 	if (ret || !connector)
3345b809074SNoralf Trønnes 		return ret;
3355b809074SNoralf Trønnes 
336cde4c44dSDaniel Vetter 	return drm_connector_attach_encoder(connector, encoder);
3375b809074SNoralf Trønnes }
3385b809074SNoralf Trønnes EXPORT_SYMBOL(drm_simple_display_pipe_init);
3395b809074SNoralf Trønnes 
3405b809074SNoralf Trønnes MODULE_LICENSE("GPL");
341