Merge "drm/msm: add kconfig for enabling event log"
This commit is contained in:
commit
a26a92216f
30 changed files with 2947 additions and 307 deletions
|
@ -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>;
|
||||
};
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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).
|
||||
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
#include "msm_drv.h"
|
||||
|
||||
#define SDE_DBG_NAME "sde"
|
||||
|
||||
#define SDE_NONE 0
|
||||
|
||||
#ifndef SDE_CSC_MATRIX_COEFF_SIZE
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
2070
drivers/gpu/drm/msm/sde_dbg.c
Normal file
2070
drivers/gpu/drm/msm/sde_dbg.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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_ */
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue