forked from Minki/linux
0b20a0f8c3
The old state is useful for drivers that need to perform operations at enable time that depend on the transition between the old and new states. While at it, rename the operation to .atomic_enable() to be consistent with .atomic_disable(), as the .enable() operation is used by atomic helpers only. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com> # for sun4i Acked-by: Philipp Zabel <p.zabel@pengutronix.de> # for imx-drm and mediatek Acked-by: Alexey Brodkin <abrodkin@synopsys.com> # for arcpgu Acked-by: Boris Brezillon <boris.brezillon@free-electrons.com> # for atmel-hlcdc Acked-by: Liviu Dudau <Liviu.Dudau@arm.com> # for hdlcd and mali-dp Acked-by: Stefan Agner <stefan@agner.ch> # for fsl-dcu Tested-by: Philippe Cornu <philippe.cornu@st.com> # for stm Acked-by: Philippe Cornu <philippe.cornu@st.com> # for stm Acked-by: Vincent Abriou <vincent.abriou@st.com> # for sti Reviewed-by: Thomas Hellstrom <thellstrom@vmware.com> # for vmwgfx Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> Link: http://patchwork.freedesktop.org/patch/msgid/20170630093646.7928-2-laurent.pinchart+renesas@ideasonboard.com
202 lines
5.1 KiB
C
202 lines
5.1 KiB
C
/*
|
|
* Copyright (C) 2015 Free Electrons
|
|
* Copyright (C) 2015 NextThing Co
|
|
*
|
|
* Maxime Ripard <maxime.ripard@free-electrons.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of
|
|
* the License, or (at your option) any later version.
|
|
*/
|
|
|
|
#include <drm/drmP.h>
|
|
#include <drm/drm_atomic_helper.h>
|
|
#include <drm/drm_crtc.h>
|
|
#include <drm/drm_crtc_helper.h>
|
|
#include <drm/drm_modes.h>
|
|
|
|
#include <linux/clk-provider.h>
|
|
#include <linux/ioport.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/of_graph.h>
|
|
#include <linux/of_irq.h>
|
|
#include <linux/regmap.h>
|
|
|
|
#include <video/videomode.h>
|
|
|
|
#include "sun4i_crtc.h"
|
|
#include "sun4i_drv.h"
|
|
#include "sunxi_engine.h"
|
|
#include "sun4i_tcon.h"
|
|
|
|
static void sun4i_crtc_atomic_begin(struct drm_crtc *crtc,
|
|
struct drm_crtc_state *old_state)
|
|
{
|
|
struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
|
|
struct drm_device *dev = crtc->dev;
|
|
unsigned long flags;
|
|
|
|
if (crtc->state->event) {
|
|
WARN_ON(drm_crtc_vblank_get(crtc) != 0);
|
|
|
|
spin_lock_irqsave(&dev->event_lock, flags);
|
|
scrtc->event = crtc->state->event;
|
|
spin_unlock_irqrestore(&dev->event_lock, flags);
|
|
crtc->state->event = NULL;
|
|
}
|
|
}
|
|
|
|
static void sun4i_crtc_atomic_flush(struct drm_crtc *crtc,
|
|
struct drm_crtc_state *old_state)
|
|
{
|
|
struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
|
|
struct drm_pending_vblank_event *event = crtc->state->event;
|
|
|
|
DRM_DEBUG_DRIVER("Committing plane changes\n");
|
|
|
|
sunxi_engine_commit(scrtc->engine);
|
|
|
|
if (event) {
|
|
crtc->state->event = NULL;
|
|
|
|
spin_lock_irq(&crtc->dev->event_lock);
|
|
if (drm_crtc_vblank_get(crtc) == 0)
|
|
drm_crtc_arm_vblank_event(crtc, event);
|
|
else
|
|
drm_crtc_send_vblank_event(crtc, event);
|
|
spin_unlock_irq(&crtc->dev->event_lock);
|
|
}
|
|
}
|
|
|
|
static void sun4i_crtc_disable(struct drm_crtc *crtc)
|
|
{
|
|
struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
|
|
|
|
DRM_DEBUG_DRIVER("Disabling the CRTC\n");
|
|
|
|
sun4i_tcon_disable(scrtc->tcon);
|
|
|
|
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 void sun4i_crtc_atomic_enable(struct drm_crtc *crtc,
|
|
struct drm_crtc_state *old_state)
|
|
{
|
|
struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
|
|
|
|
DRM_DEBUG_DRIVER("Enabling the CRTC\n");
|
|
|
|
sun4i_tcon_enable(scrtc->tcon);
|
|
}
|
|
|
|
static const struct drm_crtc_helper_funcs sun4i_crtc_helper_funcs = {
|
|
.atomic_begin = sun4i_crtc_atomic_begin,
|
|
.atomic_flush = sun4i_crtc_atomic_flush,
|
|
.atomic_enable = sun4i_crtc_atomic_enable,
|
|
.disable = sun4i_crtc_disable,
|
|
};
|
|
|
|
static int sun4i_crtc_enable_vblank(struct drm_crtc *crtc)
|
|
{
|
|
struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
|
|
|
|
DRM_DEBUG_DRIVER("Enabling VBLANK on crtc %p\n", crtc);
|
|
|
|
sun4i_tcon_enable_vblank(scrtc->tcon, true);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void sun4i_crtc_disable_vblank(struct drm_crtc *crtc)
|
|
{
|
|
struct sun4i_crtc *scrtc = drm_crtc_to_sun4i_crtc(crtc);
|
|
|
|
DRM_DEBUG_DRIVER("Disabling VBLANK on crtc %p\n", crtc);
|
|
|
|
sun4i_tcon_enable_vblank(scrtc->tcon, false);
|
|
}
|
|
|
|
static const struct drm_crtc_funcs sun4i_crtc_funcs = {
|
|
.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
|
|
.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
|
|
.destroy = drm_crtc_cleanup,
|
|
.page_flip = drm_atomic_helper_page_flip,
|
|
.reset = drm_atomic_helper_crtc_reset,
|
|
.set_config = drm_atomic_helper_set_config,
|
|
.enable_vblank = sun4i_crtc_enable_vblank,
|
|
.disable_vblank = sun4i_crtc_disable_vblank,
|
|
};
|
|
|
|
struct sun4i_crtc *sun4i_crtc_init(struct drm_device *drm,
|
|
struct sunxi_engine *engine,
|
|
struct sun4i_tcon *tcon)
|
|
{
|
|
struct sun4i_crtc *scrtc;
|
|
struct drm_plane **planes;
|
|
struct drm_plane *primary = NULL, *cursor = NULL;
|
|
int ret, i;
|
|
|
|
scrtc = devm_kzalloc(drm->dev, sizeof(*scrtc), GFP_KERNEL);
|
|
if (!scrtc)
|
|
return ERR_PTR(-ENOMEM);
|
|
scrtc->engine = engine;
|
|
scrtc->tcon = tcon;
|
|
|
|
/* Create our layers */
|
|
planes = sunxi_engine_layers_init(drm, engine);
|
|
if (IS_ERR(planes)) {
|
|
dev_err(drm->dev, "Couldn't create the planes\n");
|
|
return NULL;
|
|
}
|
|
|
|
/* find primary and cursor planes for drm_crtc_init_with_planes */
|
|
for (i = 0; planes[i]; i++) {
|
|
struct drm_plane *plane = planes[i];
|
|
|
|
switch (plane->type) {
|
|
case DRM_PLANE_TYPE_PRIMARY:
|
|
primary = plane;
|
|
break;
|
|
case DRM_PLANE_TYPE_CURSOR:
|
|
cursor = plane;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
ret = drm_crtc_init_with_planes(drm, &scrtc->crtc,
|
|
primary,
|
|
cursor,
|
|
&sun4i_crtc_funcs,
|
|
NULL);
|
|
if (ret) {
|
|
dev_err(drm->dev, "Couldn't init DRM CRTC\n");
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
drm_crtc_helper_add(&scrtc->crtc, &sun4i_crtc_helper_funcs);
|
|
|
|
/* Set crtc.port to output port node of the tcon */
|
|
scrtc->crtc.port = of_graph_get_port_by_id(scrtc->tcon->dev->of_node,
|
|
1);
|
|
|
|
/* Set possible_crtcs to this crtc for overlay planes */
|
|
for (i = 0; planes[i]; i++) {
|
|
uint32_t possible_crtcs = BIT(drm_crtc_index(&scrtc->crtc));
|
|
struct drm_plane *plane = planes[i];
|
|
|
|
if (plane->type == DRM_PLANE_TYPE_OVERLAY)
|
|
plane->possible_crtcs = possible_crtcs;
|
|
}
|
|
|
|
return scrtc;
|
|
}
|