summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinux Build Service Account <lnxbuild@quicinc.com>2017-08-29 01:40:57 -0700
committerGerrit - the friendly Code Review server <code-review@localhost>2017-08-29 01:40:56 -0700
commita26a92216f59334ce1772891a34f357f4574edf3 (patch)
treea06603766e51f2e2597b774bf045103a601ec5c9
parent2ed94ee2e31b1e9cebf04c659c3cb5152e87e5d3 (diff)
parent04639dcc26169d4be65ef885a11e5e034622ba7e (diff)
Merge "drm/msm: add kconfig for enabling event log"
-rw-r--r--Documentation/devicetree/bindings/display/msm/sde.txt4
-rw-r--r--arch/arm/boot/dts/qcom/msm8998-sde.dtsi31
-rw-r--r--drivers/gpu/drm/msm/Kconfig10
-rw-r--r--drivers/gpu/drm/msm/Makefile1
-rw-r--r--drivers/gpu/drm/msm/msm_drv.c16
-rw-r--r--drivers/gpu/drm/msm/sde/sde_core_irq.c2
-rw-r--r--drivers/gpu/drm/msm/sde/sde_crtc.c73
-rw-r--r--drivers/gpu/drm/msm/sde/sde_encoder.c11
-rw-r--r--drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c55
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_catalog.c74
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_catalog.h12
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_cdm.c4
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_ctl.c66
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_ctl.h20
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_dspp.c7
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_intf.c10
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_lm.c10
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_mdss.h2
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_pingpong.c7
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_sspp.c37
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_top.c11
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_util.h2
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_vbif.c6
-rw-r--r--drivers/gpu/drm/msm/sde/sde_hw_wb.c7
-rw-r--r--drivers/gpu/drm/msm/sde/sde_kms.c83
-rw-r--r--drivers/gpu/drm/msm/sde/sde_kms.h44
-rw-r--r--drivers/gpu/drm/msm/sde/sde_kms_utils.c26
-rw-r--r--drivers/gpu/drm/msm/sde_dbg.c2070
-rw-r--r--drivers/gpu/drm/msm/sde_dbg.h292
-rw-r--r--drivers/gpu/drm/msm/sde_dbg_evtlog.c257
30 files changed, 2945 insertions, 305 deletions
diff --git a/Documentation/devicetree/bindings/display/msm/sde.txt b/Documentation/devicetree/bindings/display/msm/sde.txt
index e14acdc6303e..1583da81c090 100644
--- a/Documentation/devicetree/bindings/display/msm/sde.txt
+++ b/Documentation/devicetree/bindings/display/msm/sde.txt
@@ -169,6 +169,7 @@ Optional properties:
e.g. qcom,sde-sspp-vig-blocks
-- qcom,sde-vig-csc-off: offset of CSC hardware
-- qcom,sde-vig-qseed-off: offset of QSEED hardware
+ -- qcom,sde-vig-qseed-size: A u32 address range for qseed scaler.
-- qcom,sde-vig-pcc: offset and version of PCC hardware
-- qcom,sde-vig-hsic: offset and version of global PA adjustment
-- qcom,sde-vig-memcolor: offset and version of PA memcolor hardware
@@ -178,6 +179,7 @@ Optional properties:
indicates that the SSPP RGB contains that feature hardware.
e.g. qcom,sde-sspp-vig-blocks
-- qcom,sde-rgb-scaler-off: offset of RGB scaler hardware
+ -- qcom,sde-rgb-scaler-size: A u32 address range for scaler.
-- qcom,sde-rgb-pcc: offset and version of PCC hardware
- qcom,sde-dspp-blocks: A node that lists the blocks inside the DSPP hardware. The
block entries will contain the offset and version of each
@@ -417,6 +419,7 @@ Example:
qcom,sde-sspp-vig-blocks {
qcom,sde-vig-csc-off = <0x320>;
qcom,sde-vig-qseed-off = <0x200>;
+ qcom,sde-vig-qseed-size = <0x74>;
/* Offset from vig top, version of HSIC */
qcom,sde-vig-hsic = <0x200 0x00010000>;
qcom,sde-vig-memcolor = <0x200 0x00010000>;
@@ -425,6 +428,7 @@ Example:
qcom,sde-sspp-rgb-blocks {
qcom,sde-rgb-scaler-off = <0x200>;
+ qcom,sde-rgb-scaler-size = <0x74>;
qcom,sde-rgb-pcc = <0x380 0x00010000>;
};
diff --git a/arch/arm/boot/dts/qcom/msm8998-sde.dtsi b/arch/arm/boot/dts/qcom/msm8998-sde.dtsi
index 354ac830e0fa..f95ef2b84e2c 100644
--- a/arch/arm/boot/dts/qcom/msm8998-sde.dtsi
+++ b/arch/arm/boot/dts/qcom/msm8998-sde.dtsi
@@ -14,7 +14,7 @@
sde_kms: qcom,sde_kms@c900000 {
compatible = "qcom,sde-kms";
reg = <0x0c900000 0x90000>,
- <0x0c9b0000 0x1040>;
+ <0x0c9b0000 0x2008>;
reg-names = "mdp_phys", "vbif_phys";
/* clock and supply entries */
@@ -52,23 +52,44 @@
/* hw blocks */
qcom,sde-off = <0x1000>;
+ qcom,sde-len = <0x458>;
+
qcom,sde-ctl-off = <0x2000 0x2200 0x2400
0x2600 0x2800>;
+ qcom,sde-ctl-size = <0x94>;
+
qcom,sde-mixer-off = <0x45000 0x46000 0x47000
0x48000 0x49000 0x4a000>;
+ qcom,sde-mixer-size = <0x31c>;
+
qcom,sde-dspp-off = <0x55000 0x57000>;
+ qcom,sde-dspp-size = <0x17e0>;
+
qcom,sde-wb-off = <0x66000>;
+ qcom,sde-wb-size = <0x2dc>;
+
qcom,sde-wb-id = <2>;
qcom,sde-wb-xin-id = <6>;
qcom,sde-wb-clk-ctrl = <0x2bc 0x10>;
qcom,sde-intf-off = <0x6b000 0x6b800
0x6c000 0x6c800>;
+ qcom,sde-intf-size = <0x280>;
+
qcom,sde-intf-type = "dp", "dsi", "dsi", "hdmi";
+
qcom,sde-pp-off = <0x71000 0x71800
- 0x72000 0x72800>;
- qcom,sde-te2-off = <0x2000 0x2000 0x0 0x0>;
+ 0x72000 0x72800 0x73000>;
+ qcom,sde-pp-slave = <0x0 0x0 0x0 0x0 0x1>;
+
+ qcom,sde-pp-size = <0xd4>;
+
+ qcom,sde-te2-off = <0x2000 0x2000 0x0 0x0 0x0>;
qcom,sde-cdm-off = <0x7a200>;
- qcom,sde-dsc-off = <0x10000 0x10000 0x0 0x0>;
+ qcom,sde-cdm-size = <0x224>;
+
+ qcom,sde-dsc-off = <0x81000 0x81400>;
+ qcom,sde-dsc-size = <0x140>;
+
qcom,sde-intf-max-prefetch-lines = <0x15 0x15 0x15 0x15>;
qcom,sde-sspp-type = "vig", "vig", "vig", "vig",
@@ -78,6 +99,7 @@
qcom,sde-sspp-off = <0x5000 0x7000 0x9000 0xb000
0x25000 0x27000 0x29000 0x2b000
0x35000 0x37000>;
+ qcom,sde-sspp-src-size = <0x1ac>;
qcom,sde-sspp-xin-id = <0 4 8 12 1 5 9 13 2 10>;
@@ -113,6 +135,7 @@
qcom,sde-sspp-vig-blocks {
qcom,sde-vig-csc-off = <0x1a00>;
qcom,sde-vig-qseed-off = <0xa00>;
+ qcom,sde-vig-qseed-size = <0xa0>;
};
qcom,platform-supply-entries {
diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig
index dbc198b00792..cb3b25ddd0da 100644
--- a/drivers/gpu/drm/msm/Kconfig
+++ b/drivers/gpu/drm/msm/Kconfig
@@ -98,3 +98,13 @@ config DRM_SDE_HDMI
default y
help
Choose this option if HDMI connector support is needed in SDE driver.
+
+config DRM_SDE_EVTLOG_DEBUG
+ bool "Enable event logging in MSM DRM"
+ depends on DRM_MSM
+ help
+ The SDE DRM debugging provides support to enable display debugging
+ features to: dump SDE registers during driver errors, panic
+ driver during fatal errors and enable some display-driver logging
+ into an internal buffer (this avoids logging overhead).
+
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 4c082fff2fc5..678b2178cb69 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -49,6 +49,7 @@ msm_drm-y := \
sde/sde_color_processing.o \
sde/sde_vbif.o \
sde/sde_splash.o \
+ sde_dbg.o \
sde_dbg_evtlog.o \
sde_io_util.o \
dba_bridge.o \
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index c8b11425a817..969af4c6f0c0 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -293,7 +293,7 @@ static int msm_unload(struct drm_device *dev)
priv->vram.paddr, &attrs);
}
- sde_evtlog_destroy();
+ sde_dbg_destroy();
sde_power_client_destroy(&priv->phandle, priv->pclient);
sde_power_resource_deinit(pdev, &priv->phandle);
@@ -423,11 +423,17 @@ static int msm_component_bind_all(struct device *dev,
}
#endif
+static int msm_power_enable_wrapper(void *handle, void *client, bool enable)
+{
+ return sde_power_resource_enable(handle, client, enable);
+}
+
static int msm_load(struct drm_device *dev, unsigned long flags)
{
struct platform_device *pdev = dev->platformdev;
struct msm_drm_private *priv;
struct msm_kms *kms;
+ struct sde_dbg_power_ctrl dbg_power_ctrl = { NULL };
int ret, i;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
@@ -477,9 +483,13 @@ static int msm_load(struct drm_device *dev, unsigned long flags)
if (ret)
goto fail;
- ret = sde_evtlog_init(dev->primary->debugfs_root);
+ dbg_power_ctrl.handle = &priv->phandle;
+ dbg_power_ctrl.client = priv->pclient;
+ dbg_power_ctrl.enable_fn = msm_power_enable_wrapper;
+ ret = sde_dbg_init(dev->primary->debugfs_root, &pdev->dev,
+ &dbg_power_ctrl);
if (ret) {
- dev_err(dev->dev, "failed to init evtlog: %d\n", ret);
+ dev_err(dev->dev, "failed to init sde dbg: %d\n", ret);
goto fail;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_core_irq.c b/drivers/gpu/drm/msm/sde/sde_core_irq.c
index dbfc2dd11a17..83c8982b2e00 100644
--- a/drivers/gpu/drm/msm/sde/sde_core_irq.c
+++ b/drivers/gpu/drm/msm/sde/sde_core_irq.c
@@ -32,7 +32,7 @@ static void sde_core_irq_callback_handler(void *arg, int irq_idx)
struct sde_irq_callback *cb;
unsigned long irq_flags;
- SDE_DEBUG("irq_idx=%d\n", irq_idx);
+ pr_debug("irq_idx=%d\n", irq_idx);
if (list_empty(&irq_obj->irq_cb_tbl[irq_idx]))
SDE_ERROR("irq_idx=%d has no registered callback\n", irq_idx);
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index 983695d886fd..5d9a4e907318 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -1336,8 +1336,7 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,
struct drm_display_mode *mode;
int cnt = 0, rc = 0, mixer_width, i, z_pos;
- int left_crtc_zpos_cnt[SDE_STAGE_MAX] = {0};
- int right_crtc_zpos_cnt[SDE_STAGE_MAX] = {0};
+ int left_zpos_cnt = 0, right_zpos_cnt = 0;
if (!crtc) {
SDE_ERROR("invalid crtc\n");
@@ -1391,11 +1390,12 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,
}
}
+ /* assign mixer stages based on sorted zpos property */
+ sort(pstates, cnt, sizeof(pstates[0]), pstate_cmp, NULL);
+
if (!sde_is_custom_client()) {
int stage_old = pstates[0].stage;
- /* assign mixer stages based on sorted zpos property */
- sort(pstates, cnt, sizeof(pstates[0]), pstate_cmp, NULL);
z_pos = 0;
for (i = 0; i < cnt; i++) {
if (stage_old != pstates[i].stage)
@@ -1405,8 +1405,14 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,
}
}
+ z_pos = -1;
for (i = 0; i < cnt; i++) {
- z_pos = pstates[i].stage;
+ /* reset counts at every new blend stage */
+ if (pstates[i].stage != z_pos) {
+ left_zpos_cnt = 0;
+ right_zpos_cnt = 0;
+ z_pos = pstates[i].stage;
+ }
/* verify z_pos setting before using it */
if (z_pos >= SDE_STAGE_MAX - SDE_STAGE_0) {
@@ -1415,22 +1421,24 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,
rc = -EINVAL;
goto end;
} else if (pstates[i].drm_pstate->crtc_x < mixer_width) {
- if (left_crtc_zpos_cnt[z_pos] == 2) {
- SDE_ERROR("> 2 plane @ stage%d on left\n",
+ if (left_zpos_cnt == 2) {
+ SDE_ERROR("> 2 planes @ stage %d on left\n",
z_pos);
rc = -EINVAL;
goto end;
}
- left_crtc_zpos_cnt[z_pos]++;
+ left_zpos_cnt++;
+
} else {
- if (right_crtc_zpos_cnt[z_pos] == 2) {
- SDE_ERROR("> 2 plane @ stage%d on right\n",
+ if (right_zpos_cnt == 2) {
+ SDE_ERROR("> 2 planes @ stage %d on right\n",
z_pos);
rc = -EINVAL;
goto end;
}
- right_crtc_zpos_cnt[z_pos]++;
+ right_zpos_cnt++;
}
+
pstates[i].sde_pstate->stage = z_pos + SDE_STAGE_0;
SDE_DEBUG("%s: zpos %d", sde_crtc->name, z_pos);
}
@@ -1442,6 +1450,49 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,
goto end;
}
+ /*
+ * enforce pipe priority restrictions
+ * use pstates sorted by stage to check planes on same stage
+ * we assume that all pipes are in source split so its valid to compare
+ * without taking into account left/right mixer placement
+ */
+ for (i = 1; i < cnt; i++) {
+ struct plane_state *prv_pstate, *cur_pstate;
+ int32_t prv_x, cur_x, prv_id, cur_id;
+
+ prv_pstate = &pstates[i - 1];
+ cur_pstate = &pstates[i];
+ if (prv_pstate->stage != cur_pstate->stage)
+ continue;
+
+ prv_x = prv_pstate->drm_pstate->crtc_x;
+ cur_x = cur_pstate->drm_pstate->crtc_x;
+ prv_id = prv_pstate->sde_pstate->base.plane->base.id;
+ cur_id = cur_pstate->sde_pstate->base.plane->base.id;
+
+ /*
+ * Planes are enumerated in pipe-priority order such that planes
+ * with lower drm_id must be left-most in a shared blend-stage
+ * when using source split.
+ */
+ if (cur_x > prv_x && cur_id < prv_id) {
+ SDE_ERROR(
+ "shared z_pos %d lower id plane%d @ x%d should be left of plane%d @ x %d\n",
+ cur_pstate->stage, cur_id, cur_x,
+ prv_id, prv_x);
+ rc = -EINVAL;
+ goto end;
+ } else if (cur_x < prv_x && cur_id > prv_id) {
+ SDE_ERROR(
+ "shared z_pos %d lower id plane%d @ x%d should be left of plane%d @ x %d\n",
+ cur_pstate->stage, prv_id, prv_x,
+ cur_id, cur_x);
+ rc = -EINVAL;
+ goto end;
+ }
+ }
+
+
end:
return rc;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index 23fb79241d84..cb8b349e72c7 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -506,11 +506,6 @@ static void sde_encoder_virt_disable(struct drm_encoder *drm_enc)
SDE_EVT32(DRMID(drm_enc));
- if (atomic_xchg(&sde_enc->frame_done_timeout, 0)) {
- SDE_ERROR("enc%d timeout pending\n", drm_enc->base.id);
- del_timer_sync(&sde_enc->frame_done_timer);
- }
-
for (i = 0; i < sde_enc->num_phys_encs; i++) {
struct sde_encoder_phys *phys = sde_enc->phys_encs[i];
@@ -523,6 +518,12 @@ static void sde_encoder_virt_disable(struct drm_encoder *drm_enc)
}
}
+ /* after phys waits for frame-done, should be no more frames pending */
+ if (atomic_xchg(&sde_enc->frame_done_timeout, 0)) {
+ SDE_ERROR("enc%d timeout pending\n", drm_enc->base.id);
+ del_timer_sync(&sde_enc->frame_done_timer);
+ }
+
if (sde_enc->cur_master && sde_enc->cur_master->ops.disable)
sde_enc->cur_master->ops.disable(sde_enc->cur_master);
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
index 69a4237f7b67..d58c06de1684 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
@@ -281,23 +281,40 @@ static void sde_encoder_phys_vid_vblank_irq(void *arg, int irq_idx)
{
struct sde_encoder_phys_vid *vid_enc = arg;
struct sde_encoder_phys *phys_enc;
+ struct sde_hw_ctl *hw_ctl;
unsigned long lock_flags;
- int new_cnt;
+ u32 flush_register = 0;
+ int new_cnt = -1, old_cnt = -1;
if (!vid_enc)
return;
phys_enc = &vid_enc->base;
+ hw_ctl = phys_enc->hw_ctl;
+
if (phys_enc->parent_ops.handle_vblank_virt)
phys_enc->parent_ops.handle_vblank_virt(phys_enc->parent,
phys_enc);
+ old_cnt = atomic_read(&phys_enc->pending_kickoff_cnt);
+
+ /*
+ * only decrement the pending flush count if we've actually flushed
+ * hardware. due to sw irq latency, vblank may have already happened
+ * so we need to double-check with hw that it accepted the flush bits
+ */
spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags);
- new_cnt = atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
- SDE_EVT32_IRQ(DRMID(phys_enc->parent), vid_enc->hw_intf->idx - INTF_0,
- new_cnt);
+ if (hw_ctl && hw_ctl->ops.get_flush_register)
+ flush_register = hw_ctl->ops.get_flush_register(hw_ctl);
+
+ if (flush_register == 0)
+ new_cnt = atomic_add_unless(&phys_enc->pending_kickoff_cnt,
+ -1, 0);
spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags);
+ SDE_EVT32_IRQ(DRMID(phys_enc->parent), vid_enc->hw_intf->idx - INTF_0,
+ old_cnt, new_cnt, flush_register);
+
/* Signal any waiting atomic commit thread */
wake_up_all(&phys_enc->pending_kickoff_wq);
}
@@ -700,6 +717,35 @@ static int sde_encoder_phys_vid_wait_for_commit_done(
return ret;
}
+static void sde_encoder_phys_vid_prepare_for_kickoff(
+ struct sde_encoder_phys *phys_enc)
+{
+ struct sde_encoder_phys_vid *vid_enc;
+ struct sde_hw_ctl *ctl;
+ int rc;
+
+ if (!phys_enc) {
+ SDE_ERROR("invalid encoder\n");
+ return;
+ }
+ vid_enc = to_sde_encoder_phys_vid(phys_enc);
+
+ ctl = phys_enc->hw_ctl;
+ if (!ctl || !ctl->ops.wait_reset_status)
+ return;
+
+ /*
+ * hw supports hardware initiated ctl reset, so before we kickoff a new
+ * frame, need to check and wait for hw initiated ctl reset completion
+ */
+ rc = ctl->ops.wait_reset_status(ctl);
+ if (rc) {
+ SDE_ERROR_VIDENC(vid_enc, "ctl %d reset failure: %d\n",
+ ctl->idx, rc);
+ SDE_DBG_DUMP("panic");
+ }
+}
+
static void sde_encoder_phys_vid_disable(struct sde_encoder_phys *phys_enc)
{
struct msm_drm_private *priv;
@@ -832,6 +878,7 @@ static void sde_encoder_phys_vid_init_ops(struct sde_encoder_phys_ops *ops)
ops->get_hw_resources = sde_encoder_phys_vid_get_hw_resources;
ops->control_vblank_irq = sde_encoder_phys_vid_control_vblank_irq;
ops->wait_for_commit_done = sde_encoder_phys_vid_wait_for_commit_done;
+ ops->prepare_for_kickoff = sde_encoder_phys_vid_prepare_for_kickoff;
ops->handle_post_kickoff = sde_encoder_phys_vid_handle_post_kickoff;
ops->needs_single_flush = sde_encoder_phys_vid_needs_single_flush;
ops->setup_misr = sde_encoder_phys_vid_setup_misr;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
index a185eb338134..1a563ea5f50c 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
@@ -134,6 +134,7 @@ enum {
enum {
VIG_QSEED_OFF,
+ VIG_QSEED_LEN,
VIG_CSC_OFF,
VIG_HSIC_PROP,
VIG_MEMCOLOR_PROP,
@@ -143,6 +144,7 @@ enum {
enum {
RGB_SCALER_OFF,
+ RGB_SCALER_LEN,
RGB_PCC_PROP,
RGB_PROP_MAX,
};
@@ -301,6 +303,7 @@ static struct sde_prop_type sspp_prop[] = {
static struct sde_prop_type vig_prop[] = {
{VIG_QSEED_OFF, "qcom,sde-vig-qseed-off", false, PROP_TYPE_U32},
+ {VIG_QSEED_LEN, "qcom,sde-vig-qseed-size", false, PROP_TYPE_U32},
{VIG_CSC_OFF, "qcom,sde-vig-csc-off", false, PROP_TYPE_U32},
{VIG_HSIC_PROP, "qcom,sde-vig-hsic", false, PROP_TYPE_U32_ARRAY},
{VIG_MEMCOLOR_PROP, "qcom,sde-vig-memcolor", false,
@@ -310,6 +313,7 @@ static struct sde_prop_type vig_prop[] = {
static struct sde_prop_type rgb_prop[] = {
{RGB_SCALER_OFF, "qcom,sde-rgb-scaler-off", false, PROP_TYPE_U32},
+ {RGB_SCALER_LEN, "qcom,sde-rgb-scaler-size", false, PROP_TYPE_U32},
{RGB_PCC_PROP, "qcom,sde-rgb-pcc", false, PROP_TYPE_U32_ARRAY},
};
@@ -691,6 +695,8 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg,
sblk->maxdwnscale = MAX_SSPP_DOWNSCALE;
sblk->format_list = plane_formats_yuv;
sspp->id = SSPP_VIG0 + *vig_count;
+ snprintf(sspp->name, SDE_HW_BLK_NAME_LEN, "sspp_%u",
+ sspp->id - SSPP_VIG0);
sspp->clk_ctrl = SDE_CLK_CTRL_VIG0 + *vig_count;
sspp->type = SSPP_TYPE_VIG;
set_bit(SDE_SSPP_QOS, &sspp->features);
@@ -704,14 +710,24 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg,
sblk->scaler_blk.id = SDE_SSPP_SCALER_QSEED2;
sblk->scaler_blk.base = PROP_VALUE_ACCESS(prop_value,
VIG_QSEED_OFF, 0);
- } else if (sde_cfg->qseed_type == SDE_SSPP_SCALER_QSEED3) {
+ sblk->scaler_blk.len = PROP_VALUE_ACCESS(prop_value,
+ VIG_QSEED_LEN, 0);
+ snprintf(sblk->scaler_blk.name, SDE_HW_BLK_NAME_LEN,
+ "sspp_scaler%u", sspp->id - SSPP_VIG0);
+ } else if (sde_cfg->qseed_type == SDE_SSPP_SCALER_QSEED3) {
set_bit(SDE_SSPP_SCALER_QSEED3, &sspp->features);
sblk->scaler_blk.id = SDE_SSPP_SCALER_QSEED3;
sblk->scaler_blk.base = PROP_VALUE_ACCESS(prop_value,
VIG_QSEED_OFF, 0);
+ sblk->scaler_blk.len = PROP_VALUE_ACCESS(prop_value,
+ VIG_QSEED_LEN, 0);
+ snprintf(sblk->scaler_blk.name, SDE_HW_BLK_NAME_LEN,
+ "sspp_scaler%u", sspp->id - SSPP_VIG0);
}
sblk->csc_blk.id = SDE_SSPP_CSC;
+ snprintf(sblk->csc_blk.name, SDE_HW_BLK_NAME_LEN,
+ "sspp_csc%u", sspp->id - SSPP_VIG0);
if (sde_cfg->csc_type == SDE_SSPP_CSC) {
set_bit(SDE_SSPP_CSC, &sspp->features);
sblk->csc_blk.base = PROP_VALUE_ACCESS(prop_value,
@@ -723,6 +739,8 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg,
}
sblk->hsic_blk.id = SDE_SSPP_HSIC;
+ snprintf(sblk->hsic_blk.name, SDE_HW_BLK_NAME_LEN,
+ "sspp_hsic%u", sspp->id - SSPP_VIG0);
if (prop_exists[VIG_HSIC_PROP]) {
sblk->hsic_blk.base = PROP_VALUE_ACCESS(prop_value,
VIG_HSIC_PROP, 0);
@@ -733,6 +751,8 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg,
}
sblk->memcolor_blk.id = SDE_SSPP_MEMCOLOR;
+ snprintf(sblk->memcolor_blk.name, SDE_HW_BLK_NAME_LEN,
+ "sspp_memcolor%u", sspp->id - SSPP_VIG0);
if (prop_exists[VIG_MEMCOLOR_PROP]) {
sblk->memcolor_blk.base = PROP_VALUE_ACCESS(prop_value,
VIG_MEMCOLOR_PROP, 0);
@@ -743,6 +763,8 @@ static void _sde_sspp_setup_vig(struct sde_mdss_cfg *sde_cfg,
}
sblk->pcc_blk.id = SDE_SSPP_PCC;
+ snprintf(sblk->pcc_blk.name, SDE_HW_BLK_NAME_LEN,
+ "sspp_pcc%u", sspp->id - SSPP_VIG0);
if (prop_exists[VIG_PCC_PROP]) {
sblk->pcc_blk.base = PROP_VALUE_ACCESS(prop_value,
VIG_PCC_PROP, 0);
@@ -762,6 +784,8 @@ static void _sde_sspp_setup_rgb(struct sde_mdss_cfg *sde_cfg,
sblk->maxdwnscale = MAX_SSPP_DOWNSCALE;
sblk->format_list = plane_formats;
sspp->id = SSPP_RGB0 + *rgb_count;
+ snprintf(sspp->name, SDE_HW_BLK_NAME_LEN, "sspp_%u",
+ sspp->id - SSPP_VIG0);
sspp->clk_ctrl = SDE_CLK_CTRL_RGB0 + *rgb_count;
sspp->type = SSPP_TYPE_RGB;
set_bit(SDE_SSPP_QOS, &sspp->features);
@@ -775,11 +799,19 @@ static void _sde_sspp_setup_rgb(struct sde_mdss_cfg *sde_cfg,
sblk->scaler_blk.id = SDE_SSPP_SCALER_QSEED2;
sblk->scaler_blk.base = PROP_VALUE_ACCESS(prop_value,
RGB_SCALER_OFF, 0);
+ sblk->scaler_blk.len = PROP_VALUE_ACCESS(prop_value,
+ RGB_SCALER_LEN, 0);
+ snprintf(sblk->scaler_blk.name, SDE_HW_BLK_NAME_LEN,
+ "sspp_scaler%u", sspp->id - SSPP_VIG0);
} else if (sde_cfg->qseed_type == SDE_SSPP_SCALER_QSEED3) {
set_bit(SDE_SSPP_SCALER_RGB, &sspp->features);
sblk->scaler_blk.id = SDE_SSPP_SCALER_QSEED3;
sblk->scaler_blk.base = PROP_VALUE_ACCESS(prop_value,
- RGB_SCALER_OFF, 0);
+ RGB_SCALER_LEN, 0);
+ sblk->scaler_blk.len = PROP_VALUE_ACCESS(prop_value,
+ SSPP_SCALE_SIZE, 0);
+ snprintf(sblk->scaler_blk.name, SDE_HW_BLK_NAME_LEN,
+ "sspp_scaler%u", sspp->id - SSPP_VIG0);
}
sblk->pcc_blk.id = SDE_SSPP_PCC;
@@ -803,6 +835,8 @@ static void _sde_sspp_setup_cursor(struct sde_mdss_cfg *sde_cfg,
sblk->maxdwnscale = SSPP_UNITY_SCALE;
sblk->format_list = cursor_formats;
sspp->id = SSPP_CURSOR0 + *cursor_count;
+ snprintf(sspp->name, SDE_HW_BLK_NAME_LEN, "sspp_%u",
+ sspp->id - SSPP_VIG0);
sspp->clk_ctrl = SDE_CLK_CTRL_CURSOR0 + *cursor_count;
sspp->type = SSPP_TYPE_CURSOR;
(*cursor_count)++;
@@ -819,6 +853,8 @@ static void _sde_sspp_setup_dma(struct sde_mdss_cfg *sde_cfg,
sspp->id = SSPP_DMA0 + *dma_count;
sspp->clk_ctrl = SDE_CLK_CTRL_DMA0 + *dma_count;
sspp->type = SSPP_TYPE_DMA;
+ snprintf(sspp->name, SDE_HW_BLK_NAME_LEN, "sspp_%u",
+ sspp->id - SSPP_VIG0);
set_bit(SDE_SSPP_QOS, &sspp->features);
(*dma_count)++;
snprintf(sspp->name, sizeof(sspp->name), "dma%d", *dma_count-1);
@@ -917,6 +953,7 @@ static int sde_sspp_parse_dt(struct device_node *np,
sspp->sblk = sblk;
sspp->base = PROP_VALUE_ACCESS(prop_value, SSPP_OFF, i);
+ sspp->len = PROP_VALUE_ACCESS(prop_value, SSPP_SIZE, 0);
sblk->maxlinewidth = sde_cfg->max_sspp_linewidth;
set_bit(SDE_SSPP_SRC, &sspp->features);
@@ -944,6 +981,9 @@ static int sde_sspp_parse_dt(struct device_node *np,
goto end;
}
+ snprintf(sblk->src_blk.name, SDE_HW_BLK_NAME_LEN, "sspp_src_%u",
+ sspp->id - SSPP_VIG0);
+
sblk->maxhdeciexp = MAX_HORZ_DECIMATION;
sblk->maxvdeciexp = MAX_VERT_DECIMATION;
@@ -1033,7 +1073,10 @@ static int sde_ctl_parse_dt(struct device_node *np,
for (i = 0; i < off_count; i++) {
ctl = sde_cfg->ctl + i;
ctl->base = PROP_VALUE_ACCESS(prop_value, HW_OFF, i);
+ ctl->len = PROP_VALUE_ACCESS(prop_value, HW_LEN, 0);
ctl->id = CTL_0 + i;
+ snprintf(ctl->name, SDE_HW_BLK_NAME_LEN, "ctl_%u",
+ ctl->id - CTL_0);
if (i < MAX_SPLIT_DISPLAY_CTL)
set_bit(SDE_CTL_SPLIT_DISPLAY, &ctl->features);
@@ -1125,6 +1168,9 @@ static int sde_mixer_parse_dt(struct device_node *np,
mixer->base = PROP_VALUE_ACCESS(prop_value, MIXER_OFF, i);
mixer->len = PROP_VALUE_ACCESS(prop_value, MIXER_LEN, 0);
mixer->id = LM_0 + i;
+ snprintf(mixer->name, SDE_HW_BLK_NAME_LEN, "lm_%u",
+ mixer->id - LM_0);
+
if (!prop_exists[MIXER_LEN])
mixer->len = DEFAULT_SDE_HW_BLOCK_LEN;
@@ -1211,6 +1257,9 @@ static int sde_intf_parse_dt(struct device_node *np,
intf->base = PROP_VALUE_ACCESS(prop_value, INTF_OFF, i);
intf->len = PROP_VALUE_ACCESS(prop_value, INTF_LEN, 0);
intf->id = INTF_0 + i;
+ snprintf(intf->name, SDE_HW_BLK_NAME_LEN, "intf_%u",
+ intf->id - INTF_0);
+
if (!prop_exists[INTF_LEN])
intf->len = DEFAULT_SDE_HW_BLOCK_LEN;
@@ -1290,6 +1339,8 @@ static int sde_wb_parse_dt(struct device_node *np,
wb->base = PROP_VALUE_ACCESS(prop_value, WB_OFF, i);
wb->id = WB_0 + PROP_VALUE_ACCESS(prop_value, WB_ID, i);
+ snprintf(wb->name, SDE_HW_BLK_NAME_LEN, "wb_%u",
+ wb->id - WB_0);
wb->clk_ctrl = SDE_CLK_CTRL_WB0 +
PROP_VALUE_ACCESS(prop_value, WB_ID, i);
wb->xin_id = PROP_VALUE_ACCESS(prop_value, WB_XIN_ID, i);
@@ -1515,7 +1566,10 @@ static int sde_dspp_parse_dt(struct device_node *np,
for (i = 0; i < off_count; i++) {
dspp = sde_cfg->dspp + i;
dspp->base = PROP_VALUE_ACCESS(prop_value, DSPP_OFF, i);
+ dspp->len = PROP_VALUE_ACCESS(prop_value, DSPP_SIZE, 0);
dspp->id = DSPP_0 + i;
+ snprintf(dspp->name, SDE_HW_BLK_NAME_LEN, "dspp_%u",
+ dspp->id - DSPP_0);
sblk = kzalloc(sizeof(*sblk), GFP_KERNEL);
if (!sblk) {
@@ -1585,6 +1639,8 @@ static int sde_cdm_parse_dt(struct device_node *np,
cdm = sde_cfg->cdm + i;
cdm->base = PROP_VALUE_ACCESS(prop_value, HW_OFF, i);
cdm->id = CDM_0 + i;
+ snprintf(cdm->name, SDE_HW_BLK_NAME_LEN, "cdm_%u",
+ cdm->id - CDM_0);
cdm->len = PROP_VALUE_ACCESS(prop_value, HW_LEN, 0);
/* intf3 and wb2 for cdm block */
@@ -1650,6 +1706,8 @@ static int sde_vbif_parse_dt(struct device_node *np,
vbif->base = PROP_VALUE_ACCESS(prop_value, VBIF_OFF, i);
vbif->len = vbif_len;
vbif->id = VBIF_0 + PROP_VALUE_ACCESS(prop_value, VBIF_ID, i);
+ snprintf(vbif->name, SDE_HW_BLK_NAME_LEN, "vbif_%u",
+ vbif->id - VBIF_0);
SDE_DEBUG("vbif:%d\n", vbif->id - VBIF_0);
@@ -1777,15 +1835,21 @@ static int sde_pp_parse_dt(struct device_node *np,
pp->base = PROP_VALUE_ACCESS(prop_value, PP_OFF, i);
pp->id = PINGPONG_0 + i;
+ snprintf(pp->name, SDE_HW_BLK_NAME_LEN, "pingpong_%u",
+ pp->id - PINGPONG_0);
pp->len = PROP_VALUE_ACCESS(prop_value, PP_LEN, 0);
sblk->te.base = PROP_VALUE_ACCESS(prop_value, TE_OFF, i);
sblk->te.id = SDE_PINGPONG_TE;
+ snprintf(sblk->te.name, SDE_HW_BLK_NAME_LEN, "te_%u",
+ pp->id - PINGPONG_0);
set_bit(SDE_PINGPONG_TE, &pp->features);
sblk->te2.base = PROP_VALUE_ACCESS(prop_value, TE2_OFF, i);
if (sblk->te2.base) {
sblk->te2.id = SDE_PINGPONG_TE2;
+ snprintf(sblk->te2.name, SDE_HW_BLK_NAME_LEN, "te2_%u",
+ pp->id - PINGPONG_0);
set_bit(SDE_PINGPONG_TE2, &pp->features);
set_bit(SDE_PINGPONG_SPLIT, &pp->features);
}
@@ -1796,6 +1860,8 @@ static int sde_pp_parse_dt(struct device_node *np,
sblk->dsc.base = PROP_VALUE_ACCESS(prop_value, DSC_OFF, i);
if (sblk->dsc.base) {
sblk->dsc.id = SDE_PINGPONG_DSC;
+ snprintf(sblk->dsc.name, SDE_HW_BLK_NAME_LEN, "dsc_%u",
+ sblk->dsc.id - PINGPONG_0);
set_bit(SDE_PINGPONG_DSC, &pp->features);
}
}
@@ -1926,9 +1992,13 @@ static int sde_parse_dt(struct device_node *np, struct sde_mdss_cfg *cfg)
cfg->mdss_count = 1;
cfg->mdss[0].base = MDSS_BASE_OFFSET;
cfg->mdss[0].id = MDP_TOP;
+ snprintf(cfg->mdss[0].name, SDE_HW_BLK_NAME_LEN, "mdss_%u",
+ cfg->mdss[0].id - MDP_TOP);
cfg->mdp_count = 1;
cfg->mdp[0].id = MDP_TOP;
+ snprintf(cfg->mdp[0].name, SDE_HW_BLK_NAME_LEN, "top_%u",
+ cfg->mdp[0].id - MDP_TOP);
cfg->mdp[0].base = PROP_VALUE_ACCESS(prop_value, SDE_OFF, 0);
cfg->mdp[0].len = PROP_VALUE_ACCESS(prop_value, SDE_LEN, 0);
if (!prop_exists[SDE_LEN])
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
index 73bb77b7afa6..81e6bfe6defe 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
@@ -44,10 +44,12 @@
#define SDE_HW_VER_172 SDE_HW_VER(1, 7, 2) /* 8996 v3.0 */
#define SDE_HW_VER_300 SDE_HW_VER(3, 0, 0) /* 8998 v1.0 */
#define SDE_HW_VER_301 SDE_HW_VER(3, 0, 1) /* 8998 v1.1 */
-#define SDE_HW_VER_400 SDE_HW_VER(4, 0, 0) /* msmskunk v1.0 */
+#define SDE_HW_VER_400 SDE_HW_VER(4, 0, 0) /* sdm845 v1.0 */
#define IS_MSMSKUNK_TARGET(rev) IS_SDE_MAJOR_MINOR_SAME((rev), SDE_HW_VER_400)
+#define SDE_HW_BLK_NAME_LEN 16
+
#define MAX_IMG_WIDTH 0x3fff
#define MAX_IMG_HEIGHT 0x3fff
@@ -58,8 +60,6 @@
#define SDE_COLOR_PROCESS_MAJOR(version) (((version) & 0xFFFF0000) >> 16)
#define SDE_COLOR_PROCESS_MINOR(version) ((version) & 0xFFFF)
-#define SSPP_NAME_SIZE 12
-
/**
* MDP TOP BLOCK features
* @SDE_MDP_PANIC_PER_PIPE Panic configuration needs to be be done per pipe
@@ -236,12 +236,14 @@ enum {
/**
* MACRO SDE_HW_BLK_INFO - information of HW blocks inside SDE
+ * @name: string name for debug purposes
* @id: enum identifying this block
* @base: register base offset to mdss
* @len: length of hardware block
* @features bit mask identifying sub-blocks/features
*/
#define SDE_HW_BLK_INFO \
+ char name[SDE_HW_BLK_NAME_LEN]; \
u32 id; \
u32 base; \
u32 len; \
@@ -249,12 +251,14 @@ enum {
/**
* MACRO SDE_HW_SUBBLK_INFO - information of HW sub-block inside SDE
+ * @name: string name for debug purposes
* @id: enum identifying this sub-block
* @base: offset of this sub-block relative to the block
* offset
* @len register block length of this sub-block
*/
#define SDE_HW_SUBBLK_INFO \
+ char name[SDE_HW_BLK_NAME_LEN]; \
u32 id; \
u32 base; \
u32 len
@@ -458,7 +462,6 @@ struct sde_ctl_cfg {
* @sblk: SSPP sub-blocks information
* @xin_id: bus client identifier
* @clk_ctrl clock control identifier
- * @name source pipe name
* @type sspp type identifier
*/
struct sde_sspp_cfg {
@@ -466,7 +469,6 @@ struct sde_sspp_cfg {
const struct sde_sspp_sub_blks *sblk;
u32 xin_id;
enum sde_clk_ctrl_type clk_ctrl;
- char name[SSPP_NAME_SIZE];
u32 type;
};
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
index 9ec81c227e60..da04be4e9719 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
@@ -14,6 +14,7 @@
#include "sde_hwio.h"
#include "sde_hw_catalog.h"
#include "sde_hw_cdm.h"
+#include "sde_dbg.h"
#define CDM_CSC_10_OPMODE 0x000
#define CDM_CSC_10_BASE 0x004
@@ -295,6 +296,9 @@ struct sde_hw_cdm *sde_hw_cdm_init(enum sde_cdm idx,
*/
sde_hw_cdm_setup_csc_10bit(c, &rgb2yuv_cfg);
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+ c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
return c;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
index 56d9f2a4a9b8..270e79a774b2 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
@@ -13,6 +13,7 @@
#include <linux/delay.h>
#include "sde_hwio.h"
#include "sde_hw_ctl.h"
+#include "sde_dbg.h"
#define CTL_LAYER(lm) \
(((lm) == LM_5) ? (0x024) : (((lm) - LM_0) * 0x004))
@@ -39,6 +40,7 @@ static struct sde_ctl_cfg *_ctl_offset(enum sde_ctl ctl,
if (ctl == m->ctl[i].id) {
b->base_off = addr;
b->blk_off = m->ctl[i].base;
+ b->length = m->ctl[i].len;
b->hwversion = m->hwversion;
b->log_mask = SDE_DBG_MASK_CTL;
return &m->ctl[i];
@@ -92,6 +94,12 @@ static inline void sde_hw_ctl_trigger_flush(struct sde_hw_ctl *ctx)
SDE_REG_WRITE(&ctx->hw, CTL_FLUSH, ctx->pending_flush_mask);
}
+static inline u32 sde_hw_ctl_get_flush_register(struct sde_hw_ctl *ctx)
+{
+ struct sde_hw_blk_reg_map *c = &ctx->hw;
+
+ return SDE_REG_READ(c, CTL_FLUSH);
+}
static inline uint32_t sde_hw_ctl_get_bitmask_sspp(struct sde_hw_ctl *ctx,
enum sde_sspp sspp)
@@ -247,23 +255,58 @@ static inline int sde_hw_ctl_get_bitmask_cdm(struct sde_hw_ctl *ctx,
return 0;
}
+static u32 sde_hw_ctl_poll_reset_status(struct sde_hw_ctl *ctx, u32 count)
+{
+ struct sde_hw_blk_reg_map *c = &ctx->hw;
+ u32 status;
+
+ /* protect to do at least one iteration */
+ if (!count)
+ count = 1;
+
+ /*
+ * it takes around 30us to have mdp finish resetting its ctl path
+ * poll every 50us so that reset should be completed at 1st poll
+ */
+ do {
+ status = SDE_REG_READ(c, CTL_SW_RESET);
+ status &= 0x01;
+ if (status)
+ usleep_range(20, 50);
+ } while (status && --count > 0);
+
+ return status;
+}
+
static int sde_hw_ctl_reset_control(struct sde_hw_ctl *ctx)
{
struct sde_hw_blk_reg_map *c = &ctx->hw;
- int count = SDE_REG_RESET_TIMEOUT_COUNT;
- int reset;
+ pr_debug("issuing hw ctl reset for ctl:%d\n", ctx->idx);
SDE_REG_WRITE(c, CTL_SW_RESET, 0x1);
+ if (sde_hw_ctl_poll_reset_status(ctx, SDE_REG_RESET_TIMEOUT_COUNT))
+ return -EINVAL;
- for (; count > 0; count--) {
- /* insert small delay to avoid spinning the cpu while waiting */
- usleep_range(20, 50);
- reset = SDE_REG_READ(c, CTL_SW_RESET);
- if (reset == 0)
- return 0;
+ return 0;
+}
+
+static int sde_hw_ctl_wait_reset_status(struct sde_hw_ctl *ctx)
+{
+ struct sde_hw_blk_reg_map *c = &ctx->hw;
+ u32 status;
+
+ status = SDE_REG_READ(c, CTL_SW_RESET);
+ status &= 0x01;
+ if (!status)
+ return 0;
+
+ pr_debug("hw ctl reset is set for ctl:%d\n", ctx->idx);
+ if (sde_hw_ctl_poll_reset_status(ctx, SDE_REG_RESET_TIMEOUT_COUNT)) {
+ pr_err("hw recovery is not complete for ctl:%d\n", ctx->idx);
+ return -EINVAL;
}
- return -EINVAL;
+ return 0;
}
static void sde_hw_ctl_clear_all_blendstages(struct sde_hw_ctl *ctx)
@@ -415,9 +458,11 @@ static void _setup_ctl_ops(struct sde_hw_ctl_ops *ops,
ops->update_pending_flush = sde_hw_ctl_update_pending_flush;
ops->get_pending_flush = sde_hw_ctl_get_pending_flush;
ops->trigger_flush = sde_hw_ctl_trigger_flush;
+ ops->get_flush_register = sde_hw_ctl_get_flush_register;
ops->trigger_start = sde_hw_ctl_trigger_start;
ops->setup_intf_cfg = sde_hw_ctl_intf_cfg;
ops->reset = sde_hw_ctl_reset_control;
+ ops->wait_reset_status = sde_hw_ctl_wait_reset_status;
ops->clear_all_blendstages = sde_hw_ctl_clear_all_blendstages;
ops->setup_blendstage = sde_hw_ctl_setup_blendstage;
ops->get_bitmask_sspp = sde_hw_ctl_get_bitmask_sspp;
@@ -452,6 +497,9 @@ struct sde_hw_ctl *sde_hw_ctl_init(enum sde_ctl idx,
c->mixer_count = m->mixer_count;
c->mixer_hw_caps = m->mixer;
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+ c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
return c;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
index 2fb7b377e51d..74dbde92639a 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, 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
@@ -94,6 +94,13 @@ struct sde_hw_ctl_ops {
void (*trigger_flush)(struct sde_hw_ctl *ctx);
/**
+ * Read the value of the flush register
+ * @ctx : ctl path ctx pointer
+ * @Return: value of the ctl flush register.
+ */
+ u32 (*get_flush_register)(struct sde_hw_ctl *ctx);
+
+ /**
* Setup ctl_path interface config
* @ctx
* @cfg : interface config structure pointer
@@ -103,6 +110,17 @@ struct sde_hw_ctl_ops {
int (*reset)(struct sde_hw_ctl *c);
+ /*
+ * wait_reset_status - checks ctl reset status
+ * @ctx : ctl path ctx pointer
+ *
+ * This function checks the ctl reset status bit.
+ * If the reset bit is set, it keeps polling the status till the hw
+ * reset is complete.
+ * Returns: 0 on success or -error if reset incomplete within interval
+ */
+ int (*wait_reset_status)(struct sde_hw_ctl *ctx);
+
uint32_t (*get_bitmask_sspp)(struct sde_hw_ctl *ctx,
enum sde_sspp blk);
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
index d6250b07b4f0..2fd879a0030d 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, 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
@@ -15,6 +15,7 @@
#include "sde_hw_catalog.h"
#include "sde_hw_dspp.h"
#include "sde_hw_color_processing.h"
+#include "sde_dbg.h"
static struct sde_dspp_cfg *_dspp_offset(enum sde_dspp dspp,
struct sde_mdss_cfg *m,
@@ -27,6 +28,7 @@ static struct sde_dspp_cfg *_dspp_offset(enum sde_dspp dspp,
if (dspp == m->dspp[i].id) {
b->base_off = addr;
b->blk_off = m->dspp[i].base;
+ b->length = m->dspp[i].len;
b->hwversion = m->hwversion;
b->log_mask = SDE_DBG_MASK_DSPP;
return &m->dspp[i];
@@ -111,6 +113,9 @@ struct sde_hw_dspp *sde_hw_dspp_init(enum sde_dspp idx,
c->cap = cfg;
_setup_dspp_ops(c, c->cap->features);
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+ c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
return c;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_intf.c b/drivers/gpu/drm/msm/sde/sde_hw_intf.c
index 042b0ee7909a..9e1b97800cb9 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_intf.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_intf.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, 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
@@ -13,6 +13,7 @@
#include "sde_hwio.h"
#include "sde_hw_catalog.h"
#include "sde_hw_intf.h"
+#include "sde_dbg.h"
#define INTF_TIMING_ENGINE_EN 0x000
#define INTF_CONFIG 0x004
@@ -83,6 +84,7 @@ static struct sde_intf_cfg *_intf_offset(enum sde_intf intf,
(m->intf[i].type != INTF_NONE)) {
b->base_off = addr;
b->blk_off = m->intf[i].base;
+ b->length = m->intf[i].len;
b->hwversion = m->hwversion;
b->log_mask = SDE_DBG_MASK_INTF;
return &m->intf[i];
@@ -324,9 +326,9 @@ struct sde_hw_intf *sde_hw_intf_init(enum sde_intf idx,
c->mdss = m;
_setup_intf_ops(&c->ops, c->cap->features);
- /*
- * Perform any default initialization for the intf
- */
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+ c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
return c;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_lm.c b/drivers/gpu/drm/msm/sde/sde_hw_lm.c
index 365b9b17715d..8b4e0901458f 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_lm.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_lm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, 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
@@ -14,6 +14,7 @@
#include "sde_hwio.h"
#include "sde_hw_lm.h"
#include "sde_hw_mdss.h"
+#include "sde_dbg.h"
#define LM_OP_MODE 0x00
#define LM_OUT_SIZE 0x04
@@ -37,6 +38,7 @@ static struct sde_lm_cfg *_lm_offset(enum sde_lm mixer,
if (mixer == m->mixer[i].id) {
b->base_off = addr;
b->blk_off = m->mixer[i].base;
+ b->length = m->mixer[i].len;
b->hwversion = m->hwversion;
b->log_mask = SDE_DBG_MASK_LM;
return &m->mixer[i];
@@ -195,9 +197,9 @@ struct sde_hw_mixer *sde_hw_lm_init(enum sde_lm idx,
c->cap = cfg;
_setup_mixer_ops(m, &c->ops, c->cap->features);
- /*
- * Perform any default initialization for the sspp blocks
- */
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+ c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
return c;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
index 92dd829eee3e..3d63d01a6d4e 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
@@ -18,6 +18,8 @@
#include "msm_drv.h"
+#define SDE_DBG_NAME "sde"
+
#define SDE_NONE 0
#ifndef SDE_CSC_MATRIX_COEFF_SIZE
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c b/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c
index 837edeeba4c6..8488d03af79a 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_pingpong.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, 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
@@ -14,6 +14,7 @@
#include "sde_hwio.h"
#include "sde_hw_catalog.h"
#include "sde_hw_pingpong.h"
+#include "sde_dbg.h"
#define PP_TEAR_CHECK_EN 0x000
#define PP_SYNC_CONFIG_VSYNC 0x004
@@ -47,6 +48,7 @@ static struct sde_pingpong_cfg *_pingpong_offset(enum sde_pingpong pp,
if (pp == m->pingpong[i].id) {
b->base_off = addr;
b->blk_off = m->pingpong[i].base;
+ b->length = m->pingpong[i].len;
b->hwversion = m->hwversion;
b->log_mask = SDE_DBG_MASK_PINGPONG;
return &m->pingpong[i];
@@ -159,6 +161,9 @@ struct sde_hw_pingpong *sde_hw_pingpong_init(enum sde_pingpong idx,
c->pingpong_hw_cap = cfg;
_setup_pingpong_ops(&c->ops, c->pingpong_hw_cap->features);
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+ c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
return c;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
index ea2890d776ae..be620aebf850 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
@@ -15,6 +15,7 @@
#include "sde_hw_lm.h"
#include "sde_hw_sspp.h"
#include "sde_hw_color_processing.h"
+#include "sde_dbg.h"
#define SDE_FETCH_CONFIG_RESET_VALUE 0x00000087
@@ -903,6 +904,7 @@ static struct sde_sspp_cfg *_sspp_offset(enum sde_sspp sspp,
if (sspp == catalog->sspp[i].id) {
b->base_off = addr;
b->blk_off = catalog->sspp[i].base;
+ b->length = catalog->sspp[i].len;
b->hwversion = catalog->hwversion;
b->log_mask = SDE_DBG_MASK_SSPP;
return &catalog->sspp[i];
@@ -917,26 +919,39 @@ struct sde_hw_pipe *sde_hw_sspp_init(enum sde_sspp idx,
void __iomem *addr,
struct sde_mdss_cfg *catalog)
{
- struct sde_hw_pipe *ctx;
+ struct sde_hw_pipe *hw_pipe;
struct sde_sspp_cfg *cfg;
- ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
- if (!ctx)
+ hw_pipe = kzalloc(sizeof(*hw_pipe), GFP_KERNEL);
+ if (!hw_pipe)
return ERR_PTR(-ENOMEM);
- cfg = _sspp_offset(idx, addr, catalog, &ctx->hw);
+ cfg = _sspp_offset(idx, addr, catalog, &hw_pipe->hw);
if (IS_ERR_OR_NULL(cfg)) {
- kfree(ctx);
+ kfree(hw_pipe);
return ERR_PTR(-EINVAL);
}
/* Assign ops */
- ctx->idx = idx;
- ctx->cap = cfg;
- _setup_layer_ops(ctx, ctx->cap->features);
- ctx->highest_bank_bit = catalog->mdp[0].highest_bank_bit;
-
- return ctx;
+ hw_pipe->idx = idx;
+ hw_pipe->cap = cfg;
+ _setup_layer_ops(hw_pipe, hw_pipe->cap->features);
+ hw_pipe->highest_bank_bit = catalog->mdp[0].highest_bank_bit;
+
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name,
+ hw_pipe->hw.blk_off,
+ hw_pipe->hw.blk_off + hw_pipe->hw.length,
+ hw_pipe->hw.xin_id);
+
+ if (cfg->sblk->scaler_blk.len)
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME,
+ cfg->sblk->scaler_blk.name,
+ hw_pipe->hw.blk_off + cfg->sblk->scaler_blk.base,
+ hw_pipe->hw.blk_off + cfg->sblk->scaler_blk.base +
+ cfg->sblk->scaler_blk.len,
+ hw_pipe->hw.xin_id);
+
+ return hw_pipe;
}
void sde_hw_sspp_destroy(struct sde_hw_pipe *ctx)
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_top.c b/drivers/gpu/drm/msm/sde/sde_hw_top.c
index d6d2e41ff5aa..218797e623a2 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_top.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_top.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, 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
@@ -13,6 +13,7 @@
#include "sde_hwio.h"
#include "sde_hw_catalog.h"
#include "sde_hw_top.h"
+#include "sde_dbg.h"
#define SSPP_SPARE 0x28
@@ -225,6 +226,7 @@ static const struct sde_mdp_cfg *_top_offset(enum sde_mdp mdp,
if (mdp == m->mdp[i].id) {
b->base_off = addr;
b->blk_off = m->mdp[i].base;
+ b->length = m->mdp[i].len;
b->hwversion = m->hwversion;
b->log_mask = SDE_DBG_MASK_TOP;
return &m->mdp[i];
@@ -258,9 +260,10 @@ struct sde_hw_mdp *sde_hw_mdptop_init(enum sde_mdp idx,
mdp->cap = cfg;
_setup_mdp_ops(&mdp->ops, mdp->cap->features);
- /*
- * Perform any default initialization for the intf
- */
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name,
+ mdp->hw.blk_off, mdp->hw.blk_off + mdp->hw.length,
+ mdp->hw.xin_id);
+ sde_dbg_set_sde_top_offset(mdp->hw.blk_off);
return mdp;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_util.h b/drivers/gpu/drm/msm/sde/sde_hw_util.h
index c38c22237a57..008b657966b6 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_util.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_util.h
@@ -24,12 +24,14 @@
* @base_off: mdp register mapped offset
* @blk_off: pipe offset relative to mdss offset
* @length length of register block offset
+ * @xin_id xin id
* @hwversion mdss hw version number
*/
struct sde_hw_blk_reg_map {
void __iomem *base_off;
u32 blk_off;
u32 length;
+ u32 xin_id;
u32 hwversion;
u32 log_mask;
};
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_vbif.c b/drivers/gpu/drm/msm/sde/sde_hw_vbif.c
index 76473fa879c5..048ec47d7c72 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_vbif.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_vbif.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, 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
@@ -13,6 +13,7 @@
#include "sde_hwio.h"
#include "sde_hw_catalog.h"
#include "sde_hw_vbif.h"
+#include "sde_dbg.h"
#define VBIF_VERSION 0x0000
#define VBIF_CLK_FORCE_CTRL0 0x0008
@@ -123,6 +124,7 @@ static const struct sde_vbif_cfg *_top_offset(enum sde_vbif vbif,
if (vbif == m->vbif[i].id) {
b->base_off = addr;
b->blk_off = m->vbif[i].base;
+ b->length = m->vbif[i].len;
b->hwversion = m->hwversion;
b->log_mask = SDE_DBG_MASK_VBIF;
return &m->vbif[i];
@@ -156,6 +158,8 @@ struct sde_hw_vbif *sde_hw_vbif_init(enum sde_vbif idx,
c->cap = cfg;
_setup_vbif_ops(&c->ops, c->cap->features);
+ /* no need to register sub-range in sde dbg, dump entire vbif io base */
+
return c;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_wb.c b/drivers/gpu/drm/msm/sde/sde_hw_wb.c
index 426e9991a6b5..320b05f67669 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_wb.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_wb.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, 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
@@ -15,6 +15,7 @@
#include "sde_hw_catalog.h"
#include "sde_hw_wb.h"
#include "sde_formats.h"
+#include "sde_dbg.h"
#define WB_DST_FORMAT 0x000
#define WB_DST_OP_MODE 0x004
@@ -57,6 +58,7 @@ static struct sde_wb_cfg *_wb_offset(enum sde_wb wb,
if (wb == m->wb[i].id) {
b->base_off = addr;
b->blk_off = m->wb[i].base;
+ b->length = m->wb[i].len;
b->hwversion = m->hwversion;
b->log_mask = SDE_DBG_MASK_WB;
return &m->wb[i];
@@ -215,6 +217,9 @@ struct sde_hw_wb *sde_hw_wb_init(enum sde_wb idx,
c->highest_bank_bit = m->mdp[0].highest_bank_bit;
c->hw_mdp = hw_mdp;
+ sde_dbg_reg_register_dump_range(SDE_DBG_NAME, cfg->name, c->hw.blk_off,
+ c->hw.blk_off + c->hw.length, c->hw.xin_id);
+
return c;
}
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index cdf67c0aa864..34a32d79f22c 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -1167,6 +1167,44 @@ fail:
return ret;
}
+static void __iomem *_sde_kms_ioremap(struct platform_device *pdev,
+ const char *name, unsigned long *out_size)
+{
+ struct resource *res;
+ unsigned long size;
+ void __iomem *ptr;
+
+ if (out_size)
+ *out_size = 0;
+
+ if (name)
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
+ else
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!res) {
+ /* availability depends on platform */
+ SDE_DEBUG("failed to get memory resource: %s\n", name);
+ return NULL;
+ }
+
+ size = resource_size(res);
+
+ ptr = devm_ioremap_nocache(&pdev->dev, res->start, size);
+ if (!ptr) {
+ SDE_ERROR("failed to ioremap: %s\n", name);
+ return NULL;
+ }
+
+ SDE_DEBUG("IO:region %s %pK %08lx\n", name, ptr, size);
+
+ if (out_size)
+ *out_size = size;
+
+ return ptr;
+}
+
+
static int sde_kms_hw_init(struct msm_kms *kms)
{
struct sde_kms *sde_kms;
@@ -1193,29 +1231,42 @@ static int sde_kms_hw_init(struct msm_kms *kms)
goto end;
}
- sde_kms->mmio = msm_ioremap(dev->platformdev, "mdp_phys", "SDE");
- if (IS_ERR(sde_kms->mmio)) {
- rc = PTR_ERR(sde_kms->mmio);
- SDE_ERROR("mdp register memory map failed: %d\n", rc);
- sde_kms->mmio = NULL;
+ sde_kms->mmio = _sde_kms_ioremap(dev->platformdev, "mdp_phys",
+ &sde_kms->mmio_len);
+ if (!sde_kms->mmio) {
+ SDE_ERROR("mdp register memory map failed\n");
goto error;
}
DRM_INFO("mapped mdp address space @%p\n", sde_kms->mmio);
- sde_kms->vbif[VBIF_RT] = msm_ioremap(dev->platformdev,
- "vbif_phys", "VBIF");
- if (IS_ERR(sde_kms->vbif[VBIF_RT])) {
- rc = PTR_ERR(sde_kms->vbif[VBIF_RT]);
- SDE_ERROR("vbif register memory map failed: %d\n", rc);
- sde_kms->vbif[VBIF_RT] = NULL;
+ rc = sde_dbg_reg_register_base(SDE_DBG_NAME, sde_kms->mmio,
+ sde_kms->mmio_len);
+ if (rc)
+ SDE_ERROR("dbg base register kms failed: %d\n", rc);
+
+ sde_kms->vbif[VBIF_RT] = _sde_kms_ioremap(dev->platformdev, "vbif_phys",
+ &sde_kms->vbif_len[VBIF_RT]);
+ if (!sde_kms->vbif[VBIF_RT]) {
+ SDE_ERROR("vbif register memory map failed\n");
goto error;
}
- sde_kms->vbif[VBIF_NRT] = msm_ioremap(dev->platformdev,
- "vbif_nrt_phys", "VBIF_NRT");
- if (IS_ERR(sde_kms->vbif[VBIF_NRT])) {
- sde_kms->vbif[VBIF_NRT] = NULL;
+ rc = sde_dbg_reg_register_base("vbif_rt", sde_kms->vbif[VBIF_RT],
+ sde_kms->vbif_len[VBIF_RT]);
+ if (rc)
+ SDE_ERROR("dbg base register vbif_rt failed: %d\n", rc);
+
+ sde_kms->vbif[VBIF_NRT] = _sde_kms_ioremap(dev->platformdev,
+ "vbif_nrt_phys", &sde_kms->vbif_len[VBIF_NRT]);
+ if (!sde_kms->vbif[VBIF_NRT]) {
SDE_DEBUG("VBIF NRT is not defined");
+ } else {
+ rc = sde_dbg_reg_register_base("vbif_nrt",
+ sde_kms->vbif[VBIF_NRT],
+ sde_kms->vbif_len[VBIF_NRT]);
+ if (rc)
+ SDE_ERROR("dbg base register vbif_nrt failed: %d\n",
+ rc);
}
sde_kms->core_client = sde_power_client_create(&priv->phandle, "core");
@@ -1245,6 +1296,8 @@ static int sde_kms_hw_init(struct msm_kms *kms)
goto power_error;
}
+ sde_dbg_init_dbg_buses(sde_kms->core_rev);
+
rc = sde_rm_init(&sde_kms->rm, sde_kms->catalog, sde_kms->mmio,
sde_kms->dev);
if (rc) {
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h
index d929e48a3fe8..88ec4753a8a7 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.h
+++ b/drivers/gpu/drm/msm/sde/sde_kms.h
@@ -134,6 +134,7 @@ struct sde_kms {
/* io/register spaces: */
void __iomem *mmio, *vbif[VBIF_MAX];
+ unsigned long mmio_len, vbif_len[VBIF_MAX];
struct regulator *vdd;
struct regulator *mmagic;
@@ -368,6 +369,49 @@ void sde_kms_info_append_format(struct sde_kms_info *info,
void sde_kms_info_stop(struct sde_kms_info *info);
/**
+ * sde_kms_rect_intersect - intersect two rectangles
+ * @r1: first rectangle
+ * @r2: scissor rectangle
+ * @result: result rectangle, all 0's on no intersection found
+ */
+void sde_kms_rect_intersect(const struct sde_rect *r1,
+ const struct sde_rect *r2,
+ struct sde_rect *result);
+
+/**
+ * sde_kms_rect_is_equal - compares two rects
+ * @r1: rect value to compare
+ * @r2: rect value to compare
+ *
+ * Returns 1 if the rects are same, 0 otherwise.
+ */
+static inline bool sde_kms_rect_is_equal(struct sde_rect *r1,
+ struct sde_rect *r2)
+{
+ if ((!r1 && r2) || (r1 && !r2))
+ return false;
+
+ if (!r1 && !r2)
+ return true;
+
+ return r1->x == r2->x && r1->y == r2->y && r1->w == r2->w &&
+ r1->h == r2->h;
+}
+
+/**
+ * sde_kms_rect_is_null - returns true if the width or height of a rect is 0
+ * @rect: rectangle to check for zero size
+ * @Return: True if width or height of rectangle is 0
+ */
+static inline bool sde_kms_rect_is_null(const struct sde_rect *r)
+{
+ if (!r)
+ return true;
+
+ return (!r->w || !r->h);
+}
+
+/**
* Vblank enable/disable functions
*/
int sde_enable_vblank(struct msm_kms *kms, struct drm_crtc *crtc);
diff --git a/drivers/gpu/drm/msm/sde/sde_kms_utils.c b/drivers/gpu/drm/msm/sde/sde_kms_utils.c
index 6e29c09deb40..30e12c969538 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms_utils.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms_utils.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, 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
@@ -151,3 +151,27 @@ void sde_kms_info_stop(struct sde_kms_info *info)
info->len = info->staged_len + len;
}
}
+
+void sde_kms_rect_intersect(const struct sde_rect *r1,
+ const struct sde_rect *r2,
+ struct sde_rect *result)
+{
+ int l, t, r, b;
+
+ if (!r1 || !r2 || !result)
+ return;
+
+ l = max(r1->x, r2->x);
+ t = max(r1->y, r2->y);
+ r = min((r1->x + r1->w), (r2->x + r2->w));
+ b = min((r1->y + r1->h), (r2->y + r2->h));
+
+ if (r < l || b < t) {
+ memset(result, 0, sizeof(*result));
+ } else {
+ result->x = l;
+ result->y = t;
+ result->w = r - l;
+ result->h = b - t;
+ }
+}
diff --git a/drivers/gpu/drm/msm/sde_dbg.c b/drivers/gpu/drm/msm/sde_dbg.c
new file mode 100644
index 000000000000..b87bc75f17ea
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde_dbg.c
@@ -0,0 +1,2070 @@
+/* Copyright (c) 2009-2017, 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.
+ */
+
+#define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__
+
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/ktime.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/dma-buf.h>
+#include <linux/slab.h>
+#include <linux/list_sort.h>
+
+#include "sde_dbg.h"
+#include "sde/sde_hw_catalog.h"
+
+#define SDE_DBG_BASE_MAX 10
+
+#define DEFAULT_PANIC 1
+#define DEFAULT_REGDUMP SDE_DBG_DUMP_IN_MEM
+#define DEFAULT_DBGBUS_SDE SDE_DBG_DUMP_IN_MEM
+#define DEFAULT_DBGBUS_VBIFRT SDE_DBG_DUMP_IN_MEM
+#define DEFAULT_BASE_REG_CNT 0x100
+#define GROUP_BYTES 4
+#define ROW_BYTES 16
+#define RANGE_NAME_LEN 40
+#define REG_BASE_NAME_LEN 80
+
+#define DBGBUS_FLAGS_DSPP BIT(0)
+#define DBGBUS_DSPP_STATUS 0x34C
+
+#define DBGBUS_NAME_SDE "sde"
+#define DBGBUS_NAME_VBIF_RT "vbif_rt"
+
+/* offsets from sde top address for the debug buses */
+#define DBGBUS_SSPP0 0x188
+#define DBGBUS_SSPP1 0x298
+#define DBGBUS_DSPP 0x348
+#define DBGBUS_PERIPH 0x418
+
+#define TEST_MASK(id, tp) ((id << 4) | (tp << 1) | BIT(0))
+
+/* following offsets are with respect to MDP VBIF base for DBG BUS access */
+#define MMSS_VBIF_CLKON 0x4
+#define MMSS_VBIF_TEST_BUS_OUT_CTRL 0x210
+#define MMSS_VBIF_TEST_BUS_OUT 0x230
+
+/* print debug ranges in groups of 4 u32s */
+#define REG_DUMP_ALIGN 16
+
+/**
+ * struct sde_dbg_reg_offset - tracking for start and end of region
+ * @start: start offset
+ * @start: end offset
+ */
+struct sde_dbg_reg_offset {
+ u32 start;
+ u32 end;
+};
+
+/**
+ * struct sde_dbg_reg_range - register dumping named sub-range
+ * @head: head of this node
+ * @reg_dump: address for the mem dump
+ * @range_name: name of this range
+ * @offset: offsets for range to dump
+ * @xin_id: client xin id
+ */
+struct sde_dbg_reg_range {
+ struct list_head head;
+ u32 *reg_dump;
+ char range_name[RANGE_NAME_LEN];
+ struct sde_dbg_reg_offset offset;
+ uint32_t xin_id;
+};
+
+/**
+ * struct sde_dbg_reg_base - register region base.
+ * may sub-ranges: sub-ranges are used for dumping
+ * or may not have sub-ranges: dumping is base -> max_offset
+ * @reg_base_head: head of this node
+ * @sub_range_list: head to the list with dump ranges
+ * @name: register base name
+ * @base: base pointer
+ * @off: cached offset of region for manual register dumping
+ * @cnt: cached range of region for manual register dumping
+ * @max_offset: length of region
+ * @buf: buffer used for manual register dumping
+ * @buf_len: buffer length used for manual register dumping
+ * @reg_dump: address for the mem dump if no ranges used
+ */
+struct sde_dbg_reg_base {
+ struct list_head reg_base_head;
+ struct list_head sub_range_list;
+ char name[REG_BASE_NAME_LEN];
+ void __iomem *base;
+ size_t off;
+ size_t cnt;
+ size_t max_offset;
+ char *buf;
+ size_t buf_len;
+ u32 *reg_dump;
+};
+
+struct sde_debug_bus_entry {
+ u32 wr_addr;
+ u32 block_id;
+ u32 test_id;
+};
+
+struct vbif_debug_bus_entry {
+ u32 disable_bus_addr;
+ u32 block_bus_addr;
+ u32 bit_offset;
+ u32 block_cnt;
+ u32 test_pnt_start;
+ u32 test_pnt_cnt;
+};
+
+struct sde_dbg_debug_bus_common {
+ char *name;
+ u32 enable_mask;
+ bool include_in_deferred_work;
+ u32 flags;
+ u32 entries_size;
+ u32 *dumped_content;
+};
+
+struct sde_dbg_sde_debug_bus {
+ struct sde_dbg_debug_bus_common cmn;
+ struct sde_debug_bus_entry *entries;
+ u32 top_blk_off;
+};
+
+struct sde_dbg_vbif_debug_bus {
+ struct sde_dbg_debug_bus_common cmn;
+ struct vbif_debug_bus_entry *entries;
+};
+
+/**
+ * struct sde_dbg_base - global sde debug base structure
+ * @evtlog: event log instance
+ * @reg_base_list: list of register dumping regions
+ * @root: base debugfs root
+ * @dev: device pointer
+ * @power_ctrl: callback structure for enabling power for reading hw registers
+ * @req_dump_blks: list of blocks requested for dumping
+ * @panic_on_err: whether to kernel panic after triggering dump via debugfs
+ * @dump_work: work struct for deferring register dump work to separate thread
+ * @work_panic: panic after dump if internal user passed "panic" special region
+ * @enable_reg_dump: whether to dump registers into memory, kernel log, or both
+ * @dbgbus_sde: debug bus structure for the sde
+ * @dbgbus_vbif_rt: debug bus structure for the realtime vbif
+ */
+static struct sde_dbg_base {
+ struct sde_dbg_evtlog *evtlog;
+ struct list_head reg_base_list;
+ struct dentry *root;
+ struct device *dev;
+ struct sde_dbg_power_ctrl power_ctrl;
+
+ struct sde_dbg_reg_base *req_dump_blks[SDE_DBG_BASE_MAX];
+
+ u32 panic_on_err;
+ struct work_struct dump_work;
+ bool work_panic;
+ u32 enable_reg_dump;
+
+ struct sde_dbg_sde_debug_bus dbgbus_sde;
+ struct sde_dbg_vbif_debug_bus dbgbus_vbif_rt;
+} sde_dbg_base;
+
+/* sde_dbg_base_evtlog - global pointer to main sde event log for macro use */
+struct sde_dbg_evtlog *sde_dbg_base_evtlog;
+
+static struct sde_debug_bus_entry dbg_bus_sde_8998[] = {
+
+ /* Unpack 0 sspp 0*/
+ { DBGBUS_SSPP0, 50, 2 },
+ { DBGBUS_SSPP0, 60, 2 },
+ { DBGBUS_SSPP0, 70, 2 },
+ { DBGBUS_SSPP0, 85, 2 },
+
+ /* Upack 0 sspp 1*/
+ { DBGBUS_SSPP1, 50, 2 },
+ { DBGBUS_SSPP1, 60, 2 },
+ { DBGBUS_SSPP1, 70, 2 },
+ { DBGBUS_SSPP1, 85, 2 },
+
+ /* scheduler */
+ { DBGBUS_DSPP, 130, 0 },
+ { DBGBUS_DSPP, 130, 1 },
+ { DBGBUS_DSPP, 130, 2 },
+ { DBGBUS_DSPP, 130, 3 },
+ { DBGBUS_DSPP, 130, 4 },
+ { DBGBUS_DSPP, 130, 5 },
+
+ /* qseed */
+ { DBGBUS_SSPP0, 6, 0},
+ { DBGBUS_SSPP0, 6, 1},
+ { DBGBUS_SSPP0, 26, 0},
+ { DBGBUS_SSPP0, 26, 1},
+ { DBGBUS_SSPP1, 6, 0},
+ { DBGBUS_SSPP1, 6, 1},
+ { DBGBUS_SSPP1, 26, 0},
+ { DBGBUS_SSPP1, 26, 1},
+
+ /* scale */
+ { DBGBUS_SSPP0, 16, 0},
+ { DBGBUS_SSPP0, 16, 1},
+ { DBGBUS_SSPP0, 36, 0},
+ { DBGBUS_SSPP0, 36, 1},
+ { DBGBUS_SSPP1, 16, 0},
+ { DBGBUS_SSPP1, 16, 1},
+ { DBGBUS_SSPP1, 36, 0},
+ { DBGBUS_SSPP1, 36, 1},
+
+ /* fetch sspp0 */
+
+ /* vig 0 */
+ { DBGBUS_SSPP0, 0, 0 },
+ { DBGBUS_SSPP0, 0, 1 },
+ { DBGBUS_SSPP0, 0, 2 },
+ { DBGBUS_SSPP0, 0, 3 },
+ { DBGBUS_SSPP0, 0, 4 },
+ { DBGBUS_SSPP0, 0, 5 },
+ { DBGBUS_SSPP0, 0, 6 },
+ { DBGBUS_SSPP0, 0, 7 },
+
+ { DBGBUS_SSPP0, 1, 0 },
+ { DBGBUS_SSPP0, 1, 1 },
+ { DBGBUS_SSPP0, 1, 2 },
+ { DBGBUS_SSPP0, 1, 3 },
+ { DBGBUS_SSPP0, 1, 4 },
+ { DBGBUS_SSPP0, 1, 5 },
+ { DBGBUS_SSPP0, 1, 6 },
+ { DBGBUS_SSPP0, 1, 7 },
+
+ { DBGBUS_SSPP0, 2, 0 },
+ { DBGBUS_SSPP0, 2, 1 },
+ { DBGBUS_SSPP0, 2, 2 },
+ { DBGBUS_SSPP0, 2, 3 },
+ { DBGBUS_SSPP0, 2, 4 },
+ { DBGBUS_SSPP0, 2, 5 },
+ { DBGBUS_SSPP0, 2, 6 },
+ { DBGBUS_SSPP0, 2, 7 },
+
+ { DBGBUS_SSPP0, 4, 0 },
+ { DBGBUS_SSPP0, 4, 1 },
+ { DBGBUS_SSPP0, 4, 2 },
+ { DBGBUS_SSPP0, 4, 3 },
+ { DBGBUS_SSPP0, 4, 4 },
+ { DBGBUS_SSPP0, 4, 5 },
+ { DBGBUS_SSPP0, 4, 6 },
+ { DBGBUS_SSPP0, 4, 7 },
+
+ { DBGBUS_SSPP0, 5, 0 },
+ { DBGBUS_SSPP0, 5, 1 },
+ { DBGBUS_SSPP0, 5, 2 },
+ { DBGBUS_SSPP0, 5, 3 },
+ { DBGBUS_SSPP0, 5, 4 },
+ { DBGBUS_SSPP0, 5, 5 },
+ { DBGBUS_SSPP0, 5, 6 },
+ { DBGBUS_SSPP0, 5, 7 },
+
+ /* vig 2 */
+ { DBGBUS_SSPP0, 20, 0 },
+ { DBGBUS_SSPP0, 20, 1 },
+ { DBGBUS_SSPP0, 20, 2 },
+ { DBGBUS_SSPP0, 20, 3 },
+ { DBGBUS_SSPP0, 20, 4 },
+ { DBGBUS_SSPP0, 20, 5 },
+ { DBGBUS_SSPP0, 20, 6 },
+ { DBGBUS_SSPP0, 20, 7 },
+
+ { DBGBUS_SSPP0, 21, 0 },
+ { DBGBUS_SSPP0, 21, 1 },
+ { DBGBUS_SSPP0, 21, 2 },
+ { DBGBUS_SSPP0, 21, 3 },
+ { DBGBUS_SSPP0, 21, 4 },
+ { DBGBUS_SSPP0, 21, 5 },
+ { DBGBUS_SSPP0, 21, 6 },
+ { DBGBUS_SSPP0, 21, 7 },
+
+ { DBGBUS_SSPP0, 22, 0 },
+ { DBGBUS_SSPP0, 22, 1 },
+ { DBGBUS_SSPP0, 22, 2 },
+ { DBGBUS_SSPP0, 22, 3 },
+ { DBGBUS_SSPP0, 22, 4 },
+ { DBGBUS_SSPP0, 22, 5 },
+ { DBGBUS_SSPP0, 22, 6 },
+ { DBGBUS_SSPP0, 22, 7 },
+
+ { DBGBUS_SSPP0, 24, 0 },
+ { DBGBUS_SSPP0, 24, 1 },
+ { DBGBUS_SSPP0, 24, 2 },
+ { DBGBUS_SSPP0, 24, 3 },
+ { DBGBUS_SSPP0, 24, 4 },
+ { DBGBUS_SSPP0, 24, 5 },
+ { DBGBUS_SSPP0, 24, 6 },
+ { DBGBUS_SSPP0, 24, 7 },
+
+ { DBGBUS_SSPP0, 25, 0 },
+ { DBGBUS_SSPP0, 25, 1 },
+ { DBGBUS_SSPP0, 25, 2 },
+ { DBGBUS_SSPP0, 25, 3 },
+ { DBGBUS_SSPP0, 25, 4 },
+ { DBGBUS_SSPP0, 25, 5 },
+ { DBGBUS_SSPP0, 25, 6 },
+ { DBGBUS_SSPP0, 25, 7 },
+
+ /* dma 2 */
+ { DBGBUS_SSPP0, 30, 0 },
+ { DBGBUS_SSPP0, 30, 1 },
+ { DBGBUS_SSPP0, 30, 2 },
+ { DBGBUS_SSPP0, 30, 3 },
+ { DBGBUS_SSPP0, 30, 4 },
+ { DBGBUS_SSPP0, 30, 5 },
+ { DBGBUS_SSPP0, 30, 6 },
+ { DBGBUS_SSPP0, 30, 7 },
+
+ { DBGBUS_SSPP0, 31, 0 },
+ { DBGBUS_SSPP0, 31, 1 },
+ { DBGBUS_SSPP0, 31, 2 },
+ { DBGBUS_SSPP0, 31, 3 },
+ { DBGBUS_SSPP0, 31, 4 },
+ { DBGBUS_SSPP0, 31, 5 },
+ { DBGBUS_SSPP0, 31, 6 },
+ { DBGBUS_SSPP0, 31, 7 },
+
+ { DBGBUS_SSPP0, 32, 0 },
+ { DBGBUS_SSPP0, 32, 1 },
+ { DBGBUS_SSPP0, 32, 2 },
+ { DBGBUS_SSPP0, 32, 3 },
+ { DBGBUS_SSPP0, 32, 4 },
+ { DBGBUS_SSPP0, 32, 5 },
+ { DBGBUS_SSPP0, 32, 6 },
+ { DBGBUS_SSPP0, 32, 7 },
+
+ { DBGBUS_SSPP0, 33, 0 },
+ { DBGBUS_SSPP0, 33, 1 },
+ { DBGBUS_SSPP0, 33, 2 },
+ { DBGBUS_SSPP0, 33, 3 },
+ { DBGBUS_SSPP0, 33, 4 },
+ { DBGBUS_SSPP0, 33, 5 },
+ { DBGBUS_SSPP0, 33, 6 },
+ { DBGBUS_SSPP0, 33, 7 },
+
+ { DBGBUS_SSPP0, 34, 0 },
+ { DBGBUS_SSPP0, 34, 1 },
+ { DBGBUS_SSPP0, 34, 2 },
+ { DBGBUS_SSPP0, 34, 3 },
+ { DBGBUS_SSPP0, 34, 4 },
+ { DBGBUS_SSPP0, 34, 5 },
+ { DBGBUS_SSPP0, 34, 6 },
+ { DBGBUS_SSPP0, 34, 7 },
+
+ { DBGBUS_SSPP0, 35, 0 },
+ { DBGBUS_SSPP0, 35, 1 },
+ { DBGBUS_SSPP0, 35, 2 },
+ { DBGBUS_SSPP0, 35, 3 },
+
+ /* dma 0 */
+ { DBGBUS_SSPP0, 40, 0 },
+ { DBGBUS_SSPP0, 40, 1 },
+ { DBGBUS_SSPP0, 40, 2 },
+ { DBGBUS_SSPP0, 40, 3 },
+ { DBGBUS_SSPP0, 40, 4 },
+ { DBGBUS_SSPP0, 40, 5 },
+ { DBGBUS_SSPP0, 40, 6 },
+ { DBGBUS_SSPP0, 40, 7 },
+
+ { DBGBUS_SSPP0, 41, 0 },
+ { DBGBUS_SSPP0, 41, 1 },
+ { DBGBUS_SSPP0, 41, 2 },
+ { DBGBUS_SSPP0, 41, 3 },
+ { DBGBUS_SSPP0, 41, 4 },
+ { DBGBUS_SSPP0, 41, 5 },
+ { DBGBUS_SSPP0, 41, 6 },
+ { DBGBUS_SSPP0, 41, 7 },
+
+ { DBGBUS_SSPP0, 42, 0 },
+ { DBGBUS_SSPP0, 42, 1 },
+ { DBGBUS_SSPP0, 42, 2 },
+ { DBGBUS_SSPP0, 42, 3 },
+ { DBGBUS_SSPP0, 42, 4 },
+ { DBGBUS_SSPP0, 42, 5 },
+ { DBGBUS_SSPP0, 42, 6 },
+ { DBGBUS_SSPP0, 42, 7 },
+
+ { DBGBUS_SSPP0, 44, 0 },
+ { DBGBUS_SSPP0, 44, 1 },
+ { DBGBUS_SSPP0, 44, 2 },
+ { DBGBUS_SSPP0, 44, 3 },
+ { DBGBUS_SSPP0, 44, 4 },
+ { DBGBUS_SSPP0, 44, 5 },
+ { DBGBUS_SSPP0, 44, 6 },
+ { DBGBUS_SSPP0, 44, 7 },
+
+ { DBGBUS_SSPP0, 45, 0 },
+ { DBGBUS_SSPP0, 45, 1 },
+ { DBGBUS_SSPP0, 45, 2 },
+ { DBGBUS_SSPP0, 45, 3 },
+ { DBGBUS_SSPP0, 45, 4 },
+ { DBGBUS_SSPP0, 45, 5 },
+ { DBGBUS_SSPP0, 45, 6 },
+ { DBGBUS_SSPP0, 45, 7 },
+
+ /* fetch sspp1 */
+ /* vig 1 */
+ { DBGBUS_SSPP1, 0, 0 },
+ { DBGBUS_SSPP1, 0, 1 },
+ { DBGBUS_SSPP1, 0, 2 },
+ { DBGBUS_SSPP1, 0, 3 },
+ { DBGBUS_SSPP1, 0, 4 },
+ { DBGBUS_SSPP1, 0, 5 },
+ { DBGBUS_SSPP1, 0, 6 },
+ { DBGBUS_SSPP1, 0, 7 },
+
+ { DBGBUS_SSPP1, 1, 0 },
+ { DBGBUS_SSPP1, 1, 1 },
+ { DBGBUS_SSPP1, 1, 2 },
+ { DBGBUS_SSPP1, 1, 3 },
+ { DBGBUS_SSPP1, 1, 4 },
+ { DBGBUS_SSPP1, 1, 5 },
+ { DBGBUS_SSPP1, 1, 6 },
+ { DBGBUS_SSPP1, 1, 7 },
+
+ { DBGBUS_SSPP1, 2, 0 },
+ { DBGBUS_SSPP1, 2, 1 },
+ { DBGBUS_SSPP1, 2, 2 },
+ { DBGBUS_SSPP1, 2, 3 },
+ { DBGBUS_SSPP1, 2, 4 },
+ { DBGBUS_SSPP1, 2, 5 },
+ { DBGBUS_SSPP1, 2, 6 },
+ { DBGBUS_SSPP1, 2, 7 },
+
+ { DBGBUS_SSPP1, 4, 0 },
+ { DBGBUS_SSPP1, 4, 1 },
+ { DBGBUS_SSPP1, 4, 2 },
+ { DBGBUS_SSPP1, 4, 3 },
+ { DBGBUS_SSPP1, 4, 4 },
+ { DBGBUS_SSPP1, 4, 5 },
+ { DBGBUS_SSPP1, 4, 6 },
+ { DBGBUS_SSPP1, 4, 7 },
+
+ { DBGBUS_SSPP1, 5, 0 },
+ { DBGBUS_SSPP1, 5, 1 },
+ { DBGBUS_SSPP1, 5, 2 },
+ { DBGBUS_SSPP1, 5, 3 },
+ { DBGBUS_SSPP1, 5, 4 },
+ { DBGBUS_SSPP1, 5, 5 },
+ { DBGBUS_SSPP1, 5, 6 },
+ { DBGBUS_SSPP1, 5, 7 },
+
+ /* vig 3 */
+ { DBGBUS_SSPP1, 20, 0 },
+ { DBGBUS_SSPP1, 20, 1 },
+ { DBGBUS_SSPP1, 20, 2 },
+ { DBGBUS_SSPP1, 20, 3 },
+ { DBGBUS_SSPP1, 20, 4 },
+ { DBGBUS_SSPP1, 20, 5 },
+ { DBGBUS_SSPP1, 20, 6 },
+ { DBGBUS_SSPP1, 20, 7 },
+
+ { DBGBUS_SSPP1, 21, 0 },
+ { DBGBUS_SSPP1, 21, 1 },
+ { DBGBUS_SSPP1, 21, 2 },
+ { DBGBUS_SSPP1, 21, 3 },
+ { DBGBUS_SSPP1, 21, 4 },
+ { DBGBUS_SSPP1, 21, 5 },
+ { DBGBUS_SSPP1, 21, 6 },
+ { DBGBUS_SSPP1, 21, 7 },
+
+ { DBGBUS_SSPP1, 22, 0 },
+ { DBGBUS_SSPP1, 22, 1 },
+ { DBGBUS_SSPP1, 22, 2 },
+ { DBGBUS_SSPP1, 22, 3 },
+ { DBGBUS_SSPP1, 22, 4 },
+ { DBGBUS_SSPP1, 22, 5 },
+ { DBGBUS_SSPP1, 22, 6 },
+ { DBGBUS_SSPP1, 22, 7 },
+
+ { DBGBUS_SSPP1, 24, 0 },
+ { DBGBUS_SSPP1, 24, 1 },
+ { DBGBUS_SSPP1, 24, 2 },
+ { DBGBUS_SSPP1, 24, 3 },
+ { DBGBUS_SSPP1, 24, 4 },
+ { DBGBUS_SSPP1, 24, 5 },
+ { DBGBUS_SSPP1, 24, 6 },
+ { DBGBUS_SSPP1, 24, 7 },
+
+ { DBGBUS_SSPP1, 25, 0 },
+ { DBGBUS_SSPP1, 25, 1 },
+ { DBGBUS_SSPP1, 25, 2 },
+ { DBGBUS_SSPP1, 25, 3 },
+ { DBGBUS_SSPP1, 25, 4 },
+ { DBGBUS_SSPP1, 25, 5 },
+ { DBGBUS_SSPP1, 25, 6 },
+ { DBGBUS_SSPP1, 25, 7 },
+
+ /* dma 3 */
+ { DBGBUS_SSPP1, 30, 0 },
+ { DBGBUS_SSPP1, 30, 1 },
+ { DBGBUS_SSPP1, 30, 2 },
+ { DBGBUS_SSPP1, 30, 3 },
+ { DBGBUS_SSPP1, 30, 4 },
+ { DBGBUS_SSPP1, 30, 5 },
+ { DBGBUS_SSPP1, 30, 6 },
+ { DBGBUS_SSPP1, 30, 7 },
+
+ { DBGBUS_SSPP1, 31, 0 },
+ { DBGBUS_SSPP1, 31, 1 },
+ { DBGBUS_SSPP1, 31, 2 },
+ { DBGBUS_SSPP1, 31, 3 },
+ { DBGBUS_SSPP1, 31, 4 },
+ { DBGBUS_SSPP1, 31, 5 },
+ { DBGBUS_SSPP1, 31, 6 },
+ { DBGBUS_SSPP1, 31, 7 },
+
+ { DBGBUS_SSPP1, 32, 0 },
+ { DBGBUS_SSPP1, 32, 1 },
+ { DBGBUS_SSPP1, 32, 2 },
+ { DBGBUS_SSPP1, 32, 3 },
+ { DBGBUS_SSPP1, 32, 4 },
+ { DBGBUS_SSPP1, 32, 5 },
+ { DBGBUS_SSPP1, 32, 6 },
+ { DBGBUS_SSPP1, 32, 7 },
+
+ { DBGBUS_SSPP1, 33, 0 },
+ { DBGBUS_SSPP1, 33, 1 },
+ { DBGBUS_SSPP1, 33, 2 },
+ { DBGBUS_SSPP1, 33, 3 },
+ { DBGBUS_SSPP1, 33, 4 },
+ { DBGBUS_SSPP1, 33, 5 },
+ { DBGBUS_SSPP1, 33, 6 },
+ { DBGBUS_SSPP1, 33, 7 },
+
+ { DBGBUS_SSPP1, 34, 0 },
+ { DBGBUS_SSPP1, 34, 1 },
+ { DBGBUS_SSPP1, 34, 2 },
+ { DBGBUS_SSPP1, 34, 3 },
+ { DBGBUS_SSPP1, 34, 4 },
+ { DBGBUS_SSPP1, 34, 5 },
+ { DBGBUS_SSPP1, 34, 6 },
+ { DBGBUS_SSPP1, 34, 7 },
+
+ { DBGBUS_SSPP1, 35, 0 },
+ { DBGBUS_SSPP1, 35, 1 },
+ { DBGBUS_SSPP1, 35, 2 },
+
+ /* dma 1 */
+ { DBGBUS_SSPP1, 40, 0 },
+ { DBGBUS_SSPP1, 40, 1 },
+ { DBGBUS_SSPP1, 40, 2 },
+ { DBGBUS_SSPP1, 40, 3 },
+ { DBGBUS_SSPP1, 40, 4 },
+ { DBGBUS_SSPP1, 40, 5 },
+ { DBGBUS_SSPP1, 40, 6 },
+ { DBGBUS_SSPP1, 40, 7 },
+
+ { DBGBUS_SSPP1, 41, 0 },
+ { DBGBUS_SSPP1, 41, 1 },
+ { DBGBUS_SSPP1, 41, 2 },
+ { DBGBUS_SSPP1, 41, 3 },
+ { DBGBUS_SSPP1, 41, 4 },
+ { DBGBUS_SSPP1, 41, 5 },
+ { DBGBUS_SSPP1, 41, 6 },
+ { DBGBUS_SSPP1, 41, 7 },
+
+ { DBGBUS_SSPP1, 42, 0 },
+ { DBGBUS_SSPP1, 42, 1 },
+ { DBGBUS_SSPP1, 42, 2 },
+ { DBGBUS_SSPP1, 42, 3 },
+ { DBGBUS_SSPP1, 42, 4 },
+ { DBGBUS_SSPP1, 42, 5 },
+ { DBGBUS_SSPP1, 42, 6 },
+ { DBGBUS_SSPP1, 42, 7 },
+
+ { DBGBUS_SSPP1, 44, 0 },
+ { DBGBUS_SSPP1, 44, 1 },
+ { DBGBUS_SSPP1, 44, 2 },
+ { DBGBUS_SSPP1, 44, 3 },
+ { DBGBUS_SSPP1, 44, 4 },
+ { DBGBUS_SSPP1, 44, 5 },
+ { DBGBUS_SSPP1, 44, 6 },
+ { DBGBUS_SSPP1, 44, 7 },
+
+ { DBGBUS_SSPP1, 45, 0 },
+ { DBGBUS_SSPP1, 45, 1 },
+ { DBGBUS_SSPP1, 45, 2 },
+ { DBGBUS_SSPP1, 45, 3 },
+ { DBGBUS_SSPP1, 45, 4 },
+ { DBGBUS_SSPP1, 45, 5 },
+ { DBGBUS_SSPP1, 45, 6 },
+ { DBGBUS_SSPP1, 45, 7 },
+
+ /* cursor 1 */
+ { DBGBUS_SSPP1, 80, 0 },
+ { DBGBUS_SSPP1, 80, 1 },
+ { DBGBUS_SSPP1, 80, 2 },
+ { DBGBUS_SSPP1, 80, 3 },
+ { DBGBUS_SSPP1, 80, 4 },
+ { DBGBUS_SSPP1, 80, 5 },
+ { DBGBUS_SSPP1, 80, 6 },
+ { DBGBUS_SSPP1, 80, 7 },
+
+ { DBGBUS_SSPP1, 81, 0 },
+ { DBGBUS_SSPP1, 81, 1 },
+ { DBGBUS_SSPP1, 81, 2 },
+ { DBGBUS_SSPP1, 81, 3 },
+ { DBGBUS_SSPP1, 81, 4 },
+ { DBGBUS_SSPP1, 81, 5 },
+ { DBGBUS_SSPP1, 81, 6 },
+ { DBGBUS_SSPP1, 81, 7 },
+
+ { DBGBUS_SSPP1, 82, 0 },
+ { DBGBUS_SSPP1, 82, 1 },
+ { DBGBUS_SSPP1, 82, 2 },
+ { DBGBUS_SSPP1, 82, 3 },
+ { DBGBUS_SSPP1, 82, 4 },
+ { DBGBUS_SSPP1, 82, 5 },
+ { DBGBUS_SSPP1, 82, 6 },
+ { DBGBUS_SSPP1, 82, 7 },
+
+ { DBGBUS_SSPP1, 83, 0 },
+ { DBGBUS_SSPP1, 83, 1 },
+ { DBGBUS_SSPP1, 83, 2 },
+ { DBGBUS_SSPP1, 83, 3 },
+ { DBGBUS_SSPP1, 83, 4 },
+ { DBGBUS_SSPP1, 83, 5 },
+ { DBGBUS_SSPP1, 83, 6 },
+ { DBGBUS_SSPP1, 83, 7 },
+
+ { DBGBUS_SSPP1, 84, 0 },
+ { DBGBUS_SSPP1, 84, 1 },
+ { DBGBUS_SSPP1, 84, 2 },
+ { DBGBUS_SSPP1, 84, 3 },
+ { DBGBUS_SSPP1, 84, 4 },
+ { DBGBUS_SSPP1, 84, 5 },
+ { DBGBUS_SSPP1, 84, 6 },
+ { DBGBUS_SSPP1, 84, 7 },
+
+ /* dspp */
+ { DBGBUS_DSPP, 13, 0 },
+ { DBGBUS_DSPP, 19, 0 },
+ { DBGBUS_DSPP, 14, 0 },
+ { DBGBUS_DSPP, 14, 1 },
+ { DBGBUS_DSPP, 14, 3 },
+ { DBGBUS_DSPP, 20, 0 },
+ { DBGBUS_DSPP, 20, 1 },
+ { DBGBUS_DSPP, 20, 3 },
+
+ /* ppb_0 */
+ { DBGBUS_DSPP, 31, 0 },
+ { DBGBUS_DSPP, 33, 0 },
+ { DBGBUS_DSPP, 35, 0 },
+ { DBGBUS_DSPP, 42, 0 },
+
+ /* ppb_1 */
+ { DBGBUS_DSPP, 32, 0 },
+ { DBGBUS_DSPP, 34, 0 },
+ { DBGBUS_DSPP, 36, 0 },
+ { DBGBUS_DSPP, 43, 0 },
+
+ /* lm_lut */
+ { DBGBUS_DSPP, 109, 0 },
+ { DBGBUS_DSPP, 105, 0 },
+ { DBGBUS_DSPP, 103, 0 },
+
+ /* tear-check */
+ { DBGBUS_PERIPH, 63, 0 },
+ { DBGBUS_PERIPH, 64, 0 },
+ { DBGBUS_PERIPH, 65, 0 },
+ { DBGBUS_PERIPH, 73, 0 },
+ { DBGBUS_PERIPH, 74, 0 },
+
+ /* crossbar */
+ { DBGBUS_DSPP, 0, 0},
+
+ /* rotator */
+ { DBGBUS_DSPP, 9, 0},
+
+ /* blend */
+ /* LM0 */
+ { DBGBUS_DSPP, 63, 0},
+ { DBGBUS_DSPP, 63, 1},
+ { DBGBUS_DSPP, 63, 2},
+ { DBGBUS_DSPP, 63, 3},
+ { DBGBUS_DSPP, 63, 4},
+ { DBGBUS_DSPP, 63, 5},
+ { DBGBUS_DSPP, 63, 6},
+ { DBGBUS_DSPP, 63, 7},
+
+ { DBGBUS_DSPP, 64, 0},
+ { DBGBUS_DSPP, 64, 1},
+ { DBGBUS_DSPP, 64, 2},
+ { DBGBUS_DSPP, 64, 3},
+ { DBGBUS_DSPP, 64, 4},
+ { DBGBUS_DSPP, 64, 5},
+ { DBGBUS_DSPP, 64, 6},
+ { DBGBUS_DSPP, 64, 7},
+
+ { DBGBUS_DSPP, 65, 0},
+ { DBGBUS_DSPP, 65, 1},
+ { DBGBUS_DSPP, 65, 2},
+ { DBGBUS_DSPP, 65, 3},
+ { DBGBUS_DSPP, 65, 4},
+ { DBGBUS_DSPP, 65, 5},
+ { DBGBUS_DSPP, 65, 6},
+ { DBGBUS_DSPP, 65, 7},
+
+ { DBGBUS_DSPP, 66, 0},
+ { DBGBUS_DSPP, 66, 1},
+ { DBGBUS_DSPP, 66, 2},
+ { DBGBUS_DSPP, 66, 3},
+ { DBGBUS_DSPP, 66, 4},
+ { DBGBUS_DSPP, 66, 5},
+ { DBGBUS_DSPP, 66, 6},
+ { DBGBUS_DSPP, 66, 7},
+
+ { DBGBUS_DSPP, 67, 0},
+ { DBGBUS_DSPP, 67, 1},
+ { DBGBUS_DSPP, 67, 2},
+ { DBGBUS_DSPP, 67, 3},
+ { DBGBUS_DSPP, 67, 4},
+ { DBGBUS_DSPP, 67, 5},
+ { DBGBUS_DSPP, 67, 6},
+ { DBGBUS_DSPP, 67, 7},
+
+ { DBGBUS_DSPP, 68, 0},
+ { DBGBUS_DSPP, 68, 1},
+ { DBGBUS_DSPP, 68, 2},
+ { DBGBUS_DSPP, 68, 3},
+ { DBGBUS_DSPP, 68, 4},
+ { DBGBUS_DSPP, 68, 5},
+ { DBGBUS_DSPP, 68, 6},
+ { DBGBUS_DSPP, 68, 7},
+
+ { DBGBUS_DSPP, 69, 0},
+ { DBGBUS_DSPP, 69, 1},
+ { DBGBUS_DSPP, 69, 2},
+ { DBGBUS_DSPP, 69, 3},
+ { DBGBUS_DSPP, 69, 4},
+ { DBGBUS_DSPP, 69, 5},
+ { DBGBUS_DSPP, 69, 6},
+ { DBGBUS_DSPP, 69, 7},
+
+ /* LM1 */
+ { DBGBUS_DSPP, 70, 0},
+ { DBGBUS_DSPP, 70, 1},
+ { DBGBUS_DSPP, 70, 2},
+ { DBGBUS_DSPP, 70, 3},
+ { DBGBUS_DSPP, 70, 4},
+ { DBGBUS_DSPP, 70, 5},
+ { DBGBUS_DSPP, 70, 6},
+ { DBGBUS_DSPP, 70, 7},
+
+ { DBGBUS_DSPP, 71, 0},
+ { DBGBUS_DSPP, 71, 1},
+ { DBGBUS_DSPP, 71, 2},
+ { DBGBUS_DSPP, 71, 3},
+ { DBGBUS_DSPP, 71, 4},
+ { DBGBUS_DSPP, 71, 5},
+ { DBGBUS_DSPP, 71, 6},
+ { DBGBUS_DSPP, 71, 7},
+
+ { DBGBUS_DSPP, 72, 0},
+ { DBGBUS_DSPP, 72, 1},
+ { DBGBUS_DSPP, 72, 2},
+ { DBGBUS_DSPP, 72, 3},
+ { DBGBUS_DSPP, 72, 4},
+ { DBGBUS_DSPP, 72, 5},
+ { DBGBUS_DSPP, 72, 6},
+ { DBGBUS_DSPP, 72, 7},
+
+ { DBGBUS_DSPP, 73, 0},
+ { DBGBUS_DSPP, 73, 1},
+ { DBGBUS_DSPP, 73, 2},
+ { DBGBUS_DSPP, 73, 3},
+ { DBGBUS_DSPP, 73, 4},
+ { DBGBUS_DSPP, 73, 5},
+ { DBGBUS_DSPP, 73, 6},
+ { DBGBUS_DSPP, 73, 7},
+
+ { DBGBUS_DSPP, 74, 0},
+ { DBGBUS_DSPP, 74, 1},
+ { DBGBUS_DSPP, 74, 2},
+ { DBGBUS_DSPP, 74, 3},
+ { DBGBUS_DSPP, 74, 4},
+ { DBGBUS_DSPP, 74, 5},
+ { DBGBUS_DSPP, 74, 6},
+ { DBGBUS_DSPP, 74, 7},
+
+ { DBGBUS_DSPP, 75, 0},
+ { DBGBUS_DSPP, 75, 1},
+ { DBGBUS_DSPP, 75, 2},
+ { DBGBUS_DSPP, 75, 3},
+ { DBGBUS_DSPP, 75, 4},
+ { DBGBUS_DSPP, 75, 5},
+ { DBGBUS_DSPP, 75, 6},
+ { DBGBUS_DSPP, 75, 7},
+
+ { DBGBUS_DSPP, 76, 0},
+ { DBGBUS_DSPP, 76, 1},
+ { DBGBUS_DSPP, 76, 2},
+ { DBGBUS_DSPP, 76, 3},
+ { DBGBUS_DSPP, 76, 4},
+ { DBGBUS_DSPP, 76, 5},
+ { DBGBUS_DSPP, 76, 6},
+ { DBGBUS_DSPP, 76, 7},
+
+ /* LM2 */
+ { DBGBUS_DSPP, 77, 0},
+ { DBGBUS_DSPP, 77, 1},
+ { DBGBUS_DSPP, 77, 2},
+ { DBGBUS_DSPP, 77, 3},
+ { DBGBUS_DSPP, 77, 4},
+ { DBGBUS_DSPP, 77, 5},
+ { DBGBUS_DSPP, 77, 6},
+ { DBGBUS_DSPP, 77, 7},
+
+ { DBGBUS_DSPP, 78, 0},
+ { DBGBUS_DSPP, 78, 1},
+ { DBGBUS_DSPP, 78, 2},
+ { DBGBUS_DSPP, 78, 3},
+ { DBGBUS_DSPP, 78, 4},
+ { DBGBUS_DSPP, 78, 5},
+ { DBGBUS_DSPP, 78, 6},
+ { DBGBUS_DSPP, 78, 7},
+
+ { DBGBUS_DSPP, 79, 0},
+ { DBGBUS_DSPP, 79, 1},
+ { DBGBUS_DSPP, 79, 2},
+ { DBGBUS_DSPP, 79, 3},
+ { DBGBUS_DSPP, 79, 4},
+ { DBGBUS_DSPP, 79, 5},
+ { DBGBUS_DSPP, 79, 6},
+ { DBGBUS_DSPP, 79, 7},
+
+ { DBGBUS_DSPP, 80, 0},
+ { DBGBUS_DSPP, 80, 1},
+ { DBGBUS_DSPP, 80, 2},
+ { DBGBUS_DSPP, 80, 3},
+ { DBGBUS_DSPP, 80, 4},
+ { DBGBUS_DSPP, 80, 5},
+ { DBGBUS_DSPP, 80, 6},
+ { DBGBUS_DSPP, 80, 7},
+
+ { DBGBUS_DSPP, 81, 0},
+ { DBGBUS_DSPP, 81, 1},
+ { DBGBUS_DSPP, 81, 2},
+ { DBGBUS_DSPP, 81, 3},
+ { DBGBUS_DSPP, 81, 4},
+ { DBGBUS_DSPP, 81, 5},
+ { DBGBUS_DSPP, 81, 6},
+ { DBGBUS_DSPP, 81, 7},
+
+ { DBGBUS_DSPP, 82, 0},
+ { DBGBUS_DSPP, 82, 1},
+ { DBGBUS_DSPP, 82, 2},
+ { DBGBUS_DSPP, 82, 3},
+ { DBGBUS_DSPP, 82, 4},
+ { DBGBUS_DSPP, 82, 5},
+ { DBGBUS_DSPP, 82, 6},
+ { DBGBUS_DSPP, 82, 7},
+
+ { DBGBUS_DSPP, 83, 0},
+ { DBGBUS_DSPP, 83, 1},
+ { DBGBUS_DSPP, 83, 2},
+ { DBGBUS_DSPP, 83, 3},
+ { DBGBUS_DSPP, 83, 4},
+ { DBGBUS_DSPP, 83, 5},
+ { DBGBUS_DSPP, 83, 6},
+ { DBGBUS_DSPP, 83, 7},
+
+ /* csc */
+ { DBGBUS_SSPP0, 7, 0},
+ { DBGBUS_SSPP0, 7, 1},
+ { DBGBUS_SSPP0, 27, 0},
+ { DBGBUS_SSPP0, 27, 1},
+ { DBGBUS_SSPP1, 7, 0},
+ { DBGBUS_SSPP1, 7, 1},
+ { DBGBUS_SSPP1, 27, 0},
+ { DBGBUS_SSPP1, 27, 1},
+
+ /* pcc */
+ { DBGBUS_SSPP0, 3, 3},
+ { DBGBUS_SSPP0, 23, 3},
+ { DBGBUS_SSPP0, 33, 3},
+ { DBGBUS_SSPP0, 43, 3},
+ { DBGBUS_SSPP1, 3, 3},
+ { DBGBUS_SSPP1, 23, 3},
+ { DBGBUS_SSPP1, 33, 3},
+ { DBGBUS_SSPP1, 43, 3},
+
+ /* spa */
+ { DBGBUS_SSPP0, 8, 0},
+ { DBGBUS_SSPP0, 28, 0},
+ { DBGBUS_SSPP1, 8, 0},
+ { DBGBUS_SSPP1, 28, 0},
+ { DBGBUS_DSPP, 13, 0},
+ { DBGBUS_DSPP, 19, 0},
+
+ /* igc */
+ { DBGBUS_SSPP0, 9, 0},
+ { DBGBUS_SSPP0, 9, 1},
+ { DBGBUS_SSPP0, 9, 3},
+ { DBGBUS_SSPP0, 29, 0},
+ { DBGBUS_SSPP0, 29, 1},
+ { DBGBUS_SSPP0, 29, 3},
+ { DBGBUS_SSPP0, 17, 0},
+ { DBGBUS_SSPP0, 17, 1},
+ { DBGBUS_SSPP0, 17, 3},
+ { DBGBUS_SSPP0, 37, 0},
+ { DBGBUS_SSPP0, 37, 1},
+ { DBGBUS_SSPP0, 37, 3},
+ { DBGBUS_SSPP0, 46, 0},
+ { DBGBUS_SSPP0, 46, 1},
+ { DBGBUS_SSPP0, 46, 3},
+
+ { DBGBUS_SSPP1, 9, 0},
+ { DBGBUS_SSPP1, 9, 1},
+ { DBGBUS_SSPP1, 9, 3},
+ { DBGBUS_SSPP1, 29, 0},
+ { DBGBUS_SSPP1, 29, 1},
+ { DBGBUS_SSPP1, 29, 3},
+ { DBGBUS_SSPP1, 17, 0},
+ { DBGBUS_SSPP1, 17, 1},
+ { DBGBUS_SSPP1, 17, 3},
+ { DBGBUS_SSPP1, 37, 0},
+ { DBGBUS_SSPP1, 37, 1},
+ { DBGBUS_SSPP1, 37, 3},
+ { DBGBUS_SSPP1, 46, 0},
+ { DBGBUS_SSPP1, 46, 1},
+ { DBGBUS_SSPP1, 46, 3},
+
+ { DBGBUS_DSPP, 14, 0},
+ { DBGBUS_DSPP, 14, 1},
+ { DBGBUS_DSPP, 14, 3},
+ { DBGBUS_DSPP, 20, 0},
+ { DBGBUS_DSPP, 20, 1},
+ { DBGBUS_DSPP, 20, 3},
+
+ { DBGBUS_PERIPH, 60, 0},
+};
+
+static struct vbif_debug_bus_entry vbif_dbg_bus_msm8998[] = {
+ {0x214, 0x21c, 16, 2, 0x0, 0xd}, /* arb clients */
+ {0x214, 0x21c, 16, 2, 0x80, 0xc0}, /* arb clients */
+ {0x214, 0x21c, 16, 2, 0x100, 0x140}, /* arb clients */
+ {0x214, 0x21c, 0, 16, 0x0, 0xf}, /* xin blocks - axi side */
+ {0x214, 0x21c, 0, 16, 0x80, 0xa4}, /* xin blocks - axi side */
+ {0x214, 0x21c, 0, 15, 0x100, 0x124}, /* xin blocks - axi side */
+ {0x21c, 0x214, 0, 14, 0, 0xc}, /* xin blocks - clock side */
+};
+
+/**
+ * _sde_dbg_enable_power - use callback to turn power on for hw register access
+ * @enable: whether to turn power on or off
+ */
+static inline void _sde_dbg_enable_power(int enable)
+{
+ if (!sde_dbg_base.power_ctrl.enable_fn)
+ return;
+ sde_dbg_base.power_ctrl.enable_fn(
+ sde_dbg_base.power_ctrl.handle,
+ sde_dbg_base.power_ctrl.client,
+ enable);
+}
+
+/**
+ * _sde_dump_reg - helper function for dumping rotator register set content
+ * @dump_name: register set name
+ * @reg_dump_flag: dumping flag controlling in-log/memory dump location
+ * @base_addr: starting address of io region for calculating offsets to print
+ * @addr: starting address offset for dumping
+ * @len_bytes: range of the register set
+ * @dump_mem: output buffer for memory dump location option
+ * @from_isr: whether being called from isr context
+ */
+static void _sde_dump_reg(const char *dump_name, u32 reg_dump_flag,
+ char __iomem *base_addr, char __iomem *addr, size_t len_bytes,
+ u32 **dump_mem, bool from_isr)
+{
+ u32 in_log, in_mem, len_align, len_padded;
+ u32 *dump_addr = NULL;
+ char __iomem *end_addr;
+ int i;
+
+ if (!len_bytes)
+ return;
+
+ in_log = (reg_dump_flag & SDE_DBG_DUMP_IN_LOG);
+ in_mem = (reg_dump_flag & SDE_DBG_DUMP_IN_MEM);
+
+ pr_debug("%s: reg_dump_flag=%d in_log=%d in_mem=%d\n",
+ dump_name, reg_dump_flag, in_log, in_mem);
+
+ if (!in_log && !in_mem)
+ return;
+
+ if (in_log)
+ dev_info(sde_dbg_base.dev, "%s: start_offset 0x%lx len 0x%zx\n",
+ dump_name, addr - base_addr, len_bytes);
+
+ len_align = (len_bytes + REG_DUMP_ALIGN - 1) / REG_DUMP_ALIGN;
+ len_padded = len_align * REG_DUMP_ALIGN;
+ end_addr = addr + len_bytes;
+
+ if (in_mem) {
+ if (dump_mem && !(*dump_mem)) {
+ phys_addr_t phys = 0;
+ *dump_mem = dma_alloc_coherent(sde_dbg_base.dev,
+ len_padded, &phys, GFP_KERNEL);
+ }
+
+ if (dump_mem && *dump_mem) {
+ dump_addr = *dump_mem;
+ dev_info(sde_dbg_base.dev,
+ "%s: start_addr:0x%pK len:0x%x reg_offset=0x%lx\n",
+ dump_name, dump_addr, len_padded,
+ addr - base_addr);
+ } else {
+ in_mem = 0;
+ pr_err("dump_mem: kzalloc fails!\n");
+ }
+ }
+
+ if (!from_isr)
+ _sde_dbg_enable_power(true);
+
+ for (i = 0; i < len_align; i++) {
+ u32 x0, x4, x8, xc;
+
+ x0 = (addr < end_addr) ? readl_relaxed(addr + 0x0) : 0;
+ x4 = (addr + 0x4 < end_addr) ? readl_relaxed(addr + 0x4) : 0;
+ x8 = (addr + 0x8 < end_addr) ? readl_relaxed(addr + 0x8) : 0;
+ xc = (addr + 0xc < end_addr) ? readl_relaxed(addr + 0xc) : 0;
+
+ if (in_log)
+ dev_info(sde_dbg_base.dev,
+ "0x%lx : %08x %08x %08x %08x\n",
+ addr - base_addr, x0, x4, x8, xc);
+
+ if (dump_addr) {
+ dump_addr[i * 4] = x0;
+ dump_addr[i * 4 + 1] = x4;
+ dump_addr[i * 4 + 2] = x8;
+ dump_addr[i * 4 + 3] = xc;
+ }
+
+ addr += REG_DUMP_ALIGN;
+ }
+
+ if (!from_isr)
+ _sde_dbg_enable_power(false);
+}
+
+/**
+ * _sde_dbg_get_dump_range - helper to retrieve dump length for a range node
+ * @range_node: range node to dump
+ * @max_offset: max offset of the register base
+ * @Return: length
+ */
+static u32 _sde_dbg_get_dump_range(struct sde_dbg_reg_offset *range_node,
+ size_t max_offset)
+{
+ u32 length = 0;
+
+ if ((range_node->start > range_node->end) ||
+ (range_node->end > max_offset) || (range_node->start == 0
+ && range_node->end == 0)) {
+ length = max_offset;
+ } else {
+ length = range_node->end - range_node->start;
+ }
+
+ return length;
+}
+
+static int _sde_dump_reg_range_cmp(void *priv, struct list_head *a,
+ struct list_head *b)
+{
+ struct sde_dbg_reg_range *ar, *br;
+
+ if (!a || !b)
+ return 0;
+
+ ar = container_of(a, struct sde_dbg_reg_range, head);
+ br = container_of(b, struct sde_dbg_reg_range, head);
+
+ return ar->offset.start - br->offset.start;
+}
+
+/**
+ * _sde_dump_reg_by_ranges - dump ranges or full range of the register blk base
+ * @dbg: register blk base structure
+ * @reg_dump_flag: dump target, memory, kernel log, or both
+ */
+static void _sde_dump_reg_by_ranges(struct sde_dbg_reg_base *dbg,
+ u32 reg_dump_flag)
+{
+ char __iomem *addr;
+ size_t len;
+ struct sde_dbg_reg_range *range_node;
+
+ if (!dbg || !dbg->base) {
+ pr_err("dbg base is null!\n");
+ return;
+ }
+
+ dev_info(sde_dbg_base.dev, "%s:=========%s DUMP=========\n", __func__,
+ dbg->name);
+
+ /* If there is a list to dump the registers by ranges, use the ranges */
+ if (!list_empty(&dbg->sub_range_list)) {
+ /* sort the list by start address first */
+ list_sort(NULL, &dbg->sub_range_list, _sde_dump_reg_range_cmp);
+ list_for_each_entry(range_node, &dbg->sub_range_list, head) {
+ len = _sde_dbg_get_dump_range(&range_node->offset,
+ dbg->max_offset);
+ addr = dbg->base + range_node->offset.start;
+ pr_debug("%s: range_base=0x%pK start=0x%x end=0x%x\n",
+ range_node->range_name,
+ addr, range_node->offset.start,
+ range_node->offset.end);
+
+ _sde_dump_reg(range_node->range_name, reg_dump_flag,
+ dbg->base, addr, len,
+ &range_node->reg_dump, false);
+ }
+ } else {
+ /* If there is no list to dump ranges, dump all registers */
+ dev_info(sde_dbg_base.dev,
+ "Ranges not found, will dump full registers\n");
+ dev_info(sde_dbg_base.dev, "base:0x%pK len:0x%zx\n", dbg->base,
+ dbg->max_offset);
+ addr = dbg->base;
+ len = dbg->max_offset;
+ _sde_dump_reg(dbg->name, reg_dump_flag, dbg->base, addr, len,
+ &dbg->reg_dump, false);
+ }
+}
+
+/**
+ * _sde_dump_reg_by_blk - dump a named register base region
+ * @blk_name: register blk name
+ */
+static void _sde_dump_reg_by_blk(const char *blk_name)
+{
+ struct sde_dbg_base *dbg_base = &sde_dbg_base;
+ struct sde_dbg_reg_base *blk_base;
+
+ if (!dbg_base)
+ return;
+
+ list_for_each_entry(blk_base, &dbg_base->reg_base_list, reg_base_head) {
+ if (strlen(blk_base->name) &&
+ !strcmp(blk_base->name, blk_name)) {
+ _sde_dump_reg_by_ranges(blk_base,
+ dbg_base->enable_reg_dump);
+ break;
+ }
+ }
+}
+
+/**
+ * _sde_dump_reg_all - dump all register regions
+ */
+static void _sde_dump_reg_all(void)
+{
+ struct sde_dbg_base *dbg_base = &sde_dbg_base;
+ struct sde_dbg_reg_base *blk_base;
+
+ if (!dbg_base)
+ return;
+
+ list_for_each_entry(blk_base, &dbg_base->reg_base_list, reg_base_head)
+ if (strlen(blk_base->name))
+ _sde_dump_reg_by_blk(blk_base->name);
+}
+
+/**
+ * _sde_dump_get_blk_addr - retrieve register block address by name
+ * @blk_name: register blk name
+ * @Return: register blk base, or NULL
+ */
+static struct sde_dbg_reg_base *_sde_dump_get_blk_addr(const char *blk_name)
+{
+ struct sde_dbg_base *dbg_base = &sde_dbg_base;
+ struct sde_dbg_reg_base *blk_base;
+
+ list_for_each_entry(blk_base, &dbg_base->reg_base_list, reg_base_head)
+ if (strlen(blk_base->name) && !strcmp(blk_base->name, blk_name))
+ return blk_base;
+
+ return NULL;
+}
+
+static void _sde_dbg_dump_sde_dbg_bus(struct sde_dbg_sde_debug_bus *bus)
+{
+ bool in_log, in_mem;
+ u32 **dump_mem = NULL;
+ u32 *dump_addr = NULL;
+ u32 status = 0;
+ struct sde_debug_bus_entry *head;
+ phys_addr_t phys = 0;
+ int list_size;
+ int i;
+ u32 offset;
+ void __iomem *mem_base = NULL;
+ struct sde_dbg_reg_base *reg_base;
+
+ if (!bus || !bus->cmn.entries_size)
+ return;
+
+ list_for_each_entry(reg_base, &sde_dbg_base.reg_base_list,
+ reg_base_head)
+ if (strlen(reg_base->name) &&
+ !strcmp(reg_base->name, bus->cmn.name))
+ mem_base = reg_base->base + bus->top_blk_off;
+
+ if (!mem_base) {
+ pr_err("unable to find mem_base for %s\n", bus->cmn.name);
+ return;
+ }
+
+ dump_mem = &bus->cmn.dumped_content;
+
+ /* will keep in memory 4 entries of 4 bytes each */
+ list_size = (bus->cmn.entries_size * 4 * 4);
+
+ in_log = (bus->cmn.enable_mask & SDE_DBG_DUMP_IN_LOG);
+ in_mem = (bus->cmn.enable_mask & SDE_DBG_DUMP_IN_MEM);
+
+ if (!in_log && !in_mem)
+ return;
+
+ dev_info(sde_dbg_base.dev, "======== start %s dump =========\n",
+ bus->cmn.name);
+
+ if (in_mem) {
+ if (!(*dump_mem))
+ *dump_mem = dma_alloc_coherent(sde_dbg_base.dev,
+ list_size, &phys, GFP_KERNEL);
+
+ if (*dump_mem) {
+ dump_addr = *dump_mem;
+ dev_info(sde_dbg_base.dev,
+ "%s: start_addr:0x%pK len:0x%x\n",
+ __func__, dump_addr, list_size);
+ } else {
+ in_mem = false;
+ pr_err("dump_mem: allocation fails\n");
+ }
+ }
+
+ _sde_dbg_enable_power(true);
+ for (i = 0; i < bus->cmn.entries_size; i++) {
+ head = bus->entries + i;
+ writel_relaxed(TEST_MASK(head->block_id, head->test_id),
+ mem_base + head->wr_addr);
+ wmb(); /* make sure test bits were written */
+
+ if (bus->cmn.flags & DBGBUS_FLAGS_DSPP)
+ offset = DBGBUS_DSPP_STATUS;
+ else
+ offset = head->wr_addr + 0x4;
+
+ status = readl_relaxed(mem_base + offset);
+
+ if (in_log)
+ dev_info(sde_dbg_base.dev,
+ "waddr=0x%x blk=%d tst=%d val=0x%x\n",
+ head->wr_addr, head->block_id,
+ head->test_id, status);
+
+ if (dump_addr && in_mem) {
+ dump_addr[i*4] = head->wr_addr;
+ dump_addr[i*4 + 1] = head->block_id;
+ dump_addr[i*4 + 2] = head->test_id;
+ dump_addr[i*4 + 3] = status;
+ }
+
+ /* Disable debug bus once we are done */
+ writel_relaxed(0, mem_base + head->wr_addr);
+
+ }
+ _sde_dbg_enable_power(false);
+
+ dev_info(sde_dbg_base.dev, "======== end %s dump =========\n",
+ bus->cmn.name);
+}
+
+static void _sde_dbg_dump_vbif_debug_bus_entry(
+ struct vbif_debug_bus_entry *head, void __iomem *mem_base,
+ u32 *dump_addr, bool in_log)
+{
+ int i, j;
+ u32 val;
+
+ if (!dump_addr && !in_log)
+ return;
+
+ for (i = 0; i < head->block_cnt; i++) {
+ writel_relaxed(1 << (i + head->bit_offset),
+ mem_base + head->block_bus_addr);
+ /* make sure that current bus blcok enable */
+ wmb();
+ for (j = head->test_pnt_start; j < head->test_pnt_cnt; j++) {
+ writel_relaxed(j, mem_base + head->block_bus_addr + 4);
+ /* make sure that test point is enabled */
+ wmb();
+ val = readl_relaxed(mem_base + MMSS_VBIF_TEST_BUS_OUT);
+ if (dump_addr) {
+ *dump_addr++ = head->block_bus_addr;
+ *dump_addr++ = i;
+ *dump_addr++ = j;
+ *dump_addr++ = val;
+ }
+ if (in_log)
+ dev_info(sde_dbg_base.dev,
+ "testpoint:%x arb/xin id=%d index=%d val=0x%x\n",
+ head->block_bus_addr, i, j, val);
+ }
+ }
+}
+
+static void _sde_dbg_dump_vbif_dbg_bus(struct sde_dbg_vbif_debug_bus *bus)
+{
+ bool in_log, in_mem;
+ u32 **dump_mem = NULL;
+ u32 *dump_addr = NULL;
+ u32 value;
+ struct vbif_debug_bus_entry *head;
+ phys_addr_t phys = 0;
+ int i, list_size = 0;
+ void __iomem *mem_base = NULL;
+ struct vbif_debug_bus_entry *dbg_bus;
+ u32 bus_size;
+ struct sde_dbg_reg_base *reg_base;
+
+ if (!bus || !bus->cmn.entries_size)
+ return;
+
+ list_for_each_entry(reg_base, &sde_dbg_base.reg_base_list,
+ reg_base_head)
+ if (strlen(reg_base->name) &&
+ !strcmp(reg_base->name, bus->cmn.name))
+ mem_base = reg_base->base;
+
+ if (!mem_base) {
+ pr_err("unable to find mem_base for %s\n", bus->cmn.name);
+ return;
+ }
+
+ dbg_bus = bus->entries;
+ bus_size = bus->cmn.entries_size;
+ list_size = bus->cmn.entries_size;
+ dump_mem = &bus->cmn.dumped_content;
+
+ dev_info(sde_dbg_base.dev, "======== start %s dump =========\n",
+ bus->cmn.name);
+
+ if (!dump_mem || !dbg_bus || !bus_size || !list_size)
+ return;
+
+ /* allocate memory for each test point */
+ for (i = 0; i < bus_size; i++) {
+ head = dbg_bus + i;
+ list_size += (head->block_cnt * head->test_pnt_cnt);
+ }
+
+ /* 4 bytes * 4 entries for each test point*/
+ list_size *= 16;
+
+ in_log = (bus->cmn.enable_mask & SDE_DBG_DUMP_IN_LOG);
+ in_mem = (bus->cmn.enable_mask & SDE_DBG_DUMP_IN_MEM);
+
+ if (!in_log && !in_mem)
+ return;
+
+ if (in_mem) {
+ if (!(*dump_mem))
+ *dump_mem = dma_alloc_coherent(sde_dbg_base.dev,
+ list_size, &phys, GFP_KERNEL);
+
+ if (*dump_mem) {
+ dump_addr = *dump_mem;
+ dev_info(sde_dbg_base.dev,
+ "%s: start_addr:0x%pK len:0x%x\n",
+ __func__, dump_addr, list_size);
+ } else {
+ in_mem = false;
+ pr_err("dump_mem: allocation fails\n");
+ }
+ }
+
+ _sde_dbg_enable_power(true);
+
+ value = readl_relaxed(mem_base + MMSS_VBIF_CLKON);
+ writel_relaxed(value | BIT(1), mem_base + MMSS_VBIF_CLKON);
+
+ /* make sure that vbif core is on */
+ wmb();
+
+ for (i = 0; i < bus_size; i++) {
+ head = dbg_bus + i;
+
+ writel_relaxed(0, mem_base + head->disable_bus_addr);
+ writel_relaxed(BIT(0), mem_base + MMSS_VBIF_TEST_BUS_OUT_CTRL);
+ /* make sure that other bus is off */
+ wmb();
+
+ _sde_dbg_dump_vbif_debug_bus_entry(head, mem_base, dump_addr,
+ in_log);
+ if (dump_addr)
+ dump_addr += (head->block_cnt * head->test_pnt_cnt * 4);
+ }
+
+ _sde_dbg_enable_power(false);
+
+ dev_info(sde_dbg_base.dev, "======== end %s dump =========\n",
+ bus->cmn.name);
+}
+
+/**
+ * _sde_dump_array - dump array of register bases
+ * @blk_arr: array of register base pointers
+ * @len: length of blk_arr
+ * @do_panic: whether to trigger a panic after dumping
+ * @name: string indicating origin of dump
+ * @dump_dbgbus_sde: whether to dump the sde debug bus
+ * @dump_dbgbus_vbif_rt: whether to dump the vbif rt debug bus
+ */
+static void _sde_dump_array(struct sde_dbg_reg_base *blk_arr[],
+ u32 len, bool do_panic, const char *name, bool dump_dbgbus_sde,
+ bool dump_dbgbus_vbif_rt)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ if (blk_arr[i] != NULL)
+ _sde_dump_reg_by_ranges(blk_arr[i],
+ sde_dbg_base.enable_reg_dump);
+ }
+
+ sde_evtlog_dump_all(sde_dbg_base.evtlog);
+
+ if (dump_dbgbus_sde)
+ _sde_dbg_dump_sde_dbg_bus(&sde_dbg_base.dbgbus_sde);
+
+ if (dump_dbgbus_vbif_rt)
+ _sde_dbg_dump_vbif_dbg_bus(&sde_dbg_base.dbgbus_vbif_rt);
+
+ if (do_panic && sde_dbg_base.panic_on_err)
+ panic(name);
+}
+
+/**
+ * _sde_dump_work - deferred dump work function
+ * @work: work structure
+ */
+static void _sde_dump_work(struct work_struct *work)
+{
+ _sde_dump_array(sde_dbg_base.req_dump_blks,
+ ARRAY_SIZE(sde_dbg_base.req_dump_blks),
+ sde_dbg_base.work_panic, "evtlog_workitem",
+ sde_dbg_base.dbgbus_sde.cmn.include_in_deferred_work,
+ sde_dbg_base.dbgbus_vbif_rt.cmn.include_in_deferred_work);
+}
+
+void sde_dbg_dump(bool queue_work, const char *name, ...)
+{
+ int i, index = 0;
+ bool do_panic = false;
+ bool dump_dbgbus_sde = false;
+ bool dump_dbgbus_vbif_rt = false;
+ va_list args;
+ char *blk_name = NULL;
+ struct sde_dbg_reg_base *blk_base = NULL;
+ struct sde_dbg_reg_base **blk_arr;
+ u32 blk_len;
+
+ if (!sde_evtlog_is_enabled(sde_dbg_base.evtlog, SDE_EVTLOG_DEFAULT))
+ return;
+
+ if (queue_work && work_pending(&sde_dbg_base.dump_work))
+ return;
+
+ blk_arr = &sde_dbg_base.req_dump_blks[0];
+ blk_len = ARRAY_SIZE(sde_dbg_base.req_dump_blks);
+
+ memset(sde_dbg_base.req_dump_blks, 0,
+ sizeof(sde_dbg_base.req_dump_blks));
+
+ va_start(args, name);
+ i = 0;
+ while ((blk_name = va_arg(args, char*))) {
+ if (i++ >= SDE_EVTLOG_MAX_DATA) {
+ pr_err("could not parse all dump arguments\n");
+ break;
+ }
+ if (IS_ERR_OR_NULL(blk_name))
+ break;
+
+ blk_base = _sde_dump_get_blk_addr(blk_name);
+ if (blk_base) {
+ if (index < blk_len) {
+ blk_arr[index] = blk_base;
+ index++;
+ } else {
+ pr_err("insufficient space to to dump %s\n",
+ blk_name);
+ }
+ }
+
+ if (!strcmp(blk_name, "dbg_bus"))
+ dump_dbgbus_sde = true;
+
+ if (!strcmp(blk_name, "vbif_dbg_bus"))
+ dump_dbgbus_vbif_rt = true;
+
+ if (!strcmp(blk_name, "panic"))
+ do_panic = true;
+ }
+ va_end(args);
+
+ if (queue_work) {
+ /* schedule work to dump later */
+ sde_dbg_base.work_panic = do_panic;
+ sde_dbg_base.dbgbus_sde.cmn.include_in_deferred_work =
+ dump_dbgbus_sde;
+ sde_dbg_base.dbgbus_vbif_rt.cmn.include_in_deferred_work =
+ dump_dbgbus_vbif_rt;
+ schedule_work(&sde_dbg_base.dump_work);
+ } else {
+ _sde_dump_array(blk_arr, blk_len, do_panic, name,
+ dump_dbgbus_sde, dump_dbgbus_vbif_rt);
+ }
+}
+
+/*
+ * sde_dbg_debugfs_open - debugfs open handler for evtlog dump
+ * @inode: debugfs inode
+ * @file: file handle
+ */
+static int sde_dbg_debugfs_open(struct inode *inode, struct file *file)
+{
+ /* non-seekable */
+ file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+/**
+ * sde_evtlog_dump_read - debugfs read handler for evtlog dump
+ * @file: file handler
+ * @buff: user buffer content for debugfs
+ * @count: size of user buffer
+ * @ppos: position offset of user buffer
+ */
+static ssize_t sde_evtlog_dump_read(struct file *file, char __user *buff,
+ size_t count, loff_t *ppos)
+{
+ ssize_t len = 0;
+ char evtlog_buf[SDE_EVTLOG_BUF_MAX];
+
+ len = sde_evtlog_dump_to_buffer(sde_dbg_base.evtlog, evtlog_buf,
+ SDE_EVTLOG_BUF_MAX);
+ if (copy_to_user(buff, evtlog_buf, len))
+ return -EFAULT;
+ *ppos += len;
+
+ return len;
+}
+
+/**
+ * sde_evtlog_dump_write - debugfs write handler for evtlog dump
+ * @file: file handler
+ * @user_buf: user buffer content from debugfs
+ * @count: size of user buffer
+ * @ppos: position offset of user buffer
+ */
+static ssize_t sde_evtlog_dump_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ _sde_dump_reg_all();
+
+ sde_evtlog_dump_all(sde_dbg_base.evtlog);
+
+ _sde_dbg_dump_sde_dbg_bus(&sde_dbg_base.dbgbus_sde);
+ _sde_dbg_dump_vbif_dbg_bus(&sde_dbg_base.dbgbus_vbif_rt);
+
+ if (sde_dbg_base.panic_on_err)
+ panic("sde");
+
+ return count;
+}
+
+static const struct file_operations sde_evtlog_fops = {
+ .open = sde_dbg_debugfs_open,
+ .read = sde_evtlog_dump_read,
+ .write = sde_evtlog_dump_write,
+};
+
+void sde_dbg_init_dbg_buses(u32 hwversion)
+{
+ static struct sde_dbg_base *dbg = &sde_dbg_base;
+ char debug_name[80] = "";
+
+ memset(&dbg->dbgbus_sde, 0, sizeof(dbg->dbgbus_sde));
+ memset(&dbg->dbgbus_vbif_rt, 0, sizeof(dbg->dbgbus_vbif_rt));
+
+ switch (hwversion) {
+ case SDE_HW_VER_300:
+ case SDE_HW_VER_301:
+ dbg->dbgbus_sde.entries = dbg_bus_sde_8998;
+ dbg->dbgbus_sde.cmn.entries_size = ARRAY_SIZE(dbg_bus_sde_8998);
+ dbg->dbgbus_sde.cmn.flags = DBGBUS_FLAGS_DSPP;
+
+ dbg->dbgbus_vbif_rt.entries = vbif_dbg_bus_msm8998;
+ dbg->dbgbus_vbif_rt.cmn.entries_size =
+ ARRAY_SIZE(vbif_dbg_bus_msm8998);
+ break;
+ default:
+ pr_err("unsupported chipset id %u\n", hwversion);
+ break;
+ }
+
+ if (dbg->dbgbus_sde.entries) {
+ dbg->dbgbus_sde.cmn.name = DBGBUS_NAME_SDE;
+ snprintf(debug_name, sizeof(debug_name), "%s_dbgbus",
+ dbg->dbgbus_sde.cmn.name);
+ dbg->dbgbus_sde.cmn.enable_mask = DEFAULT_DBGBUS_SDE;
+ debugfs_create_u32(debug_name, 0600, dbg->root,
+ &dbg->dbgbus_sde.cmn.enable_mask);
+ }
+
+ if (dbg->dbgbus_vbif_rt.entries) {
+ dbg->dbgbus_vbif_rt.cmn.name = DBGBUS_NAME_VBIF_RT;
+ snprintf(debug_name, sizeof(debug_name), "%s_dbgbus",
+ dbg->dbgbus_vbif_rt.cmn.name);
+ dbg->dbgbus_vbif_rt.cmn.enable_mask = DEFAULT_DBGBUS_VBIFRT;
+ debugfs_create_u32(debug_name, 0600, dbg->root,
+ &dbg->dbgbus_vbif_rt.cmn.enable_mask);
+ }
+}
+
+int sde_dbg_init(struct dentry *debugfs_root, struct device *dev,
+ struct sde_dbg_power_ctrl *power_ctrl)
+{
+ int i;
+
+ INIT_LIST_HEAD(&sde_dbg_base.reg_base_list);
+ sde_dbg_base.dev = dev;
+ sde_dbg_base.power_ctrl = *power_ctrl;
+
+
+ sde_dbg_base.evtlog = sde_evtlog_init();
+ if (IS_ERR_OR_NULL(sde_dbg_base.evtlog))
+ return PTR_ERR(sde_dbg_base.evtlog);
+
+ sde_dbg_base_evtlog = sde_dbg_base.evtlog;
+
+ sde_dbg_base.root = debugfs_create_dir("evt_dbg", debugfs_root);
+ if (IS_ERR_OR_NULL(sde_dbg_base.root)) {
+ pr_err("debugfs_create_dir fail, error %ld\n",
+ PTR_ERR(sde_dbg_base.root));
+ sde_dbg_base.root = NULL;
+ return -ENODEV;
+ }
+
+ INIT_WORK(&sde_dbg_base.dump_work, _sde_dump_work);
+ sde_dbg_base.work_panic = false;
+
+ for (i = 0; i < SDE_EVTLOG_ENTRY; i++)
+ sde_dbg_base.evtlog->logs[i].counter = i;
+
+ debugfs_create_file("dump", 0600, sde_dbg_base.root, NULL,
+ &sde_evtlog_fops);
+ debugfs_create_u32("enable", 0600, sde_dbg_base.root,
+ &(sde_dbg_base.evtlog->enable));
+ debugfs_create_u32("panic", 0600, sde_dbg_base.root,
+ &sde_dbg_base.panic_on_err);
+ debugfs_create_u32("reg_dump", 0600, sde_dbg_base.root,
+ &sde_dbg_base.enable_reg_dump);
+
+ sde_dbg_base.panic_on_err = DEFAULT_PANIC;
+ sde_dbg_base.enable_reg_dump = DEFAULT_REGDUMP;
+
+ pr_info("evtlog_status: enable:%d, panic:%d, dump:%d\n",
+ sde_dbg_base.evtlog->enable, sde_dbg_base.panic_on_err,
+ sde_dbg_base.enable_reg_dump);
+
+ return 0;
+}
+
+/**
+ * sde_dbg_destroy - destroy sde debug facilities
+ */
+void sde_dbg_destroy(void)
+{
+ debugfs_remove_recursive(sde_dbg_base.root);
+ sde_dbg_base.root = NULL;
+
+ sde_dbg_base_evtlog = NULL;
+ sde_evtlog_destroy(sde_dbg_base.evtlog);
+ sde_dbg_base.evtlog = NULL;
+}
+
+/**
+ * sde_dbg_reg_base_release - release allocated reg dump file private data
+ * @inode: debugfs inode
+ * @file: file handle
+ * @Return: 0 on success
+ */
+static int sde_dbg_reg_base_release(struct inode *inode, struct file *file)
+{
+ struct sde_dbg_reg_base *dbg = file->private_data;
+
+ if (dbg && dbg->buf) {
+ kfree(dbg->buf);
+ dbg->buf_len = 0;
+ dbg->buf = NULL;
+ }
+ return 0;
+}
+
+
+/**
+ * sde_dbg_reg_base_offset_write - set new offset and len to debugfs reg base
+ * @file: file handler
+ * @user_buf: user buffer content from debugfs
+ * @count: size of user buffer
+ * @ppos: position offset of user buffer
+ */
+static ssize_t sde_dbg_reg_base_offset_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct sde_dbg_reg_base *dbg = file->private_data;
+ u32 off = 0;
+ u32 cnt = DEFAULT_BASE_REG_CNT;
+ char buf[24];
+
+ if (!dbg)
+ return -ENODEV;
+
+ if (count >= sizeof(buf))
+ return -EFAULT;
+
+ if (copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+
+ buf[count] = 0; /* end of string */
+
+ if (sscanf(buf, "%5x %x", &off, &cnt) != 2)
+ return -EFAULT;
+
+ if (off > dbg->max_offset)
+ return -EINVAL;
+
+ if (off % sizeof(u32))
+ return -EINVAL;
+
+ if (cnt > (dbg->max_offset - off))
+ cnt = dbg->max_offset - off;
+
+ if (cnt % sizeof(u32))
+ return -EINVAL;
+
+ dbg->off = off;
+ dbg->cnt = cnt;
+
+ pr_debug("offset=%x cnt=%x\n", off, cnt);
+
+ return count;
+}
+
+/**
+ * sde_dbg_reg_base_offset_read - read current offset and len of register base
+ * @file: file handler
+ * @user_buf: user buffer content from debugfs
+ * @count: size of user buffer
+ * @ppos: position offset of user buffer
+ */
+static ssize_t sde_dbg_reg_base_offset_read(struct file *file,
+ char __user *buff, size_t count, loff_t *ppos)
+{
+ struct sde_dbg_reg_base *dbg = file->private_data;
+ int len = 0;
+ char buf[24] = {'\0'};
+
+ if (!dbg)
+ return -ENODEV;
+
+ if (*ppos)
+ return 0; /* the end */
+
+ len = snprintf(buf, sizeof(buf), "0x%08zx %zx\n", dbg->off, dbg->cnt);
+ if (len < 0 || len >= sizeof(buf))
+ return 0;
+
+ if ((count < sizeof(buf)) || copy_to_user(buff, buf, len))
+ return -EFAULT;
+
+ *ppos += len; /* increase offset */
+
+ return len;
+}
+
+/**
+ * sde_dbg_reg_base_reg_write - write to reg base hw at offset a given value
+ * @file: file handler
+ * @user_buf: user buffer content from debugfs
+ * @count: size of user buffer
+ * @ppos: position offset of user buffer
+ */
+static ssize_t sde_dbg_reg_base_reg_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct sde_dbg_reg_base *dbg = file->private_data;
+ size_t off;
+ u32 data, cnt;
+ char buf[24];
+
+ if (!dbg)
+ return -ENODEV;
+
+ if (count >= sizeof(buf))
+ return -EFAULT;
+
+ if (copy_from_user(buf, user_buf, count))
+ return -EFAULT;
+
+ buf[count] = 0; /* end of string */
+
+ cnt = sscanf(buf, "%zx %x", &off, &data);
+
+ if (cnt < 2)
+ return -EFAULT;
+
+ if (off >= dbg->max_offset)
+ return -EFAULT;
+
+ _sde_dbg_enable_power(true);
+
+ writel_relaxed(data, dbg->base + off);
+
+ _sde_dbg_enable_power(false);
+
+ pr_debug("addr=%zx data=%x\n", off, data);
+
+ return count;
+}
+
+/**
+ * sde_dbg_reg_base_reg_read - read len from reg base hw at current offset
+ * @file: file handler
+ * @user_buf: user buffer content from debugfs
+ * @count: size of user buffer
+ * @ppos: position offset of user buffer
+ */
+static ssize_t sde_dbg_reg_base_reg_read(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct sde_dbg_reg_base *dbg = file->private_data;
+ size_t len;
+
+ if (!dbg) {
+ pr_err("invalid handle\n");
+ return -ENODEV;
+ }
+
+ if (!dbg->buf) {
+ char *hwbuf, *hwbuf_cur;
+ char dump_buf[64];
+ char __iomem *ioptr;
+ int cnt, tot;
+
+ dbg->buf_len = sizeof(dump_buf) *
+ DIV_ROUND_UP(dbg->cnt, ROW_BYTES);
+
+ if (dbg->buf_len % sizeof(u32))
+ return -EINVAL;
+
+ dbg->buf = kzalloc(dbg->buf_len, GFP_KERNEL);
+
+ if (!dbg->buf)
+ return -ENOMEM;
+
+ hwbuf = kzalloc(dbg->buf_len, GFP_KERNEL);
+ if (!hwbuf) {
+ kfree(dbg->buf);
+ return -ENOMEM;
+ }
+ hwbuf_cur = hwbuf;
+
+ ioptr = dbg->base + dbg->off;
+ tot = 0;
+
+ _sde_dbg_enable_power(true);
+
+ memcpy_fromio(hwbuf, ioptr, dbg->buf_len);
+
+ _sde_dbg_enable_power(false);
+
+ for (cnt = dbg->cnt; cnt > 0; cnt -= ROW_BYTES) {
+ hex_dump_to_buffer(hwbuf_cur,
+ min(cnt, ROW_BYTES),
+ ROW_BYTES, GROUP_BYTES, dump_buf,
+ sizeof(dump_buf), false);
+ len = scnprintf(dbg->buf + tot, dbg->buf_len - tot,
+ "0x%08x: %s\n",
+ ((int) (unsigned long) hwbuf_cur) -
+ ((int) (unsigned long) dbg->base),
+ dump_buf);
+
+ hwbuf_cur += ROW_BYTES;
+ tot += len;
+ if (tot >= dbg->buf_len)
+ break;
+ }
+
+ dbg->buf_len = tot;
+ kfree(hwbuf);
+ }
+
+ if (*ppos >= dbg->buf_len)
+ return 0; /* done reading */
+
+ len = min(count, dbg->buf_len - (size_t) *ppos);
+ if (copy_to_user(user_buf, dbg->buf + *ppos, len)) {
+ pr_err("failed to copy to user\n");
+ return -EFAULT;
+ }
+
+ *ppos += len; /* increase offset */
+
+ return len;
+}
+
+static const struct file_operations sde_off_fops = {
+ .open = sde_dbg_debugfs_open,
+ .release = sde_dbg_reg_base_release,
+ .read = sde_dbg_reg_base_offset_read,
+ .write = sde_dbg_reg_base_offset_write,
+};
+
+static const struct file_operations sde_reg_fops = {
+ .open = sde_dbg_debugfs_open,
+ .release = sde_dbg_reg_base_release,
+ .read = sde_dbg_reg_base_reg_read,
+ .write = sde_dbg_reg_base_reg_write,
+};
+
+int sde_dbg_reg_register_base(const char *name, void __iomem *base,
+ size_t max_offset)
+{
+ struct sde_dbg_base *dbg_base = &sde_dbg_base;
+ struct sde_dbg_reg_base *reg_base;
+ struct dentry *ent_off, *ent_reg;
+ char dn[80] = "";
+ int prefix_len = 0;
+
+ reg_base = kzalloc(sizeof(*reg_base), GFP_KERNEL);
+ if (!reg_base)
+ return -ENOMEM;
+
+ if (name)
+ strlcpy(reg_base->name, name, sizeof(reg_base->name));
+ reg_base->base = base;
+ reg_base->max_offset = max_offset;
+ reg_base->off = 0;
+ reg_base->cnt = DEFAULT_BASE_REG_CNT;
+ reg_base->reg_dump = NULL;
+
+ if (name)
+ prefix_len = snprintf(dn, sizeof(dn), "%s_", name);
+ strlcpy(dn + prefix_len, "off", sizeof(dn) - prefix_len);
+ ent_off = debugfs_create_file(dn, 0600, dbg_base->root, reg_base,
+ &sde_off_fops);
+ if (IS_ERR_OR_NULL(ent_off)) {
+ pr_err("debugfs_create_file: offset fail\n");
+ goto off_fail;
+ }
+
+ strlcpy(dn + prefix_len, "reg", sizeof(dn) - prefix_len);
+ ent_reg = debugfs_create_file(dn, 0600, dbg_base->root, reg_base,
+ &sde_reg_fops);
+ if (IS_ERR_OR_NULL(ent_reg)) {
+ pr_err("debugfs_create_file: reg fail\n");
+ goto reg_fail;
+ }
+
+ /* Initialize list to make sure check for null list will be valid */
+ INIT_LIST_HEAD(&reg_base->sub_range_list);
+
+ pr_debug("%s base: %pK max_offset 0x%zX\n", reg_base->name,
+ reg_base->base, reg_base->max_offset);
+
+ list_add(&reg_base->reg_base_head, &dbg_base->reg_base_list);
+
+ return 0;
+reg_fail:
+ debugfs_remove(ent_off);
+off_fail:
+ kfree(reg_base);
+ return -ENODEV;
+}
+
+void sde_dbg_reg_register_dump_range(const char *base_name,
+ const char *range_name, u32 offset_start, u32 offset_end,
+ uint32_t xin_id)
+{
+ struct sde_dbg_reg_base *reg_base;
+ struct sde_dbg_reg_range *range;
+
+ reg_base = _sde_dump_get_blk_addr(base_name);
+ if (!reg_base) {
+ pr_err("error: for range %s unable to locate base %s\n",
+ range_name, base_name);
+ return;
+ }
+
+ if (!range_name || strlen(range_name) == 0) {
+ pr_err("%pS: bad range name, base_name %s, offset_start 0x%X, end 0x%X\n",
+ __builtin_return_address(0), base_name,
+ offset_start, offset_end);
+ return;
+ }
+
+ if (offset_end - offset_start < REG_DUMP_ALIGN ||
+ offset_start > offset_end) {
+ pr_err("%pS: bad range, base_name %s, range_name %s, offset_start 0x%X, end 0x%X\n",
+ __builtin_return_address(0), base_name,
+ range_name, offset_start, offset_end);
+ return;
+ }
+
+ range = kzalloc(sizeof(*range), GFP_KERNEL);
+ if (!range)
+ return;
+
+ strlcpy(range->range_name, range_name, sizeof(range->range_name));
+ range->offset.start = offset_start;
+ range->offset.end = offset_end;
+ range->xin_id = xin_id;
+ list_add_tail(&range->head, &reg_base->sub_range_list);
+
+ pr_debug("base %s, range %s, start 0x%X, end 0x%X\n",
+ base_name, range->range_name,
+ range->offset.start, range->offset.end);
+}
+
+void sde_dbg_set_sde_top_offset(u32 blk_off)
+{
+ sde_dbg_base.dbgbus_sde.top_blk_off = blk_off;
+}
diff --git a/drivers/gpu/drm/msm/sde_dbg.h b/drivers/gpu/drm/msm/sde_dbg.h
index 271c41f05ce5..74fd4c94b490 100644
--- a/drivers/gpu/drm/msm/sde_dbg.h
+++ b/drivers/gpu/drm/msm/sde_dbg.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, 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
@@ -29,34 +29,288 @@ enum sde_dbg_evtlog_flag {
SDE_EVTLOG_ALL = BIT(7)
};
+enum sde_dbg_dump_flag {
+ SDE_DBG_DUMP_IN_LOG = BIT(0),
+ SDE_DBG_DUMP_IN_MEM = BIT(1),
+};
+
+#ifdef CONFIG_DRM_SDE_EVTLOG_DEBUG
+#define SDE_EVTLOG_DEFAULT_ENABLE 1
+#else
+#define SDE_EVTLOG_DEFAULT_ENABLE 0
+#endif
+
+/*
+ * evtlog will print this number of entries when it is called through
+ * sysfs node or panic. This prevents kernel log from evtlog message
+ * flood.
+ */
+#define SDE_EVTLOG_PRINT_ENTRY 256
+
+/*
+ * evtlog keeps this number of entries in memory for debug purpose. This
+ * number must be greater than print entry to prevent out of bound evtlog
+ * entry array access.
+ */
+#define SDE_EVTLOG_ENTRY (SDE_EVTLOG_PRINT_ENTRY * 4)
+#define SDE_EVTLOG_MAX_DATA 15
+#define SDE_EVTLOG_BUF_MAX 512
+#define SDE_EVTLOG_BUF_ALIGN 32
+
+struct sde_dbg_power_ctrl {
+ void *handle;
+ void *client;
+ int (*enable_fn)(void *handle, void *client, bool enable);
+};
+
+struct sde_dbg_evtlog_log {
+ u32 counter;
+ s64 time;
+ const char *name;
+ int line;
+ u32 data[SDE_EVTLOG_MAX_DATA];
+ u32 data_cnt;
+ int pid;
+};
+
+struct sde_dbg_evtlog {
+ struct sde_dbg_evtlog_log logs[SDE_EVTLOG_ENTRY];
+ u32 first;
+ u32 last;
+ u32 curr;
+ u32 next;
+ u32 enable;
+ spinlock_t spin_lock;
+};
+
+extern struct sde_dbg_evtlog *sde_dbg_base_evtlog;
+
+/**
+ * SDE_EVT32 - Write a list of 32bit values to the event log, default area
+ * ... - variable arguments
+ */
+#define SDE_EVT32(...) sde_evtlog_log(sde_dbg_base_evtlog, __func__, \
+ __LINE__, SDE_EVTLOG_DEFAULT, ##__VA_ARGS__, \
+ SDE_EVTLOG_DATA_LIMITER)
+
/**
- * SDE_EVT32 - Write an list of 32bit values as an event into the event log
+ * SDE_EVT32_IRQ - Write a list of 32bit values to the event log, IRQ area
* ... - variable arguments
*/
-#define SDE_EVT32(...) sde_evtlog(__func__, __LINE__, SDE_EVTLOG_DEFAULT, \
- ##__VA_ARGS__, SDE_EVTLOG_DATA_LIMITER)
-#define SDE_EVT32_IRQ(...) sde_evtlog(__func__, __LINE__, SDE_EVTLOG_IRQ, \
- ##__VA_ARGS__, SDE_EVTLOG_DATA_LIMITER)
+#define SDE_EVT32_IRQ(...) sde_evtlog_log(sde_dbg_base_evtlog, __func__, \
+ __LINE__, SDE_EVTLOG_IRQ, ##__VA_ARGS__, \
+ SDE_EVTLOG_DATA_LIMITER)
-#define SDE_DBG_DUMP(...) \
- sde_dbg_dump(false, __func__, ##__VA_ARGS__, \
+/**
+ * SDE_DBG_DUMP - trigger dumping of all sde_dbg facilities
+ * @va_args: list of named register dump ranges and regions to dump, as
+ * registered previously through sde_dbg_reg_register_base and
+ * sde_dbg_reg_register_dump_range.
+ * Including the special name "panic" will trigger a panic after
+ * the dumping work has completed.
+ */
+#define SDE_DBG_DUMP(...) sde_dbg_dump(false, __func__, ##__VA_ARGS__, \
SDE_DBG_DUMP_DATA_LIMITER)
-#define SDE_DBG_DUMP_WQ(...) \
- sde_dbg_dump(true, __func__, ##__VA_ARGS__, \
+/**
+ * SDE_DBG_DUMP_WQ - trigger dumping of all sde_dbg facilities, queuing the work
+ * @va_args: list of named register dump ranges and regions to dump, as
+ * registered previously through sde_dbg_reg_register_base and
+ * sde_dbg_reg_register_dump_range.
+ * Including the special name "panic" will trigger a panic after
+ * the dumping work has completed.
+ */
+#define SDE_DBG_DUMP_WQ(...) sde_dbg_dump(true, __func__, ##__VA_ARGS__, \
SDE_DBG_DUMP_DATA_LIMITER)
#if defined(CONFIG_DEBUG_FS)
-int sde_evtlog_init(struct dentry *debugfs_root);
-void sde_evtlog_destroy(void);
-void sde_evtlog(const char *name, int line, int flag, ...);
-void sde_dbg_dump(bool queue, const char *name, ...);
+/**
+ * sde_evtlog_init - allocate a new event log object
+ * Returns: evtlog or -ERROR
+ */
+struct sde_dbg_evtlog *sde_evtlog_init(void);
+
+/**
+ * sde_evtlog_destroy - destroy previously allocated event log
+ * @evtlog: pointer to evtlog
+ * Returns: none
+ */
+void sde_evtlog_destroy(struct sde_dbg_evtlog *evtlog);
+
+/**
+ * sde_evtlog_log - log an entry into the event log.
+ * log collection may be enabled/disabled entirely via debugfs
+ * log area collection may be filtered by user provided flags via debugfs.
+ * @evtlog: pointer to evtlog
+ * @name: function name of call site
+ * @line: line number of call site
+ * @flag: log area filter flag checked against user's debugfs request
+ * Returns: none
+ */
+void sde_evtlog_log(struct sde_dbg_evtlog *evtlog, const char *name, int line,
+ int flag, ...);
+
+/**
+ * sde_evtlog_dump_all - print all entries in event log to kernel log
+ * @evtlog: pointer to evtlog
+ * Returns: none
+ */
+void sde_evtlog_dump_all(struct sde_dbg_evtlog *evtlog);
+
+/**
+ * sde_evtlog_is_enabled - check whether log collection is enabled for given
+ * event log and log area flag
+ * @evtlog: pointer to evtlog
+ * @flag: log area filter flag
+ * Returns: none
+ */
+bool sde_evtlog_is_enabled(struct sde_dbg_evtlog *evtlog, u32 flag);
+
+/**
+ * sde_evtlog_dump_to_buffer - print content of event log to the given buffer
+ * @evtlog: pointer to evtlog
+ * @evtlog_buf: target buffer to print into
+ * @evtlog_buf_size: size of target buffer
+ * Returns: number of bytes written to buffer
+ */
+ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog,
+ char *evtlog_buf, ssize_t evtlog_buf_size);
+
+/**
+ * sde_dbg_init_dbg_buses - initialize debug bus dumping support for the chipset
+ * @hwversion: Chipset revision
+ */
+void sde_dbg_init_dbg_buses(u32 hwversion);
+
+/**
+ * sde_dbg_init - initialize global sde debug facilities: evtlog, regdump
+ * @debugfs_root: debugfs root in which to create sde debug entries
+ * @dev: device handle
+ * @power_ctrl: power control callback structure for enabling clocks
+ * during register dumping
+ * Returns: 0 or -ERROR
+ */
+int sde_dbg_init(struct dentry *debugfs_root, struct device *dev,
+ struct sde_dbg_power_ctrl *power_ctrl);
+
+/**
+ * sde_dbg_destroy - destroy the global sde debug facilities
+ * Returns: none
+ */
+void sde_dbg_destroy(void);
+
+/**
+ * sde_dbg_dump - trigger dumping of all sde_dbg facilities
+ * @queue_work: whether to queue the dumping work to the work_struct
+ * @name: string indicating origin of dump
+ * @va_args: list of named register dump ranges and regions to dump, as
+ * registered previously through sde_dbg_reg_register_base and
+ * sde_dbg_reg_register_dump_range.
+ * Including the special name "panic" will trigger a panic after
+ * the dumping work has completed.
+ * Returns: none
+ */
+void sde_dbg_dump(bool queue_work, const char *name, ...);
+
+/**
+ * sde_dbg_reg_register_base - register a hw register address section for later
+ * dumping. call this before calling sde_dbg_reg_register_dump_range
+ * to be able to specify sub-ranges within the base hw range.
+ * @name: name of base region
+ * @base: base pointer of region
+ * @max_offset: length of region
+ * Returns: 0 or -ERROR
+ */
+int sde_dbg_reg_register_base(const char *name, void __iomem *base,
+ size_t max_offset);
+
+/**
+ * sde_dbg_reg_register_dump_range - register a hw register sub-region for
+ * later register dumping associated with base specified by
+ * sde_dbg_reg_register_base
+ * @base_name: name of base region
+ * @range_name: name of sub-range within base region
+ * @offset_start: sub-range's start offset from base's base pointer
+ * @offset_end: sub-range's end offset from base's base pointer
+ * @xin_id: xin id
+ * Returns: none
+ */
+void sde_dbg_reg_register_dump_range(const char *base_name,
+ const char *range_name, u32 offset_start, u32 offset_end,
+ uint32_t xin_id);
+
+/**
+ * sde_dbg_set_sde_top_offset - set the target specific offset from mdss base
+ * address of the top registers. Used for accessing debug bus controls.
+ * @blk_off: offset from mdss base of the top block
+ */
+void sde_dbg_set_sde_top_offset(u32 blk_off);
#else
-static inline int sde_evtlog_init(struct dentry *debugfs_root) { return 0; }
-static inline void sde_evtlog(const char *name, int line, flag, ...) {}
-static inline void sde_evtlog_destroy(void) { }
-static inline void sde_dbg_dump(bool queue, const char *name, ...) {}
-#endif
+static inline struct sde_dbg_evtlog *sde_evtlog_init(void)
+{
+ return NULL;
+}
+
+static inline void sde_evtlog_destroy(struct sde_dbg_evtlog *evtlog)
+{
+}
+
+static inline void sde_evtlog_log(struct sde_dbg_evtlog *evtlog,
+ const char *name, int line, int flag, ...)
+{
+}
+
+static inline void sde_evtlog_dump_all(struct sde_dbg_evtlog *evtlog)
+{
+}
+
+static inline bool sde_evtlog_is_enabled(struct sde_dbg_evtlog *evtlog,
+ u32 flag)
+{
+ return false;
+}
+
+static inline ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog,
+ char *evtlog_buf, ssize_t evtlog_buf_size)
+{
+ return 0;
+}
+
+void sde_dbg_init_dbg_buses(u32 hwversion)
+{
+}
+
+static inline int sde_dbg_init(struct dentry *debugfs_root, struct device *dev,
+ struct sde_dbg_power_ctrl *power_ctrl)
+{
+ return 0;
+}
+
+static inline void sde_dbg_destroy(void)
+{
+}
+
+static inline void sde_dbg_dump(bool queue_work, const char *name, ...)
+{
+}
+
+static inline int sde_dbg_reg_register_base(const char *name,
+ void __iomem *base, size_t max_offset)
+{
+ return 0;
+}
+
+static inline void sde_dbg_reg_register_dump_range(const char *base_name,
+ const char *range_name, u32 offset_start, u32 offset_end,
+ uint32_t xin_id)
+{
+}
+
+void sde_dbg_set_sde_top_offset(u32 blk_off)
+{
+}
+#endif /* defined(CONFIG_DEBUG_FS) */
+
#endif /* SDE_DBG_H_ */
diff --git a/drivers/gpu/drm/msm/sde_dbg_evtlog.c b/drivers/gpu/drm/msm/sde_dbg_evtlog.c
index 72832776659d..759bdab48840 100644
--- a/drivers/gpu/drm/msm/sde_dbg_evtlog.c
+++ b/drivers/gpu/drm/msm/sde_dbg_evtlog.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, 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
@@ -10,7 +10,7 @@
* GNU General Public License for more details.
*/
-#define pr_fmt(fmt) "sde_evtlog:[%s] " fmt, __func__
+#define pr_fmt(fmt) "sde_dbg:[%s] " fmt, __func__
#include <linux/delay.h>
#include <linux/spinlock.h>
@@ -18,77 +18,36 @@
#include <linux/debugfs.h>
#include <linux/uaccess.h>
#include <linux/dma-buf.h>
+#include <linux/slab.h>
#include "sde_dbg.h"
#include "sde_trace.h"
-#ifdef CONFIG_DRM_SDE_EVTLOG_DEBUG
-#define SDE_EVTLOG_DEFAULT_ENABLE 1
-#else
-#define SDE_EVTLOG_DEFAULT_ENABLE 0
-#endif
-
-#define SDE_DBG_DEFAULT_PANIC 1
-
-/*
- * evtlog will print this number of entries when it is called through
- * sysfs node or panic. This prevents kernel log from evtlog message
- * flood.
- */
-#define SDE_EVTLOG_PRINT_ENTRY 256
-
-/*
- * evtlog keeps this number of entries in memory for debug purpose. This
- * number must be greater than print entry to prevent out of bound evtlog
- * entry array access.
- */
-#define SDE_EVTLOG_ENTRY (SDE_EVTLOG_PRINT_ENTRY * 4)
-#define SDE_EVTLOG_MAX_DATA 15
-#define SDE_EVTLOG_BUF_MAX 512
-#define SDE_EVTLOG_BUF_ALIGN 32
-
-DEFINE_SPINLOCK(sde_evtloglock);
-
-struct tlog {
- u32 counter;
- s64 time;
- const char *name;
- int line;
- u32 data[SDE_EVTLOG_MAX_DATA];
- u32 data_cnt;
- int pid;
-};
-
-static struct sde_dbg_evtlog {
- struct tlog logs[SDE_EVTLOG_ENTRY];
- u32 first;
- u32 last;
- u32 curr;
- struct dentry *evtlog;
- u32 evtlog_enable;
- u32 panic_on_err;
- struct work_struct evtlog_dump_work;
- bool work_panic;
-} sde_dbg_evtlog;
-
-static inline bool sde_evtlog_is_enabled(u32 flag)
+bool sde_evtlog_is_enabled(struct sde_dbg_evtlog *evtlog, u32 flag)
{
- return (flag & sde_dbg_evtlog.evtlog_enable) ||
- (flag == SDE_EVTLOG_ALL && sde_dbg_evtlog.evtlog_enable);
+ if (!evtlog)
+ return false;
+
+ return (flag & evtlog->enable) ||
+ (flag == SDE_EVTLOG_ALL && evtlog->enable);
}
-void sde_evtlog(const char *name, int line, int flag, ...)
+void sde_evtlog_log(struct sde_dbg_evtlog *evtlog, const char *name, int line,
+ int flag, ...)
{
unsigned long flags;
int i, val = 0;
va_list args;
- struct tlog *log;
+ struct sde_dbg_evtlog_log *log;
+
+ if (!evtlog)
+ return;
- if (!sde_evtlog_is_enabled(flag))
+ if (!sde_evtlog_is_enabled(evtlog, flag))
return;
- spin_lock_irqsave(&sde_evtloglock, flags);
- log = &sde_dbg_evtlog.logs[sde_dbg_evtlog.curr];
+ spin_lock_irqsave(&evtlog->spin_lock, flags);
+ log = &evtlog->logs[evtlog->curr];
log->time = ktime_to_us(ktime_get());
log->name = name;
log->line = line;
@@ -106,26 +65,27 @@ void sde_evtlog(const char *name, int line, int flag, ...)
}
va_end(args);
log->data_cnt = i;
- sde_dbg_evtlog.curr = (sde_dbg_evtlog.curr + 1) % SDE_EVTLOG_ENTRY;
- sde_dbg_evtlog.last++;
+ evtlog->curr = (evtlog->curr + 1) % SDE_EVTLOG_ENTRY;
+ evtlog->last++;
trace_sde_evtlog(name, line, i > 0 ? log->data[0] : 0,
i > 1 ? log->data[1] : 0);
- spin_unlock_irqrestore(&sde_evtloglock, flags);
+ spin_unlock_irqrestore(&evtlog->spin_lock, flags);
}
/* always dump the last entries which are not dumped yet */
-static bool _sde_evtlog_dump_calc_range(void)
+static bool _sde_evtlog_dump_calc_range(struct sde_dbg_evtlog *evtlog)
{
- static u32 next;
bool need_dump = true;
unsigned long flags;
- struct sde_dbg_evtlog *evtlog = &sde_dbg_evtlog;
- spin_lock_irqsave(&sde_evtloglock, flags);
+ if (!evtlog)
+ return false;
+
+ spin_lock_irqsave(&evtlog->spin_lock, flags);
- evtlog->first = next;
+ evtlog->first = evtlog->next;
if (evtlog->last == evtlog->first) {
need_dump = false;
@@ -143,27 +103,34 @@ static bool _sde_evtlog_dump_calc_range(void)
evtlog->last - evtlog->first);
evtlog->first = evtlog->last - SDE_EVTLOG_PRINT_ENTRY;
}
- next = evtlog->first + 1;
+ evtlog->next = evtlog->first + 1;
dump_exit:
- spin_unlock_irqrestore(&sde_evtloglock, flags);
+ spin_unlock_irqrestore(&evtlog->spin_lock, flags);
return need_dump;
}
-static ssize_t sde_evtlog_dump_entry(char *evtlog_buf, ssize_t evtlog_buf_size)
+ssize_t sde_evtlog_dump_to_buffer(struct sde_dbg_evtlog *evtlog,
+ char *evtlog_buf, ssize_t evtlog_buf_size)
{
int i;
ssize_t off = 0;
- struct tlog *log, *prev_log;
+ struct sde_dbg_evtlog_log *log, *prev_log;
unsigned long flags;
- spin_lock_irqsave(&sde_evtloglock, flags);
+ if (!evtlog || !evtlog_buf)
+ return 0;
- log = &sde_dbg_evtlog.logs[sde_dbg_evtlog.first %
- SDE_EVTLOG_ENTRY];
+ /* update markers, exit if nothing to print */
+ if (!_sde_evtlog_dump_calc_range(evtlog))
+ return 0;
+
+ spin_lock_irqsave(&evtlog->spin_lock, flags);
- prev_log = &sde_dbg_evtlog.logs[(sde_dbg_evtlog.first - 1) %
+ log = &evtlog->logs[evtlog->first % SDE_EVTLOG_ENTRY];
+
+ prev_log = &evtlog->logs[(evtlog->first - 1) %
SDE_EVTLOG_ENTRY];
off = snprintf((evtlog_buf + off), (evtlog_buf_size - off), "%s:%-4d",
@@ -175,7 +142,7 @@ static ssize_t sde_evtlog_dump_entry(char *evtlog_buf, ssize_t evtlog_buf_size)
}
off += snprintf((evtlog_buf + off), (evtlog_buf_size - off),
- "=>[%-8d:%-11llu:%9llu][%-4d]:", sde_dbg_evtlog.first,
+ "=>[%-8d:%-11llu:%9llu][%-4d]:", evtlog->first,
log->time, (log->time - prev_log->time), log->pid);
for (i = 0; i < log->data_cnt; i++)
@@ -184,143 +151,37 @@ static ssize_t sde_evtlog_dump_entry(char *evtlog_buf, ssize_t evtlog_buf_size)
off += snprintf((evtlog_buf + off), (evtlog_buf_size - off), "\n");
- spin_unlock_irqrestore(&sde_evtloglock, flags);
+ spin_unlock_irqrestore(&evtlog->spin_lock, flags);
return off;
}
-static void _sde_evtlog_dump_all(void)
-{
- char evtlog_buf[SDE_EVTLOG_BUF_MAX];
-
- while (_sde_evtlog_dump_calc_range()) {
- sde_evtlog_dump_entry(evtlog_buf, SDE_EVTLOG_BUF_MAX);
- pr_info("%s", evtlog_buf);
- }
-}
-
-static void _sde_dump_array(bool dead, const char *name)
-{
- _sde_evtlog_dump_all();
-
- if (dead && sde_dbg_evtlog.panic_on_err)
- panic(name);
-}
-
-static void _sde_dump_work(struct work_struct *work)
+void sde_evtlog_dump_all(struct sde_dbg_evtlog *evtlog)
{
- _sde_dump_array(sde_dbg_evtlog.work_panic, "evtlog_workitem");
-}
-
-void sde_dbg_dump(bool queue, const char *name, ...)
-{
- int i;
- bool dead = false;
- va_list args;
- char *blk_name = NULL;
-
- if (!sde_evtlog_is_enabled(SDE_EVTLOG_DEFAULT))
- return;
+ char buf[SDE_EVTLOG_BUF_MAX];
- if (queue && work_pending(&sde_dbg_evtlog.evtlog_dump_work))
+ if (!evtlog)
return;
- va_start(args, name);
- for (i = 0; i < SDE_EVTLOG_MAX_DATA; i++) {
- blk_name = va_arg(args, char*);
- if (IS_ERR_OR_NULL(blk_name))
- break;
-
- if (!strcmp(blk_name, "panic"))
- dead = true;
- }
- va_end(args);
-
- if (queue) {
- /* schedule work to dump later */
- sde_dbg_evtlog.work_panic = dead;
- schedule_work(&sde_dbg_evtlog.evtlog_dump_work);
- } else {
- _sde_dump_array(dead, name);
- }
+ while (sde_evtlog_dump_to_buffer(evtlog, buf, sizeof(buf)))
+ pr_info("%s", buf);
}
-static int sde_evtlog_dump_open(struct inode *inode, struct file *file)
+struct sde_dbg_evtlog *sde_evtlog_init(void)
{
- /* non-seekable */
- file->f_mode &= ~(FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
- file->private_data = inode->i_private;
- return 0;
-}
-
-static ssize_t sde_evtlog_dump_read(struct file *file, char __user *buff,
- size_t count, loff_t *ppos)
-{
- ssize_t len = 0;
- char evtlog_buf[SDE_EVTLOG_BUF_MAX];
-
- if (_sde_evtlog_dump_calc_range()) {
- len = sde_evtlog_dump_entry(evtlog_buf, SDE_EVTLOG_BUF_MAX);
- if (copy_to_user(buff, evtlog_buf, len))
- return -EFAULT;
- *ppos += len;
- }
-
- return len;
-}
-
-static ssize_t sde_evtlog_dump_write(struct file *file,
- const char __user *user_buf, size_t count, loff_t *ppos)
-{
- _sde_evtlog_dump_all();
-
- if (sde_dbg_evtlog.panic_on_err)
- panic("sde");
-
- return count;
-}
-
-static const struct file_operations sde_evtlog_fops = {
- .open = sde_evtlog_dump_open,
- .read = sde_evtlog_dump_read,
- .write = sde_evtlog_dump_write,
-};
-
-int sde_evtlog_init(struct dentry *debugfs_root)
-{
- int i;
-
- sde_dbg_evtlog.evtlog = debugfs_create_dir("evt_dbg", debugfs_root);
- if (IS_ERR_OR_NULL(sde_dbg_evtlog.evtlog)) {
- pr_err("debugfs_create_dir fail, error %ld\n",
- PTR_ERR(sde_dbg_evtlog.evtlog));
- sde_dbg_evtlog.evtlog = NULL;
- return -ENODEV;
- }
-
- INIT_WORK(&sde_dbg_evtlog.evtlog_dump_work, _sde_dump_work);
- sde_dbg_evtlog.work_panic = false;
-
- for (i = 0; i < SDE_EVTLOG_ENTRY; i++)
- sde_dbg_evtlog.logs[i].counter = i;
-
- debugfs_create_file("dump", 0644, sde_dbg_evtlog.evtlog, NULL,
- &sde_evtlog_fops);
- debugfs_create_u32("enable", 0644, sde_dbg_evtlog.evtlog,
- &sde_dbg_evtlog.evtlog_enable);
- debugfs_create_u32("panic", 0644, sde_dbg_evtlog.evtlog,
- &sde_dbg_evtlog.panic_on_err);
+ struct sde_dbg_evtlog *evtlog;
- sde_dbg_evtlog.evtlog_enable = SDE_EVTLOG_DEFAULT_ENABLE;
- sde_dbg_evtlog.panic_on_err = SDE_DBG_DEFAULT_PANIC;
+ evtlog = kzalloc(sizeof(*evtlog), GFP_KERNEL);
+ if (!evtlog)
+ return ERR_PTR(-ENOMEM);
- pr_info("evtlog_status: enable:%d, panic:%d\n",
- sde_dbg_evtlog.evtlog_enable, sde_dbg_evtlog.panic_on_err);
+ spin_lock_init(&evtlog->spin_lock);
+ evtlog->enable = SDE_EVTLOG_DEFAULT_ENABLE;
- return 0;
+ return evtlog;
}
-void sde_evtlog_destroy(void)
+void sde_evtlog_destroy(struct sde_dbg_evtlog *evtlog)
{
- debugfs_remove(sde_dbg_evtlog.evtlog);
+ kfree(evtlog);
}