History

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

Brian Starkey committed 9 years ago

Commit 935774c1 parent f664a5212 files · +541 −2Browse files

Unified · Split

12 files
  1. Documentation/gpu/drm-kms.rst +9 −0
  2. drivers/gpu/drm/Makefile +1 −1
  3. drivers/gpu/drm/drm_atomic.c +124 −0
  4. drivers/gpu/drm/drm_atomic_helper.c +25 −0
  5. drivers/gpu/drm/drm_connector.c +3 −1
  6. drivers/gpu/drm/drm_writeback.c +245 −0
  7. include/drm/drm_atomic.h +3 −0
  8. include/drm/drm_connector.h +13 −0
  9. include/drm/drm_mode_config.h +15 −0
  10. include/drm/drm_modeset_helper_vtables.h +11 −0
  11. include/drm/drm_writeback.h +91 −0
  12. include/uapi/drm/drm_mode.h +1 −0
Documentation/gpu/drm-kms.rst+9−0
@@ -373,6 +373,15 @@ Connector Functions Reference · 372 unmodified lines
373373 .. kernel-doc:: drivers/gpu/drm/drm_connector.c
374374 :export:
375375
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+
376385 Encoder Abstraction
377386 ===================
378387
drivers/gpu/drm/Makefile+1−1
@@ -18,7 +18,7 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \ · 17 unmodified lines
1818 drm_encoder.o drm_mode_object.o drm_property.o \
1919 drm_plane.o drm_color_mgmt.o drm_print.o \
2020 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
2222
2323 drm-$(CONFIG_DRM_LIB_RANDOM) += lib/drm_random.o
2424 drm-$(CONFIG_DRM_VM) += drm_vm.o
drivers/gpu/drm/drm_atomic.c+124−0
@@ -30,6 +30,7 @@ · 29 unmodified lines
3030 #include <drm/drm_atomic.h>
3131 #include <drm/drm_mode.h>
3232 #include <drm/drm_print.h>
33+#include <drm/drm_writeback.h>
3334 #include <linux/sync_file.h>
3435
3536 #include "drm_crtc_internal.h"
@@ -690,6 +691,45 @@ static void drm_atomic_crtc_print_state(struct drm_printer *p, · 654 unmodified lines
690691 crtc->funcs->atomic_print_state(p, state);
691692 }
692693
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+
693733 /**
694734 * drm_atomic_get_plane_state - get plane state
695735 * @state: global atomic state object
@@ -1321,6 +1361,12 @@ static int drm_atomic_connector_set_property(struct drm_connector *connector, · 625 unmodified lines
13211361 return -EINVAL;
13221362 }
13231363 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;
13241370 } else if (connector->funcs->atomic_set_property) {
13251371 return connector->funcs->atomic_set_property(connector,
13261372 state, property, val);
@@ -1407,6 +1453,9 @@ drm_atomic_connector_get_property(struct drm_connector *connector, · 80 unmodified lines
14071453 *val = state->scaling_mode;
14081454 } else if (property == connector->content_protection_property) {
14091455 *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;
14101459 } else if (connector->funcs->atomic_get_property) {
14111460 return connector->funcs->atomic_get_property(connector,
14121461 state, property, val);
@@ -1631,6 +1680,70 @@ drm_atomic_set_crtc_for_connector(struct drm_connector_state *conn_state, · 218 unmodified lines
16311680 }
16321681 EXPORT_SYMBOL(drm_atomic_set_crtc_for_connector);
16331682
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+
16341747 /**
16351748 * drm_atomic_add_affected_connectors - add connectors for crtc
16361749 * @state: atomic state
@@ -1752,6 +1865,8 @@ int drm_atomic_check_only(struct drm_atomic_state *state) · 115 unmodified lines
17521865 struct drm_plane_state *plane_state;
17531866 struct drm_crtc *crtc;
17541867 struct drm_crtc_state *crtc_state;
1868+ struct drm_connector *conn;
1869+ struct drm_connector_state *conn_state;
17551870 int i, ret = 0;
17561871
17571872 DRM_DEBUG_ATOMIC("checking %p\n", state);
@@ -1774,6 +1889,15 @@ int drm_atomic_check_only(struct drm_atomic_state *state) · 16 unmodified lines
17741889 }
17751890 }
17761891
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+
17771901 if (config->funcs->atomic_check)
17781902 ret = config->funcs->atomic_check(state->dev, state);
17791903
drivers/gpu/drm/drm_atomic_helper.c+25−0
@@ -30,6 +30,7 @@ · 29 unmodified lines
3030 #include <drm/drm_plane_helper.h>
3131 #include <drm/drm_crtc_helper.h>
3232 #include <drm/drm_atomic_helper.h>
33+#include <drm/drm_writeback.h>
3334 #include <linux/dma-fence.h>
3435
3536 #include "drm_crtc_helper_internal.h"
@@ -1172,6 +1173,25 @@ void drm_atomic_helper_commit_modeset_disables(struct drm_device *dev, · 1136 unmodified lines
11721173 }
11731174 EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_disables);
11741175
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+
11751195 /**
11761196 * drm_atomic_helper_commit_modeset_enables - modeset commit to enable outputs
11771197 * @dev: DRM device
@@ -1251,6 +1271,8 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, · 73 unmodified lines
12511271
12521272 drm_bridge_enable(encoder->bridge);
12531273 }
1274+
1275+ drm_atomic_helper_commit_writebacks(dev, old_state);
12541276 }
12551277 EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_enables);
12561278
@@ -3647,6 +3669,9 @@ __drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector, · 2390 unmodified lines
36473669 if (state->crtc)
36483670 drm_connector_get(connector);
36493671 state->commit = NULL;
3672+
3673+ /* Don't copy over a writeback job, they are used only once */
3674+ state->writeback_job = NULL;
36503675 }
36513676 EXPORT_SYMBOL(__drm_atomic_helper_connector_duplicate_state);
36523677
drivers/gpu/drm/drm_connector.c+3−1
@@ -87,6 +87,7 @@ static struct drm_conn_prop_enum_list drm_connector_enum_list[] = { · 86 unmodified lines
8787 { DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" },
8888 { DRM_MODE_CONNECTOR_DSI, "DSI" },
8989 { DRM_MODE_CONNECTOR_DPI, "DPI" },
90+ { DRM_MODE_CONNECTOR_WRITEBACK, "Writeback" },
9091 };
9192
9293 void drm_connector_ida_init(void)
@@ -253,7 +254,8 @@ int drm_connector_init(struct drm_device *dev, · 160 unmodified lines
253254 config->num_connector++;
254255 spin_unlock_irq(&config->connector_list_lock);
255256
256− if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL)
257+ if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL &&
258+ connector_type != DRM_MODE_CONNECTOR_WRITEBACK)
257259 drm_object_attach_property(&connector->base,
258260 config->edid_property,
259261 0);
drivers/gpu/drm/drm_writeback.c+245−0
@@ -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);
include/drm/drm_atomic.h+3−0
@@ -594,6 +594,9 @@ void drm_atomic_set_fence_for_plane(struct drm_plane_state *plane_state, · 593 unmodified lines
594594 int __must_check
595595 drm_atomic_set_crtc_for_connector(struct drm_connector_state *conn_state,
596596 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);
597600 int __must_check
598601 drm_atomic_add_affected_connectors(struct drm_atomic_state *state,
599602 struct drm_crtc *crtc);
include/drm/drm_connector.h+13−0
@@ -437,6 +437,19 @@ struct drm_connector_state { · 436 unmodified lines
437437 * protection. This is most commonly used for HDCP.
438438 */
439439 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;
440453 };
441454
442455 /**
include/drm/drm_mode_config.h+15−0
@@ -784,6 +784,21 @@ struct drm_mode_config { · 783 unmodified lines
784784 */
785785 struct drm_property *panel_orientation_property;
786786
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+
787802 /* dumb ioctl parameters */
788803 uint32_t preferred_depth, prefer_shadow;
789804
include/drm/drm_modeset_helper_vtables.h+11−0
@@ -974,6 +974,17 @@ struct drm_connector_helper_funcs { · 973 unmodified lines
974974 */
975975 int (*atomic_check)(struct drm_connector *connector,
976976 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);
977988 };
978989
979990 /**
include/drm/drm_writeback.h+91−0
@@ -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
include/uapi/drm/drm_mode.h+1−0
@@ -351,6 +351,7 @@ enum drm_mode_subconnector { · 350 unmodified lines
351351 #define DRM_MODE_CONNECTOR_VIRTUAL 15
352352 #define DRM_MODE_CONNECTOR_DSI 16
353353 #define DRM_MODE_CONNECTOR_DPI 17
354+#define DRM_MODE_CONNECTOR_WRITEBACK 18
354355
355356 struct drm_mode_get_connector {
356357