|
@@ -14,6 +14,7 @@
|
|
#include <drm/drm_property.h>
|
|
#include <drm/drm_property.h>
|
|
#include <drm/drm_writeback.h>
|
|
#include <drm/drm_writeback.h>
|
|
#include <drm/drmP.h>
|
|
#include <drm/drmP.h>
|
|
|
|
+#include <linux/dma-fence.h>
|
|
|
|
|
|
/**
|
|
/**
|
|
* DOC: overview
|
|
* DOC: overview
|
|
@@ -31,6 +32,16 @@
|
|
* framebuffer applies only to a single commit (see below). A framebuffer may
|
|
* framebuffer applies only to a single commit (see below). A framebuffer may
|
|
* not be attached while the CRTC is off.
|
|
* not be attached while the CRTC is off.
|
|
*
|
|
*
|
|
|
|
+ * Unlike with planes, when a writeback framebuffer is removed by userspace DRM
|
|
|
|
+ * makes no attempt to remove it from active use by the connector. This is
|
|
|
|
+ * because no method is provided to abort a writeback operation, and in any
|
|
|
|
+ * case making a new commit whilst a writeback is ongoing is undefined (see
|
|
|
|
+ * WRITEBACK_OUT_FENCE_PTR below). As soon as the current writeback is finished,
|
|
|
|
+ * the framebuffer will automatically no longer be in active use. As it will
|
|
|
|
+ * also have already been removed from the framebuffer list, there will be no
|
|
|
|
+ * way for any userspace application to retrieve a reference to it in the
|
|
|
|
+ * intervening period.
|
|
|
|
+ *
|
|
* Writeback connectors have some additional properties, which userspace
|
|
* Writeback connectors have some additional properties, which userspace
|
|
* can use to query and control them:
|
|
* can use to query and control them:
|
|
*
|
|
*
|
|
@@ -47,8 +58,54 @@
|
|
* data is an array of u32 DRM_FORMAT_* fourcc values.
|
|
* data is an array of u32 DRM_FORMAT_* fourcc values.
|
|
* Userspace can use this blob to find out what pixel formats are supported
|
|
* Userspace can use this blob to find out what pixel formats are supported
|
|
* by the connector's writeback engine.
|
|
* by the connector's writeback engine.
|
|
|
|
+ *
|
|
|
|
+ * "WRITEBACK_OUT_FENCE_PTR":
|
|
|
|
+ * Userspace can use this property to provide a pointer for the kernel to
|
|
|
|
+ * fill with a sync_file file descriptor, which will signal once the
|
|
|
|
+ * writeback is finished. The value should be the address of a 32-bit
|
|
|
|
+ * signed integer, cast to a u64.
|
|
|
|
+ * Userspace should wait for this fence to signal before making another
|
|
|
|
+ * commit affecting any of the same CRTCs, Planes or Connectors.
|
|
|
|
+ * **Failure to do so will result in undefined behaviour.**
|
|
|
|
+ * For this reason it is strongly recommended that all userspace
|
|
|
|
+ * applications making use of writeback connectors *always* retrieve an
|
|
|
|
+ * out-fence for the commit and use it appropriately.
|
|
|
|
+ * From userspace, this property will always read as zero.
|
|
*/
|
|
*/
|
|
|
|
|
|
|
|
+#define fence_to_wb_connector(x) container_of(x->lock, \
|
|
|
|
+ struct drm_writeback_connector, \
|
|
|
|
+ fence_lock)
|
|
|
|
+
|
|
|
|
+static const char *drm_writeback_fence_get_driver_name(struct dma_fence *fence)
|
|
|
|
+{
|
|
|
|
+ struct drm_writeback_connector *wb_connector =
|
|
|
|
+ fence_to_wb_connector(fence);
|
|
|
|
+
|
|
|
|
+ return wb_connector->base.dev->driver->name;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static const char *
|
|
|
|
+drm_writeback_fence_get_timeline_name(struct dma_fence *fence)
|
|
|
|
+{
|
|
|
|
+ struct drm_writeback_connector *wb_connector =
|
|
|
|
+ fence_to_wb_connector(fence);
|
|
|
|
+
|
|
|
|
+ return wb_connector->timeline_name;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static bool drm_writeback_fence_enable_signaling(struct dma_fence *fence)
|
|
|
|
+{
|
|
|
|
+ return true;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static const struct dma_fence_ops drm_writeback_fence_ops = {
|
|
|
|
+ .get_driver_name = drm_writeback_fence_get_driver_name,
|
|
|
|
+ .get_timeline_name = drm_writeback_fence_get_timeline_name,
|
|
|
|
+ .enable_signaling = drm_writeback_fence_enable_signaling,
|
|
|
|
+ .wait = dma_fence_default_wait,
|
|
|
|
+};
|
|
|
|
+
|
|
static int create_writeback_properties(struct drm_device *dev)
|
|
static int create_writeback_properties(struct drm_device *dev)
|
|
{
|
|
{
|
|
struct drm_property *prop;
|
|
struct drm_property *prop;
|
|
@@ -72,6 +129,15 @@ static int create_writeback_properties(struct drm_device *dev)
|
|
dev->mode_config.writeback_pixel_formats_property = prop;
|
|
dev->mode_config.writeback_pixel_formats_property = prop;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (!dev->mode_config.writeback_out_fence_ptr_property) {
|
|
|
|
+ prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC,
|
|
|
|
+ "WRITEBACK_OUT_FENCE_PTR", 0,
|
|
|
|
+ U64_MAX);
|
|
|
|
+ if (!prop)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+ dev->mode_config.writeback_out_fence_ptr_property = prop;
|
|
|
|
+ }
|
|
|
|
+
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -141,6 +207,15 @@ int drm_writeback_connector_init(struct drm_device *dev,
|
|
INIT_LIST_HEAD(&wb_connector->job_queue);
|
|
INIT_LIST_HEAD(&wb_connector->job_queue);
|
|
spin_lock_init(&wb_connector->job_lock);
|
|
spin_lock_init(&wb_connector->job_lock);
|
|
|
|
|
|
|
|
+ wb_connector->fence_context = dma_fence_context_alloc(1);
|
|
|
|
+ spin_lock_init(&wb_connector->fence_lock);
|
|
|
|
+ snprintf(wb_connector->timeline_name,
|
|
|
|
+ sizeof(wb_connector->timeline_name),
|
|
|
|
+ "CONNECTOR:%d-%s", connector->base.id, connector->name);
|
|
|
|
+
|
|
|
|
+ drm_object_attach_property(&connector->base,
|
|
|
|
+ config->writeback_out_fence_ptr_property, 0);
|
|
|
|
+
|
|
drm_object_attach_property(&connector->base,
|
|
drm_object_attach_property(&connector->base,
|
|
config->writeback_fb_id_property, 0);
|
|
config->writeback_fb_id_property, 0);
|
|
|
|
|
|
@@ -210,6 +285,7 @@ static void cleanup_work(struct work_struct *work)
|
|
/**
|
|
/**
|
|
* drm_writeback_signal_completion - Signal the completion of a writeback job
|
|
* drm_writeback_signal_completion - Signal the completion of a writeback job
|
|
* @wb_connector: The writeback connector whose job is complete
|
|
* @wb_connector: The writeback connector whose job is complete
|
|
|
|
+ * @status: Status code to set in the writeback out_fence (0 for success)
|
|
*
|
|
*
|
|
* Drivers should call this to signal the completion of a previously queued
|
|
* Drivers should call this to signal the completion of a previously queued
|
|
* writeback job. It should be called as soon as possible after the hardware
|
|
* writeback job. It should be called as soon as possible after the hardware
|
|
@@ -223,7 +299,8 @@ static void cleanup_work(struct work_struct *work)
|
|
* See also: drm_writeback_queue_job()
|
|
* See also: drm_writeback_queue_job()
|
|
*/
|
|
*/
|
|
void
|
|
void
|
|
-drm_writeback_signal_completion(struct drm_writeback_connector *wb_connector)
|
|
|
|
|
|
+drm_writeback_signal_completion(struct drm_writeback_connector *wb_connector,
|
|
|
|
+ int status)
|
|
{
|
|
{
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
struct drm_writeback_job *job;
|
|
struct drm_writeback_job *job;
|
|
@@ -232,8 +309,15 @@ drm_writeback_signal_completion(struct drm_writeback_connector *wb_connector)
|
|
job = list_first_entry_or_null(&wb_connector->job_queue,
|
|
job = list_first_entry_or_null(&wb_connector->job_queue,
|
|
struct drm_writeback_job,
|
|
struct drm_writeback_job,
|
|
list_entry);
|
|
list_entry);
|
|
- if (job)
|
|
|
|
|
|
+ if (job) {
|
|
list_del(&job->list_entry);
|
|
list_del(&job->list_entry);
|
|
|
|
+ if (job->out_fence) {
|
|
|
|
+ if (status)
|
|
|
|
+ dma_fence_set_error(job->out_fence, status);
|
|
|
|
+ dma_fence_signal(job->out_fence);
|
|
|
|
+ dma_fence_put(job->out_fence);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
spin_unlock_irqrestore(&wb_connector->job_lock, flags);
|
|
spin_unlock_irqrestore(&wb_connector->job_lock, flags);
|
|
|
|
|
|
if (WARN_ON(!job))
|
|
if (WARN_ON(!job))
|
|
@@ -243,3 +327,24 @@ drm_writeback_signal_completion(struct drm_writeback_connector *wb_connector)
|
|
queue_work(system_long_wq, &job->cleanup_work);
|
|
queue_work(system_long_wq, &job->cleanup_work);
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(drm_writeback_signal_completion);
|
|
EXPORT_SYMBOL(drm_writeback_signal_completion);
|
|
|
|
+
|
|
|
|
+struct dma_fence *
|
|
|
|
+drm_writeback_get_out_fence(struct drm_writeback_connector *wb_connector)
|
|
|
|
+{
|
|
|
|
+ struct dma_fence *fence;
|
|
|
|
+
|
|
|
|
+ if (WARN_ON(wb_connector->base.connector_type !=
|
|
|
|
+ DRM_MODE_CONNECTOR_WRITEBACK))
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ fence = kzalloc(sizeof(*fence), GFP_KERNEL);
|
|
|
|
+ if (!fence)
|
|
|
|
+ return NULL;
|
|
|
|
+
|
|
|
|
+ dma_fence_init(fence, &drm_writeback_fence_ops,
|
|
|
|
+ &wb_connector->fence_lock, wb_connector->fence_context,
|
|
|
|
+ ++wb_connector->fence_seqno);
|
|
|
|
+
|
|
|
|
+ return fence;
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL(drm_writeback_get_out_fence);
|