2014-11-18 17:50:16

by Stephane Viau

[permalink] [raw]
Subject: [PATCH 0/3] Update SMP, add CFG and Overlay support

This patch set updates the SMP config for MDP5, introduces a Config module for
easier platform config update and allows MDP5 driver to use multiple CRTCs as
well as multiple planes (overlay).

Stephane Viau (3):
drm/msm/mdp5: make SMP module dynamically configurable
drm/msm/mdp5: introduce mdp5_cfg module
drm/msm: add multiple CRTC and overlay support

drivers/gpu/drm/msm/Makefile | 2 +
drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c | 215 ++++++++++++++++++
drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h | 89 ++++++++
drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 265 +++++++++++++++++------
drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c | 325 ++++++++++++++++++++++++++++
drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h | 121 +++++++++++
drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c | 13 ++
drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c | 251 ++++++++-------------
drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h | 97 ++-------
drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c | 175 +++++++--------
drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.c | 247 +++++++++++++++++----
drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.h | 22 +-
12 files changed, 1379 insertions(+), 443 deletions(-)
create mode 100644 drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c
create mode 100644 drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h
create mode 100644 drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c
create mode 100644 drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h

--
Qualcomm Innovation Center, Inc.

The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project


2014-11-18 17:50:13

by Stephane Viau

[permalink] [raw]
Subject: [PATCH 1/3] drm/msm/mdp5: make SMP module dynamically configurable

The Shared Memory Pool (SMP) has its own limitation, features and
state. Some examples are:
- the number of Memory Macro Block (MMB) and their size
- the number of lines that can be fetched
- the state of MMB currently allocated
- the computation of number of blocks required per plane
- client IDs ...

In order to avoid private data to be overwritten by other modules,
let's make these private to the SMP module.

Some of these depend on the hardware configuration, let's add them
to the mdp5_config struct.

In some hw configurations, some MMBs are statically tied to RGB
pipes and cannot be re-allocated dynamically. This change
introduces the concept of MMB static usage and makes sure that
dynamic MMB requests are dimensioned accordingly.

A note on passing a pipe pointer, instead of client IDs:
Client IDs are SMP-related information. Passing PIPE information
to SMP lets SMP module to find out which SMP client(s) are used.
This allows the SMP module to access the PIPE pointer, which can
be used for FIFO watermark configuration.
By the way, even though REG_MDP5_PIPE_REQPRIO_FIFO_WM_* registers
are part of the PIPE registers, their functionality is to reflect
the behavior of the SMP block. These registers access is now
restricted to the SMP module.

Signed-off-by: Stephane Viau <[email protected]>
---
drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c | 28 +++-
drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h | 34 ++---
drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c | 92 ++----------
drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.c | 242 +++++++++++++++++++++++++-----
drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.h | 22 +--
5 files changed, 265 insertions(+), 153 deletions(-)

diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
index f2c15bd..0d6306d 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
@@ -30,6 +30,10 @@ const struct mdp5_config *mdp5_cfg;

