|
@@ -691,6 +691,52 @@ static const struct drm_crtc_helper_funcs crtc_helper_funcs = {
|
|
|
.atomic_disable = rcar_du_crtc_atomic_disable,
|
|
|
};
|
|
|
|
|
|
+static struct drm_crtc_state *
|
|
|
+rcar_du_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
|
|
|
+{
|
|
|
+ struct rcar_du_crtc_state *state;
|
|
|
+ struct rcar_du_crtc_state *copy;
|
|
|
+
|
|
|
+ if (WARN_ON(!crtc->state))
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ state = to_rcar_crtc_state(crtc->state);
|
|
|
+ copy = kmemdup(state, sizeof(*state), GFP_KERNEL);
|
|
|
+ if (copy == NULL)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ __drm_atomic_helper_crtc_duplicate_state(crtc, ©->state);
|
|
|
+
|
|
|
+ return ©->state;
|
|
|
+}
|
|
|
+
|
|
|
+static void rcar_du_crtc_atomic_destroy_state(struct drm_crtc *crtc,
|
|
|
+ struct drm_crtc_state *state)
|
|
|
+{
|
|
|
+ __drm_atomic_helper_crtc_destroy_state(state);
|
|
|
+ kfree(to_rcar_crtc_state(state));
|
|
|
+}
|
|
|
+
|
|
|
+static void rcar_du_crtc_reset(struct drm_crtc *crtc)
|
|
|
+{
|
|
|
+ struct rcar_du_crtc_state *state;
|
|
|
+
|
|
|
+ if (crtc->state) {
|
|
|
+ rcar_du_crtc_atomic_destroy_state(crtc, crtc->state);
|
|
|
+ crtc->state = NULL;
|
|
|
+ }
|
|
|
+
|
|
|
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
|
|
|
+ if (state == NULL)
|
|
|
+ return;
|
|
|
+
|
|
|
+ state->crc.source = VSP1_DU_CRC_NONE;
|
|
|
+ state->crc.index = 0;
|
|
|
+
|
|
|
+ crtc->state = &state->state;
|
|
|
+ crtc->state->crtc = crtc;
|
|
|
+}
|
|
|
+
|
|
|
static int rcar_du_crtc_enable_vblank(struct drm_crtc *crtc)
|
|
|
{
|
|
|
struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
|
|
@@ -710,15 +756,111 @@ static void rcar_du_crtc_disable_vblank(struct drm_crtc *crtc)
|
|
|
rcrtc->vblank_enable = false;
|
|
|
}
|
|
|
|
|
|
-static const struct drm_crtc_funcs crtc_funcs = {
|
|
|
- .reset = drm_atomic_helper_crtc_reset,
|
|
|
+static int rcar_du_crtc_set_crc_source(struct drm_crtc *crtc,
|
|
|
+ const char *source_name,
|
|
|
+ size_t *values_cnt)
|
|
|
+{
|
|
|
+ struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc);
|
|
|
+ struct drm_modeset_acquire_ctx ctx;
|
|
|
+ struct drm_crtc_state *crtc_state;
|
|
|
+ struct drm_atomic_state *state;
|
|
|
+ enum vsp1_du_crc_source source;
|
|
|
+ unsigned int index = 0;
|
|
|
+ unsigned int i;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Parse the source name. Supported values are "plane%u" to compute the
|
|
|
+ * CRC on an input plane (%u is the plane ID), and "auto" to compute the
|
|
|
+ * CRC on the composer (VSP) output.
|
|
|
+ */
|
|
|
+ if (!source_name) {
|
|
|
+ source = VSP1_DU_CRC_NONE;
|
|
|
+ } else if (!strcmp(source_name, "auto")) {
|
|
|
+ source = VSP1_DU_CRC_OUTPUT;
|
|
|
+ } else if (strstarts(source_name, "plane")) {
|
|
|
+ source = VSP1_DU_CRC_PLANE;
|
|
|
+
|
|
|
+ ret = kstrtouint(source_name + strlen("plane"), 10, &index);
|
|
|
+ if (ret < 0)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ for (i = 0; i < rcrtc->vsp->num_planes; ++i) {
|
|
|
+ if (index == rcrtc->vsp->planes[i].plane.base.id) {
|
|
|
+ index = i;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (i >= rcrtc->vsp->num_planes)
|
|
|
+ return -EINVAL;
|
|
|
+ } else {
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ *values_cnt = 1;
|
|
|
+
|
|
|
+ /* Perform an atomic commit to set the CRC source. */
|
|
|
+ drm_modeset_acquire_init(&ctx, 0);
|
|
|
+
|
|
|
+ state = drm_atomic_state_alloc(crtc->dev);
|
|
|
+ if (!state) {
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto unlock;
|
|
|
+ }
|
|
|
+
|
|
|
+ state->acquire_ctx = &ctx;
|
|
|
+
|
|
|
+retry:
|
|
|
+ crtc_state = drm_atomic_get_crtc_state(state, crtc);
|
|
|
+ if (!IS_ERR(crtc_state)) {
|
|
|
+ struct rcar_du_crtc_state *rcrtc_state;
|
|
|
+
|
|
|
+ rcrtc_state = to_rcar_crtc_state(crtc_state);
|
|
|
+ rcrtc_state->crc.source = source;
|
|
|
+ rcrtc_state->crc.index = index;
|
|
|
+
|
|
|
+ ret = drm_atomic_commit(state);
|
|
|
+ } else {
|
|
|
+ ret = PTR_ERR(crtc_state);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (ret == -EDEADLK) {
|
|
|
+ drm_atomic_state_clear(state);
|
|
|
+ drm_modeset_backoff(&ctx);
|
|
|
+ goto retry;
|
|
|
+ }
|
|
|
+
|
|
|
+ drm_atomic_state_put(state);
|
|
|
+
|
|
|
+unlock:
|
|
|
+ drm_modeset_drop_locks(&ctx);
|
|
|
+ drm_modeset_acquire_fini(&ctx);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static const struct drm_crtc_funcs crtc_funcs_gen2 = {
|
|
|
+ .reset = rcar_du_crtc_reset,
|
|
|
+ .destroy = drm_crtc_cleanup,
|
|
|
+ .set_config = drm_atomic_helper_set_config,
|
|
|
+ .page_flip = drm_atomic_helper_page_flip,
|
|
|
+ .atomic_duplicate_state = rcar_du_crtc_atomic_duplicate_state,
|
|
|
+ .atomic_destroy_state = rcar_du_crtc_atomic_destroy_state,
|
|
|
+ .enable_vblank = rcar_du_crtc_enable_vblank,
|
|
|
+ .disable_vblank = rcar_du_crtc_disable_vblank,
|
|
|
+};
|
|
|
+
|
|
|
+static const struct drm_crtc_funcs crtc_funcs_gen3 = {
|
|
|
+ .reset = rcar_du_crtc_reset,
|
|
|
.destroy = drm_crtc_cleanup,
|
|
|
.set_config = drm_atomic_helper_set_config,
|
|
|
.page_flip = drm_atomic_helper_page_flip,
|
|
|
- .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
|
|
|
- .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
|
|
|
+ .atomic_duplicate_state = rcar_du_crtc_atomic_duplicate_state,
|
|
|
+ .atomic_destroy_state = rcar_du_crtc_atomic_destroy_state,
|
|
|
.enable_vblank = rcar_du_crtc_enable_vblank,
|
|
|
.disable_vblank = rcar_du_crtc_disable_vblank,
|
|
|
+ .set_crc_source = rcar_du_crtc_set_crc_source,
|
|
|
};
|
|
|
|
|
|
/* -----------------------------------------------------------------------------
|
|
@@ -821,8 +963,10 @@ int rcar_du_crtc_create(struct rcar_du_group *rgrp, unsigned int index)
|
|
|
else
|
|
|
primary = &rgrp->planes[index % 2].plane;
|
|
|
|
|
|
- ret = drm_crtc_init_with_planes(rcdu->ddev, crtc, primary,
|
|
|
- NULL, &crtc_funcs, NULL);
|
|
|
+ ret = drm_crtc_init_with_planes(rcdu->ddev, crtc, primary, NULL,
|
|
|
+ rcdu->info->gen <= 2 ?
|
|
|
+ &crtc_funcs_gen2 : &crtc_funcs_gen3,
|
|
|
+ NULL);
|
|
|
if (ret < 0)
|
|
|
return ret;
|
|
|
|