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
26a85cb24fSFranç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
drm_simple_kms_crtc_check(struct drm_crtc * crtc,struct drm_crtc_state * state)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 {
40*3f2dd94aSFrançois Tigeot bool has_primary = state->plane_mask &
41*3f2dd94aSFrançois Tigeot BIT(drm_plane_index(crtc->primary));
42*3f2dd94aSFrançois Tigeot
43*3f2dd94aSFrançois Tigeot /* We always want to have an active plane with an active CRTC */
44*3f2dd94aSFrançois Tigeot if (has_primary != state->enable)
45*3f2dd94aSFrançois Tigeot return -EINVAL;
46*3f2dd94aSFrançois Tigeot
471dedbd3bSFrançois Tigeot return drm_atomic_add_affected_planes(state->state, crtc);
481dedbd3bSFrançois Tigeot }
491dedbd3bSFrançois Tigeot
drm_simple_kms_crtc_enable(struct drm_crtc * crtc,struct drm_crtc_state * old_state)50*3f2dd94aSFrançois Tigeot static void drm_simple_kms_crtc_enable(struct drm_crtc *crtc,
51*3f2dd94aSFrançois Tigeot struct drm_crtc_state *old_state)
521dedbd3bSFrançois Tigeot {
531dedbd3bSFrançois Tigeot struct drm_simple_display_pipe *pipe;
541dedbd3bSFrançois Tigeot
551dedbd3bSFrançois Tigeot pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
561dedbd3bSFrançois Tigeot if (!pipe->funcs || !pipe->funcs->enable)
571dedbd3bSFrançois Tigeot return;
581dedbd3bSFrançois Tigeot
591dedbd3bSFrançois Tigeot pipe->funcs->enable(pipe, crtc->state);
601dedbd3bSFrançois Tigeot }
611dedbd3bSFrançois Tigeot
drm_simple_kms_crtc_disable(struct drm_crtc * crtc,struct drm_crtc_state * old_state)62*3f2dd94aSFrançois Tigeot static void drm_simple_kms_crtc_disable(struct drm_crtc *crtc,
63*3f2dd94aSFrançois Tigeot struct drm_crtc_state *old_state)
641dedbd3bSFrançois Tigeot {
651dedbd3bSFrançois Tigeot struct drm_simple_display_pipe *pipe;
661dedbd3bSFrançois Tigeot
671dedbd3bSFrançois Tigeot pipe = container_of(crtc, struct drm_simple_display_pipe, crtc);
681dedbd3bSFrançois Tigeot if (!pipe->funcs || !pipe->funcs->disable)
691dedbd3bSFrançois Tigeot return;
701dedbd3bSFrançois Tigeot
711dedbd3bSFrançois Tigeot pipe->funcs->disable(pipe);
721dedbd3bSFrançois Tigeot }
731dedbd3bSFrançois Tigeot
741dedbd3bSFrançois Tigeot static const struct drm_crtc_helper_funcs drm_simple_kms_crtc_helper_funcs = {
751dedbd3bSFrançois Tigeot .atomic_check = drm_simple_kms_crtc_check,
76*3f2dd94aSFrançois Tigeot .atomic_enable = drm_simple_kms_crtc_enable,
77*3f2dd94aSFrançois Tigeot .atomic_disable = drm_simple_kms_crtc_disable,
781dedbd3bSFrançois Tigeot };
791dedbd3bSFrançois Tigeot
801dedbd3bSFrançois Tigeot static const struct drm_crtc_funcs drm_simple_kms_crtc_funcs = {
811dedbd3bSFrançois Tigeot .reset = drm_atomic_helper_crtc_reset,
821dedbd3bSFrançois Tigeot .destroy = drm_crtc_cleanup,
831dedbd3bSFrançois Tigeot .set_config = drm_atomic_helper_set_config,
841dedbd3bSFrançois Tigeot .page_flip = drm_atomic_helper_page_flip,
851dedbd3bSFrançois Tigeot .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
861dedbd3bSFrançois Tigeot .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
871dedbd3bSFrançois Tigeot };
881dedbd3bSFrançois Tigeot
drm_simple_kms_plane_atomic_check(struct drm_plane * plane,struct drm_plane_state * plane_state)891dedbd3bSFrançois Tigeot static int drm_simple_kms_plane_atomic_check(struct drm_plane *plane,
901dedbd3bSFrançois Tigeot struct drm_plane_state *plane_state)
911dedbd3bSFrançois Tigeot {
921dedbd3bSFrançois Tigeot struct drm_rect clip = { 0 };
931dedbd3bSFrançois Tigeot struct drm_simple_display_pipe *pipe;
941dedbd3bSFrançois Tigeot struct drm_crtc_state *crtc_state;
951dedbd3bSFrançois Tigeot int ret;
961dedbd3bSFrançois Tigeot
971dedbd3bSFrançois Tigeot pipe = container_of(plane, struct drm_simple_display_pipe, plane);
98a85cb24fSFrançois Tigeot crtc_state = drm_atomic_get_new_crtc_state(plane_state->state,
991dedbd3bSFrançois Tigeot &pipe->crtc);
1001dedbd3bSFrançois Tigeot if (!crtc_state->enable)
1011dedbd3bSFrançois Tigeot return 0; /* nothing to check when disabling or disabled */
1021dedbd3bSFrançois Tigeot
1031dedbd3bSFrançois Tigeot clip.x2 = crtc_state->adjusted_mode.hdisplay;
1041dedbd3bSFrançois Tigeot clip.y2 = crtc_state->adjusted_mode.vdisplay;
1051dedbd3bSFrançois Tigeot
1061dedbd3bSFrançois Tigeot ret = drm_plane_helper_check_state(plane_state, &clip,
1071dedbd3bSFrançois Tigeot DRM_PLANE_HELPER_NO_SCALING,
1081dedbd3bSFrançois Tigeot DRM_PLANE_HELPER_NO_SCALING,
1091dedbd3bSFrançois Tigeot false, true);
1101dedbd3bSFrançois Tigeot if (ret)
1111dedbd3bSFrançois Tigeot return ret;
1121dedbd3bSFrançois Tigeot
1131dedbd3bSFrançois Tigeot if (!plane_state->visible)
1141dedbd3bSFrançois Tigeot return -EINVAL;
1151dedbd3bSFrançois Tigeot
1161dedbd3bSFrançois Tigeot if (!pipe->funcs || !pipe->funcs->check)
1171dedbd3bSFrançois Tigeot return 0;
1181dedbd3bSFrançois Tigeot
1191dedbd3bSFrançois Tigeot return pipe->funcs->check(pipe, plane_state, crtc_state);
1201dedbd3bSFrançois Tigeot }
1211dedbd3bSFrançois Tigeot
drm_simple_kms_plane_atomic_update(struct drm_plane * plane,struct drm_plane_state * old_pstate)1221dedbd3bSFrançois Tigeot static void drm_simple_kms_plane_atomic_update(struct drm_plane *plane,
123a85cb24fSFrançois Tigeot struct drm_plane_state *old_pstate)
1241dedbd3bSFrançois Tigeot {
1251dedbd3bSFrançois Tigeot struct drm_simple_display_pipe *pipe;
1261dedbd3bSFrançois Tigeot
1271dedbd3bSFrançois Tigeot pipe = container_of(plane, struct drm_simple_display_pipe, plane);
1281dedbd3bSFrançois Tigeot if (!pipe->funcs || !pipe->funcs->update)
1291dedbd3bSFrançois Tigeot return;
1301dedbd3bSFrançois Tigeot
131a85cb24fSFrançois Tigeot pipe->funcs->update(pipe, old_pstate);
1321dedbd3bSFrançois Tigeot }
1331dedbd3bSFrançois Tigeot
drm_simple_kms_plane_prepare_fb(struct drm_plane * plane,struct drm_plane_state * state)1341dedbd3bSFrançois Tigeot static int drm_simple_kms_plane_prepare_fb(struct drm_plane *plane,
1351dedbd3bSFrançois Tigeot struct drm_plane_state *state)
1361dedbd3bSFrançois Tigeot {
1371dedbd3bSFrançois Tigeot struct drm_simple_display_pipe *pipe;
1381dedbd3bSFrançois Tigeot
1391dedbd3bSFrançois Tigeot pipe = container_of(plane, struct drm_simple_display_pipe, plane);
1401dedbd3bSFrançois Tigeot if (!pipe->funcs || !pipe->funcs->prepare_fb)
1411dedbd3bSFrançois Tigeot return 0;
1421dedbd3bSFrançois Tigeot
1431dedbd3bSFrançois Tigeot return pipe->funcs->prepare_fb(pipe, state);
1441dedbd3bSFrançois Tigeot }
1451dedbd3bSFrançois Tigeot
drm_simple_kms_plane_cleanup_fb(struct drm_plane * plane,struct drm_plane_state * state)1461dedbd3bSFrançois Tigeot static void drm_simple_kms_plane_cleanup_fb(struct drm_plane *plane,
1471dedbd3bSFrançois Tigeot struct drm_plane_state *state)
1481dedbd3bSFrançois Tigeot {
1491dedbd3bSFrançois Tigeot struct drm_simple_display_pipe *pipe;
1501dedbd3bSFrançois Tigeot
1511dedbd3bSFrançois Tigeot pipe = container_of(plane, struct drm_simple_display_pipe, plane);
1521dedbd3bSFrançois Tigeot if (!pipe->funcs || !pipe->funcs->cleanup_fb)
1531dedbd3bSFrançois Tigeot return;
1541dedbd3bSFrançois Tigeot
1551dedbd3bSFrançois Tigeot pipe->funcs->cleanup_fb(pipe, state);
1561dedbd3bSFrançois Tigeot }
1571dedbd3bSFrançois Tigeot
1581dedbd3bSFrançois Tigeot static const struct drm_plane_helper_funcs drm_simple_kms_plane_helper_funcs = {
1591dedbd3bSFrançois Tigeot .prepare_fb = drm_simple_kms_plane_prepare_fb,
1601dedbd3bSFrançois Tigeot .cleanup_fb = drm_simple_kms_plane_cleanup_fb,
1611dedbd3bSFrançois Tigeot .atomic_check = drm_simple_kms_plane_atomic_check,
1621dedbd3bSFrançois Tigeot .atomic_update = drm_simple_kms_plane_atomic_update,
1631dedbd3bSFrançois Tigeot };
1641dedbd3bSFrançois Tigeot
1651dedbd3bSFrançois Tigeot static const struct drm_plane_funcs drm_simple_kms_plane_funcs = {
1661dedbd3bSFrançois Tigeot .update_plane = drm_atomic_helper_update_plane,
1671dedbd3bSFrançois Tigeot .disable_plane = drm_atomic_helper_disable_plane,
1681dedbd3bSFrançois Tigeot .destroy = drm_plane_cleanup,
1691dedbd3bSFrançois Tigeot .reset = drm_atomic_helper_plane_reset,
1701dedbd3bSFrançois Tigeot .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
1711dedbd3bSFrançois Tigeot .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
1721dedbd3bSFrançois Tigeot };
1731dedbd3bSFrançois Tigeot
1741dedbd3bSFrançois Tigeot /**
1751dedbd3bSFrançois Tigeot * drm_simple_display_pipe_attach_bridge - Attach a bridge to the display pipe
1761dedbd3bSFrançois Tigeot * @pipe: simple display pipe object
1771dedbd3bSFrançois Tigeot * @bridge: bridge to attach
1781dedbd3bSFrançois Tigeot *
1791dedbd3bSFrançois Tigeot * Makes it possible to still use the drm_simple_display_pipe helpers when
1801dedbd3bSFrançois Tigeot * a DRM bridge has to be used.
1811dedbd3bSFrançois Tigeot *
1821dedbd3bSFrançois Tigeot * Note that you probably want to initialize the pipe by passing a NULL
1831dedbd3bSFrançois Tigeot * connector to drm_simple_display_pipe_init().
1841dedbd3bSFrançois Tigeot *
1851dedbd3bSFrançois Tigeot * Returns:
1861dedbd3bSFrançois Tigeot * Zero on success, negative error code on failure.
1871dedbd3bSFrançois Tigeot */
drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe * pipe,struct drm_bridge * bridge)1881dedbd3bSFrançois Tigeot int drm_simple_display_pipe_attach_bridge(struct drm_simple_display_pipe *pipe,
1891dedbd3bSFrançois Tigeot struct drm_bridge *bridge)
1901dedbd3bSFrançois Tigeot {
191a85cb24fSFrançois Tigeot return drm_bridge_attach(&pipe->encoder, bridge, NULL);
1921dedbd3bSFrançois Tigeot }
1931dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_simple_display_pipe_attach_bridge);
1941dedbd3bSFrançois Tigeot
1951dedbd3bSFrançois Tigeot /**
1961dedbd3bSFrançois Tigeot * drm_simple_display_pipe_init - Initialize a simple display pipeline
1971dedbd3bSFrançois Tigeot * @dev: DRM device
1981dedbd3bSFrançois Tigeot * @pipe: simple display pipe object to initialize
1991dedbd3bSFrançois Tigeot * @funcs: callbacks for the display pipe (optional)
2001dedbd3bSFrançois Tigeot * @formats: array of supported formats (DRM_FORMAT\_\*)
2011dedbd3bSFrançois Tigeot * @format_count: number of elements in @formats
202*3f2dd94aSFrançois Tigeot * @format_modifiers: array of formats modifiers
2031dedbd3bSFrançois Tigeot * @connector: connector to attach and register (optional)
2041dedbd3bSFrançois Tigeot *
2051dedbd3bSFrançois Tigeot * Sets up a display pipeline which consist of a really simple
2061dedbd3bSFrançois Tigeot * plane-crtc-encoder pipe.
2071dedbd3bSFrançois Tigeot *
2081dedbd3bSFrançois Tigeot * If a connector is supplied, the pipe will be coupled with the provided
2091dedbd3bSFrançois Tigeot * connector. You may supply a NULL connector when using drm bridges, that
2101dedbd3bSFrançois Tigeot * handle connectors themselves (see drm_simple_display_pipe_attach_bridge()).
2111dedbd3bSFrançois Tigeot *
2121dedbd3bSFrançois Tigeot * Teardown of a simple display pipe is all handled automatically by the drm
2131dedbd3bSFrançois Tigeot * core through calling drm_mode_config_cleanup(). Drivers afterwards need to
2141dedbd3bSFrançois Tigeot * release the memory for the structure themselves.
2151dedbd3bSFrançois Tigeot *
2161dedbd3bSFrançois Tigeot * Returns:
2171dedbd3bSFrançois Tigeot * Zero on success, negative error code on failure.
2181dedbd3bSFrançois Tigeot */
drm_simple_display_pipe_init(struct drm_device * dev,struct drm_simple_display_pipe * pipe,const struct drm_simple_display_pipe_funcs * funcs,const uint32_t * formats,unsigned int format_count,const uint64_t * format_modifiers,struct drm_connector * connector)2191dedbd3bSFrançois Tigeot int drm_simple_display_pipe_init(struct drm_device *dev,
2201dedbd3bSFrançois Tigeot struct drm_simple_display_pipe *pipe,
2211dedbd3bSFrançois Tigeot const struct drm_simple_display_pipe_funcs *funcs,
2221dedbd3bSFrançois Tigeot const uint32_t *formats, unsigned int format_count,
223*3f2dd94aSFrançois Tigeot const uint64_t *format_modifiers,
2241dedbd3bSFrançois Tigeot struct drm_connector *connector)
2251dedbd3bSFrançois Tigeot {
2261dedbd3bSFrançois Tigeot struct drm_encoder *encoder = &pipe->encoder;
2271dedbd3bSFrançois Tigeot struct drm_plane *plane = &pipe->plane;
2281dedbd3bSFrançois Tigeot struct drm_crtc *crtc = &pipe->crtc;
2291dedbd3bSFrançois Tigeot int ret;
2301dedbd3bSFrançois Tigeot
2311dedbd3bSFrançois Tigeot pipe->connector = connector;
2321dedbd3bSFrançois Tigeot pipe->funcs = funcs;
2331dedbd3bSFrançois Tigeot
2341dedbd3bSFrançois Tigeot drm_plane_helper_add(plane, &drm_simple_kms_plane_helper_funcs);
2351dedbd3bSFrançois Tigeot ret = drm_universal_plane_init(dev, plane, 0,
2361dedbd3bSFrançois Tigeot &drm_simple_kms_plane_funcs,
2371dedbd3bSFrançois Tigeot formats, format_count,
238*3f2dd94aSFrançois Tigeot format_modifiers,
2391dedbd3bSFrançois Tigeot DRM_PLANE_TYPE_PRIMARY, NULL);
2401dedbd3bSFrançois Tigeot if (ret)
2411dedbd3bSFrançois Tigeot return ret;
2421dedbd3bSFrançois Tigeot
2431dedbd3bSFrançois Tigeot drm_crtc_helper_add(crtc, &drm_simple_kms_crtc_helper_funcs);
2441dedbd3bSFrançois Tigeot ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
2451dedbd3bSFrançois Tigeot &drm_simple_kms_crtc_funcs, NULL);
2461dedbd3bSFrançois Tigeot if (ret)
2471dedbd3bSFrançois Tigeot return ret;
2481dedbd3bSFrançois Tigeot
2491dedbd3bSFrançois Tigeot encoder->possible_crtcs = 1 << drm_crtc_index(crtc);
2501dedbd3bSFrançois Tigeot ret = drm_encoder_init(dev, encoder, &drm_simple_kms_encoder_funcs,
2511dedbd3bSFrançois Tigeot DRM_MODE_ENCODER_NONE, NULL);
2521dedbd3bSFrançois Tigeot if (ret || !connector)
2531dedbd3bSFrançois Tigeot return ret;
2541dedbd3bSFrançois Tigeot
2551dedbd3bSFrançois Tigeot return drm_mode_connector_attach_encoder(connector, encoder);
2561dedbd3bSFrançois Tigeot }
2571dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_simple_display_pipe_init);
2581dedbd3bSFrançois Tigeot
2591dedbd3bSFrançois Tigeot MODULE_LICENSE("GPL");
260