static const struct mdp5_config msm8x74_config = {
.name = "msm8x74",
+ .smp = {
+ .mmb_count = 22,
+ .mmb_size = 4096,
+ },
.ctl = {
.count = 5,
.base = { 0x00600, 0x00700, 0x00800, 0x00900, 0x00a00 },
@@ -67,6 +71,15 @@ static const struct mdp5_config msm8x74_config = {

static const struct mdp5_config apq8084_config = {
.name = "apq8084",
+ .smp = {
+ .mmb_count = 44,
+ .mmb_size = 8192,
+ .reserved_state[0] = GENMASK(7, 0), /* first 8 MMBs */
+ .reserved[CID_RGB0] = 2,
+ .reserved[CID_RGB1] = 2,
+ .reserved[CID_RGB2] = 2,
+ .reserved[CID_RGB3] = 2,
+ },
.ctl = {
.count = 5,
.base = { 0x00600, 0x00700, 0x00800, 0x00900, 0x00a00 },
@@ -222,11 +235,15 @@ static void mdp5_destroy(struct msm_kms *kms)
{
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
struct msm_mmu *mmu = mdp5_kms->mmu;
+ void *smp = mdp5_kms->smp_priv;

if (mmu) {
mmu->funcs->detach(mmu, iommu_ports, ARRAY_SIZE(iommu_ports));
mmu->funcs->destroy(mmu);
}
+ if (smp)
+ mdp5_smp_destroy(smp);
+
kfree(mdp5_kms);
}

@@ -363,6 +380,7 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev)
struct mdp5_kms *mdp5_kms;
struct msm_kms *kms = NULL;
struct msm_mmu *mmu;
+ void *priv;
int i, ret;

mdp5_kms = kzalloc(sizeof(*mdp5_kms), GFP_KERNEL);
@@ -377,7 +395,6 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev)
kms = &mdp5_kms->base.base;

mdp5_kms->dev = dev;
- mdp5_kms->smp_blk_cnt = config->smp_blk_cnt;

mdp5_kms->mmio = msm_ioremap(pdev, "mdp_phys", "MDP5");
if (IS_ERR(mdp5_kms->mmio)) {
@@ -429,6 +446,13 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev)
/* TODO: compute core clock rate at runtime */
clk_set_rate(mdp5_kms->src_clk, mdp5_kms->hw_cfg->max_clk);

+ priv = mdp5_smp_init(mdp5_kms->dev, &mdp5_kms->hw_cfg->smp);
+ if (IS_ERR(priv)) {
+ ret = PTR_ERR(priv);
+ goto fail;
+ }
+ mdp5_kms->smp_priv = priv;
+
/* make sure things are off before attaching iommu (bootloader could
* have left things on, in which case we'll start getting faults if
* we don't disable):
@@ -489,8 +513,6 @@ static struct mdp5_platform_config *mdp5_get_config(struct platform_device *dev)
/* TODO */
#endif
config.iommu = iommu_domain_alloc(&platform_bus_type);
- /* TODO get from DT: */
- config.smp_blk_cnt = 22;

return &config;
}
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
index bdbdcda..753659b 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
@@ -23,12 +23,22 @@
#include "mdp/mdp_kms.h"
/* dynamic offsets used by mdp5.xml.h (initialized in mdp5_kms.c) */
#define MDP5_MAX_BASES 8
+#define MAX_SMP_BLOCKS 44
+#define MAX_CLIENTS 32
+typedef DECLARE_BITMAP(mdp5_smp_state_t, MAX_SMP_BLOCKS);
struct mdp5_sub_block {
int count;
uint32_t base[MDP5_MAX_BASES];
};
+struct mdp5_smp_block {
+ int mmb_count; /* number of SMP MMBs */
+ int mmb_size; /* MMB: size in bytes */
+ mdp5_smp_state_t reserved_state;/* SMP MMBs statically allocated */
+ int reserved[MAX_CLIENTS]; /* # of MMBs reserved per client */
+};
struct mdp5_config {
char *name;
+ struct mdp5_smp_block smp;
struct mdp5_sub_block ctl;
struct mdp5_sub_block pipe_vig;
struct mdp5_sub_block pipe_rgb;
@@ -56,10 +66,7 @@ struct mdp5_kms {
int id;
struct msm_mmu *mmu;

- /* for tracking smp allocation amongst pipes: */
- mdp5_smp_state_t smp_state;
- struct mdp5_client_smp_state smp_client_state[CID_MAX];
- int smp_blk_cnt;
+ void *smp_priv;

/* io/register spaces: */
void __iomem *mmio, *vbif;
@@ -80,7 +87,6 @@ struct mdp5_kms {
/* platform config data (ie. from DT, or pdata) */
struct mdp5_platform_config {
struct iommu_domain *iommu;
- int smp_blk_cnt;
};

static inline void mdp5_write(struct mdp5_kms *mdp5_kms, u32 reg, u32 data)
@@ -136,24 +142,6 @@ static inline int pipe2nclients(enum mdp5_pipe pipe)
}
}

-static inline enum mdp5_client_id pipe2client(enum mdp5_pipe pipe, int plane)
-{
- WARN_ON(plane >= pipe2nclients(pipe));
- switch (pipe) {
- case SSPP_VIG0: return CID_VIG0_Y + plane;
- case SSPP_VIG1: return CID_VIG1_Y + plane;
- case SSPP_VIG2: return CID_VIG2_Y + plane;
- case SSPP_RGB0: return CID_RGB0;
- case SSPP_RGB1: return CID_RGB1;
- case SSPP_RGB2: return CID_RGB2;
- case SSPP_DMA0: return CID_DMA0_Y + plane;
- case SSPP_DMA1: return CID_DMA1_Y + plane;
- case SSPP_VIG3: return CID_VIG3_Y + plane;
- case SSPP_RGB3: return CID_RGB3;
- default: return CID_UNUSED;
- }
-}
-
static inline uint32_t mixer2flush(int lm)
{
switch (lm) {
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
index f3daec4..633ca08 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
@@ -63,13 +63,13 @@ static int mdp5_plane_disable(struct drm_plane *plane)
struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
struct mdp5_kms *mdp5_kms = get_kms(plane);
enum mdp5_pipe pipe = mdp5_plane->pipe;
- int i;

DBG("%s: disable", mdp5_plane->name);

- /* update our SMP request to zero (release all our blks): */
- for (i = 0; i < pipe2nclients(pipe); i++)
- mdp5_smp_request(mdp5_kms, pipe2client(pipe, i), 0);
+ if (mdp5_kms) {
+ /* Release the memory we requested earlier from the SMP: */
+ mdp5_smp_release(mdp5_kms->smp_priv, pipe);
+ }

/* TODO detaching now will cause us not to get the last
* vblank and mdp5_smp_commit().. so other planes will
@@ -149,68 +149,6 @@ void mdp5_plane_set_scanout(struct drm_plane *plane,
plane->fb = fb;
}

-/* NOTE: looks like if horizontal decimation is used (if we supported that)
- * then the width used to calculate SMP block requirements is the post-
- * decimated width. Ie. SMP buffering sits downstream of decimation (which
- * presumably happens during the dma from scanout buffer).
- */
-static int request_smp_blocks(struct drm_plane *plane, uint32_t format,
- uint32_t nplanes, uint32_t width)
-{
- struct drm_device *dev = plane->dev;
- struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
- struct mdp5_kms *mdp5_kms = get_kms(plane);
- enum mdp5_pipe pipe = mdp5_plane->pipe;
- int i, hsub, nlines, nblks, ret;
-
- hsub = drm_format_horz_chroma_subsampling(format);
-
- /* different if BWC (compressed framebuffer?) enabled: */
- nlines = 2;
-
- for (i = 0, nblks = 0; i < nplanes; i++) {
- int n, fetch_stride, cpp;
-
- cpp = drm_format_plane_cpp(format, i);
- fetch_stride = width * cpp / (i ? hsub : 1);
-
- n = DIV_ROUND_UP(fetch_stride * nlines, SMP_BLK_SIZE);
-
- /* for hw rev v1.00 */
- if (mdp5_kms->rev == 0)
- n = roundup_pow_of_two(n);
-
- DBG("%s[%d]: request %d SMP blocks", mdp5_plane->name, i, n);
- ret = mdp5_smp_request(mdp5_kms, pipe2client(pipe, i), n);
- if (ret) {
- dev_err(dev->dev, "Could not allocate %d SMP blocks: %d\n",
- n, ret);
- return ret;
- }
-
- nblks += n;
- }
-
- /* in success case, return total # of blocks allocated: */
- return nblks;
-}
-
-static void set_fifo_thresholds(struct drm_plane *plane, int nblks)
-{
- struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
- struct mdp5_kms *mdp5_kms = get_kms(plane);
- enum mdp5_pipe pipe = mdp5_plane->pipe;
- uint32_t val;
-
- /* 1/4 of SMP pool that is being fetched */
- val = (nblks * SMP_ENTRIES_PER_BLK) / 4;
-
- mdp5_write(mdp5_kms, REG_MDP5_PIPE_REQPRIO_FIFO_WM_0(pipe), val * 1);
- mdp5_write(mdp5_kms, REG_MDP5_PIPE_REQPRIO_FIFO_WM_1(pipe), val * 2);
- mdp5_write(mdp5_kms, REG_MDP5_PIPE_REQPRIO_FIFO_WM_2(pipe), val * 3);
-
-}
-
int mdp5_plane_mode_set(struct drm_plane *plane,
struct drm_crtc *crtc, struct drm_framebuffer *fb,
int crtc_x, int crtc_y,
@@ -225,7 +163,7 @@ int mdp5_plane_mode_set(struct drm_plane *plane,
uint32_t nplanes, config = 0;
uint32_t phasex_step = 0, phasey_step = 0;
uint32_t hdecm = 0, vdecm = 0;
- int i, nblks;
+ int ret;

nplanes = drm_format_num_planes(fb->pixel_format);

@@ -243,12 +181,11 @@ int mdp5_plane_mode_set(struct drm_plane *plane,
fb->base.id, src_x, src_y, src_w, src_h,
crtc->base.id, crtc_x, crtc_y, crtc_w, crtc_h);

- /*
- * Calculate and request required # of smp blocks:
- */
- nblks = request_smp_blocks(plane, fb->pixel_format, nplanes, src_w);
- if (nblks < 0)
- return nblks;
+ /* Request some memory from the SMP: */
+ ret = mdp5_smp_request(mdp5_kms->smp_priv,
+ mdp5_plane->pipe, fb->pixel_format, src_w);
+ if (ret)
+ return ret;

/*
* Currently we update the hw for allocations/requests immediately,
@@ -256,8 +193,7 @@ int mdp5_plane_mode_set(struct drm_plane *plane,
* would move into atomic->check_plane_state(), while updating the
* hw would remain here:
*/
- for (i = 0; i < pipe2nclients(pipe); i++)
- mdp5_smp_configure(mdp5_kms, pipe2client(pipe, i));
+ mdp5_smp_configure(mdp5_kms->smp_priv, pipe);

if (src_w != crtc_w) {
config |= MDP5_PIPE_SCALE_CONFIG_SCALEX_EN;
@@ -330,8 +266,6 @@ int mdp5_plane_mode_set(struct drm_plane *plane,
MDP5_PIPE_SCALE_CONFIG_SCALEX_MAX_FILTER(SCALE_FILTER_NEAREST) |
MDP5_PIPE_SCALE_CONFIG_SCALEY_MAX_FILTER(SCALE_FILTER_NEAREST));

- set_fifo_thresholds(plane, nblks);
-
/* TODO detach from old crtc (if we had more than one) */
mdp5_crtc_attach(crtc, plane);

@@ -342,10 +276,8 @@ void mdp5_plane_complete_flip(struct drm_plane *plane)
{
struct mdp5_kms *mdp5_kms = get_kms(plane);
enum mdp5_pipe pipe = to_mdp5_plane(plane)->pipe;
- int i;

- for (i = 0; i < pipe2nclients(pipe); i++)
- mdp5_smp_commit(mdp5_kms, pipe2client(pipe, i));
+ mdp5_smp_commit(mdp5_kms->smp_priv, pipe);
}

enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane)
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.c
index 2d0236b..e61e1cf 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.c
@@ -1,4 +1,5 @@
/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
* Copyright (C) 2013 Red Hat
* Author: Rob Clark <[email protected]>
*
@@ -29,8 +30,11 @@
* Based on the size of the attached scanout buffer, a certain # of
* blocks must be allocated to that client out of the shared pool.
*
- * For each block, it can be either free, or pending/in-use by a
- * client. The updates happen in three steps:
+ * In some hw, some blocks are statically allocated for certain pipes
+ * and CANNOT be re-allocated (eg: MMB0 and MMB1 both tied to RGB0).
+ *
+ * For each block that can be dynamically allocated, it can be either
+ * free, or pending/in-use by a client. The updates happen in three steps:
*
* 1) mdp5_smp_request():
* When plane scanout is setup, calculate required number of
@@ -61,21 +65,64 @@
* inuse and pending state of all clients..
*/

-static DEFINE_SPINLOCK(smp_lock);
+struct mdp5_smp {
+ struct drm_device *dev;
+
+ int blk_cnt;
+ int blk_size;
+
+ spinlock_t state_lock;
+ mdp5_smp_state_t state; /* to track smp allocation amongst pipes: */
+
+ struct mdp5_client_smp_state client_state[CID_MAX];
+};

+static inline
+struct mdp5_kms *get_kms(struct mdp5_smp *smp)
+{
+ struct msm_drm_private *priv = smp->dev->dev_private;
+
+ return to_mdp5_kms(to_mdp_kms(priv->kms));
+}
+
+static inline enum mdp5_client_id pipe2client(enum mdp5_pipe pipe, int plane)
+{
+ WARN_ON(plane >= pipe2nclients(pipe));
+ switch (pipe) {
+ case SSPP_VIG0: return CID_VIG0_Y + plane;
+ case SSPP_VIG1: return CID_VIG1_Y + plane;
+ case SSPP_VIG2: return CID_VIG2_Y + plane;
+ case SSPP_RGB0: return CID_RGB0;
+ case SSPP_RGB1: return CID_RGB1;
+ case SSPP_RGB2: return CID_RGB2;
+ case SSPP_DMA0: return CID_DMA0_Y + plane;
+ case SSPP_DMA1: return CID_DMA1_Y + plane;
+ case SSPP_VIG3: return CID_VIG3_Y + plane;
+ case SSPP_RGB3: return CID_RGB3;
+ default: return CID_UNUSED;
+ }
+}

/* step #1: update # of blocks pending for the client: */
-int mdp5_smp_request(struct mdp5_kms *mdp5_kms,
+static int smp_request_block(struct mdp5_smp *smp,
enum mdp5_client_id cid, int nblks)
{
- struct mdp5_client_smp_state *ps = &mdp5_kms->smp_client_state[cid];
- int i, ret, avail, cur_nblks, cnt = mdp5_kms->smp_blk_cnt;
+ struct mdp5_kms *mdp5_kms = get_kms(smp);
+ struct mdp5_client_smp_state *ps = &smp->client_state[cid];
+ int i, ret, avail, cur_nblks, cnt = smp->blk_cnt;
+ int reserved = mdp5_kms->hw_cfg->smp.reserved[cid];
unsigned long flags;

- spin_lock_irqsave(&smp_lock, flags);
+ spin_lock_irqsave(&smp->state_lock, flags);
+
+ nblks -= reserved;
+ if (reserved)
+ DBG("%d MMBs allocated (%d reserved)", nblks, reserved);

- avail = cnt - bitmap_weight(mdp5_kms->smp_state, cnt);
+ avail = cnt - bitmap_weight(smp->state, cnt);
if (nblks > avail) {
+ dev_err(mdp5_kms->dev->dev, "out of blks (req=%d > avail=%d)\n",
+ nblks, avail);
ret = -ENOSPC;
goto fail;
}
@@ -84,9 +131,9 @@ int mdp5_smp_request(struct mdp5_kms *mdp5_kms,
if (nblks > cur_nblks) {
/* grow the existing pending reservation: */
for (i = cur_nblks; i < nblks; i++) {
- int blk = find_first_zero_bit(mdp5_kms->smp_state, cnt);
+ int blk = find_first_zero_bit(smp->state, cnt);
set_bit(blk, ps->pending);
- set_bit(blk, mdp5_kms->smp_state);
+ set_bit(blk, smp->state);
}
} else {
/* shrink the existing pending reservation: */
@@ -98,15 +145,89 @@ int mdp5_smp_request(struct mdp5_kms *mdp5_kms,
}

fail:
- spin_unlock_irqrestore(&smp_lock, flags);
+ spin_unlock_irqrestore(&smp->state_lock, flags);
+ return 0;
+}
+
+static void set_fifo_thresholds(struct mdp5_smp *smp,
+ enum mdp5_pipe pipe, int nblks)
+{
+ struct mdp5_kms *mdp5_kms = get_kms(smp);
+ u32 smp_entries_per_blk = smp->blk_size / (128 / BITS_PER_BYTE);
+ u32 val;
+
+ /* 1/4 of SMP pool that is being fetched */
+ val = (nblks * smp_entries_per_blk) / 4;
+
+ mdp5_write(mdp5_kms, REG_MDP5_PIPE_REQPRIO_FIFO_WM_0(pipe), val * 1);
+ mdp5_write(mdp5_kms, REG_MDP5_PIPE_REQPRIO_FIFO_WM_1(pipe), val * 2);
+ mdp5_write(mdp5_kms, REG_MDP5_PIPE_REQPRIO_FIFO_WM_2(pipe), val * 3);
+}
+
+/*
+ * NOTE: looks like if horizontal decimation is used (if we supported that)
+ * then the width used to calculate SMP block requirements is the post-
+ * decimated width. Ie. SMP buffering sits downstream of decimation (which
+ * presumably happens during the dma from scanout buffer).
+ */
+int mdp5_smp_request(void *handler, enum mdp5_pipe pipe, u32 fmt, u32 width)
+{
+ struct mdp5_smp *smp = handler;
+ struct mdp5_kms *mdp5_kms = get_kms(smp);
+ struct drm_device *dev = mdp5_kms->dev;
+ int i, hsub, nplanes, nlines, nblks, ret;
+
+ nplanes = drm_format_num_planes(fmt);
+ hsub = drm_format_horz_chroma_subsampling(fmt);
+
+ /* different if BWC (compressed framebuffer?) enabled: */
+ nlines = 2;
+
+ for (i = 0, nblks = 0; i < nplanes; i++) {
+ int n, fetch_stride, cpp;
+
+ cpp = drm_format_plane_cpp(fmt, i);
+ fetch_stride = width * cpp / (i ? hsub : 1);
+
+ n = DIV_ROUND_UP(fetch_stride * nlines, smp->blk_size);
+
+ /* for hw rev v1.00 */
+ if (mdp5_kms->rev == 0)
+ n = roundup_pow_of_two(n);
+
+ DBG("%s[%d]: request %d SMP blocks", pipe2name(pipe), i, n);
+ ret = smp_request_block(smp, pipe2client(pipe, i), n);
+ if (ret) {
+ dev_err(dev->dev, "Cannot allocate %d SMP blocks: %d\n",
+ n, ret);
+ return ret;
+ }
+
+ nblks += n;
+ }
+
+ set_fifo_thresholds(smp, pipe, nblks);
+
return 0;
}

-static void update_smp_state(struct mdp5_kms *mdp5_kms,
+/* Release SMP blocks for all clients of the pipe */
+void mdp5_smp_release(void *handler, enum mdp5_pipe pipe)
+{
+ struct mdp5_smp *smp = handler;
+ int i, nblks;
+
+ for (i = 0, nblks = 0; i < pipe2nclients(pipe); i++)
+ smp_request_block(smp, pipe2client(pipe, i), 0);
+ set_fifo_thresholds(smp, pipe, 0);
+}
+
+static void update_smp_state(struct mdp5_smp *smp,
enum mdp5_client_id cid, mdp5_smp_state_t *assigned)
{
- int cnt = mdp5_kms->smp_blk_cnt;
- uint32_t blk, val;
+ struct mdp5_kms *mdp5_kms = get_kms(smp);
+ int cnt = smp->blk_cnt;
+ u32 blk, val;

for_each_set_bit(blk, *assigned, cnt) {
int idx = blk / 3;
@@ -135,39 +256,84 @@ static void update_smp_state(struct mdp5_kms *mdp5_kms,
}

/* step #2: configure hw for union(pending, inuse): */
-void mdp5_smp_configure(struct mdp5_kms *mdp5_kms, enum mdp5_client_id cid)
+void mdp5_smp_configure(void *handler, enum mdp5_pipe pipe)
{
- struct mdp5_client_smp_state *ps = &mdp5_kms->smp_client_state[cid];
- int cnt = mdp5_kms->smp_blk_cnt;
+ struct mdp5_smp *smp = handler;
+ int cnt = smp->blk_cnt;
mdp5_smp_state_t assigned;
+ int i;
+
+ for (i = 0; i < pipe2nclients(pipe); i++) {
+ enum mdp5_client_id cid = pipe2client(pipe, i);
+ struct mdp5_client_smp_state *ps = &smp->client_state[cid];

- bitmap_or(assigned, ps->inuse, ps->pending, cnt);
- update_smp_state(mdp5_kms, cid, &assigned);
+ bitmap_or(assigned, ps->inuse, ps->pending, cnt);
+ update_smp_state(smp, cid, &assigned);
+ }
}

/* step #3: after vblank, copy pending -> inuse: */
-void mdp5_smp_commit(struct mdp5_kms *mdp5_kms, enum mdp5_client_id cid)
+void mdp5_smp_commit(void *handler, enum mdp5_pipe pipe)
{
- struct mdp5_client_smp_state *ps = &mdp5_kms->smp_client_state[cid];
- int cnt = mdp5_kms->smp_blk_cnt;
+ struct mdp5_smp *smp = handler;
+ int cnt = smp->blk_cnt;
mdp5_smp_state_t released;
+ int i;
+
+ for (i = 0; i < pipe2nclients(pipe); i++) {
+ enum mdp5_client_id cid = pipe2client(pipe, i);
+ struct mdp5_client_smp_state *ps = &smp->client_state[cid];
+
+ /*
+ * Figure out if there are any blocks we where previously
+ * using, which can be released and made available to other
+ * clients:
+ */
+ if (bitmap_andnot(released, ps->inuse, ps->pending, cnt)) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&smp->state_lock, flags);
+ /* clear released blocks: */
+ bitmap_andnot(smp->state, smp->state, released, cnt);
+ spin_unlock_irqrestore(&smp->state_lock, flags);
+
+ update_smp_state(smp, CID_UNUSED, &released);
+ }

- /*
- * Figure out if there are any blocks we where previously
- * using, which can be released and made available to other
- * clients:
- */
- if (bitmap_andnot(released, ps->inuse, ps->pending, cnt)) {
- unsigned long flags;
-
- spin_lock_irqsave(&smp_lock, flags);
- /* clear released blocks: */
- bitmap_andnot(mdp5_kms->smp_state, mdp5_kms->smp_state,
- released, cnt);
- spin_unlock_irqrestore(&smp_lock, flags);
-
- update_smp_state(mdp5_kms, CID_UNUSED, &released);
+ bitmap_copy(ps->inuse, ps->pending, cnt);
}
+}
+
+void mdp5_smp_destroy(void *handler)
+{
+ struct mdp5_smp *smp = handler;
+
+ kfree(smp);
+}
+
+void *mdp5_smp_init(struct drm_device *dev, const struct mdp5_smp_block *cfg)
+{
+ struct mdp5_smp *smp = NULL;
+ int ret;
+
+ smp = kzalloc(sizeof(*smp), GFP_KERNEL);
+ if (unlikely(!smp)) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ smp->dev = dev;
+ smp->blk_cnt = cfg->mmb_count;
+ smp->blk_size = cfg->mmb_size;
+
+ /* statically tied MMBs cannot be re-allocated: */
+ bitmap_copy(smp->state, cfg->reserved_state, smp->blk_cnt);
+ spin_lock_init(&smp->state_lock);
+
+ return smp;
+fail:
+ if (smp)
+ mdp5_smp_destroy(smp);

- bitmap_copy(ps->inuse, ps->pending, cnt);
+ return ERR_PTR(ret);
}
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.h
index 0ab739e..3e3c753 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.h
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.h
@@ -1,4 +1,5 @@
/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
* Copyright (C) 2013 Red Hat
* Author: Rob Clark <[email protected]>
*
@@ -20,12 +21,6 @@

#include "msm_drv.h"

-#define MAX_SMP_BLOCKS 22
-#define SMP_BLK_SIZE 4096
-#define SMP_ENTRIES_PER_BLK (SMP_BLK_SIZE / 16)
-
-typedef DECLARE_BITMAP(mdp5_smp_state_t, MAX_SMP_BLOCKS);
-
struct mdp5_client_smp_state {
mdp5_smp_state_t inuse;
mdp5_smp_state_t pending;
@@ -33,9 +28,18 @@ struct mdp5_client_smp_state {

struct mdp5_kms;

-int mdp5_smp_request(struct mdp5_kms *mdp5_kms, enum mdp5_client_id cid, int nblks);
-void mdp5_smp_configure(struct mdp5_kms *mdp5_kms, enum mdp5_client_id cid);
-void mdp5_smp_commit(struct mdp5_kms *mdp5_kms, enum mdp5_client_id cid);
+/*
+ * SMP module prototypes:
+ * mdp5_smp_init() returns a SMP @handler,
+ * which is then used to call the other mdp5_smp_*(handler, ...) functions.
+ */
+
+void *mdp5_smp_init(struct drm_device *dev, const struct mdp5_smp_block *cfg);
+void mdp5_smp_destroy(void *handler);

+int mdp5_smp_request(void *handler, enum mdp5_pipe pipe, u32 fmt, u32 width);
+void mdp5_smp_configure(void *handler, enum mdp5_pipe pipe);
+void mdp5_smp_commit(void *handler, enum mdp5_pipe pipe);
+void mdp5_smp_release(void *handler, enum mdp5_pipe pipe);

#endif /* __MDP5_SMP_H__ */
--
Qualcomm Innovation Center, Inc.

The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project

2014-11-18 17:50:57

by Stephane Viau

[permalink] [raw]
Subject: [PATCH 3/3] drm/msm: add multiple CRTC and overlay support

MDP5 currently support one single CRTC with its private pipe.
This change allows the configuration of multiple CRTCs with
the possibility to attach several public planes to these CRTCs.

Signed-off-by: Stephane Viau <[email protected]>
---
drivers/gpu/drm/msm/Makefile | 1 +
drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h | 1 +
drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 265 +++++++++++++++++------
drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c | 325 ++++++++++++++++++++++++++++
drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h | 121 +++++++++++
drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c | 13 ++
drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c | 48 +++-
drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h | 48 ++--
drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c | 107 +++++++--
9 files changed, 804 insertions(+), 125 deletions(-)
create mode 100644 drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c
create mode 100644 drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h

diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 51045b0..135c0e5 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -25,6 +25,7 @@ msm-y := \
mdp/mdp4/mdp4_kms.o \
mdp/mdp4/mdp4_plane.o \
mdp/mdp5/mdp5_cfg.o \
+ mdp/mdp5/mdp5_ctl.o \
mdp/mdp5/mdp5_crtc.o \
mdp/mdp5/mdp5_encoder.o \
mdp/mdp5/mdp5_irq.o \
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h
index 00c8271..d0c98f9 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h
@@ -24,6 +24,7 @@
*/
extern const struct mdp5_cfg_hw *mdp5_cfg;

+#define MAX_CTL 8
#define MAX_BASES 8
#define MAX_SMP_BLOCKS 44
#define MAX_CLIENTS 32
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
index ebe2e60..fff012a 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
@@ -1,4 +1,5 @@
/*
+ * Copyright (c) 2014 The Linux Foundation. All rights reserved.
* Copyright (C) 2013 Red Hat
* Author: Rob Clark <[email protected]>
*
@@ -22,16 +23,24 @@
#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];
struct drm_plane *plane;
- struct drm_plane *planes[8];
+ struct drm_plane *planes[SSPP_MAX];
+ int plane_count;
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;
@@ -58,6 +67,7 @@ struct mdp5_crtc {
struct mdp_irq err;
};
#define to_mdp5_crtc(x) container_of(x, struct mdp5_crtc, base)
+#define is_private_plane(mdp5_crtc, drm_plane) (drm_plane == (mdp5_crtc)->plane)

static struct mdp5_kms *get_kms(struct drm_crtc *crtc)
{
@@ -73,26 +83,35 @@ 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);
- struct mdp5_kms *mdp5_kms = get_kms(crtc);
- int id = mdp5_crtc->id;
- uint32_t i, flush = 0;
+
+ 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);
+ uint32_t i, flush_mask = 0;

for (i = 0; i < ARRAY_SIZE(mdp5_crtc->planes); i++) {
struct drm_plane *plane = mdp5_crtc->planes[i];
- if (plane) {
- enum mdp5_pipe pipe = mdp5_plane_pipe(plane);
- flush |= pipe2flush(pipe);
- }
+ if (plane)
+ flush_mask |= mdp5_plane_get_flush(plane);
}
- flush |= mixer2flush(mdp5_crtc->id);
- flush |= MDP5_CTL_FLUSH_CTL;
+ flush_mask |= mdp5_ctl_get_flush(mdp5_crtc->ctl);
+ flush_mask |= mdp5_lm_get_flush(mdp5_crtc->lm);

- 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)
@@ -120,12 +139,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);
@@ -178,6 +191,7 @@ static void pageflip_cb(struct msm_fence_cb *cb)
drm_framebuffer_reference(fb);
mdp5_plane_set_scanout(mdp5_crtc->plane, fb);
update_scanout(crtc, fb);
+ crtc_flush_all(crtc);
}

static void unref_fb_worker(struct drm_flip_work *work, void *val)
@@ -228,41 +242,66 @@ 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;
+ const struct mdp5_cfg_hw *hw_cfg;
+ uint32_t i, 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);
+
+ for (i = 0; i < ARRAY_SIZE(mdp5_crtc->planes); i++) {
+ struct drm_plane *plane = mdp5_crtc->planes[i];
+ struct mdp5_overlay_info *overlay;
+
+ if (!plane)
+ continue;
+
+ 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);
+
+ spin_unlock_irqrestore(&mdp5_crtc->lm_lock, flags);
}

static int mdp5_crtc_mode_set(struct drm_crtc *crtc,
@@ -273,6 +312,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;
@@ -286,6 +326,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);

@@ -300,12 +347,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;
}
@@ -322,7 +372,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));
}
@@ -349,6 +399,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;
}
@@ -357,6 +408,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,
@@ -405,6 +469,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)
@@ -427,9 +492,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)
@@ -450,10 +514,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);
@@ -463,6 +526,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) {
@@ -487,16 +551,15 @@ 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);
+ crtc_flush(crtc, flush_mask);
}

static void set_attach(struct drm_crtc *crtc, enum mdp5_pipe pipe_id,
@@ -509,15 +572,72 @@ static void set_attach(struct drm_crtc *crtc, enum mdp5_pipe pipe_id,
if (mdp5_crtc->planes[pipe_id] == plane)
return;

+ /*
+ * This function is called by both mdp5_crtc_attach()/detach()
+ * if plane is NULL, detach happened -> decrement plane_count,
+ * otherwise increment it (attach)
+ */
+ mdp5_crtc->plane_count += (plane ? 1 : -1);
+ DBG("%s: %d planes attached", mdp5_crtc->name, mdp5_crtc->plane_count);
+
mdp5_crtc->planes[pipe_id] = plane;
blend_setup(crtc);
if (mdp5_crtc->enabled && (plane != mdp5_crtc->plane))
- crtc_flush(crtc);
+ 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 = is_private_plane(mdp5_crtc, plane);
+ struct mdp5_overlay_info overlay_info;
+ enum mdp_mixer_stage_id stage = STAGE_BASE;
+ int i, max_nb_planes;
+
+ hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg_priv);
+ max_nb_planes = hw_cfg->lm.nb_stages;
+
+ if (mdp5_crtc->plane_count >= 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 {
+ for (i = 0; i < ARRAY_SIZE(mdp5_crtc->planes); i++) {
+ struct drm_plane *attached_plane = mdp5_crtc->planes[i];
+ 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)
@@ -528,6 +648,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)
@@ -546,6 +676,9 @@ struct drm_crtc *mdp5_crtc_init(struct drm_device *dev,

mdp5_crtc->plane = plane;
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;
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c
new file mode 100644
index 0000000..a6155b7
--- /dev/null
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.c
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 2014 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "mdp5_kms.h"
+#include "mdp5_ctl.h"
+
+/*
+ * CTL - MDP Control Pool Manager
+ *
+ * Controls are shared between all CRTCs.
+ *
+ * They are intended to be used for data path configuration.
+ * The top level register programming describes the complete data path for
+ * a specific data path ID - REG_MDP5_CTL_*(<id>, ...)
+ *
+ * Hardware capabilities determine the number of concurrent data paths
+ *
+ * In certain use cases (high-resolution dual pipe), one single CTL can be
+ * shared across multiple CRTCs.
+ *
+ * Because the number of CTLs can be less than the number of CRTCs,
+ * CTLs are dynamically allocated from a pool of CTLs, only once a CRTC is
+ * requested by the client (in mdp5_crtc_mode_set()).
+ */
+
+struct mdp5_ctl {
+ u32 id;
+
+ /* whether this CTL has been allocated or not: */
+ bool busy;
+
+ /* memory output connection (@see mdp5_ctl_mode): */
+ u32 mode;
+
+ /* REG_MDP5_CTL_*(<id>) registers access info + lock: */
+ spinlock_t hw_lock;
+ u32 reg_offset;
+
+ /* flush mask used to commit CTL registers */
+ u32 flush_mask;
+
+ bool cursor_on;
+ void *crtc;
+};
+
+struct mdp5_ctl_manager {
+ struct drm_device *dev;
+
+ /* number of CTL / Layer Mixers in this hw config: */
+ u32 nlm;
+ u32 nctl;
+
+ /* pool of CTLs + lock to protect resource allocation (ctls[i].busy) */
+ spinlock_t pool_lock;
+ struct mdp5_ctl ctls[MAX_CTL];
+};
+
+static struct mdp5_ctl_manager mdp5_ctl_mgr;
+
+static inline
+struct mdp5_kms *get_kms(struct mdp5_ctl_manager *ctl_mgr)
+{
+ struct msm_drm_private *priv = ctl_mgr->dev->dev_private;
+
+ return to_mdp5_kms(to_mdp_kms(priv->kms));
+}
+
+static inline
+void ctl_write(struct mdp5_ctl *ctl, u32 reg, u32 data)
+{
+ struct mdp5_ctl_manager *ctl_mgr = &mdp5_ctl_mgr;
+ struct mdp5_kms *mdp5_kms = get_kms(ctl_mgr);
+
+ (void)ctl->reg_offset; /* TODO use this instead of mdp5_write */
+ mdp5_write(mdp5_kms, reg, data);
+}
+
+static inline
+u32 ctl_read(struct mdp5_ctl *ctl, u32 reg)
+{
+ struct mdp5_ctl_manager *ctl_mgr = &mdp5_ctl_mgr;
+ struct mdp5_kms *mdp5_kms = get_kms(ctl_mgr);
+
+ (void)ctl->reg_offset; /* TODO use this instead of mdp5_write */
+ return mdp5_read(mdp5_kms, reg);
+}
+
+
+int mdp5_ctl_set_intf(void *c, enum mdp5_intf intf)
+{
+ struct mdp5_ctl *ctl = c;
+ unsigned long flags;
+ static const enum mdp5_intfnum intfnum[] = {
+ INTF0, INTF1, INTF2, INTF3,
+ };
+
+ spin_lock_irqsave(&ctl->hw_lock, flags);
+ ctl_write(ctl, REG_MDP5_CTL_OP(ctl->id),
+ MDP5_CTL_OP_MODE(ctl->mode) |
+ MDP5_CTL_OP_INTF_NUM(intfnum[intf]));
+ spin_unlock_irqrestore(&ctl->hw_lock, flags);
+
+ return 0;
+}
+
+int mdp5_ctl_set_cursor(void *c, bool enable)
+{
+ struct mdp5_ctl_manager *ctl_mgr = &mdp5_ctl_mgr;
+ struct mdp5_ctl *ctl = c;
+ unsigned long flags;
+ u32 blend_cfg;
+ int lm;
+
+ lm = mdp5_crtc_get_lm(ctl->crtc);
+ if (unlikely(WARN_ON(lm < 0))) {
+ dev_err(ctl_mgr->dev->dev, "CTL %d cannot find LM: %d",
+ ctl->id, lm);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&ctl->hw_lock, flags);
+
+ blend_cfg = ctl_read(ctl, REG_MDP5_CTL_LAYER_REG(ctl->id, lm));
+
+ if (enable)
+ blend_cfg |= MDP5_CTL_LAYER_REG_CURSOR_OUT;
+ else
+ blend_cfg &= ~MDP5_CTL_LAYER_REG_CURSOR_OUT;
+
+ ctl_write(ctl, REG_MDP5_CTL_LAYER_REG(ctl->id, lm), blend_cfg);
+
+ spin_unlock_irqrestore(&ctl->hw_lock, flags);
+
+ ctl->cursor_on = enable;
+
+ return 0;
+}
+
+
+int mdp5_ctl_blend(void *c, u32 lm, u32 blend_cfg)
+{
+ struct mdp5_ctl *ctl = c;
+ unsigned long flags;
+
+ if (ctl->cursor_on)
+ blend_cfg |= MDP5_CTL_LAYER_REG_CURSOR_OUT;
+ else
+ blend_cfg &= ~MDP5_CTL_LAYER_REG_CURSOR_OUT;
+
+ spin_lock_irqsave(&ctl->hw_lock, flags);
+ ctl_write(ctl, REG_MDP5_CTL_LAYER_REG(ctl->id, lm), blend_cfg);
+ spin_unlock_irqrestore(&ctl->hw_lock, flags);
+
+ return 0;
+}
+
+int mdp5_ctl_commit(void *c, u32 flush_mask)
+{
+ struct mdp5_ctl_manager *ctl_mgr = &mdp5_ctl_mgr;
+ struct mdp5_ctl *ctl = c;
+ unsigned long flags;
+
+ if (flush_mask & MDP5_CTL_FLUSH_CURSOR_DUMMY) {
+ int lm = mdp5_crtc_get_lm(ctl->crtc);
+
+ if (unlikely(WARN_ON(lm < 0))) {
+ dev_err(ctl_mgr->dev->dev, "CTL %d cannot find LM: %d",
+ ctl->id, lm);
+ return -EINVAL;
+ }
+
+ /* for current targets, cursor bit is the same as LM bit */
+ flush_mask |= mdp_ctl_flush_mask_lm(lm);
+ }
+
+ spin_lock_irqsave(&ctl->hw_lock, flags);
+ ctl_write(ctl, REG_MDP5_CTL_FLUSH(ctl->id), flush_mask);
+ spin_unlock_irqrestore(&ctl->hw_lock, flags);
+
+ return 0;
+}
+
+u32 mdp5_ctl_get_flush(void *c)
+{
+ struct mdp5_ctl *ctl = c;
+
+ return ctl->flush_mask;
+}
+
+void mdp5_ctl_release(void *c)
+{
+ struct mdp5_ctl_manager *ctl_mgr = &mdp5_ctl_mgr;
+ struct mdp5_ctl *ctl = c;
+ unsigned long flags;
+
+ if (unlikely(WARN_ON(ctl->id >= MAX_CTL) || !ctl->busy)) {
+ dev_err(ctl_mgr->dev->dev, "CTL %d in bad state (%d)",
+ ctl->id, ctl->busy);
+ return;
+ }
+
+ spin_lock_irqsave(&ctl_mgr->pool_lock, flags);
+ ctl->busy = false;
+ spin_unlock_irqrestore(&ctl_mgr->pool_lock, flags);
+
+ DBG("CTL %d released", ctl->id);
+}
+
+/*
+ * mdp5_ctl_request() - CTL dynamic allocation
+ *
+ * Note: Current implementation considers that we can only have one CRTC per CTL
+ *
+ * @return first free CTL
+ */
+void *mdp5_ctl_request(void *ctlm, void *crtc)
+{
+ struct mdp5_ctl_manager *ctl_mgr = ctlm;
+ struct mdp5_ctl *ctl = NULL;
+ unsigned long flags;
+ int c;
+
+ spin_lock_irqsave(&ctl_mgr->pool_lock, flags);
+
+ for (c = 0; c < ctl_mgr->nctl; c++)
+ if (!ctl_mgr->ctls[c].busy)
+ break;
+
+ if (unlikely(c >= ctl_mgr->nctl)) {
+ dev_err(ctl_mgr->dev->dev, "No more CTL available!");
+ goto unlock;
+ }
+
+ ctl = &ctl_mgr->ctls[c];
+
+ ctl->crtc = crtc;
+ ctl->busy = true;
+ DBG("CTL %d allocated", ctl->id);
+
+unlock:
+ spin_unlock_irqrestore(&ctl_mgr->pool_lock, flags);
+ return ctl;
+}
+
+void mdp5_ctlm_hw_reset(void *ctlm)
+{
+ struct mdp5_ctl_manager *ctl_mgr = ctlm;
+ unsigned long flags;
+ int c;
+
+ for (c = 0; c < ctl_mgr->nctl; c++) {
+ struct mdp5_ctl *ctl = &ctl_mgr->ctls[c];
+
+ spin_lock_irqsave(&ctl->hw_lock, flags);
+ ctl_write(ctl, REG_MDP5_CTL_OP(ctl->id), 0);
+ spin_unlock_irqrestore(&ctl->hw_lock, flags);
+ }
+}
+
+void mdp5_ctlm_destroy(void *ctlm)
+{
+ struct mdp5_ctl_manager *ctl_mgr = ctlm;
+
+ kfree(ctl_mgr);
+}
+
+void *mdp5_ctlm_init(struct drm_device *dev, void __iomem *mmio_base,
+ const struct mdp5_cfg_hw *hw_cfg)
+{
+ struct mdp5_ctl_manager *ctl_mgr = &mdp5_ctl_mgr;
+ const struct mdp5_sub_block *ctl_cfg = &hw_cfg->ctl;
+ unsigned long flags;
+ int c, ret;
+
+ if (unlikely(WARN_ON(ctl_cfg->count > MAX_CTL))) {
+ dev_err(dev->dev, "Increase static pool size to at least %d\n",
+ ctl_cfg->count);
+ ret = -ENOSPC;
+ goto fail;
+ }
+
+ /* initialize the CTL manager: */
+ ctl_mgr->dev = dev;
+ ctl_mgr->nlm = hw_cfg->lm.count;
+ ctl_mgr->nctl = ctl_cfg->count;
+ spin_lock_init(&ctl_mgr->pool_lock);
+
+ /* initialize each CTL of the pool: */
+ spin_lock_irqsave(&ctl_mgr->pool_lock, flags);
+ for (c = 0; c < ctl_mgr->nctl; c++) {
+ struct mdp5_ctl *ctl = &ctl_mgr->ctls[c];
+
+ if (WARN_ON(!ctl_cfg->base[c])) {
+ dev_err(dev->dev, "CTL_%d: base is null!\n", c);
+ ret = -EINVAL;
+ goto fail;
+ }
+ ctl->id = c;
+ ctl->mode = MODE_NONE;
+ ctl->reg_offset = ctl_cfg->base[c];
+ ctl->flush_mask = MDP5_CTL_FLUSH_CTL;
+ ctl->busy = false;
+ spin_lock_init(&ctl->hw_lock);
+ }
+ spin_unlock_irqrestore(&ctl_mgr->pool_lock, flags);
+ DBG("Pool of %d CTLs created.", ctl_mgr->nctl);
+
+ return ctl_mgr;
+
+fail:
+ if (ctl_mgr)
+ mdp5_ctlm_destroy(ctl_mgr);
+
+ return ERR_PTR(ret);
+}
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h
new file mode 100644
index 0000000..dbe1cae
--- /dev/null
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_ctl.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2014 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MDP5_CTL_H__
+#define __MDP5_CTL_H__
+
+#include "msm_drv.h"
+
+/*
+ * CTL Manager prototypes:
+ * mdp5_ctlm_init() returns a ctlm (CTL Manager) handler,
+ * which is then used to call the other mdp5_ctlm_*(ctlm, ...) functions.
+ */
+void *mdp5_ctlm_init(struct drm_device *dev, void __iomem *mmio_base,
+ const struct mdp5_cfg_hw *hw_cfg);
+void mdp5_ctlm_hw_reset(void *ctlm);
+void mdp5_ctlm_destroy(void *ctlm);
+
+/*
+ * CTL prototypes:
+ * mdp5_ctl_request(ctlm, ...) returns a ctl (CTL resource) handler,
+ * which is then used to call the other mdp5_ctl_*(ctl, ...) functions.
+ */
+void *mdp5_ctl_request(void *ctlm, void *crtc);
+
+int mdp5_ctl_set_intf(void *ctl, enum mdp5_intf intf);
+
+int mdp5_ctl_set_cursor(void *ctl, bool enable);
+
+/* @blend_cfg: see LM blender config definition below */
+int mdp5_ctl_blend(void *ctl, u32 lm, u32 blend_cfg);
+
+/* @flush_mask: see CTL flush masks definitions below */
+int mdp5_ctl_commit(void *ctl, u32 flush_mask);
+u32 mdp5_ctl_get_flush(void *ctl);
+
+void mdp5_ctl_release(void *ctl);
+
+/*
+ * blend_cfg (LM blender config):
+ *
+ * The function below allows the caller of mdp5_ctl_blend() to specify how pipes
+ * are being blended according to their stage (z-order), through @blend_cfg arg.
+ */
+static inline u32 mdp_ctl_blend_mask(enum mdp5_pipe pipe,
+ enum mdp_mixer_stage_id stage)
+{
+ switch (pipe) {
+ case SSPP_VIG0: return MDP5_CTL_LAYER_REG_VIG0(stage);
+ case SSPP_VIG1: return MDP5_CTL_LAYER_REG_VIG1(stage);
+ case SSPP_VIG2: return MDP5_CTL_LAYER_REG_VIG2(stage);
+ case SSPP_RGB0: return MDP5_CTL_LAYER_REG_RGB0(stage);
+ case SSPP_RGB1: return MDP5_CTL_LAYER_REG_RGB1(stage);
+ case SSPP_RGB2: return MDP5_CTL_LAYER_REG_RGB2(stage);
+ case SSPP_DMA0: return MDP5_CTL_LAYER_REG_DMA0(stage);
+ case SSPP_DMA1: return MDP5_CTL_LAYER_REG_DMA1(stage);
+ case SSPP_VIG3: return MDP5_CTL_LAYER_REG_VIG3(stage);
+ case SSPP_RGB3: return MDP5_CTL_LAYER_REG_RGB3(stage);
+ default: return 0;
+ }
+}
+
+/*
+ * flush_mask (CTL flush masks):
+ *
+ * The following functions allow each DRM entity to get and store
+ * their own flush mask.
+ * Once stored, these masks will then be accessed through each DRM's
+ * interface and used by the caller of mdp5_ctl_commit() to specify
+ * which block(s) need to be flushed through @flush_mask parameter.
+ */
+
+#define MDP5_CTL_FLUSH_CURSOR_DUMMY 0x80000000
+
+static inline u32 mdp_ctl_flush_mask_cursor(int cursor_id)
+{
+ /* TODO: use id once multiple cursor support is present */
+ (void)cursor_id;
+
+ return MDP5_CTL_FLUSH_CURSOR_DUMMY;
+}
+
+static inline u32 mdp_ctl_flush_mask_lm(int lm)
+{
+ switch (lm) {
+ case 0: return MDP5_CTL_FLUSH_LM0;
+ case 1: return MDP5_CTL_FLUSH_LM1;
+ case 2: return MDP5_CTL_FLUSH_LM2;
+ case 5: return MDP5_CTL_FLUSH_LM5;
+ default: return 0;
+ }
+}
+
+static inline u32 mdp_ctl_flush_mask_pipe(enum mdp5_pipe pipe)
+{
+ switch (pipe) {
+ case SSPP_VIG0: return MDP5_CTL_FLUSH_VIG0;
+ case SSPP_VIG1: return MDP5_CTL_FLUSH_VIG1;
+ case SSPP_VIG2: return MDP5_CTL_FLUSH_VIG2;
+ case SSPP_RGB0: return MDP5_CTL_FLUSH_RGB0;
+ case SSPP_RGB1: return MDP5_CTL_FLUSH_RGB1;
+ case SSPP_RGB2: return MDP5_CTL_FLUSH_RGB2;
+ case SSPP_DMA0: return MDP5_CTL_FLUSH_DMA0;
+ case SSPP_DMA1: return MDP5_CTL_FLUSH_DMA1;
+ case SSPP_VIG3: return MDP5_CTL_FLUSH_VIG3;
+ case SSPP_RGB3: return MDP5_CTL_FLUSH_RGB3;
+ default: return 0;
+ }
+}
+
+#endif /* __MDP5_CTL_H__ */
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c
index edec7bf..25c2fcb 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_encoder.c
@@ -24,6 +24,7 @@ struct mdp5_encoder {
struct drm_encoder base;
int intf;
enum mdp5_intf intf_id;
+ spinlock_t intf_lock; /* protect REG_MDP5_INTF_* registers */
bool enabled;
uint32_t bsc;
};
@@ -115,6 +116,7 @@ static void mdp5_encoder_dpms(struct drm_encoder *encoder, int mode)
struct mdp5_kms *mdp5_kms = get_kms(encoder);
int intf = mdp5_encoder->intf;
bool enabled = (mode == DRM_MODE_DPMS_ON);
+ unsigned long flags;

DBG("mode=%d", mode);

@@ -123,9 +125,13 @@ static void mdp5_encoder_dpms(struct drm_encoder *encoder, int mode)

if (enabled) {
bs_set(mdp5_encoder, 1);
+ spin_lock_irqsave(&mdp5_encoder->intf_lock, flags);
mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(intf), 1);
+ spin_unlock_irqrestore(&mdp5_encoder->intf_lock, flags);
} else {
+ spin_lock_irqsave(&mdp5_encoder->intf_lock, flags);
mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(intf), 0);
+ spin_unlock_irqrestore(&mdp5_encoder->intf_lock, flags);
bs_set(mdp5_encoder, 0);
}

@@ -150,6 +156,7 @@ static void mdp5_encoder_mode_set(struct drm_encoder *encoder,
uint32_t display_v_start, display_v_end;
uint32_t hsync_start_x, hsync_end_x;
uint32_t format;
+ unsigned long flags;

mode = adjusted_mode;

@@ -180,6 +187,8 @@ static void mdp5_encoder_mode_set(struct drm_encoder *encoder,
display_v_start = (mode->vtotal - mode->vsync_start) * mode->htotal + dtv_hsync_skew;
display_v_end = vsync_period - ((mode->vsync_start - mode->vdisplay) * mode->htotal) + dtv_hsync_skew - 1;

+ spin_lock_irqsave(&mdp5_encoder->intf_lock, flags);
+
mdp5_write(mdp5_kms, REG_MDP5_INTF_HSYNC_CTL(intf),
MDP5_INTF_HSYNC_CTL_PULSEW(mode->hsync_end - mode->hsync_start) |
MDP5_INTF_HSYNC_CTL_PERIOD(mode->htotal));
@@ -201,6 +210,8 @@ static void mdp5_encoder_mode_set(struct drm_encoder *encoder,
mdp5_write(mdp5_kms, REG_MDP5_INTF_ACTIVE_VEND_F0(intf), 0);
mdp5_write(mdp5_kms, REG_MDP5_INTF_PANEL_FORMAT(intf), format);
mdp5_write(mdp5_kms, REG_MDP5_INTF_FRAME_LINE_COUNT_EN(intf), 0x3); /* frame+line? */
+
+ spin_unlock_irqrestore(&mdp5_encoder->intf_lock, flags);
}

static void mdp5_encoder_prepare(struct drm_encoder *encoder)
@@ -242,6 +253,8 @@ struct drm_encoder *mdp5_encoder_init(struct drm_device *dev, int intf,
mdp5_encoder->intf_id = intf_id;
encoder = &mdp5_encoder->base;

+ spin_lock_init(&mdp5_encoder->intf_lock);
+
drm_encoder_init(dev, encoder, &mdp5_encoder_funcs,
DRM_MODE_ENCODER_TMDS);
drm_encoder_helper_add(encoder, &mdp5_encoder_helper_funcs);
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
index 76bd860..83feb9e 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
@@ -28,9 +28,8 @@ static const char *iommu_ports[] = {
static int mdp5_hw_init(struct msm_kms *kms)
{
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
- const struct mdp5_cfg_hw *hw_cfg;
struct drm_device *dev = mdp5_kms->dev;
- int i;
+ unsigned long flags;

pm_runtime_get_sync(dev->dev);

@@ -58,12 +57,11 @@ static int mdp5_hw_init(struct msm_kms *kms)
* care.
*/

+ spin_lock_irqsave(&mdp5_kms->resource_lock, flags);
mdp5_write(mdp5_kms, REG_MDP5_DISP_INTF_SEL, 0);
+ spin_unlock_irqrestore(&mdp5_kms->resource_lock, flags);

- hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg_priv);
-
- for (i = 0; i < hw_cfg->ctl.count; i++)
- mdp5_write(mdp5_kms, REG_MDP5_CTL_OP(i), 0);
+ mdp5_ctlm_hw_reset(mdp5_kms->ctl_priv);

pm_runtime_put_sync(dev->dev);

@@ -92,11 +90,14 @@ static void mdp5_destroy(struct msm_kms *kms)
struct msm_mmu *mmu = mdp5_kms->mmu;
void *smp = mdp5_kms->smp_priv;
void *cfg = mdp5_kms->cfg_priv;
+ void *ctl = mdp5_kms->ctl_priv;

if (mmu) {
mmu->funcs->detach(mmu, iommu_ports, ARRAY_SIZE(iommu_ports));
mmu->funcs->destroy(mmu);
}
+ if (ctl)
+ mdp5_ctlm_destroy(ctl);
if (smp)
mdp5_smp_destroy(smp);
if (cfg)
@@ -151,6 +152,9 @@ static int modeset_init(struct mdp5_kms *mdp5_kms)
static const enum mdp5_pipe crtcs[] = {
SSPP_RGB0, SSPP_RGB1, SSPP_RGB2, SSPP_RGB3,
};
+ static const enum mdp5_pipe pub_planes[] = {
+ SSPP_VIG0, SSPP_VIG1, SSPP_VIG2, SSPP_VIG3,
+ };
struct drm_device *dev = mdp5_kms->dev;
struct msm_drm_private *priv = dev->dev_private;
struct drm_encoder *encoder;
@@ -159,12 +163,13 @@ static int modeset_init(struct mdp5_kms *mdp5_kms)

hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg_priv);

- /* construct CRTCs: */
+ /* construct CRTCs and their private planes: */
for (i = 0; i < hw_cfg->pipe_rgb.count; i++) {
struct drm_plane *plane;
struct drm_crtc *crtc;

- plane = mdp5_plane_init(dev, crtcs[i], true);
+ plane = mdp5_plane_init(dev, crtcs[i], true,
+ hw_cfg->pipe_rgb.base[i]);
if (IS_ERR(plane)) {
ret = PTR_ERR(plane);
dev_err(dev->dev, "failed to construct plane for %s (%d)\n",
@@ -182,6 +187,20 @@ static int modeset_init(struct mdp5_kms *mdp5_kms)
priv->crtcs[priv->num_crtcs++] = crtc;
}

+ /* Construct public planes: */
+ for (i = 0; i < hw_cfg->pipe_vig.count; i++) {
+ struct drm_plane *plane;
+
+ plane = mdp5_plane_init(dev, pub_planes[i], false,
+ hw_cfg->pipe_vig.base[i]);
+ if (IS_ERR(plane)) {
+ ret = PTR_ERR(plane);
+ dev_err(dev->dev, "failed to construct %s plane: %d\n",
+ pipe2name(pub_planes[i]), ret);
+ goto fail;
+ }
+ }
+
/* NOTE: the vsync and error irq's are actually associated with
* the INTF/encoder.. the easiest way to deal with this (ie. what
* we do now) is assume a fixed relationship between crtc's and
@@ -199,9 +218,7 @@ static int modeset_init(struct mdp5_kms *mdp5_kms)
goto fail;
}

- encoder->possible_crtcs = BIT(0);
- mdp5_crtc_set_intf(priv->crtcs[0], 3, INTF_HDMI);
-
+ encoder->possible_crtcs = (1 << priv->num_crtcs) - 1;
priv->encoders[priv->num_encoders++] = encoder;

ret = hdmi_set_encoder(priv->hdmi_pdev, encoder);
@@ -267,6 +284,8 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev)
goto fail;
}

+ spin_lock_init(&mdp5_kms->resource_lock);
+
mdp_kms_init(&mdp5_kms->base, &kms_funcs);

kms = &mdp5_kms->base.base;
@@ -335,6 +354,13 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev)
}
mdp5_kms->smp_priv = priv;

+ priv = mdp5_ctlm_init(dev, mdp5_kms->mmio, config->hw);
+ if (IS_ERR(priv)) {
+ ret = PTR_ERR(priv);
+ goto fail;
+ }
+ mdp5_kms->ctl_priv = priv;
+
/* make sure things are off before attaching iommu (bootloader could
* have left things on, in which case we'll start getting faults if
* we don't disable):
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
index 7616a3b..5e7cc91 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
@@ -23,6 +23,7 @@
#include "mdp/mdp_kms.h"
#include "mdp5_cfg.h" /* must be included before mdp5.xml.h */
#include "mdp5.xml.h"
+#include "mdp5_ctl.h"
#include "mdp5_smp.h"

struct mdp5_kms {
@@ -37,6 +38,7 @@ struct mdp5_kms {
struct msm_mmu *mmu;

void *smp_priv;
+ void *ctl_priv;

/* io/register spaces: */
void __iomem *mmio, *vbif;
@@ -50,10 +52,20 @@ struct mdp5_kms {
struct clk *lut_clk;
struct clk *vsync_clk;

+ /*
+ * lock to protect access to global resources: ie., following register:
+ * - REG_MDP5_DISP_INTF_SEL
+ */
+ spinlock_t resource_lock;
+
struct mdp_irq error_handler;
};
#define to_mdp5_kms(x) container_of(x, struct mdp5_kms, base)

+struct mdp5_overlay_info {
+ enum mdp_mixer_stage_id zorder;
+};
+
static inline void mdp5_write(struct mdp5_kms *mdp5_kms, u32 reg, u32 data)
{
msm_writel(data, mdp5_kms->mmio + reg);
@@ -77,23 +89,6 @@ static inline const char *pipe2name(enum mdp5_pipe pipe)
return names[pipe];
}

-static inline uint32_t pipe2flush(enum mdp5_pipe pipe)
-{
- switch (pipe) {
- case SSPP_VIG0: return MDP5_CTL_FLUSH_VIG0;
- case SSPP_VIG1: return MDP5_CTL_FLUSH_VIG1;
- case SSPP_VIG2: return MDP5_CTL_FLUSH_VIG2;
- case SSPP_RGB0: return MDP5_CTL_FLUSH_RGB0;
- case SSPP_RGB1: return MDP5_CTL_FLUSH_RGB1;
- case SSPP_RGB2: return MDP5_CTL_FLUSH_RGB2;
- case SSPP_DMA0: return MDP5_CTL_FLUSH_DMA0;
- case SSPP_DMA1: return MDP5_CTL_FLUSH_DMA1;
- case SSPP_VIG3: return MDP5_CTL_FLUSH_VIG3;
- case SSPP_RGB3: return MDP5_CTL_FLUSH_RGB3;
- default: return 0;
- }
-}
-
static inline int pipe2nclients(enum mdp5_pipe pipe)
{
switch (pipe) {
@@ -107,16 +102,6 @@ static inline int pipe2nclients(enum mdp5_pipe pipe)
}
}

-static inline uint32_t mixer2flush(int lm)
-{
- switch (lm) {
- case 0: return MDP5_CTL_FLUSH_LM0;
- case 1: return MDP5_CTL_FLUSH_LM1;
- case 2: return MDP5_CTL_FLUSH_LM2;
- default: return 0;
- }
-}
-
static inline uint32_t intf2err(int intf)
{
switch (intf) {
@@ -162,6 +147,10 @@ uint32_t mdp5_get_formats(enum mdp5_pipe pipe, uint32_t *pixel_formats,

void mdp5_plane_install_properties(struct drm_plane *plane,
struct drm_mode_object *obj);
+void mdp5_plane_set_overlay_info(struct drm_plane *plane,
+ const struct mdp5_overlay_info *overlay_info);
+struct mdp5_overlay_info *mdp5_plane_get_overlay_info(struct drm_plane *plane);
+uint32_t mdp5_plane_get_flush(struct drm_plane *plane);
void mdp5_plane_set_scanout(struct drm_plane *plane,
struct drm_framebuffer *fb);
int mdp5_plane_mode_set(struct drm_plane *plane,
@@ -173,14 +162,15 @@ int mdp5_plane_mode_set(struct drm_plane *plane,
void mdp5_plane_complete_flip(struct drm_plane *plane);
enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane);
struct drm_plane *mdp5_plane_init(struct drm_device *dev,
- enum mdp5_pipe pipe, bool private_plane);
+ enum mdp5_pipe pipe, bool private_plane, uint32_t reg_offset);

uint32_t mdp5_crtc_vblank(struct drm_crtc *crtc);

+int mdp5_crtc_get_lm(struct drm_crtc *crtc);
void mdp5_crtc_cancel_pending_flip(struct drm_crtc *crtc, struct drm_file *file);
void mdp5_crtc_set_intf(struct drm_crtc *crtc, int intf,
enum mdp5_intf intf_id);
-void mdp5_crtc_attach(struct drm_crtc *crtc, struct drm_plane *plane);
+int mdp5_crtc_attach(struct drm_crtc *crtc, struct drm_plane *plane);
void mdp5_crtc_detach(struct drm_crtc *crtc, struct drm_plane *plane);
struct drm_crtc *mdp5_crtc_init(struct drm_device *dev,
struct drm_plane *plane, int id);
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
index 633ca08..59703fa 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_plane.c
@@ -1,4 +1,5 @@
/*
+ * Copyright (c) 2014 The Linux Foundation. All rights reserved.
* Copyright (C) 2013 Red Hat
* Author: Rob Clark <[email protected]>
*
@@ -17,6 +18,7 @@

#include "mdp5_kms.h"

+#define MAX_PLANE 4

struct mdp5_plane {
struct drm_plane base;
@@ -24,6 +26,13 @@ struct mdp5_plane {

enum mdp5_pipe pipe;

+ spinlock_t pipe_lock; /* protect REG_MDP5_PIPE_* registers */
+ uint32_t reg_offset;
+
+ uint32_t flush_mask; /* used to commit pipe registers */
+
+ struct mdp5_overlay_info overlay_info;
+
uint32_t nformats;
uint32_t formats[32];

@@ -95,6 +104,22 @@ static void mdp5_plane_destroy(struct drm_plane *plane)
kfree(mdp5_plane);
}

+void mdp5_plane_set_overlay_info(struct drm_plane *plane,
+ const struct mdp5_overlay_info *overlay_info)
+{
+ struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
+
+ memcpy(&mdp5_plane->overlay_info, overlay_info, sizeof(*overlay_info));
+}
+
+struct mdp5_overlay_info *mdp5_plane_get_overlay_info(
+ struct drm_plane *plane)
+{
+ struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
+
+ return &mdp5_plane->overlay_info;
+}
+
/* helper to install properties which are common to planes and crtcs */
void mdp5_plane_install_properties(struct drm_plane *plane,
struct drm_mode_object *obj)
@@ -116,35 +141,58 @@ static const struct drm_plane_funcs mdp5_plane_funcs = {
.set_property = mdp5_plane_set_property,
};

-void mdp5_plane_set_scanout(struct drm_plane *plane,
- struct drm_framebuffer *fb)
+static int get_fb_addr(struct drm_plane *plane, struct drm_framebuffer *fb,
+ uint32_t iova[MAX_PLANE])
{
- struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
struct mdp5_kms *mdp5_kms = get_kms(plane);
- enum mdp5_pipe pipe = mdp5_plane->pipe;
uint32_t nplanes = drm_format_num_planes(fb->pixel_format);
- uint32_t iova[4];
int i;

for (i = 0; i < nplanes; i++) {
struct drm_gem_object *bo = msm_framebuffer_bo(fb, i);
msm_gem_get_iova(bo, mdp5_kms->id, &iova[i]);
}
- for (; i < 4; i++)
+ for (; i < MAX_PLANE; i++)
iova[i] = 0;

+ return 0;
+}
+
+static void set_scanout_locked(struct drm_plane *plane,
+ uint32_t pitches[MAX_PLANE], uint32_t src_addr[MAX_PLANE])
+{
+ struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
+ struct mdp5_kms *mdp5_kms = get_kms(plane);
+ enum mdp5_pipe pipe = mdp5_plane->pipe;
+
+ WARN_ON(!spin_is_locked(&mdp5_plane->pipe_lock));
+
mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_A(pipe),
- MDP5_PIPE_SRC_STRIDE_A_P0(fb->pitches[0]) |
- MDP5_PIPE_SRC_STRIDE_A_P1(fb->pitches[1]));
+ MDP5_PIPE_SRC_STRIDE_A_P0(pitches[0]) |
+ MDP5_PIPE_SRC_STRIDE_A_P1(pitches[1]));

mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_STRIDE_B(pipe),
- MDP5_PIPE_SRC_STRIDE_B_P2(fb->pitches[2]) |
- MDP5_PIPE_SRC_STRIDE_B_P3(fb->pitches[3]));
+ MDP5_PIPE_SRC_STRIDE_B_P2(pitches[2]) |
+ MDP5_PIPE_SRC_STRIDE_B_P3(pitches[3]));
+
+ mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC0_ADDR(pipe), src_addr[0]);
+ mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC1_ADDR(pipe), src_addr[1]);
+ mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC2_ADDR(pipe), src_addr[2]);
+ mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC3_ADDR(pipe), src_addr[3]);
+}
+
+void mdp5_plane_set_scanout(struct drm_plane *plane,
+ struct drm_framebuffer *fb)
+{
+ struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
+ uint32_t src_addr[MAX_PLANE];
+ unsigned long flags;

- mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC0_ADDR(pipe), iova[0]);
- mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC1_ADDR(pipe), iova[1]);
- mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC2_ADDR(pipe), iova[2]);
- mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC3_ADDR(pipe), iova[3]);
+ get_fb_addr(plane, fb, src_addr);
+
+ spin_lock_irqsave(&mdp5_plane->pipe_lock, flags);
+ set_scanout_locked(plane, fb->pitches, src_addr);
+ spin_unlock_irqrestore(&mdp5_plane->pipe_lock, flags);

plane->fb = fb;
}
@@ -163,6 +211,8 @@ int mdp5_plane_mode_set(struct drm_plane *plane,
uint32_t nplanes, config = 0;
uint32_t phasex_step = 0, phasey_step = 0;
uint32_t hdecm = 0, vdecm = 0;
+ uint32_t src_addr[MAX_PLANE];
+ unsigned long flags;
int ret;

nplanes = drm_format_num_planes(fb->pixel_format);
@@ -205,6 +255,12 @@ int mdp5_plane_mode_set(struct drm_plane *plane,
/* TODO calc phasey_step, vdecm */
}

+ ret = get_fb_addr(plane, fb, src_addr);
+ if (ret)
+ return ret;
+
+ spin_lock_irqsave(&mdp5_plane->pipe_lock, flags);
+
mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_IMG_SIZE(pipe),
MDP5_PIPE_SRC_IMG_SIZE_WIDTH(src_w) |
MDP5_PIPE_SRC_IMG_SIZE_HEIGHT(src_h));
@@ -225,8 +281,6 @@ int mdp5_plane_mode_set(struct drm_plane *plane,
MDP5_PIPE_OUT_XY_X(crtc_x) |
MDP5_PIPE_OUT_XY_Y(crtc_y));

- mdp5_plane_set_scanout(plane, fb);
-
format = to_mdp_format(msm_framebuffer_format(fb));

mdp5_write(mdp5_kms, REG_MDP5_PIPE_SRC_FORMAT(pipe),
@@ -266,10 +320,14 @@ int mdp5_plane_mode_set(struct drm_plane *plane,
MDP5_PIPE_SCALE_CONFIG_SCALEX_MAX_FILTER(SCALE_FILTER_NEAREST) |
MDP5_PIPE_SCALE_CONFIG_SCALEY_MAX_FILTER(SCALE_FILTER_NEAREST));

+ set_scanout_locked(plane, fb->pitches, src_addr);
+
+ spin_unlock_irqrestore(&mdp5_plane->pipe_lock, flags);
+
/* TODO detach from old crtc (if we had more than one) */
- mdp5_crtc_attach(crtc, plane);
+ ret = mdp5_crtc_attach(crtc, plane);

- return 0;
+ return ret;
}

void mdp5_plane_complete_flip(struct drm_plane *plane)
@@ -286,9 +344,16 @@ enum mdp5_pipe mdp5_plane_pipe(struct drm_plane *plane)
return mdp5_plane->pipe;
}

+uint32_t mdp5_plane_get_flush(struct drm_plane *plane)
+{
+ struct mdp5_plane *mdp5_plane = to_mdp5_plane(plane);
+
+ return mdp5_plane->flush_mask;
+}
+
/* initialize plane */
struct drm_plane *mdp5_plane_init(struct drm_device *dev,
- enum mdp5_pipe pipe, bool private_plane)
+ enum mdp5_pipe pipe, bool private_plane, uint32_t reg_offset)
{
struct drm_plane *plane = NULL;
struct mdp5_plane *mdp5_plane;
@@ -309,6 +374,10 @@ struct drm_plane *mdp5_plane_init(struct drm_device *dev,
mdp5_plane->nformats = mdp5_get_formats(pipe, mdp5_plane->formats,
ARRAY_SIZE(mdp5_plane->formats));

+ mdp5_plane->flush_mask = mdp_ctl_flush_mask_pipe(pipe);
+ mdp5_plane->reg_offset = reg_offset;
+ spin_lock_init(&mdp5_plane->pipe_lock);
+
type = private_plane ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY;
drm_universal_plane_init(dev, plane, 0xff, &mdp5_plane_funcs,
mdp5_plane->formats, mdp5_plane->nformats,
--
Qualcomm Innovation Center, Inc.

The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project

2014-11-18 17:51:26

by Stephane Viau

[permalink] [raw]
Subject: [PATCH 2/3] drm/msm/mdp5: introduce mdp5_cfg module

The hardware configuration modification from a version to another
is quite consequent. Introducing a configuration module
(mdp5_cfg) may make things more clear and easier to access when a
new hardware version comes up.

Signed-off-by: Stephane Viau <[email protected]>
---
drivers/gpu/drm/msm/Makefile | 1 +
drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c | 215 ++++++++++++++++++++++++++++++++
drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h | 88 +++++++++++++
drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c | 211 ++++++-------------------------
drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h | 39 +-----
drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.c | 9 +-
6 files changed, 354 insertions(+), 209 deletions(-)
create mode 100644 drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c
create mode 100644 drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h

diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 6283dcb..51045b0 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -24,6 +24,7 @@ msm-y := \
mdp/mdp4/mdp4_irq.o \
mdp/mdp4/mdp4_kms.o \
mdp/mdp4/mdp4_plane.o \
+ mdp/mdp5/mdp5_cfg.o \
mdp/mdp5/mdp5_crtc.o \
mdp/mdp5/mdp5_encoder.o \
mdp/mdp5/mdp5_irq.o \
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c
new file mode 100644
index 0000000..62e77d1
--- /dev/null
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2014 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "mdp5_kms.h"
+#include "mdp5_cfg.h"
+
+struct mdp5_cfg_handler {
+ int revision;
+ struct mdp5_cfg config;
+};
+
+/* mdp5_cfg must be exposed (used in mdp5.xml.h) */
+const struct mdp5_cfg_hw *mdp5_cfg = NULL;
+
+const struct mdp5_cfg_hw msm8x74_config = {
+ .name = "msm8x74",
+ .smp = {
+ .mmb_count = 22,
+ .mmb_size = 4096,
+ },
+ .ctl = {
+ .count = 5,
+ .base = { 0x00600, 0x00700, 0x00800, 0x00900, 0x00a00 },
+ },
+ .pipe_vig = {
+ .count = 3,
+ .base = { 0x01200, 0x01600, 0x01a00 },
+ },
+ .pipe_rgb = {
+ .count = 3,
+ .base = { 0x01e00, 0x02200, 0x02600 },
+ },
+ .pipe_dma = {
+ .count = 2,
+ .base = { 0x02a00, 0x02e00 },
+ },
+ .lm = {
+ .count = 5,
+ .base = { 0x03200, 0x03600, 0x03a00, 0x03e00, 0x04200 },
+ .nb_stages = 5,
+ },
+ .dspp = {
+ .count = 3,
+ .base = { 0x04600, 0x04a00, 0x04e00 },
+ },
+ .ad = {
+ .count = 2,
+ .base = { 0x13100, 0x13300 }, /* NOTE: no ad in v1.0 */
+ },
+ .intf = {
+ .count = 4,
+ .base = { 0x12500, 0x12700, 0x12900, 0x12b00 },
+ },
+ .max_clk = 200000000,
+};
+
+const struct mdp5_cfg_hw apq8084_config = {
+ .name = "apq8084",
+ .smp = {
+ .mmb_count = 44,
+ .mmb_size = 8192,
+ .reserved_state[0] = GENMASK(7, 0), /* first 8 MMBs */
+ .reserved[CID_RGB0] = 2,
+ .reserved[CID_RGB1] = 2,
+ .reserved[CID_RGB2] = 2,
+ .reserved[CID_RGB3] = 2,
+ },
+ .ctl = {
+ .count = 5,
+ .base = { 0x00600, 0x00700, 0x00800, 0x00900, 0x00a00 },
+ },
+ .pipe_vig = {
+ .count = 4,
+ .base = { 0x01200, 0x01600, 0x01a00, 0x01e00 },
+ },
+ .pipe_rgb = {
+ .count = 4,
+ .base = { 0x02200, 0x02600, 0x02a00, 0x02e00 },
+ },
+ .pipe_dma = {
+ .count = 2,
+ .base = { 0x03200, 0x03600 },
+ },
+ .lm = {
+ .count = 6,
+ .base = { 0x03a00, 0x03e00, 0x04200, 0x04600, 0x04a00, 0x04e00 },
+ .nb_stages = 5,
+ },
+ .dspp = {
+ .count = 4,
+ .base = { 0x05200, 0x05600, 0x05a00, 0x05e00 },
+
+ },
+ .ad = {
+ .count = 3,
+ .base = { 0x13500, 0x13700, 0x13900 },
+ },
+ .intf = {
+ .count = 5,
+ .base = { 0x12500, 0x12700, 0x12900, 0x12b00, 0x12d00 },
+ },
+ .max_clk = 320000000,
+};
+
+static const struct mdp5_cfg_handler cfg_handlers[] = {
+ { .revision = 0, .config = { .hw = &msm8x74_config } },
+ { .revision = 2, .config = { .hw = &msm8x74_config } },
+ { .revision = 3, .config = { .hw = &apq8084_config } },
+};
+
+
+static struct mdp5_cfg_platform *mdp5_get_config(struct platform_device *dev);
+
+const struct mdp5_cfg_hw *mdp5_cfg_get_hw_config(void *cfg_hnd)
+{
+ struct mdp5_cfg_handler *cfg_handler = cfg_hnd;
+
+ return cfg_handler->config.hw;
+}
+
+struct mdp5_cfg *mdp5_cfg_get_config(void *cfg_hnd)
+{
+ struct mdp5_cfg_handler *cfg_handler = cfg_hnd;
+
+ return &cfg_handler->config;
+}
+
+int mdp5_cfg_get_hw_rev(void *cfg_hnd)
+{
+ struct mdp5_cfg_handler *cfg_handler = cfg_hnd;
+
+ return cfg_handler->revision;
+}
+
+void mdp5_cfg_destroy(void *cfg_hnd)
+{
+ struct mdp5_cfg_handler *cfg_handler = cfg_hnd;
+
+ kfree(cfg_handler);
+}
+
+void *mdp5_cfg_init(struct mdp5_kms *mdp5_kms,
+ uint32_t major, uint32_t minor)
+{
+ struct drm_device *dev = mdp5_kms->dev;
+ struct platform_device *pdev = dev->platformdev;
+ struct mdp5_cfg_handler *cfg_handler;
+ struct mdp5_cfg_platform *pconfig;
+ int i, ret = 0;
+
+ cfg_handler = kzalloc(sizeof(*cfg_handler), GFP_KERNEL);
+ if (unlikely(!cfg_handler)) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ if (major != 1) {
+ dev_err(dev->dev, "unexpected MDP major version: v%d.%d\n",
+ major, minor);
+ ret = -ENXIO;
+ goto fail;
+ }
+
+ /* only after mdp5_cfg global pointer's init can we access the hw */
+ for (i = 0; i < ARRAY_SIZE(cfg_handlers); i++) {
+ if (cfg_handlers[i].revision != minor)
+ continue;
+ mdp5_cfg = cfg_handlers[i].config.hw;
+
+ break;
+ }
+ if (unlikely(!mdp5_cfg)) {
+ dev_err(dev->dev, "unexpected MDP minor revision: v%d.%d\n",
+ major, minor);
+ ret = -ENXIO;
+ goto fail;
+ }
+
+ cfg_handler->revision = minor;
+ cfg_handler->config.hw = mdp5_cfg;
+
+ pconfig = mdp5_get_config(pdev);
+ memcpy(&cfg_handler->config.platform, pconfig, sizeof(*pconfig));
+
+ DBG("MDP5: %s hw config selected", mdp5_cfg->name);
+
+ return cfg_handler;
+
+fail:
+ if (cfg_handler)
+ mdp5_cfg_destroy(cfg_handler);
+
+ return NULL;
+}
+
+static struct mdp5_cfg_platform *mdp5_get_config(struct platform_device *dev)
+{
+ static struct mdp5_cfg_platform config = {};
+#ifdef CONFIG_OF
+ /* TODO */
+#endif
+ config.iommu = iommu_domain_alloc(&platform_bus_type);
+
+ return &config;
+}
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h
new file mode 100644
index 0000000..00c8271
--- /dev/null
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_cfg.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2014 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MDP5_CFG_H__
+#define __MDP5_CFG_H__
+
+#include "msm_drv.h"
+
+/*
+ * mdp5_cfg
+ *
+ * This module configures the dynamic offsets used by mdp5.xml.h
+ * (initialized in mdp5_cfg.c)
+ */
+extern const struct mdp5_cfg_hw *mdp5_cfg;
+
+#define MAX_BASES 8
+#define MAX_SMP_BLOCKS 44
+#define MAX_CLIENTS 32
+
+typedef DECLARE_BITMAP(mdp5_smp_state_t, MAX_SMP_BLOCKS);
+
+#define MDP5_SUB_BLOCK_DEFINITION \
+ int count; \
+ uint32_t base[MAX_BASES]
+
+struct mdp5_sub_block {
+ MDP5_SUB_BLOCK_DEFINITION;
+};
+
+struct mdp5_lm_block {
+ MDP5_SUB_BLOCK_DEFINITION;
+ uint32_t nb_stages; /* number of stages per blender */
+};
+
+struct mdp5_smp_block {
+ int mmb_count; /* number of SMP MMBs */
+ int mmb_size; /* MMB: size in bytes */
+ mdp5_smp_state_t reserved_state;/* SMP MMBs statically allocated */
+ int reserved[MAX_CLIENTS]; /* # of MMBs allocated per client */
+};
+
+struct mdp5_cfg_hw {
+ char *name;
+
+ struct mdp5_smp_block smp;
+ struct mdp5_sub_block ctl;
+ struct mdp5_sub_block pipe_vig;
+ struct mdp5_sub_block pipe_rgb;
+ struct mdp5_sub_block pipe_dma;
+ struct mdp5_lm_block lm;
+ struct mdp5_sub_block dspp;
+ struct mdp5_sub_block ad;
+ struct mdp5_sub_block intf;
+
+ uint32_t max_clk;
+};
+
+/* platform config data (ie. from DT, or pdata) */
+struct mdp5_cfg_platform {
+ struct iommu_domain *iommu;
+};
+
+struct mdp5_cfg {
+ const struct mdp5_cfg_hw *hw;
+ struct mdp5_cfg_platform platform;
+};
+
+struct mdp5_kms;
+
+const struct mdp5_cfg_hw *mdp5_cfg_get_hw_config(void *cfg_hnd);
+struct mdp5_cfg *mdp5_cfg_get_config(void *cfg_hnd);
+int mdp5_cfg_get_hw_rev(void *cfg_hnd);
+
+void *mdp5_cfg_init(struct mdp5_kms *mdp5_kms, uint32_t major, uint32_t minor);
+void mdp5_cfg_destroy(void *cfg_hnd);
+
+#endif /* __MDP5_CFG_H__ */
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
index 0d6306d..76bd860 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.c
@@ -1,4 +1,5 @@
/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
* Copyright (C) 2013 Red Hat
* Author: Rob Clark <[email protected]>
*
@@ -24,158 +25,10 @@ static const char *iommu_ports[] = {
"mdp_0",
};

-static struct mdp5_platform_config *mdp5_get_config(struct platform_device *dev);
-
-const struct mdp5_config *mdp5_cfg;
-
-static const struct mdp5_config msm8x74_config = {
- .name = "msm8x74",
- .smp = {
- .mmb_count = 22,
- .mmb_size = 4096,
- },
- .ctl = {
- .count = 5,
- .base = { 0x00600, 0x00700, 0x00800, 0x00900, 0x00a00 },
- },
- .pipe_vig = {
- .count = 3,
- .base = { 0x01200, 0x01600, 0x01a00 },
- },
- .pipe_rgb = {
- .count = 3,
- .base = { 0x01e00, 0x02200, 0x02600 },
- },
- .pipe_dma = {
- .count = 2,
- .base = { 0x02a00, 0x02e00 },
- },
- .lm = {
- .count = 5,
- .base = { 0x03200, 0x03600, 0x03a00, 0x03e00, 0x04200 },
- },
- .dspp = {
- .count = 3,
- .base = { 0x04600, 0x04a00, 0x04e00 },
- },
- .ad = {
- .count = 2,
- .base = { 0x13100, 0x13300 }, /* NOTE: no ad in v1.0 */
- },
- .intf = {
- .count = 4,
- .base = { 0x12500, 0x12700, 0x12900, 0x12b00 },
- },
- .max_clk = 200000000,
-};
-
-static const struct mdp5_config apq8084_config = {
- .name = "apq8084",
- .smp = {
- .mmb_count = 44,
- .mmb_size = 8192,
- .reserved_state[0] = GENMASK(7, 0), /* first 8 MMBs */
- .reserved[CID_RGB0] = 2,
- .reserved[CID_RGB1] = 2,
- .reserved[CID_RGB2] = 2,
- .reserved[CID_RGB3] = 2,
- },
- .ctl = {
- .count = 5,
- .base = { 0x00600, 0x00700, 0x00800, 0x00900, 0x00a00 },
- },
- .pipe_vig = {
- .count = 4,
- .base = { 0x01200, 0x01600, 0x01a00, 0x01e00 },
- },
- .pipe_rgb = {
- .count = 4,
- .base = { 0x02200, 0x02600, 0x02a00, 0x02e00 },
- },
- .pipe_dma = {
- .count = 2,
- .base = { 0x03200, 0x03600 },
- },
- .lm = {
- .count = 6,
- .base = { 0x03a00, 0x03e00, 0x04200, 0x04600, 0x04a00, 0x04e00 },
- },
- .dspp = {
- .count = 4,
- .base = { 0x05200, 0x05600, 0x05a00, 0x05e00 },
-
- },
- .ad = {
- .count = 3,
- .base = { 0x13500, 0x13700, 0x13900 },
- },
- .intf = {
- .count = 5,
- .base = { 0x12500, 0x12700, 0x12900, 0x12b00, 0x12d00 },
- },
- .max_clk = 320000000,
-};
-
-struct mdp5_config_entry {
- int revision;
- const struct mdp5_config *config;
-};
-
-static const struct mdp5_config_entry mdp5_configs[] = {
- { .revision = 0, .config = &msm8x74_config },
- { .revision = 2, .config = &msm8x74_config },
- { .revision = 3, .config = &apq8084_config },
-};
-
-static int mdp5_select_hw_cfg(struct msm_kms *kms)
-{
- struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
- struct drm_device *dev = mdp5_kms->dev;
- uint32_t version, major, minor;
- int i, ret = 0;
-
- mdp5_enable(mdp5_kms);
- version = mdp5_read(mdp5_kms, REG_MDP5_MDP_VERSION);
- mdp5_disable(mdp5_kms);
-
- major = FIELD(version, MDP5_MDP_VERSION_MAJOR);
- minor = FIELD(version, MDP5_MDP_VERSION_MINOR);
-
- DBG("found MDP5 version v%d.%d", major, minor);
-
- if (major != 1) {
- dev_err(dev->dev, "unexpected MDP major version: v%d.%d\n",
- major, minor);
- ret = -ENXIO;
- goto out;
- }
-
- mdp5_kms->rev = minor;
-
- /* only after mdp5_cfg global pointer's init can we access the hw */
- for (i = 0; i < ARRAY_SIZE(mdp5_configs); i++) {
- if (mdp5_configs[i].revision != minor)
- continue;
- mdp5_kms->hw_cfg = mdp5_cfg = mdp5_configs[i].config;
- break;
- }
- if (unlikely(!mdp5_kms->hw_cfg)) {
- dev_err(dev->dev, "unexpected MDP minor revision: v%d.%d\n",
- major, minor);
- ret = -ENXIO;
- goto out;
- }
-
- DBG("MDP5: %s config selected", mdp5_kms->hw_cfg->name);
-
- return 0;
-out:
- return ret;
-}
-
static int mdp5_hw_init(struct msm_kms *kms)
{
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
+ const struct mdp5_cfg_hw *hw_cfg;
struct drm_device *dev = mdp5_kms->dev;
int i;

@@ -207,7 +60,9 @@ static int mdp5_hw_init(struct msm_kms *kms)

mdp5_write(mdp5_kms, REG_MDP5_DISP_INTF_SEL, 0);

- for (i = 0; i < mdp5_kms->hw_cfg->ctl.count; i++)
+ hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg_priv);
+
+ for (i = 0; i < hw_cfg->ctl.count; i++)
mdp5_write(mdp5_kms, REG_MDP5_CTL_OP(i), 0);

pm_runtime_put_sync(dev->dev);
@@ -236,6 +91,7 @@ static void mdp5_destroy(struct msm_kms *kms)
struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
struct msm_mmu *mmu = mdp5_kms->mmu;
void *smp = mdp5_kms->smp_priv;
+ void *cfg = mdp5_kms->cfg_priv;

if (mmu) {
mmu->funcs->detach(mmu, iommu_ports, ARRAY_SIZE(iommu_ports));
@@ -243,6 +99,8 @@ static void mdp5_destroy(struct msm_kms *kms)
}
if (smp)
mdp5_smp_destroy(smp);
+ if (cfg)
+ mdp5_cfg_destroy(cfg);

kfree(mdp5_kms);
}
@@ -296,10 +154,13 @@ static int modeset_init(struct mdp5_kms *mdp5_kms)
struct drm_device *dev = mdp5_kms->dev;
struct msm_drm_private *priv = dev->dev_private;
struct drm_encoder *encoder;
+ const struct mdp5_cfg_hw *hw_cfg;
int i, ret;

+ hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg_priv);
+
/* construct CRTCs: */
- for (i = 0; i < mdp5_kms->hw_cfg->pipe_rgb.count; i++) {
+ for (i = 0; i < hw_cfg->pipe_rgb.count; i++) {
struct drm_plane *plane;
struct drm_crtc *crtc;

@@ -360,6 +221,21 @@ fail:
return ret;
}

+static void read_hw_revision(struct mdp5_kms *mdp5_kms,
+ uint32_t *major, uint32_t *minor)
+{
+ uint32_t version;
+
+ mdp5_enable(mdp5_kms);
+ version = mdp5_read(mdp5_kms, REG_MDP5_MDP_VERSION);
+ mdp5_disable(mdp5_kms);
+
+ *major = FIELD(version, MDP5_MDP_VERSION_MAJOR);
+ *minor = FIELD(version, MDP5_MDP_VERSION_MINOR);
+
+ DBG("MDP5 version v%d.%d", *major, *minor);
+}
+
static int get_clk(struct platform_device *pdev, struct clk **clkp,
const char *name)
{
@@ -376,10 +252,11 @@ static int get_clk(struct platform_device *pdev, struct clk **clkp,
struct msm_kms *mdp5_kms_init(struct drm_device *dev)
{
struct platform_device *pdev = dev->platformdev;
- struct mdp5_platform_config *config = mdp5_get_config(pdev);
+ struct mdp5_cfg *config;
struct mdp5_kms *mdp5_kms;
struct msm_kms *kms = NULL;
struct msm_mmu *mmu;
+ uint32_t major, minor;
void *priv;
int i, ret;

@@ -439,14 +316,19 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev)
if (ret)
goto fail;

- ret = mdp5_select_hw_cfg(kms);
- if (ret)
+ read_hw_revision(mdp5_kms, &major, &minor);
+ priv = mdp5_cfg_init(mdp5_kms, major, minor);
+ if (IS_ERR(priv)) {
+ ret = PTR_ERR(priv);
goto fail;
+ }
+ mdp5_kms->cfg_priv = priv;
+ config = mdp5_cfg_get_config(mdp5_kms->cfg_priv);

/* TODO: compute core clock rate at runtime */
- clk_set_rate(mdp5_kms->src_clk, mdp5_kms->hw_cfg->max_clk);
+ clk_set_rate(mdp5_kms->src_clk, config->hw->max_clk);

- priv = mdp5_smp_init(mdp5_kms->dev, &mdp5_kms->hw_cfg->smp);
+ priv = mdp5_smp_init(mdp5_kms->dev, &config->hw->smp);
if (IS_ERR(priv)) {
ret = PTR_ERR(priv);
goto fail;
@@ -458,13 +340,13 @@ struct msm_kms *mdp5_kms_init(struct drm_device *dev)
* we don't disable):
*/
mdp5_enable(mdp5_kms);
- for (i = 0; i < mdp5_kms->hw_cfg->intf.count; i++)
+ for (i = 0; i < config->hw->intf.count; i++)
mdp5_write(mdp5_kms, REG_MDP5_INTF_TIMING_ENGINE_EN(i), 0);
mdp5_disable(mdp5_kms);
mdelay(16);

- if (config->iommu) {
- mmu = msm_iommu_new(&pdev->dev, config->iommu);
+ if (config->platform.iommu) {
+ mmu = msm_iommu_new(&pdev->dev, config->platform.iommu);
if (IS_ERR(mmu)) {
ret = PTR_ERR(mmu);
dev_err(dev->dev, "failed to init iommu: %d\n", ret);
@@ -505,14 +387,3 @@ fail:
mdp5_destroy(kms);
return ERR_PTR(ret);
}
-
-static struct mdp5_platform_config *mdp5_get_config(struct platform_device *dev)
-{
- static struct mdp5_platform_config config = {};
-#ifdef CONFIG_OF
- /* TODO */
-#endif
- config.iommu = iommu_domain_alloc(&platform_bus_type);
-
- return &config;
-}
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
index 753659b..7616a3b 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_kms.h
@@ -21,36 +21,7 @@
#include "msm_drv.h"
#include "msm_kms.h"
#include "mdp/mdp_kms.h"
-/* dynamic offsets used by mdp5.xml.h (initialized in mdp5_kms.c) */
-#define MDP5_MAX_BASES 8
-#define MAX_SMP_BLOCKS 44
-#define MAX_CLIENTS 32
-typedef DECLARE_BITMAP(mdp5_smp_state_t, MAX_SMP_BLOCKS);
-struct mdp5_sub_block {
- int count;
- uint32_t base[MDP5_MAX_BASES];
-};
-struct mdp5_smp_block {
- int mmb_count; /* number of SMP MMBs */
- int mmb_size; /* MMB: size in bytes */
- mdp5_smp_state_t reserved_state;/* SMP MMBs statically allocated */
- int reserved[MAX_CLIENTS]; /* # of MMBs reserved per client */
-};
-struct mdp5_config {
- char *name;
- struct mdp5_smp_block smp;
- struct mdp5_sub_block ctl;
- struct mdp5_sub_block pipe_vig;
- struct mdp5_sub_block pipe_rgb;
- struct mdp5_sub_block pipe_dma;
- struct mdp5_sub_block lm;
- struct mdp5_sub_block dspp;
- struct mdp5_sub_block ad;
- struct mdp5_sub_block intf;
-
- uint32_t max_clk;
-};
-extern const struct mdp5_config *mdp5_cfg;
+#include "mdp5_cfg.h" /* must be included before mdp5.xml.h */
#include "mdp5.xml.h"
#include "mdp5_smp.h"

@@ -59,8 +30,7 @@ struct mdp5_kms {

struct drm_device *dev;

- int rev;
- const struct mdp5_config *hw_cfg;
+ void *cfg_priv;

/* mapper-id used to request GEM buffer mapped for scanout: */
int id;
@@ -84,11 +54,6 @@ struct mdp5_kms {
};
#define to_mdp5_kms(x) container_of(x, struct mdp5_kms, base)

-/* platform config data (ie. from DT, or pdata) */
-struct mdp5_platform_config {
- struct iommu_domain *iommu;
-};
-
static inline void mdp5_write(struct mdp5_kms *mdp5_kms, u32 reg, u32 data)
{
msm_writel(data, mdp5_kms->mmio + reg);
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.c
index e61e1cf..04996ca 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_smp.c
@@ -108,11 +108,15 @@ static int smp_request_block(struct mdp5_smp *smp,
enum mdp5_client_id cid, int nblks)
{
struct mdp5_kms *mdp5_kms = get_kms(smp);
+ const struct mdp5_cfg_hw *hw_cfg;
struct mdp5_client_smp_state *ps = &smp->client_state[cid];
int i, ret, avail, cur_nblks, cnt = smp->blk_cnt;
- int reserved = mdp5_kms->hw_cfg->smp.reserved[cid];
+ int reserved;
unsigned long flags;

+ hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg_priv);
+ reserved = hw_cfg->smp.reserved[cid];
+
spin_lock_irqsave(&smp->state_lock, flags);

nblks -= reserved;
@@ -175,6 +179,7 @@ int mdp5_smp_request(void *handler, enum mdp5_pipe pipe, u32 fmt, u32 width)
struct mdp5_smp *smp = handler;
struct mdp5_kms *mdp5_kms = get_kms(smp);
struct drm_device *dev = mdp5_kms->dev;
+ int rev = mdp5_cfg_get_hw_rev(mdp5_kms->cfg_priv);
int i, hsub, nplanes, nlines, nblks, ret;

nplanes = drm_format_num_planes(fmt);
@@ -192,7 +197,7 @@ int mdp5_smp_request(void *handler, enum mdp5_pipe pipe, u32 fmt, u32 width)
n = DIV_ROUND_UP(fetch_stride * nlines, smp->blk_size);

/* for hw rev v1.00 */
- if (mdp5_kms->rev == 0)
+ if (rev == 0)
n = roundup_pow_of_two(n);

DBG("%s[%d]: request %d SMP blocks", pipe2name(pipe), i, n);
--
Qualcomm Innovation Center, Inc.

The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project