summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/media/platform/vsp1/vsp1_drm.c161
-rw-r--r--drivers/media/platform/vsp1/vsp1_drm.h9
2 files changed, 144 insertions, 26 deletions
diff --git a/drivers/media/platform/vsp1/vsp1_drm.c b/drivers/media/platform/vsp1/vsp1_drm.c
index a9c5389..f82f83b 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.c
+++ b/drivers/media/platform/vsp1/vsp1_drm.c
@@ -37,10 +37,15 @@ static void vsp1_du_pipeline_frame_end(struct vsp1_pipeline *pipe,
unsigned int completion)
{
struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe);
+ bool complete = completion == VSP1_DL_FRAME_END_COMPLETED;
if (drm_pipe->du_complete)
- drm_pipe->du_complete(drm_pipe->du_private,
- completion & VSP1_DL_FRAME_END_COMPLETED);
+ drm_pipe->du_complete(drm_pipe->du_private, complete);
+
+ if (completion & VSP1_DL_FRAME_END_INTERNAL) {
+ drm_pipe->force_bru_release = false;
+ wake_up(&drm_pipe->wait_queue);
+ }
}
/* -----------------------------------------------------------------------------
@@ -150,6 +155,10 @@ static int vsp1_du_pipeline_setup_rpf(struct vsp1_device *vsp1,
}
/* Setup the BRU source pad. */
+static int vsp1_du_pipeline_setup_inputs(struct vsp1_device *vsp1,
+ struct vsp1_pipeline *pipe);
+static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe);
+
static int vsp1_du_pipeline_setup_bru(struct vsp1_device *vsp1,
struct vsp1_pipeline *pipe)
{
@@ -157,9 +166,94 @@ static int vsp1_du_pipeline_setup_bru(struct vsp1_device *vsp1,
struct v4l2_subdev_format format = {
.which = V4L2_SUBDEV_FORMAT_ACTIVE,
};
+ struct vsp1_entity *bru;
int ret;
/*
+ * Pick a BRU:
+ * - If we need more than two inputs, use the main BRU.
+ * - Otherwise, if we are not forced to release our BRU, keep it.
+ * - Else, use any free BRU (randomly starting with the main BRU).
+ */
+ if (pipe->num_inputs > 2)
+ bru = &vsp1->bru->entity;
+ else if (pipe->bru && !drm_pipe->force_bru_release)
+ bru = pipe->bru;
+ else if (!vsp1->bru->entity.pipe)
+ bru = &vsp1->bru->entity;
+ else
+ bru = &vsp1->brs->entity;
+
+ /* Switch BRU if needed. */
+ if (bru != pipe->bru) {
+ struct vsp1_entity *released_bru = NULL;
+
+ /* Release our BRU if we have one. */
+ if (pipe->bru) {
+ /*
+ * The BRU might be acquired by the other pipeline in
+ * the next step. We must thus remove it from the list
+ * of entities for this pipeline. The other pipeline's
+ * hardware configuration will reconfigure the BRU
+ * routing.
+ *
+ * However, if the other pipeline doesn't acquire our
+ * BRU, we need to keep it in the list, otherwise the
+ * hardware configuration step won't disconnect it from
+ * the pipeline. To solve this, store the released BRU
+ * pointer to add it back to the list of entities later
+ * if it isn't acquired by the other pipeline.
+ */
+ released_bru = pipe->bru;
+
+ list_del(&pipe->bru->list_pipe);
+ pipe->bru->sink = NULL;
+ pipe->bru->pipe = NULL;
+ pipe->bru = NULL;
+ }
+
+ /*
+ * If the BRU we need is in use, force the owner pipeline to
+ * switch to the other BRU and wait until the switch completes.
+ */
+ if (bru->pipe) {
+ struct vsp1_drm_pipeline *owner_pipe;
+
+ owner_pipe = to_vsp1_drm_pipeline(bru->pipe);
+ owner_pipe->force_bru_release = true;
+
+ vsp1_du_pipeline_setup_inputs(vsp1, &owner_pipe->pipe);
+ vsp1_du_pipeline_configure(&owner_pipe->pipe);
+
+ ret = wait_event_timeout(owner_pipe->wait_queue,
+ !owner_pipe->force_bru_release,
+ msecs_to_jiffies(500));
+ if (ret == 0)
+ dev_warn(vsp1->dev,
+ "DRM pipeline %u reconfiguration timeout\n",
+ owner_pipe->pipe.lif->index);
+ }
+
+ /*
+ * If the BRU we have released previously hasn't been acquired
+ * by the other pipeline, add it back to the entities list (with
+ * the pipe pointer NULL) to let vsp1_du_pipeline_configure()
+ * disconnect it from the hardware pipeline.
+ */
+ if (released_bru && !released_bru->pipe)
+ list_add_tail(&released_bru->list_pipe,
+ &pipe->entities);
+
+ /* Add the BRU to the pipeline. */
+ pipe->bru = bru;
+ pipe->bru->pipe = pipe;
+ pipe->bru->sink = &pipe->output->entity;
+ pipe->bru->sink_pad = 0;
+
+ list_add_tail(&pipe->bru->list_pipe, &pipe->entities);
+ }
+
+ /*
* Configure the format on the BRU source and verify that it matches the
* requested format. We don't set the media bus code as it is configured
* on the BRU sink pad 0 and propagated inside the entity, not on the
@@ -198,7 +292,7 @@ static int vsp1_du_pipeline_setup_inputs(struct vsp1_device *vsp1,
struct vsp1_pipeline *pipe)
{
struct vsp1_rwpf *inputs[VSP1_MAX_RPF] = { NULL, };
- struct vsp1_bru *bru = to_bru(&pipe->bru->subdev);
+ struct vsp1_bru *bru;
unsigned int i;
int ret;
@@ -209,15 +303,6 @@ static int vsp1_du_pipeline_setup_inputs(struct vsp1_device *vsp1,
struct vsp1_rwpf *rpf = vsp1->rpf[i];
unsigned int j;
- /*
- * Make sure we don't accept more inputs than the hardware can
- * handle. This is a temporary fix to avoid display stall, we
- * need to instead allocate the BRU or BRS to display pipelines
- * dynamically based on the number of planes they each use.
- */
- if (pipe->num_inputs >= pipe->bru->source_pad)
- pipe->inputs[i] = NULL;
-
if (!pipe->inputs[i])
continue;
@@ -243,6 +328,8 @@ static int vsp1_du_pipeline_setup_inputs(struct vsp1_device *vsp1,
return ret;
}
+ bru = to_bru(&pipe->bru->subdev);
+
/* Setup the RPF input pipeline for every enabled input. */
for (i = 0; i < pipe->bru->source_pad; ++i) {
struct vsp1_rwpf *rpf = inputs[i];
@@ -339,6 +426,7 @@ static int vsp1_du_pipeline_setup_output(struct vsp1_device *vsp1,
/* Configure all entities in the pipeline. */
static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe)
{
+ struct vsp1_drm_pipeline *drm_pipe = to_vsp1_drm_pipeline(pipe);
struct vsp1_entity *entity;
struct vsp1_entity *next;
struct vsp1_dl_list *dl;
@@ -369,7 +457,7 @@ static void vsp1_du_pipeline_configure(struct vsp1_pipeline *pipe)
}
}
- vsp1_dl_list_commit(dl, false);
+ vsp1_dl_list_commit(dl, drm_pipe->force_bru_release);
}
/* -----------------------------------------------------------------------------
@@ -414,7 +502,6 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
struct vsp1_device *vsp1 = dev_get_drvdata(dev);
struct vsp1_drm_pipeline *drm_pipe;
struct vsp1_pipeline *pipe;
- struct vsp1_bru *bru;
unsigned long flags;
unsigned int i;
int ret;
@@ -424,9 +511,14 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
drm_pipe = &vsp1->drm->pipe[pipe_index];
pipe = &drm_pipe->pipe;
- bru = to_bru(&pipe->bru->subdev);
if (!cfg) {
+ struct vsp1_bru *bru;
+
+ mutex_lock(&vsp1->drm->lock);
+
+ bru = to_bru(&pipe->bru->subdev);
+
/*
* NULL configuration means the CRTC is being disabled, stop
* the pipeline and turn the light off.
@@ -456,6 +548,12 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
drm_pipe->du_complete = NULL;
pipe->num_inputs = 0;
+ list_del(&pipe->bru->list_pipe);
+ pipe->bru->pipe = NULL;
+ pipe->bru = NULL;
+
+ mutex_unlock(&vsp1->drm->lock);
+
vsp1_dlm_reset(pipe->output->dlm);
vsp1_device_put(vsp1);
@@ -470,19 +568,21 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
dev_dbg(vsp1->dev, "%s: configuring LIF%u with format %ux%u\n",
__func__, pipe_index, cfg->width, cfg->height);
+ mutex_lock(&vsp1->drm->lock);
+
/* Setup formats through the pipeline. */
ret = vsp1_du_pipeline_setup_inputs(vsp1, pipe);
if (ret < 0)
- return ret;
+ goto unlock;
ret = vsp1_du_pipeline_setup_output(vsp1, pipe);
if (ret < 0)
- return ret;
+ goto unlock;
/* Enable the VSP1. */
ret = vsp1_device_get(vsp1);
if (ret < 0)
- return ret;
+ goto unlock;
/*
* Register a callback to allow us to notify the DRM driver of frame
@@ -498,6 +598,12 @@ int vsp1_du_setup_lif(struct device *dev, unsigned int pipe_index,
/* Configure all entities in the pipeline. */
vsp1_du_pipeline_configure(pipe);
+unlock:
+ mutex_unlock(&vsp1->drm->lock);
+
+ if (ret < 0)
+ return ret;
+
/* Start the pipeline. */
spin_lock_irqsave(&pipe->irqlock, flags);
vsp1_pipeline_run(pipe);
@@ -516,6 +622,9 @@ EXPORT_SYMBOL_GPL(vsp1_du_setup_lif);
*/
void vsp1_du_atomic_begin(struct device *dev, unsigned int pipe_index)
{
+ struct vsp1_device *vsp1 = dev_get_drvdata(dev);
+
+ mutex_lock(&vsp1->drm->lock);
}
EXPORT_SYMBOL_GPL(vsp1_du_atomic_begin);
@@ -629,6 +738,7 @@ void vsp1_du_atomic_flush(struct device *dev, unsigned int pipe_index)
vsp1_du_pipeline_setup_inputs(vsp1, pipe);
vsp1_du_pipeline_configure(pipe);
+ mutex_unlock(&vsp1->drm->lock);
}
EXPORT_SYMBOL_GPL(vsp1_du_atomic_flush);
@@ -667,28 +777,26 @@ int vsp1_drm_init(struct vsp1_device *vsp1)
if (!vsp1->drm)
return -ENOMEM;
+ mutex_init(&vsp1->drm->lock);
+
/* Create one DRM pipeline per LIF. */
for (i = 0; i < vsp1->info->lif_count; ++i) {
struct vsp1_drm_pipeline *drm_pipe = &vsp1->drm->pipe[i];
struct vsp1_pipeline *pipe = &drm_pipe->pipe;
+ init_waitqueue_head(&drm_pipe->wait_queue);
+
vsp1_pipeline_init(pipe);
pipe->frame_end = vsp1_du_pipeline_frame_end;
/*
- * The DRM pipeline is static, add entities manually. The first
- * pipeline uses the BRU and the second pipeline the BRS.
+ * The output side of the DRM pipeline is static, add the
+ * corresponding entities manually.
*/
- pipe->bru = i == 0 ? &vsp1->bru->entity : &vsp1->brs->entity;
pipe->output = vsp1->wpf[i];
pipe->lif = &vsp1->lif[i]->entity;
- pipe->bru->pipe = pipe;
- pipe->bru->sink = &pipe->output->entity;
- pipe->bru->sink_pad = 0;
- list_add_tail(&pipe->bru->list_pipe, &pipe->entities);
-
pipe->output->entity.pipe = pipe;
pipe->output->entity.sink = pipe->lif;
pipe->output->entity.sink_pad = 0;
@@ -710,4 +818,5 @@ int vsp1_drm_init(struct vsp1_device *vsp1)
void vsp1_drm_cleanup(struct vsp1_device *vsp1)
{
+ mutex_destroy(&vsp1->drm->lock);
}
diff --git a/drivers/media/platform/vsp1/vsp1_drm.h b/drivers/media/platform/vsp1/vsp1_drm.h
index c8dd75b..c84bc1c 100644
--- a/drivers/media/platform/vsp1/vsp1_drm.h
+++ b/drivers/media/platform/vsp1/vsp1_drm.h
@@ -13,7 +13,9 @@
#ifndef __VSP1_DRM_H__
#define __VSP1_DRM_H__
+#include <linux/mutex.h>
#include <linux/videodev2.h>
+#include <linux/wait.h>
#include "vsp1_pipe.h"
@@ -22,6 +24,8 @@
* @pipe: the VSP1 pipeline used for display
* @width: output display width
* @height: output display height
+ * @force_bru_release: when set, release the BRU during the next reconfiguration
+ * @wait_queue: wait queue to wait for BRU release completion
* @du_complete: frame completion callback for the DU driver (optional)
* @du_private: data to be passed to the du_complete callback
*/
@@ -31,6 +35,9 @@ struct vsp1_drm_pipeline {
unsigned int width;
unsigned int height;
+ bool force_bru_release;
+ wait_queue_head_t wait_queue;
+
/* Frame synchronisation */
void (*du_complete)(void *, bool);
void *du_private;
@@ -39,11 +46,13 @@ struct vsp1_drm_pipeline {
/**
* vsp1_drm - State for the API exposed to the DRM driver
* @pipe: the VSP1 DRM pipeline used for display
+ * @lock: protects the BRU and BRS allocation
* @inputs: source crop rectangle, destination compose rectangle and z-order
* position for every input (indexed by RPF index)
*/
struct vsp1_drm {
struct vsp1_drm_pipeline pipe[VSP1_MAX_LIF];
+ struct mutex lock;
struct {
struct v4l2_rect crop;
OpenPOWER on IntegriCloud