The current atomic helpers have either their object state being passed as an argument or the full atomic state. The former is the pattern that was done at first, before switching to the latter for new hooks or when it was needed. Let's start convert all the remaining helpers to provide a consistent interface, starting with the CRTC's atomic_begin and atomic_flush. 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 old_crtc_state, old_state; identifier crtc; identifier f; @@ f(struct drm_crtc_state *old_crtc_state) { ... struct drm_atomic_state *old_state = old_crtc_state->state; <... - FUNCS->atomic_begin(crtc, old_crtc_state); + FUNCS->atomic_begin(crtc, old_state); ...> } @@ struct drm_crtc_helper_funcs *FUNCS; identifier old_crtc_state, old_state; identifier crtc; identifier f; @@ f(struct drm_crtc_state *old_crtc_state) { ... struct drm_atomic_state *old_state = old_crtc_state->state; <... - FUNCS->atomic_flush(crtc, old_crtc_state); + FUNCS->atomic_flush(crtc, old_state); ...> } @@ struct drm_crtc_helper_funcs *FUNCS; struct drm_crtc *crtc; struct drm_crtc_state *crtc_state; identifier dev, state; identifier f; @@ f(struct drm_device *dev, struct drm_atomic_state *state, ...) { <... - FUNCS->atomic_begin(crtc, crtc_state); + FUNCS->atomic_begin(crtc, state); ...> } @@ struct drm_crtc_helper_funcs *FUNCS; struct drm_crtc *crtc; struct drm_crtc_state *crtc_state; identifier dev, state; identifier f; @@ f(struct drm_device *dev, struct drm_atomic_state *state, ...) { <... - FUNCS->atomic_flush(crtc, crtc_state); + FUNCS->atomic_flush(crtc, state); ...> } @@ identifier crtc, old_state; @@ struct drm_crtc_helper_funcs { ... - void (*atomic_begin)(struct drm_crtc *crtc, struct drm_crtc_state *old_state); + void (*atomic_begin)(struct drm_crtc *crtc, struct drm_atomic_state *state); ... - void (*atomic_flush)(struct drm_crtc *crtc, struct drm_crtc_state *old_state); + void (*atomic_flush)(struct drm_crtc *crtc, struct drm_atomic_state *state); ... } @ crtc_atomic_func @ identifier helpers; identifier func; @@ ( static struct drm_crtc_helper_funcs helpers = { ..., .atomic_begin = func, ..., }; | static struct drm_crtc_helper_funcs helpers = { ..., .atomic_flush = 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 ...+> } @@ identifier old_state; identifier crtc; @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc, - struct drm_crtc_state *old_state + struct drm_atomic_state *state ) { + struct drm_crtc_state *old_state = drm_atomic_get_old_crtc_state(state, crtc); ... } @@ identifier old_state; identifier crtc; @@ void vc4_hvs_atomic_flush(struct drm_crtc *crtc, - struct drm_crtc_state *old_state + struct drm_atomic_state *state ); @@ identifier old_state; identifier crtc; @@ void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc, - struct drm_crtc_state *old_state + struct drm_atomic_state *state ) { ... } @@ identifier old_state; identifier crtc; @@ void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc, - struct drm_crtc_state *old_state + struct drm_atomic_state *state ); @@ identifier old_state; identifier crtc; @@ void vmw_du_crtc_atomic_flush(struct drm_crtc *crtc, - struct drm_crtc_state *old_state + struct drm_atomic_state *state ) { ... } @@ identifier old_state; identifier crtc; @@ void vmw_du_crtc_atomic_flush(struct drm_crtc *crtc, - struct drm_crtc_state *old_state + struct drm_atomic_state *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> Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com> Acked-by: Daniel Vetter <daniel.vetter@ffwll.ch> Acked-by: Thomas Zimmermann <tzimmermann@suse.de> Link: https://patchwork.freedesktop.org/patch/msgid/20201028123222.1732139-2-maxime@cerno.tech
224 lines
6.1 KiB
C
224 lines
6.1 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright 2019 NXP.
|
|
*/
|
|
|
|
#include <drm/drm_atomic.h>
|
|
#include <drm/drm_atomic_helper.h>
|
|
#include <drm/drm_vblank.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/pm_runtime.h>
|
|
|
|
#include "dcss-dev.h"
|
|
#include "dcss-kms.h"
|
|
|
|
static int dcss_enable_vblank(struct drm_crtc *crtc)
|
|
{
|
|
struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,
|
|
base);
|
|
struct dcss_dev *dcss = crtc->dev->dev_private;
|
|
|
|
dcss_dtg_vblank_irq_enable(dcss->dtg, true);
|
|
|
|
dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, true);
|
|
|
|
enable_irq(dcss_crtc->irq);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void dcss_disable_vblank(struct drm_crtc *crtc)
|
|
{
|
|
struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,
|
|
base);
|
|
struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private;
|
|
|
|
disable_irq_nosync(dcss_crtc->irq);
|
|
|
|
dcss_dtg_vblank_irq_enable(dcss->dtg, false);
|
|
|
|
if (dcss_crtc->disable_ctxld_kick_irq)
|
|
dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, false);
|
|
}
|
|
|
|
static const struct drm_crtc_funcs dcss_crtc_funcs = {
|
|
.set_config = drm_atomic_helper_set_config,
|
|
.destroy = drm_crtc_cleanup,
|
|
.page_flip = drm_atomic_helper_page_flip,
|
|
.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 = dcss_enable_vblank,
|
|
.disable_vblank = dcss_disable_vblank,
|
|
};
|
|
|
|
static void dcss_crtc_atomic_begin(struct drm_crtc *crtc,
|
|
struct drm_atomic_state *state)
|
|
{
|
|
drm_crtc_vblank_on(crtc);
|
|
}
|
|
|
|
static void dcss_crtc_atomic_flush(struct drm_crtc *crtc,
|
|
struct drm_atomic_state *state)
|
|
{
|
|
struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,
|
|
base);
|
|
struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private;
|
|
|
|
spin_lock_irq(&crtc->dev->event_lock);
|
|
if (crtc->state->event) {
|
|
WARN_ON(drm_crtc_vblank_get(crtc));
|
|
drm_crtc_arm_vblank_event(crtc, crtc->state->event);
|
|
crtc->state->event = NULL;
|
|
}
|
|
spin_unlock_irq(&crtc->dev->event_lock);
|
|
|
|
if (dcss_dtg_is_enabled(dcss->dtg))
|
|
dcss_ctxld_enable(dcss->ctxld);
|
|
}
|
|
|
|
static void dcss_crtc_atomic_enable(struct drm_crtc *crtc,
|
|
struct drm_atomic_state *state)
|
|
{
|
|
struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state,
|
|
crtc);
|
|
struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,
|
|
base);
|
|
struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private;
|
|
struct drm_display_mode *mode = &crtc->state->adjusted_mode;
|
|
struct drm_display_mode *old_mode = &old_crtc_state->adjusted_mode;
|
|
struct videomode vm;
|
|
|
|
drm_display_mode_to_videomode(mode, &vm);
|
|
|
|
pm_runtime_get_sync(dcss->dev);
|
|
|
|
vm.pixelclock = mode->crtc_clock * 1000;
|
|
|
|
dcss_ss_subsam_set(dcss->ss);
|
|
dcss_dtg_css_set(dcss->dtg);
|
|
|
|
if (!drm_mode_equal(mode, old_mode) || !old_crtc_state->active) {
|
|
dcss_dtg_sync_set(dcss->dtg, &vm);
|
|
dcss_ss_sync_set(dcss->ss, &vm,
|
|
mode->flags & DRM_MODE_FLAG_PHSYNC,
|
|
mode->flags & DRM_MODE_FLAG_PVSYNC);
|
|
}
|
|
|
|
dcss_enable_dtg_and_ss(dcss);
|
|
|
|
dcss_ctxld_enable(dcss->ctxld);
|
|
|
|
/* Allow CTXLD kick interrupt to be disabled when VBLANK is disabled. */
|
|
dcss_crtc->disable_ctxld_kick_irq = true;
|
|
}
|
|
|
|
static void dcss_crtc_atomic_disable(struct drm_crtc *crtc,
|
|
struct drm_atomic_state *state)
|
|
{
|
|
struct drm_crtc_state *old_crtc_state = drm_atomic_get_old_crtc_state(state,
|
|
crtc);
|
|
struct dcss_crtc *dcss_crtc = container_of(crtc, struct dcss_crtc,
|
|
base);
|
|
struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private;
|
|
struct drm_display_mode *mode = &crtc->state->adjusted_mode;
|
|
struct drm_display_mode *old_mode = &old_crtc_state->adjusted_mode;
|
|
|
|
drm_atomic_helper_disable_planes_on_crtc(old_crtc_state, false);
|
|
|
|
spin_lock_irq(&crtc->dev->event_lock);
|
|
if (crtc->state->event) {
|
|
drm_crtc_send_vblank_event(crtc, crtc->state->event);
|
|
crtc->state->event = NULL;
|
|
}
|
|
spin_unlock_irq(&crtc->dev->event_lock);
|
|
|
|
dcss_dtg_ctxld_kick_irq_enable(dcss->dtg, true);
|
|
|
|
reinit_completion(&dcss->disable_completion);
|
|
|
|
dcss_disable_dtg_and_ss(dcss);
|
|
|
|
dcss_ctxld_enable(dcss->ctxld);
|
|
|
|
if (!drm_mode_equal(mode, old_mode) || !crtc->state->active)
|
|
if (!wait_for_completion_timeout(&dcss->disable_completion,
|
|
msecs_to_jiffies(100)))
|
|
dev_err(dcss->dev, "Shutting off DTG timed out.\n");
|
|
|
|
/*
|
|
* Do not shut off CTXLD kick interrupt when shutting VBLANK off. It
|
|
* will be needed to commit the last changes, before going to suspend.
|
|
*/
|
|
dcss_crtc->disable_ctxld_kick_irq = false;
|
|
|
|
drm_crtc_vblank_off(crtc);
|
|
|
|
pm_runtime_mark_last_busy(dcss->dev);
|
|
pm_runtime_put_autosuspend(dcss->dev);
|
|
}
|
|
|
|
static const struct drm_crtc_helper_funcs dcss_helper_funcs = {
|
|
.atomic_begin = dcss_crtc_atomic_begin,
|
|
.atomic_flush = dcss_crtc_atomic_flush,
|
|
.atomic_enable = dcss_crtc_atomic_enable,
|
|
.atomic_disable = dcss_crtc_atomic_disable,
|
|
};
|
|
|
|
static irqreturn_t dcss_crtc_irq_handler(int irq, void *dev_id)
|
|
{
|
|
struct dcss_crtc *dcss_crtc = dev_id;
|
|
struct dcss_dev *dcss = dcss_crtc->base.dev->dev_private;
|
|
|
|
if (!dcss_dtg_vblank_irq_valid(dcss->dtg))
|
|
return IRQ_NONE;
|
|
|
|
if (dcss_ctxld_is_flushed(dcss->ctxld))
|
|
drm_crtc_handle_vblank(&dcss_crtc->base);
|
|
|
|
dcss_dtg_vblank_irq_clear(dcss->dtg);
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
int dcss_crtc_init(struct dcss_crtc *crtc, struct drm_device *drm)
|
|
{
|
|
struct dcss_dev *dcss = drm->dev_private;
|
|
struct platform_device *pdev = to_platform_device(dcss->dev);
|
|
int ret;
|
|
|
|
crtc->plane[0] = dcss_plane_init(drm, drm_crtc_mask(&crtc->base),
|
|
DRM_PLANE_TYPE_PRIMARY, 0);
|
|
if (IS_ERR(crtc->plane[0]))
|
|
return PTR_ERR(crtc->plane[0]);
|
|
|
|
crtc->base.port = dcss->of_port;
|
|
|
|
drm_crtc_helper_add(&crtc->base, &dcss_helper_funcs);
|
|
ret = drm_crtc_init_with_planes(drm, &crtc->base, &crtc->plane[0]->base,
|
|
NULL, &dcss_crtc_funcs, NULL);
|
|
if (ret) {
|
|
dev_err(dcss->dev, "failed to init crtc\n");
|
|
return ret;
|
|
}
|
|
|
|
crtc->irq = platform_get_irq_byname(pdev, "vblank");
|
|
if (crtc->irq < 0)
|
|
return crtc->irq;
|
|
|
|
ret = request_irq(crtc->irq, dcss_crtc_irq_handler,
|
|
0, "dcss_drm", crtc);
|
|
if (ret) {
|
|
dev_err(dcss->dev, "irq request failed with %d.\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
disable_irq(crtc->irq);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void dcss_crtc_deinit(struct dcss_crtc *crtc, struct drm_device *drm)
|
|
{
|
|
free_irq(crtc->irq, crtc);
|
|
}
|