18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2016 Noralf Trønnes
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/module.h>
78c2ecf20Sopenharmony_ci#include <linux/slab.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <drm/drm_atomic.h>
108c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h>
118c2ecf20Sopenharmony_ci#include <drm/drm_bridge.h>
128c2ecf20Sopenharmony_ci#include <drm/drm_plane_helper.h>
138c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h>
148c2ecf20Sopenharmony_ci#include <drm/drm_simple_kms_helper.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci/**
178c2ecf20Sopenharmony_ci * DOC: overview
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci * This helper library provides helpers for drivers for simple display
208c2ecf20Sopenharmony_ci * hardware.
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci * drm_simple_display_pipe_init() initializes a simple display pipeline
238c2ecf20Sopenharmony_ci * which has only one full-screen scanout buffer feeding one output. The
248c2ecf20Sopenharmony_ci * pipeline is represented by &struct drm_simple_display_pipe and binds
258c2ecf20Sopenharmony_ci * together &drm_plane, &drm_crtc and &drm_encoder structures into one fixed
268c2ecf20Sopenharmony_ci * entity. Some flexibility for code reuse is provided through a separately
278c2ecf20Sopenharmony_ci * allocated &drm_connector object and supporting optional &drm_bridge
288c2ecf20Sopenharmony_ci * encoder drivers.
298c2ecf20Sopenharmony_ci *
308c2ecf20Sopenharmony_ci * Many drivers require only a very simple encoder that fulfills the minimum
318c2ecf20Sopenharmony_ci * requirements of the display pipeline and does not add additional
328c2ecf20Sopenharmony_ci * functionality. The function drm_simple_encoder_init() provides an
338c2ecf20Sopenharmony_ci * implementation of such an encoder.
348c2ecf20Sopenharmony_ci */
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic const struct drm_encoder_funcs drm_simple_encoder_funcs_cleanup = {
378c2ecf20Sopenharmony_ci	.destroy = drm_encoder_cleanup,
388c2ecf20Sopenharmony_ci};
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/**
418c2ecf20Sopenharmony_ci * drm_simple_encoder_init - Initialize a preallocated encoder with
428c2ecf20Sopenharmony_ci *                           basic functionality.
438c2ecf20Sopenharmony_ci * @dev: drm device
448c2ecf20Sopenharmony_ci * @encoder: the encoder to initialize
458c2ecf20Sopenharmony_ci * @encoder_type: user visible type of the encoder
468c2ecf20Sopenharmony_ci *
478c2ecf20Sopenharmony_ci * Initialises a preallocated encoder that has no further functionality.
488c2ecf20Sopenharmony_ci * Settings for possible CRTC and clones are left to their initial values.
498c2ecf20Sopenharmony_ci * The encoder will be cleaned up automatically as part of the mode-setting
508c2ecf20Sopenharmony_ci * cleanup.
518c2ecf20Sopenharmony_ci *
528c2ecf20Sopenharmony_ci * The caller of drm_simple_encoder_init() is responsible for freeing
538c2ecf20Sopenharmony_ci * the encoder's memory after the encoder has been cleaned up. At the
548c2ecf20Sopenharmony_ci * moment this only works reliably if the encoder data structure is
558c2ecf20Sopenharmony_ci * stored in the device structure. Free the encoder's memory as part of
568c2ecf20Sopenharmony_ci * the device release function.
578c2ecf20Sopenharmony_ci *
588c2ecf20Sopenharmony_ci * FIXME: Later improvements to DRM's resource management may allow for
598c2ecf20Sopenharmony_ci *        an automated kfree() of the encoder's memory.
608c2ecf20Sopenharmony_ci *
618c2ecf20Sopenharmony_ci * Returns:
628c2ecf20Sopenharmony_ci * Zero on success, error code on failure.
638c2ecf20Sopenharmony_ci */
648c2ecf20Sopenharmony_ciint drm_simple_encoder_init(struct drm_device *dev,
658c2ecf20Sopenharmony_ci			    struct drm_encoder *encoder,
668c2ecf20Sopenharmony_ci			    int encoder_type)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	return drm_encoder_init(dev, encoder,
698c2ecf20Sopenharmony_ci				&drm_simple_encoder_funcs_cleanup,
708c2ecf20Sopenharmony_ci				encoder_type, NULL);
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_simple_encoder_init);
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic enum drm_mode_status
758c2ecf20Sopenharmony_cidrm_simple_kms_crtc_mode_valid(struct drm_crtc *crtc,
768c2ecf20Sopenharmony_ci			       const struct drm_display_mode *mode)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	struct drm_simple_display_pipe *pipe;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
818c2ecf20Sopenharmony_ci	if (!pipe->funcs || !pipe->funcs->mode_valid)
828c2ecf20Sopenharmony_ci		/* Anything goes */
838c2ecf20Sopenharmony_ci		return MODE_OK;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	return pipe->funcs->mode_valid(pipe, mode);
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic int drm_simple_kms_crtc_check(struct drm_crtc *crtc,
898c2ecf20Sopenharmony_ci				     struct drm_crtc_state *state)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	bool has_primary = state->plane_mask &
928c2ecf20Sopenharmony_ci			   drm_plane_mask(crtc->primary);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	/* We always want to have an active plane with an active CRTC */
958c2ecf20Sopenharmony_ci	if (has_primary != state->enable)
968c2ecf20Sopenharmony_ci		return -EINVAL;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	return drm_atomic_add_affected_planes(state->state, crtc);
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic void drm_simple_kms_crtc_enable(struct drm_crtc *crtc,
1028c2ecf20Sopenharmony_ci				       struct drm_crtc_state *old_state)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	struct drm_plane *plane;
1058c2ecf20Sopenharmony_ci	struct drm_simple_display_pipe *pipe;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
1088c2ecf20Sopenharmony_ci	if (!pipe->funcs || !pipe->funcs->enable)
1098c2ecf20Sopenharmony_ci		return;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	plane = &pipe->plane;
1128c2ecf20Sopenharmony_ci	pipe->funcs->enable(pipe, crtc->state, plane->state);
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic void drm_simple_kms_crtc_disable(struct drm_crtc *crtc,
1168c2ecf20Sopenharmony_ci					struct drm_crtc_state *old_state)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	struct drm_simple_display_pipe *pipe;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
1218c2ecf20Sopenharmony_ci	if (!pipe->funcs || !pipe->funcs->disable)
1228c2ecf20Sopenharmony_ci		return;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	pipe->funcs->disable(pipe);
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic const struct drm_crtc_helper_funcs drm_simple_kms_crtc_helper_funcs = {
1288c2ecf20Sopenharmony_ci	.mode_valid = drm_simple_kms_crtc_mode_valid,
1298c2ecf20Sopenharmony_ci	.atomic_check = drm_simple_kms_crtc_check,
1308c2ecf20Sopenharmony_ci	.atomic_enable = drm_simple_kms_crtc_enable,
1318c2ecf20Sopenharmony_ci	.atomic_disable = drm_simple_kms_crtc_disable,
1328c2ecf20Sopenharmony_ci};
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic int drm_simple_kms_crtc_enable_vblank(struct drm_crtc *crtc)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	struct drm_simple_display_pipe *pipe;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
1398c2ecf20Sopenharmony_ci	if (!pipe->funcs || !pipe->funcs->enable_vblank)
1408c2ecf20Sopenharmony_ci		return 0;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	return pipe->funcs->enable_vblank(pipe);
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic void drm_simple_kms_crtc_disable_vblank(struct drm_crtc *crtc)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	struct drm_simple_display_pipe *pipe;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
1508c2ecf20Sopenharmony_ci	if (!pipe->funcs || !pipe->funcs->disable_vblank)
1518c2ecf20Sopenharmony_ci		return;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	pipe->funcs->disable_vblank(pipe);
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_cistatic const struct drm_crtc_funcs drm_simple_kms_crtc_funcs = {
1578c2ecf20Sopenharmony_ci	.reset = drm_atomic_helper_crtc_reset,
1588c2ecf20Sopenharmony_ci	.destroy = drm_crtc_cleanup,
1598c2ecf20Sopenharmony_ci	.set_config = drm_atomic_helper_set_config,
1608c2ecf20Sopenharmony_ci	.page_flip = drm_atomic_helper_page_flip,
1618c2ecf20Sopenharmony_ci	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
1628c2ecf20Sopenharmony_ci	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
1638c2ecf20Sopenharmony_ci	.enable_vblank = drm_simple_kms_crtc_enable_vblank,
1648c2ecf20Sopenharmony_ci	.disable_vblank = drm_simple_kms_crtc_disable_vblank,
1658c2ecf20Sopenharmony_ci};
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic int drm_simple_kms_plane_atomic_check(struct drm_plane *plane,
1688c2ecf20Sopenharmony_ci					struct drm_plane_state *plane_state)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	struct drm_simple_display_pipe *pipe;
1718c2ecf20Sopenharmony_ci	struct drm_crtc_state *crtc_state;
1728c2ecf20Sopenharmony_ci	int ret;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	pipe = container_of(plane, struct drm_simple_display_pipe, plane);
1758c2ecf20Sopenharmony_ci	crtc_state = drm_atomic_get_new_crtc_state(plane_state->state,
1768c2ecf20Sopenharmony_ci						   &pipe->crtc);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state,
1798c2ecf20Sopenharmony_ci						  DRM_PLANE_HELPER_NO_SCALING,
1808c2ecf20Sopenharmony_ci						  DRM_PLANE_HELPER_NO_SCALING,
1818c2ecf20Sopenharmony_ci						  false, true);
1828c2ecf20Sopenharmony_ci	if (ret)
1838c2ecf20Sopenharmony_ci		return ret;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	if (!plane_state->visible)
1868c2ecf20Sopenharmony_ci		return 0;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	if (!pipe->funcs || !pipe->funcs->check)
1898c2ecf20Sopenharmony_ci		return 0;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	return pipe->funcs->check(pipe, plane_state, crtc_state);
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic void drm_simple_kms_plane_atomic_update(struct drm_plane *plane,
1958c2ecf20Sopenharmony_ci					struct drm_plane_state *old_pstate)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	struct drm_simple_display_pipe *pipe;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	pipe = container_of(plane, struct drm_simple_display_pipe, plane);
2008c2ecf20Sopenharmony_ci	if (!pipe->funcs || !pipe->funcs->update)
2018c2ecf20Sopenharmony_ci		return;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	pipe->funcs->update(pipe, old_pstate);
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic int drm_simple_kms_plane_prepare_fb(struct drm_plane *plane,
2078c2ecf20Sopenharmony_ci					   struct drm_plane_state *state)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	struct drm_simple_display_pipe *pipe;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	pipe = container_of(plane, struct drm_simple_display_pipe, plane);
2128c2ecf20Sopenharmony_ci	if (!pipe->funcs || !pipe->funcs->prepare_fb)
2138c2ecf20Sopenharmony_ci		return 0;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	return pipe->funcs->prepare_fb(pipe, state);
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cistatic void drm_simple_kms_plane_cleanup_fb(struct drm_plane *plane,
2198c2ecf20Sopenharmony_ci					    struct drm_plane_state *state)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	struct drm_simple_display_pipe *pipe;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	pipe = container_of(plane, struct drm_simple_display_pipe, plane);
2248c2ecf20Sopenharmony_ci	if (!pipe->funcs || !pipe->funcs->cleanup_fb)
2258c2ecf20Sopenharmony_ci		return;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	pipe->funcs->cleanup_fb(pipe, state);
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic bool drm_simple_kms_format_mod_supported(struct drm_plane *plane,
2318c2ecf20Sopenharmony_ci						uint32_t format,
2328c2ecf20Sopenharmony_ci						uint64_t modifier)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	return modifier == DRM_FORMAT_MOD_LINEAR;
2358c2ecf20Sopenharmony_ci}
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_cistatic const struct drm_plane_helper_funcs drm_simple_kms_plane_helper_funcs = {
2388c2ecf20Sopenharmony_ci	.prepare_fb = drm_simple_kms_plane_prepare_fb,
2398c2ecf20Sopenharmony_ci	.cleanup_fb = drm_simple_kms_plane_cleanup_fb,
2408c2ecf20Sopenharmony_ci	.atomic_check = drm_simple_kms_plane_atomic_check,
2418c2ecf20Sopenharmony_ci	.atomic_update = drm_simple_kms_plane_atomic_update,
2428c2ecf20Sopenharmony_ci};
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic const struct drm_plane_funcs drm_simple_kms_plane_funcs = {
2458c2ecf20Sopenharmony_ci	.update_plane		= drm_atomic_helper_update_plane,
2468c2ecf20Sopenharmony_ci	.disable_plane		= drm_atomic_helper_disable_plane,
2478c2ecf20Sopenharmony_ci	.destroy		= drm_plane_cleanup,
2488c2ecf20Sopenharmony_ci	.reset			= drm_atomic_helper_plane_reset,
2498c2ecf20Sopenharmony_ci	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
2508c2ecf20Sopenharmony_ci	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
2518c2ecf20Sopenharmony_ci	.format_mod_supported   = drm_simple_kms_format_mod_supported,
2528c2ecf20Sopenharmony_ci};
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci/**
2558c2ecf20Sopenharmony_ci * drm_simple_display_pipe_attach_bridge - Attach a bridge to the display pipe
2568c2ecf20Sopenharmony_ci * @pipe: simple display pipe object
2578c2ecf20Sopenharmony_ci * @bridge: bridge to attach
2588c2ecf20Sopenharmony_ci *
2598c2ecf20Sopenharmony_ci * Makes it possible to still use the drm_simple_display_pipe helpers when
2608c2ecf20Sopenharmony_ci * a DRM bridge has to be used.
2618c2ecf20Sopenharmony_ci *
2628c2ecf20Sopenharmony_ci * Note that you probably want to initialize the pipe by passing a NULL
2638c2ecf20Sopenharmony_ci * connector to drm_simple_display_pipe_init().
2648c2ecf20Sopenharmony_ci *
2658c2ecf20Sopenharmony_ci * Returns:
2668c2ecf20Sopenharmony_ci * Zero on success, negative error code on failure.
2678c2ecf20Sopenharmony_ci */
2688c2ecf20Sopenharmony_ciint drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe *pipe,
2698c2ecf20Sopenharmony_ci					  struct drm_bridge *bridge)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	return drm_bridge_attach(&pipe->encoder, bridge, NULL, 0);
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_simple_display_pipe_attach_bridge);
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci/**
2768c2ecf20Sopenharmony_ci * drm_simple_display_pipe_init - Initialize a simple display pipeline
2778c2ecf20Sopenharmony_ci * @dev: DRM device
2788c2ecf20Sopenharmony_ci * @pipe: simple display pipe object to initialize
2798c2ecf20Sopenharmony_ci * @funcs: callbacks for the display pipe (optional)
2808c2ecf20Sopenharmony_ci * @formats: array of supported formats (DRM_FORMAT\_\*)
2818c2ecf20Sopenharmony_ci * @format_count: number of elements in @formats
2828c2ecf20Sopenharmony_ci * @format_modifiers: array of formats modifiers
2838c2ecf20Sopenharmony_ci * @connector: connector to attach and register (optional)
2848c2ecf20Sopenharmony_ci *
2858c2ecf20Sopenharmony_ci * Sets up a display pipeline which consist of a really simple
2868c2ecf20Sopenharmony_ci * plane-crtc-encoder pipe.
2878c2ecf20Sopenharmony_ci *
2888c2ecf20Sopenharmony_ci * If a connector is supplied, the pipe will be coupled with the provided
2898c2ecf20Sopenharmony_ci * connector. You may supply a NULL connector when using drm bridges, that
2908c2ecf20Sopenharmony_ci * handle connectors themselves (see drm_simple_display_pipe_attach_bridge()).
2918c2ecf20Sopenharmony_ci *
2928c2ecf20Sopenharmony_ci * Teardown of a simple display pipe is all handled automatically by the drm
2938c2ecf20Sopenharmony_ci * core through calling drm_mode_config_cleanup(). Drivers afterwards need to
2948c2ecf20Sopenharmony_ci * release the memory for the structure themselves.
2958c2ecf20Sopenharmony_ci *
2968c2ecf20Sopenharmony_ci * Returns:
2978c2ecf20Sopenharmony_ci * Zero on success, negative error code on failure.
2988c2ecf20Sopenharmony_ci */
2998c2ecf20Sopenharmony_ciint drm_simple_display_pipe_init(struct drm_device *dev,
3008c2ecf20Sopenharmony_ci			struct drm_simple_display_pipe *pipe,
3018c2ecf20Sopenharmony_ci			const struct drm_simple_display_pipe_funcs *funcs,
3028c2ecf20Sopenharmony_ci			const uint32_t *formats, unsigned int format_count,
3038c2ecf20Sopenharmony_ci			const uint64_t *format_modifiers,
3048c2ecf20Sopenharmony_ci			struct drm_connector *connector)
3058c2ecf20Sopenharmony_ci{
3068c2ecf20Sopenharmony_ci	struct drm_encoder *encoder = &pipe->encoder;
3078c2ecf20Sopenharmony_ci	struct drm_plane *plane = &pipe->plane;
3088c2ecf20Sopenharmony_ci	struct drm_crtc *crtc = &pipe->crtc;
3098c2ecf20Sopenharmony_ci	int ret;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	pipe->connector = connector;
3128c2ecf20Sopenharmony_ci	pipe->funcs = funcs;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	drm_plane_helper_add(plane, &drm_simple_kms_plane_helper_funcs);
3158c2ecf20Sopenharmony_ci	ret = drm_universal_plane_init(dev, plane, 0,
3168c2ecf20Sopenharmony_ci				       &drm_simple_kms_plane_funcs,
3178c2ecf20Sopenharmony_ci				       formats, format_count,
3188c2ecf20Sopenharmony_ci				       format_modifiers,
3198c2ecf20Sopenharmony_ci				       DRM_PLANE_TYPE_PRIMARY, NULL);
3208c2ecf20Sopenharmony_ci	if (ret)
3218c2ecf20Sopenharmony_ci		return ret;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	drm_crtc_helper_add(crtc, &drm_simple_kms_crtc_helper_funcs);
3248c2ecf20Sopenharmony_ci	ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
3258c2ecf20Sopenharmony_ci					&drm_simple_kms_crtc_funcs, NULL);
3268c2ecf20Sopenharmony_ci	if (ret)
3278c2ecf20Sopenharmony_ci		return ret;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	encoder->possible_crtcs = drm_crtc_mask(crtc);
3308c2ecf20Sopenharmony_ci	ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_NONE);
3318c2ecf20Sopenharmony_ci	if (ret || !connector)
3328c2ecf20Sopenharmony_ci		return ret;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	return drm_connector_attach_encoder(connector, encoder);
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ciEXPORT_SYMBOL(drm_simple_display_pipe_init);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
339