|
|
@@ -1,4 +1,5 @@
|
|
|
/*
|
|
|
+ * Copyright (c) 2014 The Linux Foundation. All rights reserved.
|
|
|
* Copyright (C) 2013 Red Hat
|
|
|
* Author: Rob Clark <robdclark@gmail.com>
|
|
|
*
|
|
|
@@ -22,14 +23,21 @@
|
|
|
#include "drm_crtc_helper.h"
|
|
|
#include "drm_flip_work.h"
|
|
|
|
|
|
+#define SSPP_MAX (SSPP_RGB3 + 1) /* TODO: Add SSPP_MAX in mdp5.xml.h */
|
|
|
+
|
|
|
struct mdp5_crtc {
|
|
|
struct drm_crtc base;
|
|
|
char name[8];
|
|
|
int id;
|
|
|
bool enabled;
|
|
|
|
|
|
- /* which mixer/encoder we route output to: */
|
|
|
- int mixer;
|
|
|
+ /* layer mixer used for this CRTC (+ its lock): */
|
|
|
+#define GET_LM_ID(crtc_id) ((crtc_id == 3) ? 5 : crtc_id)
|
|
|
+ int lm;
|
|
|
+ spinlock_t lm_lock; /* protect REG_MDP5_LM_* registers */
|
|
|
+
|
|
|
+ /* CTL used for this CRTC: */
|
|
|
+ void *ctl;
|
|
|
|
|
|
/* if there is a pending flip, these will be non-null: */
|
|
|
struct drm_pending_vblank_event *event;
|
|
|
@@ -71,25 +79,38 @@ static void request_pending(struct drm_crtc *crtc, uint32_t pending)
|
|
|
mdp_irq_register(&get_kms(crtc)->base, &mdp5_crtc->vblank);
|
|
|
}
|
|
|
|
|
|
-static void crtc_flush(struct drm_crtc *crtc)
|
|
|
+#define mdp5_lm_get_flush(lm) mdp_ctl_flush_mask_lm(lm)
|
|
|
+
|
|
|
+static void crtc_flush(struct drm_crtc *crtc, u32 flush_mask)
|
|
|
+{
|
|
|
+ struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
|
|
|
+
|
|
|
+ DBG("%s: flush=%08x", mdp5_crtc->name, flush_mask);
|
|
|
+ mdp5_ctl_commit(mdp5_crtc->ctl, flush_mask);
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * flush updates, to make sure hw is updated to new scanout fb,
|
|
|
+ * so that we can safely queue unref to current fb (ie. next
|
|
|
+ * vblank we know hw is done w/ previous scanout_fb).
|
|
|
+ */
|
|
|
+static void crtc_flush_all(struct drm_crtc *crtc)
|
|
|
{
|
|
|
struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
|
|
|
- struct mdp5_kms *mdp5_kms = get_kms(crtc);
|
|
|
- int id = mdp5_crtc->id;
|
|
|
struct drm_plane *plane;
|
|
|
- uint32_t flush = 0;
|
|
|
+ uint32_t flush_mask = 0;
|
|
|
+
|
|
|
+ /* we could have already released CTL in the disable path: */
|
|
|
+ if (!mdp5_crtc->ctl)
|
|
|
+ return;
|
|
|
|
|
|
for_each_plane_on_crtc(crtc, plane) {
|
|
|
- enum mdp5_pipe pipe = mdp5_plane_pipe(plane);
|
|
|
- flush |= pipe2flush(pipe);
|
|
|
+ flush_mask |= mdp5_plane_get_flush(plane);
|
|
|
}
|
|
|
+ flush_mask |= mdp5_ctl_get_flush(mdp5_crtc->ctl);
|
|
|
+ flush_mask |= mdp5_lm_get_flush(mdp5_crtc->lm);
|
|
|
|
|
|
- flush |= mixer2flush(mdp5_crtc->id);
|
|
|
- flush |= MDP5_CTL_FLUSH_CTL;
|
|
|
-
|
|
|
- DBG("%s: flush=%08x", mdp5_crtc->name, flush);
|
|
|
-
|
|
|
- mdp5_write(mdp5_kms, REG_MDP5_CTL_FLUSH(id), flush);
|
|
|
+ crtc_flush(crtc, flush_mask);
|
|
|
}
|
|
|
|
|
|
static void update_fb(struct drm_crtc *crtc, struct drm_framebuffer *new_fb)
|
|
|
@@ -117,12 +138,6 @@ static void update_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb)
|
|
|
{
|
|
|
struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
|
|
|
|
|
|
- /* flush updates, to make sure hw is updated to new scanout fb,
|
|
|
- * so that we can safely queue unref to current fb (ie. next
|
|
|
- * vblank we know hw is done w/ previous scanout_fb).
|
|
|
- */
|
|
|
- crtc_flush(crtc);
|
|
|
-
|
|
|
if (mdp5_crtc->scanout_fb)
|
|
|
drm_flip_work_queue(&mdp5_crtc->unref_fb_work,
|
|
|
mdp5_crtc->scanout_fb);
|
|
|
@@ -173,6 +188,7 @@ static void pageflip_cb(struct msm_fence_cb *cb)
|
|
|
drm_framebuffer_reference(fb);
|
|
|
mdp5_plane_set_scanout(crtc->primary, fb);
|
|
|
update_scanout(crtc, fb);
|
|
|
+ crtc_flush_all(crtc);
|
|
|
}
|
|
|
|
|
|
static void unref_fb_worker(struct drm_flip_work *work, void *val)
|
|
|
@@ -223,41 +239,68 @@ static bool mdp5_crtc_mode_fixup(struct drm_crtc *crtc,
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * blend_setup() - blend all the planes of a CRTC
|
|
|
+ *
|
|
|
+ * When border is enabled, the border color will ALWAYS be the base layer.
|
|
|
+ * Therefore, the first plane (private RGB pipe) will start at STAGE0.
|
|
|
+ * If disabled, the first plane starts at STAGE_BASE.
|
|
|
+ *
|
|
|
+ * Note:
|
|
|
+ * Border is not enabled here because the private plane is exactly
|
|
|
+ * the CRTC resolution.
|
|
|
+ */
|
|
|
static void blend_setup(struct drm_crtc *crtc)
|
|
|
{
|
|
|
struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
|
|
|
struct mdp5_kms *mdp5_kms = get_kms(crtc);
|
|
|
- int id = mdp5_crtc->id;
|
|
|
+ struct drm_plane *plane;
|
|
|
+ const struct mdp5_cfg_hw *hw_cfg;
|
|
|
+ uint32_t lm = mdp5_crtc->lm, blend_cfg = 0;
|
|
|
+ enum mdp_mixer_stage_id stage;
|
|
|
+ unsigned long flags;
|
|
|
+#define blender(stage) ((stage) - STAGE_BASE)
|
|
|
|
|
|
- /*
|
|
|
- * Hard-coded setup for now until I figure out how the
|
|
|
- * layer-mixer works
|
|
|
- */
|
|
|
+ hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg_priv);
|
|
|
|
|
|
- /* LM[id]: */
|
|
|
- mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_COLOR_OUT(id),
|
|
|
- MDP5_LM_BLEND_COLOR_OUT_STAGE0_FG_ALPHA);
|
|
|
- mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_OP_MODE(id, 0),
|
|
|
- MDP5_LM_BLEND_OP_MODE_FG_ALPHA(FG_CONST) |
|
|
|
- MDP5_LM_BLEND_OP_MODE_BG_ALPHA(FG_PIXEL) |
|
|
|
- MDP5_LM_BLEND_OP_MODE_BG_INV_ALPHA);
|
|
|
- mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_FG_ALPHA(id, 0), 0xff);
|
|
|
- mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_BG_ALPHA(id, 0), 0x00);
|
|
|
-
|
|
|
- /* NOTE: seems that LM[n] and CTL[m], we do not need n==m.. but
|
|
|
- * we want to be setting CTL[m].LAYER[n]. Not sure what the
|
|
|
- * point of having CTL[m].LAYER[o] (for o!=n).. maybe that is
|
|
|
- * used when chaining up mixers for high resolution displays?
|
|
|
- */
|
|
|
+ spin_lock_irqsave(&mdp5_crtc->lm_lock, flags);
|
|
|
+
|
|
|
+ /* ctl could be released already when we are shutting down: */
|
|
|
+ if (!mdp5_crtc->ctl)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ for_each_plane_on_crtc(crtc, plane) {
|
|
|
+ struct mdp5_overlay_info *overlay;
|
|
|
+
|
|
|
+ overlay = mdp5_plane_get_overlay_info(plane);
|
|
|
+ stage = overlay->zorder;
|
|
|
|
|
|
- /* CTL[id]: */
|
|
|
- mdp5_write(mdp5_kms, REG_MDP5_CTL_LAYER_REG(id, 0),
|
|
|
- MDP5_CTL_LAYER_REG_RGB0(STAGE0) |
|
|
|
- MDP5_CTL_LAYER_REG_BORDER_COLOR);
|
|
|
- mdp5_write(mdp5_kms, REG_MDP5_CTL_LAYER_REG(id, 1), 0);
|
|
|
- mdp5_write(mdp5_kms, REG_MDP5_CTL_LAYER_REG(id, 2), 0);
|
|
|
- mdp5_write(mdp5_kms, REG_MDP5_CTL_LAYER_REG(id, 3), 0);
|
|
|
- mdp5_write(mdp5_kms, REG_MDP5_CTL_LAYER_REG(id, 4), 0);
|
|
|
+ /*
|
|
|
+ * Note: This cannot happen with current implementation but
|
|
|
+ * we need to check this condition once z property is added
|
|
|
+ */
|
|
|
+ BUG_ON(stage > hw_cfg->lm.nb_stages);
|
|
|
+
|
|
|
+ /* LM */
|
|
|
+ mdp5_write(mdp5_kms,
|
|
|
+ REG_MDP5_LM_BLEND_OP_MODE(lm, blender(stage)),
|
|
|
+ MDP5_LM_BLEND_OP_MODE_FG_ALPHA(FG_CONST) |
|
|
|
+ MDP5_LM_BLEND_OP_MODE_BG_ALPHA(BG_CONST));
|
|
|
+ mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_FG_ALPHA(lm,
|
|
|
+ blender(stage)), 0xff);
|
|
|
+ mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_BG_ALPHA(lm,
|
|
|
+ blender(stage)), 0x00);
|
|
|
+ /* CTL */
|
|
|
+ blend_cfg |= mdp_ctl_blend_mask(mdp5_plane_pipe(plane), stage);
|
|
|
+ DBG("%s: blending pipe %s on stage=%d", mdp5_crtc->name,
|
|
|
+ pipe2name(mdp5_plane_pipe(plane)), stage);
|
|
|
+ }
|
|
|
+
|
|
|
+ DBG("%s: lm%d: blend config = 0x%08x", mdp5_crtc->name, lm, blend_cfg);
|
|
|
+ mdp5_ctl_blend(mdp5_crtc->ctl, lm, blend_cfg);
|
|
|
+
|
|
|
+out:
|
|
|
+ spin_unlock_irqrestore(&mdp5_crtc->lm_lock, flags);
|
|
|
}
|
|
|
|
|
|
static int mdp5_crtc_mode_set(struct drm_crtc *crtc,
|
|
|
@@ -268,6 +311,7 @@ static int mdp5_crtc_mode_set(struct drm_crtc *crtc,
|
|
|
{
|
|
|
struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
|
|
|
struct mdp5_kms *mdp5_kms = get_kms(crtc);
|
|
|
+ unsigned long flags;
|
|
|
int ret;
|
|
|
|
|
|
mode = adjusted_mode;
|
|
|
@@ -281,6 +325,13 @@ static int mdp5_crtc_mode_set(struct drm_crtc *crtc,
|
|
|
mode->vsync_end, mode->vtotal,
|
|
|
mode->type, mode->flags);
|
|
|
|
|
|
+ /* request a free CTL, if none is already allocated for this CRTC */
|
|
|
+ if (!mdp5_crtc->ctl) {
|
|
|
+ mdp5_crtc->ctl = mdp5_ctl_request(mdp5_kms->ctl_priv, crtc);
|
|
|
+ if (!mdp5_crtc->ctl)
|
|
|
+ return -EBUSY;
|
|
|
+ }
|
|
|
+
|
|
|
/* grab extra ref for update_scanout() */
|
|
|
drm_framebuffer_reference(crtc->primary->fb);
|
|
|
|
|
|
@@ -295,12 +346,15 @@ static int mdp5_crtc_mode_set(struct drm_crtc *crtc,
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
- mdp5_write(mdp5_kms, REG_MDP5_LM_OUT_SIZE(mdp5_crtc->id),
|
|
|
+ spin_lock_irqsave(&mdp5_crtc->lm_lock, flags);
|
|
|
+ mdp5_write(mdp5_kms, REG_MDP5_LM_OUT_SIZE(mdp5_crtc->lm),
|
|
|
MDP5_LM_OUT_SIZE_WIDTH(mode->hdisplay) |
|
|
|
MDP5_LM_OUT_SIZE_HEIGHT(mode->vdisplay));
|
|
|
+ spin_unlock_irqrestore(&mdp5_crtc->lm_lock, flags);
|
|
|
|
|
|
update_fb(crtc, crtc->primary->fb);
|
|
|
update_scanout(crtc, crtc->primary->fb);
|
|
|
+ /* crtc_flush_all(crtc) will be called in _commit callback */
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
@@ -317,7 +371,7 @@ static void mdp5_crtc_prepare(struct drm_crtc *crtc)
|
|
|
static void mdp5_crtc_commit(struct drm_crtc *crtc)
|
|
|
{
|
|
|
mdp5_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
|
|
|
- crtc_flush(crtc);
|
|
|
+ crtc_flush_all(crtc);
|
|
|
/* drop the ref to mdp clk's that we got in prepare: */
|
|
|
mdp5_disable(get_kms(crtc));
|
|
|
}
|
|
|
@@ -343,6 +397,7 @@ static int mdp5_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
|
|
|
|
|
|
update_fb(crtc, crtc->primary->fb);
|
|
|
update_scanout(crtc, crtc->primary->fb);
|
|
|
+ crtc_flush_all(crtc);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
@@ -351,6 +406,19 @@ static void mdp5_crtc_load_lut(struct drm_crtc *crtc)
|
|
|
{
|
|
|
}
|
|
|
|
|
|
+static void mdp5_crtc_disable(struct drm_crtc *crtc)
|
|
|
+{
|
|
|
+ struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
|
|
|
+
|
|
|
+ DBG("%s", mdp5_crtc->name);
|
|
|
+
|
|
|
+ if (mdp5_crtc->ctl) {
|
|
|
+ mdp5_ctl_release(mdp5_crtc->ctl);
|
|
|
+ mdp5_crtc->ctl = NULL;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
static int mdp5_crtc_page_flip(struct drm_crtc *crtc,
|
|
|
struct drm_framebuffer *new_fb,
|
|
|
struct drm_pending_vblank_event *event,
|
|
|
@@ -399,6 +467,7 @@ static const struct drm_crtc_helper_funcs mdp5_crtc_helper_funcs = {
|
|
|
.commit = mdp5_crtc_commit,
|
|
|
.mode_set_base = mdp5_crtc_mode_set_base,
|
|
|
.load_lut = mdp5_crtc_load_lut,
|
|
|
+ .disable = mdp5_crtc_disable,
|
|
|
};
|
|
|
|
|
|
static void mdp5_crtc_vblank_irq(struct mdp_irq *irq, uint32_t irqstatus)
|
|
|
@@ -421,9 +490,8 @@ static void mdp5_crtc_vblank_irq(struct mdp_irq *irq, uint32_t irqstatus)
|
|
|
static void mdp5_crtc_err_irq(struct mdp_irq *irq, uint32_t irqstatus)
|
|
|
{
|
|
|
struct mdp5_crtc *mdp5_crtc = container_of(irq, struct mdp5_crtc, err);
|
|
|
- struct drm_crtc *crtc = &mdp5_crtc->base;
|
|
|
+
|
|
|
DBG("%s: error: %08x", mdp5_crtc->name, irqstatus);
|
|
|
- crtc_flush(crtc);
|
|
|
}
|
|
|
|
|
|
uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc)
|
|
|
@@ -444,10 +512,9 @@ void mdp5_crtc_set_intf(struct drm_crtc *crtc, int intf,
|
|
|
{
|
|
|
struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
|
|
|
struct mdp5_kms *mdp5_kms = get_kms(crtc);
|
|
|
- static const enum mdp5_intfnum intfnum[] = {
|
|
|
- INTF0, INTF1, INTF2, INTF3,
|
|
|
- };
|
|
|
+ uint32_t flush_mask = 0;
|
|
|
uint32_t intf_sel;
|
|
|
+ unsigned long flags;
|
|
|
|
|
|
/* now that we know what irq's we want: */
|
|
|
mdp5_crtc->err.irqmask = intf2err(intf);
|
|
|
@@ -457,6 +524,7 @@ void mdp5_crtc_set_intf(struct drm_crtc *crtc, int intf,
|
|
|
if (!mdp5_kms)
|
|
|
return;
|
|
|
|
|
|
+ spin_lock_irqsave(&mdp5_kms->resource_lock, flags);
|
|
|
intf_sel = mdp5_read(mdp5_kms, REG_MDP5_DISP_INTF_SEL);
|
|
|
|
|
|
switch (intf) {
|
|
|
@@ -481,16 +549,24 @@ void mdp5_crtc_set_intf(struct drm_crtc *crtc, int intf,
|
|
|
break;
|
|
|
}
|
|
|
|
|
|
- blend_setup(crtc);
|
|
|
+ mdp5_write(mdp5_kms, REG_MDP5_DISP_INTF_SEL, intf_sel);
|
|
|
+ spin_unlock_irqrestore(&mdp5_kms->resource_lock, flags);
|
|
|
|
|
|
DBG("%s: intf_sel=%08x", mdp5_crtc->name, intf_sel);
|
|
|
+ mdp5_ctl_set_intf(mdp5_crtc->ctl, intf);
|
|
|
+ flush_mask |= mdp5_ctl_get_flush(mdp5_crtc->ctl);
|
|
|
+ flush_mask |= mdp5_lm_get_flush(mdp5_crtc->lm);
|
|
|
|
|
|
- mdp5_write(mdp5_kms, REG_MDP5_DISP_INTF_SEL, intf_sel);
|
|
|
- mdp5_write(mdp5_kms, REG_MDP5_CTL_OP(mdp5_crtc->id),
|
|
|
- MDP5_CTL_OP_MODE(MODE_NONE) |
|
|
|
- MDP5_CTL_OP_INTF_NUM(intfnum[intf]));
|
|
|
+ crtc_flush(crtc, flush_mask);
|
|
|
+}
|
|
|
|
|
|
- crtc_flush(crtc);
|
|
|
+static int count_planes(struct drm_crtc *crtc)
|
|
|
+{
|
|
|
+ struct drm_plane *plane;
|
|
|
+ int cnt = 0;
|
|
|
+ for_each_plane_on_crtc(crtc, plane)
|
|
|
+ cnt++;
|
|
|
+ return cnt;
|
|
|
}
|
|
|
|
|
|
static void set_attach(struct drm_crtc *crtc, enum mdp5_pipe pipe_id,
|
|
|
@@ -498,14 +574,68 @@ static void set_attach(struct drm_crtc *crtc, enum mdp5_pipe pipe_id,
|
|
|
{
|
|
|
struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
|
|
|
|
|
|
+ if (plane)
|
|
|
+ plane->crtc = crtc;
|
|
|
+
|
|
|
+ DBG("%s: %d planes attached", mdp5_crtc->name, count_planes(crtc));
|
|
|
+
|
|
|
blend_setup(crtc);
|
|
|
- if (mdp5_crtc->enabled && (plane != crtc->primary))
|
|
|
- crtc_flush(crtc);
|
|
|
+ if (mdp5_crtc->enabled)
|
|
|
+ crtc_flush_all(crtc);
|
|
|
}
|
|
|
|
|
|
-void mdp5_crtc_attach(struct drm_crtc *crtc, struct drm_plane *plane)
|
|
|
+int mdp5_crtc_attach(struct drm_crtc *crtc, struct drm_plane *plane)
|
|
|
{
|
|
|
+ struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
|
|
|
+ struct mdp5_kms *mdp5_kms = get_kms(crtc);
|
|
|
+ struct device *dev = crtc->dev->dev;
|
|
|
+ const struct mdp5_cfg_hw *hw_cfg;
|
|
|
+ bool private_plane = (plane == crtc->primary);
|
|
|
+ struct mdp5_overlay_info overlay_info;
|
|
|
+ enum mdp_mixer_stage_id stage = STAGE_BASE;
|
|
|
+ int max_nb_planes;
|
|
|
+
|
|
|
+ hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg_priv);
|
|
|
+ max_nb_planes = hw_cfg->lm.nb_stages;
|
|
|
+
|
|
|
+ if (count_planes(crtc) >= max_nb_planes) {
|
|
|
+ dev_err(dev, "%s: max # of planes (%d) reached\n",
|
|
|
+ mdp5_crtc->name, max_nb_planes);
|
|
|
+ return -EBUSY;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Set default z-ordering depending on the type of plane
|
|
|
+ * private -> lower stage
|
|
|
+ * public -> topmost stage
|
|
|
+ *
|
|
|
+ * TODO: add a property to give userspace an API to change this...
|
|
|
+ * (will come in a subsequent patch)
|
|
|
+ */
|
|
|
+ if (private_plane) {
|
|
|
+ stage = STAGE_BASE;
|
|
|
+ } else {
|
|
|
+ struct drm_plane *attached_plane;
|
|
|
+ for_each_plane_on_crtc(crtc, attached_plane) {
|
|
|
+ struct mdp5_overlay_info *overlay;
|
|
|
+
|
|
|
+ if (!attached_plane)
|
|
|
+ continue;
|
|
|
+ overlay = mdp5_plane_get_overlay_info(attached_plane);
|
|
|
+ stage = max(stage, overlay->zorder);
|
|
|
+ }
|
|
|
+ stage++;
|
|
|
+ }
|
|
|
+ overlay_info.zorder = stage;
|
|
|
+ mdp5_plane_set_overlay_info(plane, &overlay_info);
|
|
|
+
|
|
|
+ DBG("%s: %s plane %s set to stage %d by default", mdp5_crtc->name,
|
|
|
+ private_plane ? "private" : "public",
|
|
|
+ pipe2name(mdp5_plane_pipe(plane)), overlay_info.zorder);
|
|
|
+
|
|
|
set_attach(crtc, mdp5_plane_pipe(plane), plane);
|
|
|
+
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
void mdp5_crtc_detach(struct drm_crtc *crtc, struct drm_plane *plane)
|
|
|
@@ -516,6 +646,16 @@ void mdp5_crtc_detach(struct drm_crtc *crtc, struct drm_plane *plane)
|
|
|
set_attach(crtc, mdp5_plane_pipe(plane), NULL);
|
|
|
}
|
|
|
|
|
|
+int mdp5_crtc_get_lm(struct drm_crtc *crtc)
|
|
|
+{
|
|
|
+ struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc);
|
|
|
+
|
|
|
+ if (WARN_ON(!crtc))
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ return mdp5_crtc->lm;
|
|
|
+}
|
|
|
+
|
|
|
/* initialize crtc */
|
|
|
struct drm_crtc *mdp5_crtc_init(struct drm_device *dev,
|
|
|
struct drm_plane *plane, int id)
|
|
|
@@ -530,6 +670,9 @@ struct drm_crtc *mdp5_crtc_init(struct drm_device *dev,
|
|
|
crtc = &mdp5_crtc->base;
|
|
|
|
|
|
mdp5_crtc->id = id;
|
|
|
+ mdp5_crtc->lm = GET_LM_ID(id);
|
|
|
+
|
|
|
+ spin_lock_init(&mdp5_crtc->lm_lock);
|
|
|
|
|
|
mdp5_crtc->vblank.irq = mdp5_crtc_vblank_irq;
|
|
|
mdp5_crtc->err.irq = mdp5_crtc_err_irq;
|