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