11dedbd3bSFrançois Tigeot /* 21dedbd3bSFrançois Tigeot * Copyright (C) 2016 Noralf Trønnes 31dedbd3bSFrançois Tigeot * 41dedbd3bSFrançois Tigeot * This program is free software; you can redistribute it and/or modify 51dedbd3bSFrançois Tigeot * it under the terms of the GNU General Public License as published by 61dedbd3bSFrançois Tigeot * the Free Software Foundation; either version 2 of the License, or 71dedbd3bSFrançois Tigeot * (at your option) any later version. 81dedbd3bSFrançois Tigeot */ 91dedbd3bSFrançois Tigeot 101dedbd3bSFrançois Tigeot #include <drm/drmP.h> 111dedbd3bSFrançois Tigeot #include <drm/drm_atomic.h> 121dedbd3bSFrançois Tigeot #include <drm/drm_atomic_helper.h> 131dedbd3bSFrançois Tigeot #include <drm/drm_crtc_helper.h> 141dedbd3bSFrançois Tigeot #include <drm/drm_plane_helper.h> 151dedbd3bSFrançois Tigeot #include <drm/drm_simple_kms_helper.h> 161dedbd3bSFrançois Tigeot #include <linux/slab.h> 171dedbd3bSFrançois Tigeot 181dedbd3bSFrançois Tigeot /** 191dedbd3bSFrançois Tigeot * DOC: overview 201dedbd3bSFrançois Tigeot * 211dedbd3bSFrançois Tigeot * This helper library provides helpers for drivers for simple display 221dedbd3bSFrançois Tigeot * hardware. 231dedbd3bSFrançois Tigeot * 241dedbd3bSFrançois Tigeot * drm_simple_display_pipe_init() initializes a simple display pipeline 251dedbd3bSFrançois Tigeot * which has only one full-screen scanout buffer feeding one output. The 26*a85cb24fSFrançois Tigeot * pipeline is represented by &struct drm_simple_display_pipe and binds 271dedbd3bSFrançois Tigeot * together &drm_plane, &drm_crtc and &drm_encoder structures into one fixed 281dedbd3bSFrançois Tigeot * entity. Some flexibility for code reuse is provided through a separately 291dedbd3bSFrançois Tigeot * allocated &drm_connector object and supporting optional &drm_bridge 301dedbd3bSFrançois Tigeot * encoder drivers. 311dedbd3bSFrançois Tigeot */ 321dedbd3bSFrançois Tigeot 331dedbd3bSFrançois Tigeot static const struct drm_encoder_funcs drm_simple_kms_encoder_funcs = { 341dedbd3bSFrançois Tigeot .destroy = drm_encoder_cleanup, 351dedbd3bSFrançois Tigeot }; 361dedbd3bSFrançois Tigeot 371dedbd3bSFrançois Tigeot static int drm_simple_kms_crtc_check(struct drm_crtc *crtc, 381dedbd3bSFrançois Tigeot struct drm_crtc_state *state) 391dedbd3bSFrançois Tigeot { 401dedbd3bSFrançois Tigeot return drm_atomic_add_affected_planes(state->state, crtc); 411dedbd3bSFrançois Tigeot } 421dedbd3bSFrançois Tigeot 431dedbd3bSFrançois Tigeot static void drm_simple_kms_crtc_enable(struct drm_crtc *crtc) 441dedbd3bSFrançois Tigeot { 451dedbd3bSFrançois Tigeot struct drm_simple_display_pipe *pipe; 461dedbd3bSFrançois Tigeot 471dedbd3bSFrançois Tigeot pipe = container_of(crtc, struct drm_simple_display_pipe, crtc); 481dedbd3bSFrançois Tigeot if (!pipe->funcs || !pipe->funcs->enable) 491dedbd3bSFrançois Tigeot return; 501dedbd3bSFrançois Tigeot 511dedbd3bSFrançois Tigeot pipe->funcs->enable(pipe, crtc->state); 521dedbd3bSFrançois Tigeot } 531dedbd3bSFrançois Tigeot 541dedbd3bSFrançois Tigeot static void drm_simple_kms_crtc_disable(struct drm_crtc *crtc) 551dedbd3bSFrançois Tigeot { 561dedbd3bSFrançois Tigeot struct drm_simple_display_pipe *pipe; 571dedbd3bSFrançois Tigeot 581dedbd3bSFrançois Tigeot pipe = container_of(crtc, struct drm_simple_display_pipe, crtc); 591dedbd3bSFrançois Tigeot if (!pipe->funcs || !pipe->funcs->disable) 601dedbd3bSFrançois Tigeot return; 611dedbd3bSFrançois Tigeot 621dedbd3bSFrançois Tigeot pipe->funcs->disable(pipe); 631dedbd3bSFrançois Tigeot } 641dedbd3bSFrançois Tigeot 651dedbd3bSFrançois Tigeot static const struct drm_crtc_helper_funcs drm_simple_kms_crtc_helper_funcs = { 661dedbd3bSFrançois Tigeot .atomic_check = drm_simple_kms_crtc_check, 671dedbd3bSFrançois Tigeot .disable = drm_simple_kms_crtc_disable, 681dedbd3bSFrançois Tigeot .enable = drm_simple_kms_crtc_enable, 691dedbd3bSFrançois Tigeot }; 701dedbd3bSFrançois Tigeot 711dedbd3bSFrançois Tigeot static const struct drm_crtc_funcs drm_simple_kms_crtc_funcs = { 721dedbd3bSFrançois Tigeot .reset = drm_atomic_helper_crtc_reset, 731dedbd3bSFrançois Tigeot .destroy = drm_crtc_cleanup, 741dedbd3bSFrançois Tigeot .set_config = drm_atomic_helper_set_config, 751dedbd3bSFrançois Tigeot .page_flip = drm_atomic_helper_page_flip, 761dedbd3bSFrançois Tigeot .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 771dedbd3bSFrançois Tigeot .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 781dedbd3bSFrançois Tigeot }; 791dedbd3bSFrançois Tigeot 801dedbd3bSFrançois Tigeot static int drm_simple_kms_plane_atomic_check(struct drm_plane *plane, 811dedbd3bSFrançois Tigeot struct drm_plane_state *plane_state) 821dedbd3bSFrançois Tigeot { 831dedbd3bSFrançois Tigeot struct drm_rect clip = { 0 }; 841dedbd3bSFrançois Tigeot struct drm_simple_display_pipe *pipe; 851dedbd3bSFrançois Tigeot struct drm_crtc_state *crtc_state; 861dedbd3bSFrançois Tigeot int ret; 871dedbd3bSFrançois Tigeot 881dedbd3bSFrançois Tigeot pipe = container_of(plane, struct drm_simple_display_pipe, plane); 89*a85cb24fSFrançois Tigeot crtc_state = drm_atomic_get_new_crtc_state(plane_state->state, 901dedbd3bSFrançois Tigeot &pipe->crtc); 911dedbd3bSFrançois Tigeot if (crtc_state->enable != !!plane_state->crtc) 921dedbd3bSFrançois Tigeot return -EINVAL; /* plane must match crtc enable state */ 931dedbd3bSFrançois Tigeot 941dedbd3bSFrançois Tigeot if (!crtc_state->enable) 951dedbd3bSFrançois Tigeot return 0; /* nothing to check when disabling or disabled */ 961dedbd3bSFrançois Tigeot 971dedbd3bSFrançois Tigeot clip.x2 = crtc_state->adjusted_mode.hdisplay; 981dedbd3bSFrançois Tigeot clip.y2 = crtc_state->adjusted_mode.vdisplay; 991dedbd3bSFrançois Tigeot 1001dedbd3bSFrançois Tigeot ret = drm_plane_helper_check_state(plane_state, &clip, 1011dedbd3bSFrançois Tigeot DRM_PLANE_HELPER_NO_SCALING, 1021dedbd3bSFrançois Tigeot DRM_PLANE_HELPER_NO_SCALING, 1031dedbd3bSFrançois Tigeot false, true); 1041dedbd3bSFrançois Tigeot if (ret) 1051dedbd3bSFrançois Tigeot return ret; 1061dedbd3bSFrançois Tigeot 1071dedbd3bSFrançois Tigeot if (!plane_state->visible) 1081dedbd3bSFrançois Tigeot return -EINVAL; 1091dedbd3bSFrançois Tigeot 1101dedbd3bSFrançois Tigeot if (!pipe->funcs || !pipe->funcs->check) 1111dedbd3bSFrançois Tigeot return 0; 1121dedbd3bSFrançois Tigeot 1131dedbd3bSFrançois Tigeot return pipe->funcs->check(pipe, plane_state, crtc_state); 1141dedbd3bSFrançois Tigeot } 1151dedbd3bSFrançois Tigeot 1161dedbd3bSFrançois Tigeot static void drm_simple_kms_plane_atomic_update(struct drm_plane *plane, 117*a85cb24fSFrançois Tigeot struct drm_plane_state *old_pstate) 1181dedbd3bSFrançois Tigeot { 1191dedbd3bSFrançois Tigeot struct drm_simple_display_pipe *pipe; 1201dedbd3bSFrançois Tigeot 1211dedbd3bSFrançois Tigeot pipe = container_of(plane, struct drm_simple_display_pipe, plane); 1221dedbd3bSFrançois Tigeot if (!pipe->funcs || !pipe->funcs->update) 1231dedbd3bSFrançois Tigeot return; 1241dedbd3bSFrançois Tigeot 125*a85cb24fSFrançois Tigeot pipe->funcs->update(pipe, old_pstate); 1261dedbd3bSFrançois Tigeot } 1271dedbd3bSFrançois Tigeot 1281dedbd3bSFrançois Tigeot static int drm_simple_kms_plane_prepare_fb(struct drm_plane *plane, 1291dedbd3bSFrançois Tigeot struct drm_plane_state *state) 1301dedbd3bSFrançois Tigeot { 1311dedbd3bSFrançois Tigeot struct drm_simple_display_pipe *pipe; 1321dedbd3bSFrançois Tigeot 1331dedbd3bSFrançois Tigeot pipe = container_of(plane, struct drm_simple_display_pipe, plane); 1341dedbd3bSFrançois Tigeot if (!pipe->funcs || !pipe->funcs->prepare_fb) 1351dedbd3bSFrançois Tigeot return 0; 1361dedbd3bSFrançois Tigeot 1371dedbd3bSFrançois Tigeot return pipe->funcs->prepare_fb(pipe, state); 1381dedbd3bSFrançois Tigeot } 1391dedbd3bSFrançois Tigeot 1401dedbd3bSFrançois Tigeot static void drm_simple_kms_plane_cleanup_fb(struct drm_plane *plane, 1411dedbd3bSFrançois Tigeot struct drm_plane_state *state) 1421dedbd3bSFrançois Tigeot { 1431dedbd3bSFrançois Tigeot struct drm_simple_display_pipe *pipe; 1441dedbd3bSFrançois Tigeot 1451dedbd3bSFrançois Tigeot pipe = container_of(plane, struct drm_simple_display_pipe, plane); 1461dedbd3bSFrançois Tigeot if (!pipe->funcs || !pipe->funcs->cleanup_fb) 1471dedbd3bSFrançois Tigeot return; 1481dedbd3bSFrançois Tigeot 1491dedbd3bSFrançois Tigeot pipe->funcs->cleanup_fb(pipe, state); 1501dedbd3bSFrançois Tigeot } 1511dedbd3bSFrançois Tigeot 1521dedbd3bSFrançois Tigeot static const struct drm_plane_helper_funcs drm_simple_kms_plane_helper_funcs = { 1531dedbd3bSFrançois Tigeot .prepare_fb = drm_simple_kms_plane_prepare_fb, 1541dedbd3bSFrançois Tigeot .cleanup_fb = drm_simple_kms_plane_cleanup_fb, 1551dedbd3bSFrançois Tigeot .atomic_check = drm_simple_kms_plane_atomic_check, 1561dedbd3bSFrançois Tigeot .atomic_update = drm_simple_kms_plane_atomic_update, 1571dedbd3bSFrançois Tigeot }; 1581dedbd3bSFrançois Tigeot 1591dedbd3bSFrançois Tigeot static const struct drm_plane_funcs drm_simple_kms_plane_funcs = { 1601dedbd3bSFrançois Tigeot .update_plane = drm_atomic_helper_update_plane, 1611dedbd3bSFrançois Tigeot .disable_plane = drm_atomic_helper_disable_plane, 1621dedbd3bSFrançois Tigeot .destroy = drm_plane_cleanup, 1631dedbd3bSFrançois Tigeot .reset = drm_atomic_helper_plane_reset, 1641dedbd3bSFrançois Tigeot .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 1651dedbd3bSFrançois Tigeot .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 1661dedbd3bSFrançois Tigeot }; 1671dedbd3bSFrançois Tigeot 1681dedbd3bSFrançois Tigeot /** 1691dedbd3bSFrançois Tigeot * drm_simple_display_pipe_attach_bridge - Attach a bridge to the display pipe 1701dedbd3bSFrançois Tigeot * @pipe: simple display pipe object 1711dedbd3bSFrançois Tigeot * @bridge: bridge to attach 1721dedbd3bSFrançois Tigeot * 1731dedbd3bSFrançois Tigeot * Makes it possible to still use the drm_simple_display_pipe helpers when 1741dedbd3bSFrançois Tigeot * a DRM bridge has to be used. 1751dedbd3bSFrançois Tigeot * 1761dedbd3bSFrançois Tigeot * Note that you probably want to initialize the pipe by passing a NULL 1771dedbd3bSFrançois Tigeot * connector to drm_simple_display_pipe_init(). 1781dedbd3bSFrançois Tigeot * 1791dedbd3bSFrançois Tigeot * Returns: 1801dedbd3bSFrançois Tigeot * Zero on success, negative error code on failure. 1811dedbd3bSFrançois Tigeot */ 1821dedbd3bSFrançois Tigeot int drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe *pipe, 1831dedbd3bSFrançois Tigeot struct drm_bridge *bridge) 1841dedbd3bSFrançois Tigeot { 185*a85cb24fSFrançois Tigeot return drm_bridge_attach(&pipe->encoder, bridge, NULL); 1861dedbd3bSFrançois Tigeot } 1871dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_simple_display_pipe_attach_bridge); 1881dedbd3bSFrançois Tigeot 1891dedbd3bSFrançois Tigeot /** 1901dedbd3bSFrançois Tigeot * drm_simple_display_pipe_init - Initialize a simple display pipeline 1911dedbd3bSFrançois Tigeot * @dev: DRM device 1921dedbd3bSFrançois Tigeot * @pipe: simple display pipe object to initialize 1931dedbd3bSFrançois Tigeot * @funcs: callbacks for the display pipe (optional) 1941dedbd3bSFrançois Tigeot * @formats: array of supported formats (DRM_FORMAT\_\*) 1951dedbd3bSFrançois Tigeot * @format_count: number of elements in @formats 1961dedbd3bSFrançois Tigeot * @connector: connector to attach and register (optional) 1971dedbd3bSFrançois Tigeot * 1981dedbd3bSFrançois Tigeot * Sets up a display pipeline which consist of a really simple 1991dedbd3bSFrançois Tigeot * plane-crtc-encoder pipe. 2001dedbd3bSFrançois Tigeot * 2011dedbd3bSFrançois Tigeot * If a connector is supplied, the pipe will be coupled with the provided 2021dedbd3bSFrançois Tigeot * connector. You may supply a NULL connector when using drm bridges, that 2031dedbd3bSFrançois Tigeot * handle connectors themselves (see drm_simple_display_pipe_attach_bridge()). 2041dedbd3bSFrançois Tigeot * 2051dedbd3bSFrançois Tigeot * Teardown of a simple display pipe is all handled automatically by the drm 2061dedbd3bSFrançois Tigeot * core through calling drm_mode_config_cleanup(). Drivers afterwards need to 2071dedbd3bSFrançois Tigeot * release the memory for the structure themselves. 2081dedbd3bSFrançois Tigeot * 2091dedbd3bSFrançois Tigeot * Returns: 2101dedbd3bSFrançois Tigeot * Zero on success, negative error code on failure. 2111dedbd3bSFrançois Tigeot */ 2121dedbd3bSFrançois Tigeot int drm_simple_display_pipe_init(struct drm_device *dev, 2131dedbd3bSFrançois Tigeot struct drm_simple_display_pipe *pipe, 2141dedbd3bSFrançois Tigeot const struct drm_simple_display_pipe_funcs *funcs, 2151dedbd3bSFrançois Tigeot const uint32_t *formats, unsigned int format_count, 2161dedbd3bSFrançois Tigeot struct drm_connector *connector) 2171dedbd3bSFrançois Tigeot { 2181dedbd3bSFrançois Tigeot struct drm_encoder *encoder = &pipe->encoder; 2191dedbd3bSFrançois Tigeot struct drm_plane *plane = &pipe->plane; 2201dedbd3bSFrançois Tigeot struct drm_crtc *crtc = &pipe->crtc; 2211dedbd3bSFrançois Tigeot int ret; 2221dedbd3bSFrançois Tigeot 2231dedbd3bSFrançois Tigeot pipe->connector = connector; 2241dedbd3bSFrançois Tigeot pipe->funcs = funcs; 2251dedbd3bSFrançois Tigeot 2261dedbd3bSFrançois Tigeot drm_plane_helper_add(plane, &drm_simple_kms_plane_helper_funcs); 2271dedbd3bSFrançois Tigeot ret = drm_universal_plane_init(dev, plane, 0, 2281dedbd3bSFrançois Tigeot &drm_simple_kms_plane_funcs, 2291dedbd3bSFrançois Tigeot formats, format_count, 2301dedbd3bSFrançois Tigeot DRM_PLANE_TYPE_PRIMARY, NULL); 2311dedbd3bSFrançois Tigeot if (ret) 2321dedbd3bSFrançois Tigeot return ret; 2331dedbd3bSFrançois Tigeot 2341dedbd3bSFrançois Tigeot drm_crtc_helper_add(crtc, &drm_simple_kms_crtc_helper_funcs); 2351dedbd3bSFrançois Tigeot ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, 2361dedbd3bSFrançois Tigeot &drm_simple_kms_crtc_funcs, NULL); 2371dedbd3bSFrançois Tigeot if (ret) 2381dedbd3bSFrançois Tigeot return ret; 2391dedbd3bSFrançois Tigeot 2401dedbd3bSFrançois Tigeot encoder->possible_crtcs = 1 << drm_crtc_index(crtc); 2411dedbd3bSFrançois Tigeot ret = drm_encoder_init(dev, encoder, &drm_simple_kms_encoder_funcs, 2421dedbd3bSFrançois Tigeot DRM_MODE_ENCODER_NONE, NULL); 2431dedbd3bSFrançois Tigeot if (ret || !connector) 2441dedbd3bSFrançois Tigeot return ret; 2451dedbd3bSFrançois Tigeot 2461dedbd3bSFrançois Tigeot return drm_mode_connector_attach_encoder(connector, encoder); 2471dedbd3bSFrançois Tigeot } 2481dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_simple_display_pipe_init); 2491dedbd3bSFrançois Tigeot 2501dedbd3bSFrançois Tigeot MODULE_LICENSE("GPL"); 251