Merge "drm/msm: add kconfig for enabling event log"

This commit is contained in:
Linux Build Service Account 2017-08-29 01:40:57 -07:00 committed by Gerrit - the friendly Code Review server
commit a26a92216f
30 changed files with 2947 additions and 307 deletions

View file

@ -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>;
};

View file

@ -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 {

View file

@ -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).

View file

@ -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 \

View file

@ -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;
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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);

View file

@ -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;

View file

@ -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])

View file

@ -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;
};

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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
@ -93,6 +93,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
@ -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);

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -18,6 +18,8 @@
#include "msm_drv.h"
#define SDE_DBG_NAME "sde"
#define SDE_NONE 0
#ifndef SDE_CSC_MATRIX_COEFF_SIZE

View file

@ -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;
}

View file

@ -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;
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;
return ctx;
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)

View file

@ -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;
}

View file

@ -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;
};

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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) {

View file

@ -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;
@ -367,6 +368,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
*/

View file

@ -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;
}
}

File diff suppressed because it is too large Load diff

View file

@ -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 an list of 32bit values as an event into the event log
* SDE_EVT32 - Write a list of 32bit values to the event log, default 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(...) sde_evtlog_log(sde_dbg_base_evtlog, __func__, \
__LINE__, SDE_EVTLOG_DEFAULT, ##__VA_ARGS__, \
SDE_EVTLOG_DATA_LIMITER)
#define SDE_DBG_DUMP(...) \
sde_dbg_dump(false, __func__, ##__VA_ARGS__, \
/**
* SDE_EVT32_IRQ - Write a list of 32bit values to the event log, IRQ area
* ... - variable arguments
*/
#define SDE_EVT32_IRQ(...) sde_evtlog_log(sde_dbg_base_evtlog, __func__, \
__LINE__, SDE_EVTLOG_IRQ, ##__VA_ARGS__, \
SDE_EVTLOG_DATA_LIMITER)
/**
* 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_ */

View file

@ -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 (!sde_evtlog_is_enabled(flag))
if (!evtlog)
return;
spin_lock_irqsave(&sde_evtloglock, flags);
log = &sde_dbg_evtlog.logs[sde_dbg_evtlog.curr];
if (!sde_evtlog_is_enabled(evtlog, flag))
return;
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;
evtlog->first = next;
spin_lock_irqsave(&evtlog->spin_lock, flags);
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;
prev_log = &sde_dbg_evtlog.logs[(sde_dbg_evtlog.first - 1) %
spin_lock_irqsave(&evtlog->spin_lock, flags);
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)
void sde_evtlog_dump_all(struct sde_dbg_evtlog *evtlog)
{
char evtlog_buf[SDE_EVTLOG_BUF_MAX];
char 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)
{
_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))
if (!evtlog)
return;
if (queue && work_pending(&sde_dbg_evtlog.evtlog_dump_work))
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;
struct sde_dbg_evtlog *evtlog;
evtlog = kzalloc(sizeof(*evtlog), GFP_KERNEL);
if (!evtlog)
return ERR_PTR(-ENOMEM);
spin_lock_init(&evtlog->spin_lock);
evtlog->enable = SDE_EVTLOG_DEFAULT_ENABLE;
return evtlog;
}
static ssize_t sde_evtlog_dump_read(struct file *file, char __user *buff,
size_t count, loff_t *ppos)
void sde_evtlog_destroy(struct sde_dbg_evtlog *evtlog)
{
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);
sde_dbg_evtlog.evtlog_enable = SDE_EVTLOG_DEFAULT_ENABLE;
sde_dbg_evtlog.panic_on_err = SDE_DBG_DEFAULT_PANIC;
pr_info("evtlog_status: enable:%d, panic:%d\n",
sde_dbg_evtlog.evtlog_enable, sde_dbg_evtlog.panic_on_err);
return 0;
}
void sde_evtlog_destroy(void)
{
debugfs_remove(sde_dbg_evtlog.evtlog);
kfree(evtlog);
}