diff options
author | Brian Starkey <brian.starkey@arm.com> | 2017-03-29 12:42:32 -0400 |
---|---|---|
committer | Liviu Dudau <Liviu.Dudau@arm.com> | 2018-06-20 10:27:49 -0400 |
commit | 935774cd71fe604cc8ed24adcb507d7784255672 (patch) | |
tree | 61aa8d66732c288df25d002c17620521b0900fa1 | |
parent | f664a52695429b68afb4e130a0f69cd5fd1fec86 (diff) |
drm: Add writeback connector type
Writeback connectors represent writeback engines which can write the
CRTC output to a memory framebuffer. Add a writeback connector type and
related support functions.
Drivers should initialize a writeback connector with
drm_writeback_connector_init() which takes care of setting up all the
writeback-specific details on top of the normal functionality of
drm_connector_init().
Writeback connectors have a WRITEBACK_FB_ID property, used to set the
output framebuffer, and a WRITEBACK_PIXEL_FORMATS blob used to expose the
supported writeback formats to userspace.
When a framebuffer is attached to a writeback connector with the
WRITEBACK_FB_ID property, it is used only once (for the commit in which
it was included), and userspace can never read back the value of
WRITEBACK_FB_ID. WRITEBACK_FB_ID can only be set if the connector is
attached to a CRTC.
Changes since v1:
- Added drm_writeback.c + documentation
- Added helper to initialize writeback connector in one go
- Added core checks
- Squashed into a single commit
- Dropped the client cap
- Writeback framebuffers are no longer persistent
Changes since v2:
Daniel Vetter:
- Subclass drm_connector to drm_writeback_connector
- Relax check to allow CRTC to be set without an FB
- Add some writeback_ prefixes
- Drop PIXEL_FORMATS_SIZE property, as it was unnecessary
Gustavo Padovan:
- Add drm_writeback_job to handle writeback signalling centrally
Changes since v3:
- Rebased
- Rename PIXEL_FORMATS -> WRITEBACK_PIXEL_FORMATS
Chances since v4:
- Embed a drm_encoder inside the drm_writeback_connector to
reduce the amount of boilerplate code required from the drivers
that are using it.
Changes since v5:
- Added Rob Clark's atomic_commit() vfunc to connector helper
funcs, so that writeback jobs are committed from atomic helpers
- Updated create_writeback_properties() signature to return an
error code rather than a boolean false for failure.
- Free writeback job with the connector state rather than when
doing the cleanup_work()
Changes since v7:
- fix extraneous use of out_fence that is only introduced in a
subsequent patch.
Changes since v8:
- whitespace changes pull from subsequent patch
Changes since v9:
- Revert the v6 changes that free the writeback job in the connector
state cleanup and return to doing it in the cleanup_work() function
Signed-off-by: Brian Starkey <brian.starkey@arm.com>
[rebased and fixed conflicts]
Signed-off-by: Mihail Atanassov <mihail.atanassov@arm.com>
[rebased and added atomic_commit() vfunc for writeback jobs]
Signed-off-by: Rob Clark <robdclark@gmail.com>
Signed-off-by: Liviu Dudau <liviu.dudau@arm.com>
Reviewed-by: Eric Anholt <eric@anholt.net>
Link: https://patchwork.freedesktop.org/patch/229037/
-rw-r--r-- | Documentation/gpu/drm-kms.rst | 9 | ||||
-rw-r--r-- | drivers/gpu/drm/Makefile | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_atomic.c | 124 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_atomic_helper.c | 25 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_connector.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_writeback.c | 245 | ||||
-rw-r--r-- | include/drm/drm_atomic.h | 3 | ||||
-rw-r--r-- | include/drm/drm_connector.h | 13 | ||||
-rw-r--r-- | include/drm/drm_mode_config.h | 15 | ||||
-rw-r--r-- | include/drm/drm_modeset_helper_vtables.h | 11 | ||||
-rw-r--r-- | include/drm/drm_writeback.h | 91 | ||||
-rw-r--r-- | include/uapi/drm/drm_mode.h | 1 |
12 files changed, 541 insertions, 2 deletions
diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst index e233c2626bd0..4f6f113a7f5d 100644 --- a/Documentation/gpu/drm-kms.rst +++ b/Documentation/gpu/drm-kms.rst | |||
@@ -373,6 +373,15 @@ Connector Functions Reference | |||
373 | .. kernel-doc:: drivers/gpu/drm/drm_connector.c | 373 | .. kernel-doc:: drivers/gpu/drm/drm_connector.c |
374 | :export: | 374 | :export: |
375 | 375 | ||
376 | Writeback Connectors | ||
377 | -------------------- | ||
378 | |||
379 | .. kernel-doc:: drivers/gpu/drm/drm_writeback.c | ||
380 | :doc: overview | ||
381 | |||
382 | .. kernel-doc:: drivers/gpu/drm/drm_writeback.c | ||
383 | :export: | ||
384 | |||
376 | Encoder Abstraction | 385 | Encoder Abstraction |
377 | =================== | 386 | =================== |
378 | 387 | ||
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index ef9f3dab287f..69c13517ea3a 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile | |||
@@ -18,7 +18,7 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \ | |||
18 | drm_encoder.o drm_mode_object.o drm_property.o \ | 18 | drm_encoder.o drm_mode_object.o drm_property.o \ |
19 | drm_plane.o drm_color_mgmt.o drm_print.o \ | 19 | drm_plane.o drm_color_mgmt.o drm_print.o \ |
20 | drm_dumb_buffers.o drm_mode_config.o drm_vblank.o \ | 20 | drm_dumb_buffers.o drm_mode_config.o drm_vblank.o \ |
21 | drm_syncobj.o drm_lease.o | 21 | drm_syncobj.o drm_lease.o drm_writeback.o |
22 | 22 | ||
23 | drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o | 23 | drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o |
24 | drm-$(CONFIG_DRM_VM) += drm_vm.o | 24 | drm-$(CONFIG_DRM_VM) += drm_vm.o |
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 9ec5c865a043..3e53d6e5c340 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include <drm/drm_atomic.h> | 30 | #include <drm/drm_atomic.h> |
31 | #include <drm/drm_mode.h> | 31 | #include <drm/drm_mode.h> |
32 | #include <drm/drm_print.h> | 32 | #include <drm/drm_print.h> |
33 | #include <drm/drm_writeback.h> | ||
33 | #include <linux/sync_file.h> | 34 | #include <linux/sync_file.h> |
34 | 35 | ||
35 | #include "drm_crtc_internal.h" | 36 | #include "drm_crtc_internal.h" |
@@ -691,6 +692,45 @@ static void drm_atomic_crtc_print_state(struct drm_printer *p, | |||
691 | } | 692 | } |
692 | 693 | ||
693 | /** | 694 | /** |
695 | * drm_atomic_connector_check - check connector state | ||
696 | * @connector: connector to check | ||
697 | * @state: connector state to check | ||
698 | * | ||
699 | * Provides core sanity checks for connector state. | ||
700 | * | ||
701 | * RETURNS: | ||
702 | * Zero on success, error code on failure | ||
703 | */ | ||
704 | static int drm_atomic_connector_check(struct drm_connector *connector, | ||
705 | struct drm_connector_state *state) | ||
706 | { | ||
707 | struct drm_crtc_state *crtc_state; | ||
708 | struct drm_writeback_job *writeback_job = state->writeback_job; | ||
709 | |||
710 | if ((connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK) || !writeback_job) | ||
711 | return 0; | ||
712 | |||
713 | if (writeback_job->fb && !state->crtc) { | ||
714 | DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] framebuffer without CRTC\n", | ||
715 | connector->base.id, connector->name); | ||
716 | return -EINVAL; | ||
717 | } | ||
718 | |||
719 | if (state->crtc) | ||
720 | crtc_state = drm_atomic_get_existing_crtc_state(state->state, | ||
721 | state->crtc); | ||
722 | |||
723 | if (writeback_job->fb && !crtc_state->active) { | ||
724 | DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] has framebuffer, but [CRTC:%d] is off\n", | ||
725 | connector->base.id, connector->name, | ||
726 | state->crtc->base.id); | ||
727 | return -EINVAL; | ||
728 | } | ||
729 | |||
730 | return 0; | ||
731 | } | ||
732 | |||
733 | /** | ||
694 | * drm_atomic_get_plane_state - get plane state | 734 | * drm_atomic_get_plane_state - get plane state |
695 | * @state: global atomic state object | 735 | * @state: global atomic state object |
696 | * @plane: plane to get state object for | 736 | * @plane: plane to get state object for |
@@ -1321,6 +1361,12 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector, | |||
1321 | return -EINVAL; | 1361 | return -EINVAL; |
1322 | } | 1362 | } |
1323 | state->content_protection = val; | 1363 | state->content_protection = val; |
1364 | } else if (property == config->writeback_fb_id_property) { | ||
1365 | struct drm_framebuffer *fb = drm_framebuffer_lookup(dev, NULL, val); | ||
1366 | int ret = drm_atomic_set_writeback_fb_for_connector(state, fb); | ||
1367 | if (fb) | ||
1368 | drm_framebuffer_put(fb); | ||
1369 | return ret; | ||
1324 | } else if (connector->funcs->atomic_set_property) { | 1370 | } else if (connector->funcs->atomic_set_property) { |
1325 | return connector->funcs->atomic_set_property(connector, | 1371 | return connector->funcs->atomic_set_property(connector, |
1326 | state, property, val); | 1372 | state, property, val); |
@@ -1407,6 +1453,9 @@ drm_atomic_connector_get_property(struct drm_connector *connector, | |||
1407 | *val = state->scaling_mode; | 1453 | *val = state->scaling_mode; |
1408 | } else if (property == connector->content_protection_property) { | 1454 | } else if (property == connector->content_protection_property) { |
1409 | *val = state->content_protection; | 1455 | *val = state->content_protection; |
1456 | } else if (property == config->writeback_fb_id_property) { | ||
1457 | /* Writeback framebuffer is one-shot, write and forget */ | ||
1458 | *val = 0; | ||
1410 | } else if (connector->funcs->atomic_get_property) { | 1459 | } else if (connector->funcs->atomic_get_property) { |
1411 | return connector->funcs->atomic_get_property(connector, | 1460 | return connector->funcs->atomic_get_property(connector, |
1412 | state, property, val); | 1461 | state, property, val); |
@@ -1631,6 +1680,70 @@ drm_atomic_set_crtc_for_connector(struct drm_connector_state *conn_state, | |||
1631 | } | 1680 | } |
1632 | EXPORT_SYMBOL(drm_atomic_set_crtc_for_connector); | 1681 | EXPORT_SYMBOL(drm_atomic_set_crtc_for_connector); |
1633 | 1682 | ||
1683 | /* | ||
1684 | * drm_atomic_get_writeback_job - return or allocate a writeback job | ||
1685 | * @conn_state: Connector state to get the job for | ||
1686 | * | ||
1687 | * Writeback jobs have a different lifetime to the atomic state they are | ||
1688 | * associated with. This convenience function takes care of allocating a job | ||
1689 | * if there isn't yet one associated with the connector state, otherwise | ||
1690 | * it just returns the existing job. | ||
1691 | * | ||
1692 | * Returns: The writeback job for the given connector state | ||
1693 | */ | ||
1694 | static struct drm_writeback_job * | ||
1695 | drm_atomic_get_writeback_job(struct drm_connector_state *conn_state) | ||
1696 | { | ||
1697 | WARN_ON(conn_state->connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK); | ||
1698 | |||
1699 | if (!conn_state->writeback_job) | ||
1700 | conn_state->writeback_job = | ||
1701 | kzalloc(sizeof(*conn_state->writeback_job), GFP_KERNEL); | ||
1702 | |||
1703 | return conn_state->writeback_job; | ||
1704 | } | ||
1705 | |||
1706 | /** | ||
1707 | * drm_atomic_set_writeback_fb_for_connector - set writeback framebuffer | ||
1708 | * @conn_state: atomic state object for the connector | ||
1709 | * @fb: fb to use for the connector | ||
1710 | * | ||
1711 | * This is used to set the framebuffer for a writeback connector, which outputs | ||
1712 | * to a buffer instead of an actual physical connector. | ||
1713 | * Changing the assigned framebuffer requires us to grab a reference to the new | ||
1714 | * fb and drop the reference to the old fb, if there is one. This function | ||
1715 | * takes care of all these details besides updating the pointer in the | ||
1716 | * state object itself. | ||
1717 | * | ||
1718 | * Note: The only way conn_state can already have an fb set is if the commit | ||
1719 | * sets the property more than once. | ||
1720 | * | ||
1721 | * See also: drm_writeback_connector_init() | ||
1722 | * | ||
1723 | * Returns: 0 on success | ||
1724 | */ | ||
1725 | int drm_atomic_set_writeback_fb_for_connector( | ||
1726 | struct drm_connector_state *conn_state, | ||
1727 | struct drm_framebuffer *fb) | ||
1728 | { | ||
1729 | struct drm_writeback_job *job = | ||
1730 | drm_atomic_get_writeback_job(conn_state); | ||
1731 | if (!job) | ||
1732 | return -ENOMEM; | ||
1733 | |||
1734 | drm_framebuffer_assign(&job->fb, fb); | ||
1735 | |||
1736 | if (fb) | ||
1737 | DRM_DEBUG_ATOMIC("Set [FB:%d] for connector state %p\n", | ||
1738 | fb->base.id, conn_state); | ||
1739 | else | ||
1740 | DRM_DEBUG_ATOMIC("Set [NOFB] for connector state %p\n", | ||
1741 | conn_state); | ||
1742 | |||
1743 | return 0; | ||
1744 | } | ||
1745 | EXPORT_SYMBOL(drm_atomic_set_writeback_fb_for_connector); | ||
1746 | |||
1634 | /** | 1747 | /** |
1635 | * drm_atomic_add_affected_connectors - add connectors for crtc | 1748 | * drm_atomic_add_affected_connectors - add connectors for crtc |
1636 | * @state: atomic state | 1749 | * @state: atomic state |
@@ -1752,6 +1865,8 @@ int drm_atomic_check_only(struct drm_atomic_state *state) | |||
1752 | struct drm_plane_state *plane_state; | 1865 | struct drm_plane_state *plane_state; |
1753 | struct drm_crtc *crtc; | 1866 | struct drm_crtc *crtc; |
1754 | struct drm_crtc_state *crtc_state; | 1867 | struct drm_crtc_state *crtc_state; |
1868 | struct drm_connector *conn; | ||
1869 | struct drm_connector_state *conn_state; | ||
1755 | int i, ret = 0; | 1870 | int i, ret = 0; |
1756 | 1871 | ||
1757 | DRM_DEBUG_ATOMIC("checking %p\n", state); | 1872 | DRM_DEBUG_ATOMIC("checking %p\n", state); |
@@ -1774,6 +1889,15 @@ int drm_atomic_check_only(struct drm_atomic_state *state) | |||
1774 | } | 1889 | } |
1775 | } | 1890 | } |
1776 | 1891 | ||
1892 | for_each_new_connector_in_state(state, conn, conn_state, i) { | ||
1893 | ret = drm_atomic_connector_check(conn, conn_state); | ||
1894 | if (ret) { | ||
1895 | DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] atomic core check failed\n", | ||
1896 | conn->base.id, conn->name); | ||
1897 | return ret; | ||
1898 | } | ||
1899 | } | ||
1900 | |||
1777 | if (config->funcs->atomic_check) | 1901 | if (config->funcs->atomic_check) |
1778 | ret = config->funcs->atomic_check(state->dev, state); | 1902 | ret = config->funcs->atomic_check(state->dev, state); |
1779 | 1903 | ||
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 232fa11a5e31..17baf5057132 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include <drm/drm_plane_helper.h> | 30 | #include <drm/drm_plane_helper.h> |
31 | #include <drm/drm_crtc_helper.h> | 31 | #include <drm/drm_crtc_helper.h> |
32 | #include <drm/drm_atomic_helper.h> | 32 | #include <drm/drm_atomic_helper.h> |
33 | #include <drm/drm_writeback.h> | ||
33 | #include <linux/dma-fence.h> | 34 | #include <linux/dma-fence.h> |
34 | 35 | ||
35 | #include "drm_crtc_helper_internal.h" | 36 | #include "drm_crtc_helper_internal.h" |
@@ -1172,6 +1173,25 @@ void drm_atomic_helper_commit_modeset_disables(struct drm_device *dev, | |||
1172 | } | 1173 | } |
1173 | EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_disables); | 1174 | EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_disables); |
1174 | 1175 | ||
1176 | static void drm_atomic_helper_commit_writebacks(struct drm_device *dev, | ||
1177 | struct drm_atomic_state *old_state) | ||
1178 | { | ||
1179 | struct drm_connector *connector; | ||
1180 | struct drm_connector_state *new_conn_state; | ||
1181 | int i; | ||
1182 | |||
1183 | for_each_new_connector_in_state(old_state, connector, new_conn_state, i) { | ||
1184 | const struct drm_connector_helper_funcs *funcs; | ||
1185 | |||
1186 | funcs = connector->helper_private; | ||
1187 | |||
1188 | if (new_conn_state->writeback_job && new_conn_state->writeback_job->fb) { | ||
1189 | WARN_ON(connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK); | ||
1190 | funcs->atomic_commit(connector, new_conn_state->writeback_job); | ||
1191 | } | ||
1192 | } | ||
1193 | } | ||
1194 | |||
1175 | /** | 1195 | /** |
1176 | * drm_atomic_helper_commit_modeset_enables - modeset commit to enable outputs | 1196 | * drm_atomic_helper_commit_modeset_enables - modeset commit to enable outputs |
1177 | * @dev: DRM device | 1197 | * @dev: DRM device |
@@ -1251,6 +1271,8 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, | |||
1251 | 1271 | ||
1252 | drm_bridge_enable(encoder->bridge); | 1272 | drm_bridge_enable(encoder->bridge); |
1253 | } | 1273 | } |
1274 | |||
1275 | drm_atomic_helper_commit_writebacks(dev, old_state); | ||
1254 | } | 1276 | } |
1255 | EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_enables); | 1277 | EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_enables); |
1256 | 1278 | ||
@@ -3647,6 +3669,9 @@ __drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector, | |||
3647 | if (state->crtc) | 3669 | if (state->crtc) |
3648 | drm_connector_get(connector); | 3670 | drm_connector_get(connector); |
3649 | state->commit = NULL; | 3671 | state->commit = NULL; |
3672 | |||
3673 | /* Don't copy over a writeback job, they are used only once */ | ||
3674 | state->writeback_job = NULL; | ||
3650 | } | 3675 | } |
3651 | EXPORT_SYMBOL(__drm_atomic_helper_connector_duplicate_state); | 3676 | EXPORT_SYMBOL(__drm_atomic_helper_connector_duplicate_state); |
3652 | 3677 | ||
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 549b89501e01..2f9ebddd178e 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c | |||
@@ -87,6 +87,7 @@ static struct drm_conn_prop_enum_list drm_connector_enum_list[] = { | |||
87 | { DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" }, | 87 | { DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" }, |
88 | { DRM_MODE_CONNECTOR_DSI, "DSI" }, | 88 | { DRM_MODE_CONNECTOR_DSI, "DSI" }, |
89 | { DRM_MODE_CONNECTOR_DPI, "DPI" }, | 89 | { DRM_MODE_CONNECTOR_DPI, "DPI" }, |
90 | { DRM_MODE_CONNECTOR_WRITEBACK, "Writeback" }, | ||
90 | }; | 91 | }; |
91 | 92 | ||
92 | void drm_connector_ida_init(void) | 93 | void drm_connector_ida_init(void) |
@@ -253,7 +254,8 @@ int drm_connector_init(struct drm_device *dev, | |||
253 | config->num_connector++; | 254 | config->num_connector++; |
254 | spin_unlock_irq(&config->connector_list_lock); | 255 | spin_unlock_irq(&config->connector_list_lock); |
255 | 256 | ||
256 | if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL) | 257 | if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL && |
258 | connector_type != DRM_MODE_CONNECTOR_WRITEBACK) | ||
257 | drm_object_attach_property(&connector->base, | 259 | drm_object_attach_property(&connector->base, |
258 | config->edid_property, | 260 | config->edid_property, |
259 | 0); | 261 | 0); |
diff --git a/drivers/gpu/drm/drm_writeback.c b/drivers/gpu/drm/drm_writeback.c new file mode 100644 index 000000000000..e5b8a4b79724 --- /dev/null +++ b/drivers/gpu/drm/drm_writeback.c | |||
@@ -0,0 +1,245 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * (C) COPYRIGHT 2016 ARM Limited. All rights reserved. | ||
4 | * Author: Brian Starkey <brian.starkey@arm.com> | ||
5 | * | ||
6 | * This program is free software and is provided to you under the terms of the | ||
7 | * GNU General Public License version 2 as published by the Free Software | ||
8 | * Foundation, and any use by you of this program is subject to the terms | ||
9 | * of such GNU licence. | ||
10 | */ | ||
11 | |||
12 | #include <drm/drm_crtc.h> | ||
13 | #include <drm/drm_modeset_helper_vtables.h> | ||
14 | #include <drm/drm_property.h> | ||
15 | #include <drm/drm_writeback.h> | ||
16 | #include <drm/drmP.h> | ||
17 | |||
18 | /** | ||
19 | * DOC: overview | ||
20 | * | ||
21 | * Writeback connectors are used to expose hardware which can write the output | ||
22 | * from a CRTC to a memory buffer. They are used and act similarly to other | ||
23 | * types of connectors, with some important differences: | ||
24 | * - Writeback connectors don't provide a way to output visually to the user. | ||
25 | * - Writeback connectors should always report as "disconnected" (so that | ||
26 | * clients which don't understand them will ignore them). | ||
27 | * - Writeback connectors don't have EDID. | ||
28 | * | ||
29 | * A framebuffer may only be attached to a writeback connector when the | ||
30 | * connector is attached to a CRTC. The WRITEBACK_FB_ID property which sets the | ||
31 | * framebuffer applies only to a single commit (see below). A framebuffer may | ||
32 | * not be attached while the CRTC is off. | ||
33 | * | ||
34 | * Writeback connectors have some additional properties, which userspace | ||
35 | * can use to query and control them: | ||
36 | * | ||
37 | * "WRITEBACK_FB_ID": | ||
38 | * Write-only object property storing a DRM_MODE_OBJECT_FB: it stores the | ||
39 | * framebuffer to be written by the writeback connector. This property is | ||
40 | * similar to the FB_ID property on planes, but will always read as zero | ||
41 | * and is not preserved across commits. | ||
42 | * Userspace must set this property to an output buffer every time it | ||
43 | * wishes the buffer to get filled. | ||
44 | * | ||
45 | * "WRITEBACK_PIXEL_FORMATS": | ||
46 | * Immutable blob property to store the supported pixel formats table. The | ||
47 | * data is an array of u32 DRM_FORMAT_* fourcc values. | ||
48 | * Userspace can use this blob to find out what pixel formats are supported | ||
49 | * by the connector's writeback engine. | ||
50 | */ | ||
51 | |||
52 | static int create_writeback_properties(struct drm_device *dev) | ||
53 | { | ||
54 | struct drm_property *prop; | ||
55 | |||
56 | if (!dev->mode_config.writeback_fb_id_property) { | ||
57 | prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC, | ||
58 | "WRITEBACK_FB_ID", | ||
59 | DRM_MODE_OBJECT_FB); | ||
60 | if (!prop) | ||
61 | return -ENOMEM; | ||
62 | dev->mode_config.writeback_fb_id_property = prop; | ||
63 | } | ||
64 | |||
65 | if (!dev->mode_config.writeback_pixel_formats_property) { | ||
66 | prop = drm_property_create(dev, DRM_MODE_PROP_BLOB | | ||
67 | DRM_MODE_PROP_ATOMIC | | ||
68 | DRM_MODE_PROP_IMMUTABLE, | ||
69 | "WRITEBACK_PIXEL_FORMATS", 0); | ||
70 | if (!prop) | ||
71 | return -ENOMEM; | ||
72 | dev->mode_config.writeback_pixel_formats_property = prop; | ||
73 | } | ||
74 | |||
75 | return 0; | ||
76 | } | ||
77 | |||
78 | static const struct drm_encoder_funcs drm_writeback_encoder_funcs = { | ||
79 | .destroy = drm_encoder_cleanup, | ||
80 | }; | ||
81 | |||
82 | /** | ||
83 | * drm_writeback_connector_init - Initialize a writeback connector and its properties | ||
84 | * @dev: DRM device | ||
85 | * @wb_connector: Writeback connector to initialize | ||
86 | * @con_funcs: Connector funcs vtable | ||
87 | * @enc_helper_funcs: Encoder helper funcs vtable to be used by the internal encoder | ||
88 | * @formats: Array of supported pixel formats for the writeback engine | ||
89 | * @n_formats: Length of the formats array | ||
90 | * | ||
91 | * This function creates the writeback-connector-specific properties if they | ||
92 | * have not been already created, initializes the connector as | ||
93 | * type DRM_MODE_CONNECTOR_WRITEBACK, and correctly initializes the property | ||
94 | * values. It will also create an internal encoder associated with the | ||
95 | * drm_writeback_connector and set it to use the @enc_helper_funcs vtable for | ||
96 | * the encoder helper. | ||
97 | * | ||
98 | * Drivers should always use this function instead of drm_connector_init() to | ||
99 | * set up writeback connectors. | ||
100 | * | ||
101 | * Returns: 0 on success, or a negative error code | ||
102 | */ | ||
103 | int drm_writeback_connector_init(struct drm_device *dev, | ||
104 | struct drm_writeback_connector *wb_connector, | ||
105 | const struct drm_connector_funcs *con_funcs, | ||
106 | const struct drm_encoder_helper_funcs *enc_helper_funcs, | ||
107 | const u32 *formats, int n_formats) | ||
108 | { | ||
109 | struct drm_property_blob *blob; | ||
110 | struct drm_connector *connector = &wb_connector->base; | ||
111 | struct drm_mode_config *config = &dev->mode_config; | ||
112 | int ret = create_writeback_properties(dev); | ||
113 | |||
114 | if (ret != 0) | ||
115 | return ret; | ||
116 | |||
117 | blob = drm_property_create_blob(dev, n_formats * sizeof(*formats), | ||
118 | formats); | ||
119 | if (IS_ERR(blob)) | ||
120 | return PTR_ERR(blob); | ||
121 | |||
122 | drm_encoder_helper_add(&wb_connector->encoder, enc_helper_funcs); | ||
123 | ret = drm_encoder_init(dev, &wb_connector->encoder, | ||
124 | &drm_writeback_encoder_funcs, | ||
125 | DRM_MODE_ENCODER_VIRTUAL, NULL); | ||
126 | if (ret) | ||
127 | goto fail; | ||
128 | |||
129 | connector->interlace_allowed = 0; | ||
130 | |||
131 | ret = drm_connector_init(dev, connector, con_funcs, | ||
132 | DRM_MODE_CONNECTOR_WRITEBACK); | ||
133 | if (ret) | ||
134 | goto connector_fail; | ||
135 | |||
136 | ret = drm_mode_connector_attach_encoder(connector, | ||
137 | &wb_connector->encoder); | ||
138 | if (ret) | ||
139 | goto attach_fail; | ||
140 | |||
141 | INIT_LIST_HEAD(&wb_connector->job_queue); | ||
142 | spin_lock_init(&wb_connector->job_lock); | ||
143 | |||
144 | drm_object_attach_property(&connector->base, | ||
145 | config->writeback_fb_id_property, 0); | ||
146 | |||
147 | drm_object_attach_property(&connector->base, | ||
148 | config->writeback_pixel_formats_property, | ||
149 | blob->base.id); | ||
150 | wb_connector->pixel_formats_blob_ptr = blob; | ||
151 | |||
152 | return 0; | ||
153 | |||
154 | attach_fail: | ||
155 | drm_connector_cleanup(connector); | ||
156 | connector_fail: | ||
157 | drm_encoder_cleanup(&wb_connector->encoder); | ||
158 | fail: | ||
159 | drm_property_blob_put(blob); | ||
160 | return ret; | ||
161 | } | ||
162 | EXPORT_SYMBOL(drm_writeback_connector_init); | ||
163 | |||
164 | /** | ||
165 | * drm_writeback_queue_job - Queue a writeback job for later signalling | ||
166 | * @wb_connector: The writeback connector to queue a job on | ||
167 | * @job: The job to queue | ||
168 | * | ||
169 | * This function adds a job to the job_queue for a writeback connector. It | ||
170 | * should be considered to take ownership of the writeback job, and so any other | ||
171 | * references to the job must be cleared after calling this function. | ||
172 | * | ||
173 | * Drivers must ensure that for a given writeback connector, jobs are queued in | ||
174 | * exactly the same order as they will be completed by the hardware (and | ||
175 | * signaled via drm_writeback_signal_completion). | ||
176 | * | ||
177 | * For every call to drm_writeback_queue_job() there must be exactly one call to | ||
178 | * drm_writeback_signal_completion() | ||
179 | * | ||
180 | * See also: drm_writeback_signal_completion() | ||
181 | */ | ||
182 | void drm_writeback_queue_job(struct drm_writeback_connector *wb_connector, | ||
183 | struct drm_writeback_job *job) | ||
184 | { | ||
185 | unsigned long flags; | ||
186 | |||
187 | spin_lock_irqsave(&wb_connector->job_lock, flags); | ||
188 | list_add_tail(&job->list_entry, &wb_connector->job_queue); | ||
189 | spin_unlock_irqrestore(&wb_connector->job_lock, flags); | ||
190 | } | ||
191 | EXPORT_SYMBOL(drm_writeback_queue_job); | ||
192 | |||
193 | /* | ||
194 | * @cleanup_work: deferred cleanup of a writeback job | ||
195 | * | ||
196 | * The job cannot be cleaned up directly in drm_writeback_signal_completion, | ||
197 | * because it may be called in interrupt context. Dropping the framebuffer | ||
198 | * reference can sleep, and so the cleanup is deferred to a workqueue. | ||
199 | */ | ||
200 | static void cleanup_work(struct work_struct *work) | ||
201 | { | ||
202 | struct drm_writeback_job *job = container_of(work, | ||
203 | struct drm_writeback_job, | ||
204 | cleanup_work); | ||
205 | drm_framebuffer_put(job->fb); | ||
206 | kfree(job); | ||
207 | } | ||
208 | |||
209 | |||
210 | /** | ||
211 | * drm_writeback_signal_completion - Signal the completion of a writeback job | ||
212 | * @wb_connector: The writeback connector whose job is complete | ||
213 | * | ||
214 | * Drivers should call this to signal the completion of a previously queued | ||
215 | * writeback job. It should be called as soon as possible after the hardware | ||
216 | * has finished writing, and may be called from interrupt context. | ||
217 | * It is the driver's responsibility to ensure that for a given connector, the | ||
218 | * hardware completes writeback jobs in the same order as they are queued. | ||
219 | * | ||
220 | * Unless the driver is holding its own reference to the framebuffer, it must | ||
221 | * not be accessed after calling this function. | ||
222 | * | ||
223 | * See also: drm_writeback_queue_job() | ||
224 | */ | ||
225 | void | ||
226 | drm_writeback_signal_completion(struct drm_writeback_connector *wb_connector) | ||
227 | { | ||
228 | unsigned long flags; | ||
229 | struct drm_writeback_job *job; | ||
230 | |||
231 | spin_lock_irqsave(&wb_connector->job_lock, flags); | ||
232 | job = list_first_entry_or_null(&wb_connector->job_queue, | ||
233 | struct drm_writeback_job, | ||
234 | list_entry); | ||
235 | if (job) | ||
236 | list_del(&job->list_entry); | ||
237 | spin_unlock_irqrestore(&wb_connector->job_lock, flags); | ||
238 | |||
239 | if (WARN_ON(!job)) | ||
240 | return; | ||
241 | |||
242 | INIT_WORK(&job->cleanup_work, cleanup_work); | ||
243 | queue_work(system_long_wq, &job->cleanup_work); | ||
244 | } | ||
245 | EXPORT_SYMBOL(drm_writeback_signal_completion); | ||
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index ca461b6cf71f..8254521b4583 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h | |||
@@ -594,6 +594,9 @@ void drm_atomic_set_fence_for_plane(struct drm_plane_state *plane_state, | |||
594 | int __must_check | 594 | int __must_check |
595 | drm_atomic_set_crtc_for_connector(struct drm_connector_state *conn_state, | 595 | drm_atomic_set_crtc_for_connector(struct drm_connector_state *conn_state, |
596 | struct drm_crtc *crtc); | 596 | struct drm_crtc *crtc); |
597 | int drm_atomic_set_writeback_fb_for_connector( | ||
598 | struct drm_connector_state *conn_state, | ||
599 | struct drm_framebuffer *fb); | ||
597 | int __must_check | 600 | int __must_check |
598 | drm_atomic_add_affected_connectors(struct drm_atomic_state *state, | 601 | drm_atomic_add_affected_connectors(struct drm_atomic_state *state, |
599 | struct drm_crtc *crtc); | 602 | struct drm_crtc *crtc); |
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h index c5797c24edd3..716c3a0e0e1d 100644 --- a/include/drm/drm_connector.h +++ b/include/drm/drm_connector.h | |||
@@ -437,6 +437,19 @@ struct drm_connector_state { | |||
437 | * protection. This is most commonly used for HDCP. | 437 | * protection. This is most commonly used for HDCP. |
438 | */ | 438 | */ |
439 | unsigned int content_protection; | 439 | unsigned int content_protection; |
440 | |||
441 | /** | ||
442 | * @writeback_job: Writeback job for writeback connectors | ||
443 | * | ||
444 | * Holds the framebuffer for a writeback connector. As the writeback | ||
445 | * completion may be asynchronous to the normal commit cycle, the | ||
446 | * writeback job lifetime is managed separately from the normal atomic | ||
447 | * state by this object. | ||
448 | * | ||
449 | * See also: drm_writeback_queue_job() and | ||
450 | * drm_writeback_signal_completion() | ||
451 | */ | ||
452 | struct drm_writeback_job *writeback_job; | ||
440 | }; | 453 | }; |
441 | 454 | ||
442 | /** | 455 | /** |
diff --git a/include/drm/drm_mode_config.h b/include/drm/drm_mode_config.h index fb45839179dd..5f24329e6927 100644 --- a/include/drm/drm_mode_config.h +++ b/include/drm/drm_mode_config.h | |||
@@ -784,6 +784,21 @@ struct drm_mode_config { | |||
784 | */ | 784 | */ |
785 | struct drm_property *panel_orientation_property; | 785 | struct drm_property *panel_orientation_property; |
786 | 786 | ||
787 | /** | ||
788 | * @writeback_fb_id_property: Property for writeback connectors, storing | ||
789 | * the ID of the output framebuffer. | ||
790 | * See also: drm_writeback_connector_init() | ||
791 | */ | ||
792 | struct drm_property *writeback_fb_id_property; | ||
793 | |||
794 | /** | ||
795 | * @writeback_pixel_formats_property: Property for writeback connectors, | ||
796 | * storing an array of the supported pixel formats for the writeback | ||
797 | * engine (read-only). | ||
798 | * See also: drm_writeback_connector_init() | ||
799 | */ | ||
800 | struct drm_property *writeback_pixel_formats_property; | ||
801 | |||
787 | /* dumb ioctl parameters */ | 802 | /* dumb ioctl parameters */ |
788 | uint32_t preferred_depth, prefer_shadow; | 803 | uint32_t preferred_depth, prefer_shadow; |
789 | 804 | ||
diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h index 35e2a3a79fc5..3b289773297c 100644 --- a/include/drm/drm_modeset_helper_vtables.h +++ b/include/drm/drm_modeset_helper_vtables.h | |||
@@ -974,6 +974,17 @@ struct drm_connector_helper_funcs { | |||
974 | */ | 974 | */ |
975 | int (*atomic_check)(struct drm_connector *connector, | 975 | int (*atomic_check)(struct drm_connector *connector, |
976 | struct drm_connector_state *state); | 976 | struct drm_connector_state *state); |
977 | |||
978 | /** | ||
979 | * @atomic_commit: | ||
980 | * | ||
981 | * This hook is to be used by drivers implementing writeback connectors | ||
982 | * that need a point when to commit the writeback job to the hardware. | ||
983 | * | ||
984 | * This callback is used by the atomic modeset helpers. | ||
985 | */ | ||
986 | void (*atomic_commit)(struct drm_connector *connector, | ||
987 | struct drm_writeback_job *writeback_job); | ||
977 | }; | 988 | }; |
978 | 989 | ||
979 | /** | 990 | /** |
diff --git a/include/drm/drm_writeback.h b/include/drm/drm_writeback.h new file mode 100644 index 000000000000..17cd1feecd7e --- /dev/null +++ b/include/drm/drm_writeback.h | |||
@@ -0,0 +1,91 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | /* | ||
3 | * (C) COPYRIGHT 2016 ARM Limited. All rights reserved. | ||
4 | * Author: Brian Starkey <brian.starkey@arm.com> | ||
5 | * | ||
6 | * This program is free software and is provided to you under the terms of the | ||
7 | * GNU General Public License version 2 as published by the Free Software | ||
8 | * Foundation, and any use by you of this program is subject to the terms | ||
9 | * of such GNU licence. | ||
10 | */ | ||
11 | |||
12 | #ifndef __DRM_WRITEBACK_H__ | ||
13 | #define __DRM_WRITEBACK_H__ | ||
14 | #include <drm/drm_connector.h> | ||
15 | #include <drm/drm_encoder.h> | ||
16 | #include <linux/workqueue.h> | ||
17 | |||
18 | struct drm_writeback_connector { | ||
19 | struct drm_connector base; | ||
20 | |||
21 | /** | ||
22 | * @encoder: Internal encoder used by the connector to fulfill | ||
23 | * the DRM framework requirements. The users of the | ||
24 | * @drm_writeback_connector control the behaviour of the @encoder | ||
25 | * by passing the @enc_funcs parameter to drm_writeback_connector_init() | ||
26 | * function. | ||
27 | */ | ||
28 | struct drm_encoder encoder; | ||
29 | |||
30 | /** | ||
31 | * @pixel_formats_blob_ptr: | ||
32 | * | ||
33 | * DRM blob property data for the pixel formats list on writeback | ||
34 | * connectors | ||
35 | * See also drm_writeback_connector_init() | ||
36 | */ | ||
37 | struct drm_property_blob *pixel_formats_blob_ptr; | ||
38 | |||
39 | /** @job_lock: Protects job_queue */ | ||
40 | spinlock_t job_lock; | ||
41 | |||
42 | /** | ||
43 | * @job_queue: | ||
44 | * | ||
45 | * Holds a list of a connector's writeback jobs; the last item is the | ||
46 | * most recent. The first item may be either waiting for the hardware | ||
47 | * to begin writing, or currently being written. | ||
48 | * | ||
49 | * See also: drm_writeback_queue_job() and | ||
50 | * drm_writeback_signal_completion() | ||
51 | */ | ||
52 | struct list_head job_queue; | ||
53 | }; | ||
54 | |||
55 | struct drm_writeback_job { | ||
56 | /** | ||
57 | * @cleanup_work: | ||
58 | * | ||
59 | * Used to allow drm_writeback_signal_completion to defer dropping the | ||
60 | * framebuffer reference to a workqueue | ||
61 | */ | ||
62 | struct work_struct cleanup_work; | ||
63 | |||
64 | /** | ||
65 | * @list_entry: | ||
66 | * | ||
67 | * List item for the writeback connector's @job_queue | ||
68 | */ | ||
69 | struct list_head list_entry; | ||
70 | |||
71 | /** | ||
72 | * @fb: | ||
73 | * | ||
74 | * Framebuffer to be written to by the writeback connector. Do not set | ||
75 | * directly, use drm_atomic_set_writeback_fb_for_connector() | ||
76 | */ | ||
77 | struct drm_framebuffer *fb; | ||
78 | }; | ||
79 | |||
80 | int drm_writeback_connector_init(struct drm_device *dev, | ||
81 | struct drm_writeback_connector *wb_connector, | ||
82 | const struct drm_connector_funcs *con_funcs, | ||
83 | const struct drm_encoder_helper_funcs *enc_helper_funcs, | ||
84 | const u32 *formats, int n_formats); | ||
85 | |||
86 | void drm_writeback_queue_job(struct drm_writeback_connector *wb_connector, | ||
87 | struct drm_writeback_job *job); | ||
88 | |||
89 | void drm_writeback_cleanup_job(struct drm_writeback_job *job); | ||
90 | void drm_writeback_signal_completion(struct drm_writeback_connector *wb_connector); | ||
91 | #endif | ||
diff --git a/include/uapi/drm/drm_mode.h b/include/uapi/drm/drm_mode.h index 971c016b368c..8d67243952f4 100644 --- a/include/uapi/drm/drm_mode.h +++ b/include/uapi/drm/drm_mode.h | |||
@@ -351,6 +351,7 @@ enum drm_mode_subconnector { | |||
351 | #define DRM_MODE_CONNECTOR_VIRTUAL 15 | 351 | #define DRM_MODE_CONNECTOR_VIRTUAL 15 |
352 | #define DRM_MODE_CONNECTOR_DSI 16 | 352 | #define DRM_MODE_CONNECTOR_DSI 16 |
353 | #define DRM_MODE_CONNECTOR_DPI 17 | 353 | #define DRM_MODE_CONNECTOR_DPI 17 |
354 | #define DRM_MODE_CONNECTOR_WRITEBACK 18 | ||
354 | 355 | ||
355 | struct drm_mode_get_connector { | 356 | struct drm_mode_get_connector { |
356 | 357 | ||