linux/drivers/gpu/drm/exynos/exynos_drm_crtc.c
Maxime Ripard 351f950db4
drm/atomic: Pass the full state to CRTC atomic enable/disable
If the CRTC driver ever needs to access the full DRM state, it can't do so
at atomic_enable / atomic_disable time since drm_atomic_helper_swap_state
will have cleared the pointer from the struct drm_crtc_state to the struct
drm_atomic_state before calling those hooks.

In order to allow that, let's pass the full DRM state to atomic_enable and
atomic_disable. The conversion was done using the coccinelle script below,
built tested on all the drivers and actually tested on vc4.

virtual report

@@
struct drm_crtc_helper_funcs *FUNCS;
identifier dev, state;
identifier crtc, crtc_state;
@@

 disable_outputs(struct drm_device *dev, struct drm_atomic_state *state)
 {
 	<...
-	FUNCS->atomic_disable(crtc, crtc_state);
+	FUNCS->atomic_disable(crtc, state);
 	...>
 }

@@
struct drm_crtc_helper_funcs *FUNCS;
identifier dev, state;
identifier crtc, crtc_state;
@@

 drm_atomic_helper_commit_modeset_enables(struct drm_device *dev, struct drm_atomic_state *state)
 {
 	<...
-	FUNCS->atomic_enable(crtc, crtc_state);
+	FUNCS->atomic_enable(crtc, state);
 	...>
 }

@@
identifier crtc, old_state;
@@

 struct drm_crtc_helper_funcs {
	...
-	void (*atomic_enable)(struct drm_crtc *crtc, struct drm_crtc_state *old_state);
+	void (*atomic_enable)(struct drm_crtc *crtc, struct drm_atomic_state *state);
	...
-	void (*atomic_disable)(struct drm_crtc *crtc, struct drm_crtc_state *old_state);
+	void (*atomic_disable)(struct drm_crtc *crtc, struct drm_atomic_state *state);
	...
}

@ crtc_atomic_func @
identifier helpers;
identifier func;
@@

(
static struct drm_crtc_helper_funcs helpers = {
	...,
	.atomic_enable = func,
	...,
};
|
static struct drm_crtc_helper_funcs helpers = {
	...,
	.atomic_disable = func,
	...,
};
)

@ ignores_old_state @
identifier crtc_atomic_func.func;
identifier crtc, old_state;
@@

void func(struct drm_crtc *crtc,
		struct drm_crtc_state *old_state)
{
	... when != old_state
}

@ adds_old_state depends on crtc_atomic_func && !ignores_old_state @
identifier crtc_atomic_func.func;
identifier crtc, old_state;
@@

void func(struct drm_crtc *crtc, struct drm_crtc_state *old_state)
{
+	struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, crtc);
	...
}

@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
expression E;
type T;
@@

void func(...)
{
	...
-	T state = E;
+	T crtc_state = E;
	<+...
-	state
+	crtc_state
	...+>

}

@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
type T;
@@

void func(...)
{
	...
-	T state;
+	T crtc_state;
	<+...
-	state
+	crtc_state
	...+>

}

@ depends on crtc_atomic_func @
identifier crtc_atomic_func.func;
identifier old_state;
identifier crtc;
@@

void func(struct drm_crtc *crtc,
-	       struct drm_crtc_state *old_state
+	       struct drm_atomic_state *state
	       )
		{ ... }

@ include depends on adds_old_state @
@@

 #include <drm/drm_atomic.h>

@ no_include depends on !include && adds_old_state @
@@

+ #include <drm/drm_atomic.h>
  #include <drm/...>

Signed-off-by: Maxime Ripard <maxime@cerno.tech>
Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Link: https://patchwork.freedesktop.org/patch/msgid/845aa10ef171fc0ea060495efef142a0c13f7870.1602161031.git-series.maxime@cerno.tech
2020-10-09 09:55:59 +02:00

