15b809074SNoralf Trønnes /* 25b809074SNoralf Trønnes * Copyright (C) 2016 Noralf Trønnes 35b809074SNoralf Trønnes * 45b809074SNoralf Trønnes * This program is free software; you can redistribute it and/or modify 55b809074SNoralf Trønnes * it under the terms of the GNU General Public License as published by 65b809074SNoralf Trønnes * the Free Software Foundation; either version 2 of the License, or 75b809074SNoralf Trønnes * (at your option) any later version. 85b809074SNoralf Trønnes */ 95b809074SNoralf Trønnes 10*0500c04eSSam Ravnborg #include <linux/module.h> 11*0500c04eSSam Ravnborg #include <linux/slab.h> 12*0500c04eSSam Ravnborg 135b809074SNoralf Trønnes #include <drm/drm_atomic.h> 145b809074SNoralf Trønnes #include <drm/drm_atomic_helper.h> 155b809074SNoralf Trønnes #include <drm/drm_plane_helper.h> 16fcd70cd3SDaniel Vetter #include <drm/drm_probe_helper.h> 175b809074SNoralf Trønnes #include <drm/drm_simple_kms_helper.h> 185b809074SNoralf Trønnes 195b809074SNoralf Trønnes /** 205b809074SNoralf Trønnes * DOC: overview 215b809074SNoralf Trønnes * 225b809074SNoralf Trønnes * This helper library provides helpers for drivers for simple display 235b809074SNoralf Trønnes * hardware. 245b809074SNoralf Trønnes * 255b809074SNoralf Trønnes * drm_simple_display_pipe_init() initializes a simple display pipeline 265b809074SNoralf Trønnes * which has only one full-screen scanout buffer feeding one output. The 27ea0dd85aSDaniel Vetter * pipeline is represented by &struct drm_simple_display_pipe and binds 285b809074SNoralf Trønnes * together &drm_plane, &drm_crtc and &drm_encoder structures into one fixed 295b809074SNoralf Trønnes * entity. Some flexibility for code reuse is provided through a separately 305b809074SNoralf Trønnes * allocated &drm_connector object and supporting optional &drm_bridge 315b809074SNoralf Trønnes * encoder drivers. 325b809074SNoralf Trønnes */ 335b809074SNoralf Trønnes 345b809074SNoralf Trønnes static const struct drm_encoder_funcs drm_simple_kms_encoder_funcs = { 355b809074SNoralf Trønnes .destroy = drm_encoder_cleanup, 365b809074SNoralf Trønnes }; 375b809074SNoralf Trønnes 3840275dc4SLinus Walleij static enum drm_mode_status 3940275dc4SLinus Walleij drm_simple_kms_crtc_mode_valid(struct drm_crtc *crtc, 4040275dc4SLinus Walleij const struct drm_display_mode *mode) 4140275dc4SLinus Walleij { 4240275dc4SLinus Walleij struct drm_simple_display_pipe *pipe; 4340275dc4SLinus Walleij 4440275dc4SLinus Walleij pipe = container_of(crtc, struct drm_simple_display_pipe, crtc); 4540275dc4SLinus Walleij if (!pipe->funcs || !pipe->funcs->mode_valid) 4640275dc4SLinus Walleij /* Anything goes */ 4740275dc4SLinus Walleij return MODE_OK; 4840275dc4SLinus Walleij 4940275dc4SLinus Walleij return pipe->funcs->mode_valid(crtc, mode); 5040275dc4SLinus Walleij } 5140275dc4SLinus Walleij 526dcf0de7SDaniel Vetter static int drm_simple_kms_crtc_check(struct drm_crtc *crtc, 536dcf0de7SDaniel Vetter struct drm_crtc_state *state) 546dcf0de7SDaniel Vetter { 55765831dcSMaarten Lankhorst bool has_primary = state->plane_mask & 5662f77ad0SVille Syrjälä drm_plane_mask(crtc->primary); 57765831dcSMaarten Lankhorst 58765831dcSMaarten Lankhorst /* We always want to have an active plane with an active CRTC */ 59765831dcSMaarten Lankhorst if (has_primary != state->enable) 60765831dcSMaarten Lankhorst return -EINVAL; 61765831dcSMaarten Lankhorst 626dcf0de7SDaniel Vetter return drm_atomic_add_affected_planes(state->state, crtc); 636dcf0de7SDaniel Vetter } 646dcf0de7SDaniel Vetter 650b20a0f8SLaurent Pinchart static void drm_simple_kms_crtc_enable(struct drm_crtc *crtc, 660b20a0f8SLaurent Pinchart struct drm_crtc_state *old_state) 675b809074SNoralf Trønnes { 680c9c7fd0SVille Syrjälä struct drm_plane *plane; 695b809074SNoralf Trønnes struct drm_simple_display_pipe *pipe; 705b809074SNoralf Trønnes 715b809074SNoralf Trønnes pipe = container_of(crtc, struct drm_simple_display_pipe, crtc); 725b809074SNoralf Trønnes if (!pipe->funcs || !pipe->funcs->enable) 735b809074SNoralf Trønnes return; 745b809074SNoralf Trønnes 750c9c7fd0SVille Syrjälä plane = &pipe->plane; 760c9c7fd0SVille Syrjälä pipe->funcs->enable(pipe, crtc->state, plane->state); 775b809074SNoralf Trønnes } 785b809074SNoralf Trønnes 7964581714SLaurent Pinchart static void drm_simple_kms_crtc_disable(struct drm_crtc *crtc, 8064581714SLaurent Pinchart struct drm_crtc_state *old_state) 815b809074SNoralf Trønnes { 825b809074SNoralf Trønnes struct drm_simple_display_pipe *pipe; 835b809074SNoralf Trønnes 845b809074SNoralf Trønnes pipe = container_of(crtc, struct drm_simple_display_pipe, crtc); 855b809074SNoralf Trønnes if (!pipe->funcs || !pipe->funcs->disable) 865b809074SNoralf Trønnes return; 875b809074SNoralf Trønnes 885b809074SNoralf Trønnes pipe->funcs->disable(pipe); 895b809074SNoralf Trønnes } 905b809074SNoralf Trønnes 915b809074SNoralf Trønnes static const struct drm_crtc_helper_funcs drm_simple_kms_crtc_helper_funcs = { 9240275dc4SLinus Walleij .mode_valid = drm_simple_kms_crtc_mode_valid, 936dcf0de7SDaniel Vetter .atomic_check = drm_simple_kms_crtc_check, 940b20a0f8SLaurent Pinchart .atomic_enable = drm_simple_kms_crtc_enable, 9564581714SLaurent Pinchart .atomic_disable = drm_simple_kms_crtc_disable, 965b809074SNoralf Trønnes }; 975b809074SNoralf Trønnes 98ac86cba9SOleksandr Andrushchenko static int drm_simple_kms_crtc_enable_vblank(struct drm_crtc *crtc) 99ac86cba9SOleksandr Andrushchenko { 100ac86cba9SOleksandr Andrushchenko struct drm_simple_display_pipe *pipe; 101ac86cba9SOleksandr Andrushchenko 102ac86cba9SOleksandr Andrushchenko pipe = container_of(crtc, struct drm_simple_display_pipe, crtc); 103ac86cba9SOleksandr Andrushchenko if (!pipe->funcs || !pipe->funcs->enable_vblank) 104ac86cba9SOleksandr Andrushchenko return 0; 105ac86cba9SOleksandr Andrushchenko 106ac86cba9SOleksandr Andrushchenko return pipe->funcs->enable_vblank(pipe); 107ac86cba9SOleksandr Andrushchenko } 108ac86cba9SOleksandr Andrushchenko 109ac86cba9SOleksandr Andrushchenko static void drm_simple_kms_crtc_disable_vblank(struct drm_crtc *crtc) 110ac86cba9SOleksandr Andrushchenko { 111ac86cba9SOleksandr Andrushchenko struct drm_simple_display_pipe *pipe; 112ac86cba9SOleksandr Andrushchenko 113ac86cba9SOleksandr Andrushchenko pipe = container_of(crtc, struct drm_simple_display_pipe, crtc); 114ac86cba9SOleksandr Andrushchenko if (!pipe->funcs || !pipe->funcs->disable_vblank) 115ac86cba9SOleksandr Andrushchenko return; 116ac86cba9SOleksandr Andrushchenko 117ac86cba9SOleksandr Andrushchenko pipe->funcs->disable_vblank(pipe); 118ac86cba9SOleksandr Andrushchenko } 119ac86cba9SOleksandr Andrushchenko 1205b809074SNoralf Trønnes static const struct drm_crtc_funcs drm_simple_kms_crtc_funcs = { 1215b809074SNoralf Trønnes .reset = drm_atomic_helper_crtc_reset, 1225b809074SNoralf Trønnes .destroy = drm_crtc_cleanup, 1235b809074SNoralf Trønnes .set_config = drm_atomic_helper_set_config, 1245b809074SNoralf Trønnes .page_flip = drm_atomic_helper_page_flip, 1255b809074SNoralf Trønnes .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 1265b809074SNoralf Trønnes .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 127ac86cba9SOleksandr Andrushchenko .enable_vblank = drm_simple_kms_crtc_enable_vblank, 128ac86cba9SOleksandr Andrushchenko .disable_vblank = drm_simple_kms_crtc_disable_vblank, 1295b809074SNoralf Trønnes }; 1305b809074SNoralf Trønnes 1315b809074SNoralf Trønnes static int drm_simple_kms_plane_atomic_check(struct drm_plane *plane, 1325b809074SNoralf Trønnes struct drm_plane_state *plane_state) 1335b809074SNoralf Trønnes { 1345b809074SNoralf Trønnes struct drm_simple_display_pipe *pipe; 1355b809074SNoralf Trønnes struct drm_crtc_state *crtc_state; 1365b809074SNoralf Trønnes int ret; 1375b809074SNoralf Trønnes 1385b809074SNoralf Trønnes pipe = container_of(plane, struct drm_simple_display_pipe, plane); 139b4d93679SMaarten Lankhorst crtc_state = drm_atomic_get_new_crtc_state(plane_state->state, 1405b809074SNoralf Trønnes &pipe->crtc); 1414be12cc2SVille Syrjälä 142a01cb8baSVille Syrjälä ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state, 1435b809074SNoralf Trønnes DRM_PLANE_HELPER_NO_SCALING, 1445b809074SNoralf Trønnes DRM_PLANE_HELPER_NO_SCALING, 1454be12cc2SVille Syrjälä false, true); 1465b809074SNoralf Trønnes if (ret) 1475b809074SNoralf Trønnes return ret; 1485b809074SNoralf Trønnes 1494be12cc2SVille Syrjälä if (!plane_state->visible) 1504751cf73SOleksandr Andrushchenko return 0; 1514751cf73SOleksandr Andrushchenko 1525b809074SNoralf Trønnes if (!pipe->funcs || !pipe->funcs->check) 1535b809074SNoralf Trønnes return 0; 1545b809074SNoralf Trønnes 1555b809074SNoralf Trønnes return pipe->funcs->check(pipe, plane_state, crtc_state); 1565b809074SNoralf Trønnes } 1575b809074SNoralf Trønnes 1585b809074SNoralf Trønnes static void drm_simple_kms_plane_atomic_update(struct drm_plane *plane, 159bcd2ba02SEric Anholt struct drm_plane_state *old_pstate) 1605b809074SNoralf Trønnes { 1615b809074SNoralf Trønnes struct drm_simple_display_pipe *pipe; 1625b809074SNoralf Trønnes 1635b809074SNoralf Trønnes pipe = container_of(plane, struct drm_simple_display_pipe, plane); 1645b809074SNoralf Trønnes if (!pipe->funcs || !pipe->funcs->update) 1655b809074SNoralf Trønnes return; 1665b809074SNoralf Trønnes 167bcd2ba02SEric Anholt pipe->funcs->update(pipe, old_pstate); 1685b809074SNoralf Trønnes } 1695b809074SNoralf Trønnes 1707d83a155SMarek Vasut static int drm_simple_kms_plane_prepare_fb(struct drm_plane *plane, 1717d83a155SMarek Vasut struct drm_plane_state *state) 1727d83a155SMarek Vasut { 1737d83a155SMarek Vasut struct drm_simple_display_pipe *pipe; 1747d83a155SMarek Vasut 1757d83a155SMarek Vasut pipe = container_of(plane, struct drm_simple_display_pipe, plane); 1767d83a155SMarek Vasut if (!pipe->funcs || !pipe->funcs->prepare_fb) 1777d83a155SMarek Vasut return 0; 1787d83a155SMarek Vasut 1797d83a155SMarek Vasut return pipe->funcs->prepare_fb(pipe, state); 1807d83a155SMarek Vasut } 1817d83a155SMarek Vasut 1827d83a155SMarek Vasut static void drm_simple_kms_plane_cleanup_fb(struct drm_plane *plane, 1837d83a155SMarek Vasut struct drm_plane_state *state) 1847d83a155SMarek Vasut { 1857d83a155SMarek Vasut struct drm_simple_display_pipe *pipe; 1867d83a155SMarek Vasut 1877d83a155SMarek Vasut pipe = container_of(plane, struct drm_simple_display_pipe, plane); 1887d83a155SMarek Vasut if (!pipe->funcs || !pipe->funcs->cleanup_fb) 1897d83a155SMarek Vasut return; 1907d83a155SMarek Vasut 1917d83a155SMarek Vasut pipe->funcs->cleanup_fb(pipe, state); 1927d83a155SMarek Vasut } 1937d83a155SMarek Vasut 194dff906c3SEric Anholt static bool drm_simple_kms_format_mod_supported(struct drm_plane *plane, 195dff906c3SEric Anholt uint32_t format, 196dff906c3SEric Anholt uint64_t modifier) 197dff906c3SEric Anholt { 198dff906c3SEric Anholt return modifier == DRM_FORMAT_MOD_LINEAR; 199dff906c3SEric Anholt } 200dff906c3SEric Anholt 2015b809074SNoralf Trønnes static const struct drm_plane_helper_funcs drm_simple_kms_plane_helper_funcs = { 2027d83a155SMarek Vasut .prepare_fb = drm_simple_kms_plane_prepare_fb, 2037d83a155SMarek Vasut .cleanup_fb = drm_simple_kms_plane_cleanup_fb, 2045b809074SNoralf Trønnes .atomic_check = drm_simple_kms_plane_atomic_check, 2055b809074SNoralf Trønnes .atomic_update = drm_simple_kms_plane_atomic_update, 2065b809074SNoralf Trønnes }; 2075b809074SNoralf Trønnes 2085b809074SNoralf Trønnes static const struct drm_plane_funcs drm_simple_kms_plane_funcs = { 2095b809074SNoralf Trønnes .update_plane = drm_atomic_helper_update_plane, 2105b809074SNoralf Trønnes .disable_plane = drm_atomic_helper_disable_plane, 2115b809074SNoralf Trønnes .destroy = drm_plane_cleanup, 2125b809074SNoralf Trønnes .reset = drm_atomic_helper_plane_reset, 2135b809074SNoralf Trønnes .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 2145b809074SNoralf Trønnes .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 215dff906c3SEric Anholt .format_mod_supported = drm_simple_kms_format_mod_supported, 2165b809074SNoralf Trønnes }; 2175b809074SNoralf Trønnes 2185b809074SNoralf Trønnes /** 219315486c6SAndrea Merello * drm_simple_display_pipe_attach_bridge - Attach a bridge to the display pipe 220315486c6SAndrea Merello * @pipe: simple display pipe object 221315486c6SAndrea Merello * @bridge: bridge to attach 222315486c6SAndrea Merello * 223315486c6SAndrea Merello * Makes it possible to still use the drm_simple_display_pipe helpers when 224315486c6SAndrea Merello * a DRM bridge has to be used. 225315486c6SAndrea Merello * 226315486c6SAndrea Merello * Note that you probably want to initialize the pipe by passing a NULL 227315486c6SAndrea Merello * connector to drm_simple_display_pipe_init(). 228315486c6SAndrea Merello * 229315486c6SAndrea Merello * Returns: 230315486c6SAndrea Merello * Zero on success, negative error code on failure. 231315486c6SAndrea Merello */ 232315486c6SAndrea Merello int drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe *pipe, 233315486c6SAndrea Merello struct drm_bridge *bridge) 234315486c6SAndrea Merello { 2353bb80f24SLaurent Pinchart return drm_bridge_attach(&pipe->encoder, bridge, NULL); 236315486c6SAndrea Merello } 237315486c6SAndrea Merello EXPORT_SYMBOL(drm_simple_display_pipe_attach_bridge); 238315486c6SAndrea Merello 239315486c6SAndrea Merello /** 2405b809074SNoralf Trønnes * drm_simple_display_pipe_init - Initialize a simple display pipeline 2415b809074SNoralf Trønnes * @dev: DRM device 2425b809074SNoralf Trønnes * @pipe: simple display pipe object to initialize 2435b809074SNoralf Trønnes * @funcs: callbacks for the display pipe (optional) 24462cacc79SDaniel Vetter * @formats: array of supported formats (DRM_FORMAT\_\*) 2455b809074SNoralf Trønnes * @format_count: number of elements in @formats 246e6fc3b68SBen Widawsky * @format_modifiers: array of formats modifiers 2474f993973SAndrea Merello * @connector: connector to attach and register (optional) 2485b809074SNoralf Trønnes * 2495b809074SNoralf Trønnes * Sets up a display pipeline which consist of a really simple 2504f993973SAndrea Merello * plane-crtc-encoder pipe. 2514f993973SAndrea Merello * 2524f993973SAndrea Merello * If a connector is supplied, the pipe will be coupled with the provided 2534f993973SAndrea Merello * connector. You may supply a NULL connector when using drm bridges, that 2544f993973SAndrea Merello * handle connectors themselves (see drm_simple_display_pipe_attach_bridge()). 2554f993973SAndrea Merello * 2565b809074SNoralf Trønnes * Teardown of a simple display pipe is all handled automatically by the drm 2575b809074SNoralf Trønnes * core through calling drm_mode_config_cleanup(). Drivers afterwards need to 2585b809074SNoralf Trønnes * release the memory for the structure themselves. 2595b809074SNoralf Trønnes * 2605b809074SNoralf Trønnes * Returns: 2615b809074SNoralf Trønnes * Zero on success, negative error code on failure. 2625b809074SNoralf Trønnes */ 2635b809074SNoralf Trønnes int drm_simple_display_pipe_init(struct drm_device *dev, 2645b809074SNoralf Trønnes struct drm_simple_display_pipe *pipe, 2655b809074SNoralf Trønnes const struct drm_simple_display_pipe_funcs *funcs, 2665b809074SNoralf Trønnes const uint32_t *formats, unsigned int format_count, 267e6fc3b68SBen Widawsky const uint64_t *format_modifiers, 2685b809074SNoralf Trønnes struct drm_connector *connector) 2695b809074SNoralf Trønnes { 2705b809074SNoralf Trønnes struct drm_encoder *encoder = &pipe->encoder; 2715b809074SNoralf Trønnes struct drm_plane *plane = &pipe->plane; 2725b809074SNoralf Trønnes struct drm_crtc *crtc = &pipe->crtc; 2735b809074SNoralf Trønnes int ret; 2745b809074SNoralf Trønnes 2755b809074SNoralf Trønnes pipe->connector = connector; 2765b809074SNoralf Trønnes pipe->funcs = funcs; 2775b809074SNoralf Trønnes 2785b809074SNoralf Trønnes drm_plane_helper_add(plane, &drm_simple_kms_plane_helper_funcs); 2795b809074SNoralf Trønnes ret = drm_universal_plane_init(dev, plane, 0, 2805b809074SNoralf Trønnes &drm_simple_kms_plane_funcs, 2815b809074SNoralf Trønnes formats, format_count, 282e6fc3b68SBen Widawsky format_modifiers, 2835b809074SNoralf Trønnes DRM_PLANE_TYPE_PRIMARY, NULL); 2845b809074SNoralf Trønnes if (ret) 2855b809074SNoralf Trønnes return ret; 2865b809074SNoralf Trønnes 2875b809074SNoralf Trønnes drm_crtc_helper_add(crtc, &drm_simple_kms_crtc_helper_funcs); 2885b809074SNoralf Trønnes ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, 2895b809074SNoralf Trønnes &drm_simple_kms_crtc_funcs, NULL); 2905b809074SNoralf Trønnes if (ret) 2915b809074SNoralf Trønnes return ret; 2925b809074SNoralf Trønnes 2936a52193bSVille Syrjälä encoder->possible_crtcs = drm_crtc_mask(crtc); 2945b809074SNoralf Trønnes ret = drm_encoder_init(dev, encoder, &drm_simple_kms_encoder_funcs, 2955b809074SNoralf Trønnes DRM_MODE_ENCODER_NONE, NULL); 2964f993973SAndrea Merello if (ret || !connector) 2975b809074SNoralf Trønnes return ret; 2985b809074SNoralf Trønnes 299cde4c44dSDaniel Vetter return drm_connector_attach_encoder(connector, encoder); 3005b809074SNoralf Trønnes } 3015b809074SNoralf Trønnes EXPORT_SYMBOL(drm_simple_display_pipe_init); 3025b809074SNoralf Trønnes 3035b809074SNoralf Trønnes MODULE_LICENSE("GPL"); 304