240 lines
6.1 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/* exynos_drm_crtc.c
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Authors:
* Inki Dae <inki.dae@samsung.com>
* Joonyoung Shim <jy0922.shim@samsung.com>
* Seung-Woo Kim <sw0312.kim@samsung.com>
*/
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_encoder.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
#include "exynos_drm_crtc.h"
#include "exynos_drm_drv.h"
#include "exynos_drm_plane.h"
static void exynos_drm_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
if (exynos_crtc->ops->atomic_enable)
exynos_crtc->ops->atomic_enable(exynos_crtc);
drm_crtc_vblank_on(crtc);
}
static void exynos_drm_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
drm_crtc_vblank_off(crtc);
if (exynos_crtc->ops->atomic_disable)
exynos_crtc->ops->atomic_disable(exynos_crtc);
if (crtc->state->event && !crtc->state->active) {
spin_lock_irq(&crtc->dev->event_lock);
drm_crtc_send_vblank_event(crtc, crtc->state->event);
spin_unlock_irq(&crtc->dev->event_lock);
crtc->state->event = NULL;
}
}
static int exynos_crtc_atomic_check(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
if (!state->enable)
return 0;
if (exynos_crtc->ops->atomic_check)
return exynos_crtc->ops->atomic_check(exynos_crtc, state);
return 0;
}
static void exynos_crtc_atomic_begin(struct drm_crtc *crtc,
struct drm_crtc_state *old_crtc_state)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
if (exynos_crtc->ops->atomic_begin)
exynos_crtc->ops->atomic_begin(exynos_crtc);
}
static void exynos_crtc_atomic_flush(struct drm_crtc *crtc,
struct drm_crtc_state *old_crtc_state)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
if (exynos_crtc->ops->atomic_flush)
exynos_crtc->ops->atomic_flush(exynos_crtc);
}
static enum drm_mode_status exynos_crtc_mode_valid(struct drm_crtc *crtc,
const struct drm_display_mode *mode)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
if (exynos_crtc->ops->mode_valid)
return exynos_crtc->ops->mode_valid(exynos_crtc, mode);
return MODE_OK;
}
static bool exynos_crtc_mode_fixup(struct drm_crtc *crtc,
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
if (exynos_crtc->ops->mode_fixup)
return exynos_crtc->ops->mode_fixup(exynos_crtc, mode,
adjusted_mode);
return true;
}
static const struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
.mode_valid = exynos_crtc_mode_valid,
.mode_fixup = exynos_crtc_mode_fixup,
.atomic_check = exynos_crtc_atomic_check,
.atomic_begin = exynos_crtc_atomic_begin,
.atomic_flush = exynos_crtc_atomic_flush,
.atomic_enable = exynos_drm_crtc_atomic_enable,
.atomic_disable = exynos_drm_crtc_atomic_disable,
};
void exynos_crtc_handle_event(struct exynos_drm_crtc *exynos_crtc)
{
struct drm_crtc *crtc = &exynos_crtc->base;
struct drm_pending_vblank_event *event = crtc->state->event;
unsigned long flags;
if (!event)
return;
crtc->state->event = NULL;
WARN_ON(drm_crtc_vblank_get(crtc) != 0);
spin_lock_irqsave(&crtc->dev->event_lock, flags);
drm_crtc_arm_vblank_event(crtc, event);
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
}
static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
drm_crtc_cleanup(crtc);
kfree(exynos_crtc);
}
static int exynos_drm_crtc_enable_vblank(struct drm_crtc *crtc)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
if (exynos_crtc->ops->enable_vblank)
return exynos_crtc->ops->enable_vblank(exynos_crtc);
return 0;
}
static void exynos_drm_crtc_disable_vblank(struct drm_crtc *crtc)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
if (exynos_crtc->ops->disable_vblank)
exynos_crtc->ops->disable_vblank(exynos_crtc);
}
static const struct drm_crtc_funcs exynos_crtc_funcs = {
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
.destroy = exynos_drm_crtc_destroy,
.reset = drm_atomic_helper_crtc_reset,
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
.enable_vblank = exynos_drm_crtc_enable_vblank,
.disable_vblank = exynos_drm_crtc_disable_vblank,
};
struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev,
struct drm_plane *plane,
enum exynos_drm_output_type type,
const struct exynos_drm_crtc_ops *ops,
void *ctx)
{
struct exynos_drm_crtc *exynos_crtc;
struct drm_crtc *crtc;
int ret;
exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL);
if (!exynos_crtc)
return ERR_PTR(-ENOMEM);
exynos_crtc->type = type;
exynos_crtc->ops = ops;
exynos_crtc->ctx = ctx;
crtc = &exynos_crtc->base;
ret = drm_crtc_init_with_planes(drm_dev, crtc, plane, NULL,
&exynos_crtc_funcs, NULL);
if (ret < 0)
goto err_crtc;
drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs);
return exynos_crtc;
err_crtc:
plane->funcs->destroy(plane);
kfree(exynos_crtc);
return ERR_PTR(ret);
}
struct exynos_drm_crtc *exynos_drm_crtc_get_by_type(struct drm_device *drm_dev,
enum exynos_drm_output_type out_type)
{
struct drm_crtc *crtc;
drm_for_each_crtc(crtc, drm_dev)
if (to_exynos_crtc(crtc)->type == out_type)
return to_exynos_crtc(crtc);
return ERR_PTR(-ENODEV);
}
int exynos_drm_set_possible_crtcs(struct drm_encoder *encoder,
enum exynos_drm_output_type out_type)
{
struct exynos_drm_crtc *crtc = exynos_drm_crtc_get_by_type(encoder->dev,
out_type);
if (IS_ERR(crtc))
return PTR_ERR(crtc);
encoder->possible_crtcs = drm_crtc_mask(&crtc->base);
return 0;
}
void exynos_drm_crtc_te_handler(struct drm_crtc *crtc)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
if (exynos_crtc->ops->te_handler)
exynos_crtc->ops->te_handler(exynos_crtc);
}