OSDN Git Service

msm: sde: Add pre/post power event handler in SDE rotator
authorBenjamin Chan <bkchan@codeaurora.org>
Fri, 16 Sep 2016 20:01:09 +0000 (16:01 -0400)
committerGerrit - the friendly Code Review server <code-review@localhost>
Tue, 20 Sep 2016 14:04:23 +0000 (07:04 -0700)
For pre power off event, we want to check for rotator IDLE status and
generate a timeout event logging if the rotator is still in BUSY state.

For post power on event, rotator is reset to default HW state. It is
necessary to synchronize the SW timestamp with the HW register location.

CRs-Fixed: 1066270
Change-Id: I1041141b8dc80db0baec7ffe71a5c2a7b41e2cbb
Signed-off-by: Benjamin Chan <bkchan@codeaurora.org>
drivers/media/platform/msm/sde/rotator/sde_rotator_core.c
drivers/media/platform/msm/sde/rotator/sde_rotator_core.h
drivers/media/platform/msm/sde/rotator/sde_rotator_r3.c
drivers/media/platform/msm/sde/rotator/sde_rotator_r3_hwio.h
drivers/media/platform/msm/sde/rotator/sde_rotator_r3_internal.h

index 3e5cbde..041a821 100644 (file)
@@ -278,6 +278,10 @@ static void sde_rotator_footswitch_ctrl(struct sde_rot_mgr *mgr, bool on)
 
        SDEROT_EVTLOG(on);
        SDEROT_DBG("%s: rotator regulators", on ? "Enable" : "Disable");
+
+       if (mgr->ops_hw_pre_pmevent)
+               mgr->ops_hw_pre_pmevent(mgr, on);
+
        ret = sde_rot_enable_vreg(mgr->module_power.vreg_config,
                mgr->module_power.num_vreg, on);
        if (ret) {
@@ -286,10 +290,13 @@ static void sde_rotator_footswitch_ctrl(struct sde_rot_mgr *mgr, bool on)
                return;
        }
 
+       if (mgr->ops_hw_post_pmevent)
+               mgr->ops_hw_post_pmevent(mgr, on);
+
        mgr->regulator_enable = on;
 }
 
-static int sde_rotator_clk_ctrl(struct sde_rot_mgr *mgr, int enable)
+int sde_rotator_clk_ctrl(struct sde_rot_mgr *mgr, int enable)
 {
        struct clk *clk;
        int ret = 0;
index 8659d36..aa17341 100644 (file)
@@ -293,6 +293,8 @@ struct sde_rot_mgr {
        void (*ops_hw_free)(struct sde_rot_mgr *mgr,
                        struct sde_rot_hw_resource *hw);
        int (*ops_hw_init)(struct sde_rot_mgr *mgr);
+       void (*ops_hw_pre_pmevent)(struct sde_rot_mgr *mgr, bool pmon);
+       void (*ops_hw_post_pmevent)(struct sde_rot_mgr *mgr, bool pmon);
        void (*ops_hw_destroy)(struct sde_rot_mgr *mgr);
        ssize_t (*ops_hw_show_caps)(struct sde_rot_mgr *mgr,
                        struct device_attribute *attr, char *buf, ssize_t len);
@@ -405,6 +407,8 @@ int sde_rotator_validate_request(struct sde_rot_mgr *rot_dev,
        struct sde_rot_file_private *ctx,
        struct sde_rot_entry_container *req);
 
+int sde_rotator_clk_ctrl(struct sde_rot_mgr *mgr, int enable);
+
 static inline void sde_rot_mgr_lock(struct sde_rot_mgr *mgr)
 {
        mutex_lock(&mgr->lock);
index e9d2dd5..d2bc768 100644 (file)
@@ -271,6 +271,7 @@ static int sde_hw_rotator_pending_swts(struct sde_hw_rotator *rot,
 
        SDEROT_DBG("ts:0x%x, queue_id:%d, swts:0x%x, pending:%d\n",
                ctx->timestamp, ctx->q_id, swts, pending);
+       SDEROT_EVTLOG(ctx->timestamp, swts, ctx->q_id, ts_diff);
        return pending;
 }
 
@@ -1240,6 +1241,94 @@ static void sde_hw_rotator_swtc_destroy(struct sde_hw_rotator *rot)
 }
 
 /*
+ * sde_hw_rotator_pre_pmevent - SDE rotator core will call this before a
+ *                              PM event occurs
+ * @mgr: Pointer to rotator manager
+ * @pmon: Boolean indicate an on/off power event
+ */
+void sde_hw_rotator_pre_pmevent(struct sde_rot_mgr *mgr, bool pmon)
+{
+       struct sde_hw_rotator *rot;
+       u32 l_ts, h_ts, swts, hwts;
+       u32 rotsts, regdmasts;
+
+       /*
+        * Check last HW timestamp with SW timestamp before power off event.
+        * If there is a mismatch, that will be quite possible the rotator HW
+        * is either hang or not finishing last submitted job. In that case,
+        * it is best to do a timeout eventlog to capture some good events
+        * log data for analysis.
+        */
+       if (!pmon && mgr && mgr->hw_data) {
+               rot = mgr->hw_data;
+               h_ts = atomic_read(&rot->timestamp[ROT_QUEUE_HIGH_PRIORITY]);
+               l_ts = atomic_read(&rot->timestamp[ROT_QUEUE_LOW_PRIORITY]);
+
+               /* contruct the combined timstamp */
+               swts = (h_ts & SDE_REGDMA_SWTS_MASK) |
+                       ((l_ts & SDE_REGDMA_SWTS_MASK) <<
+                        SDE_REGDMA_SWTS_SHIFT);
+
+               /* Need to turn on clock to access rotator register */
+               sde_rotator_clk_ctrl(mgr, true);
+               hwts = SDE_ROTREG_READ(rot->mdss_base, REGDMA_TIMESTAMP_REG);
+               regdmasts = SDE_ROTREG_READ(rot->mdss_base,
+                               REGDMA_CSR_REGDMA_BLOCK_STATUS);
+               rotsts = SDE_ROTREG_READ(rot->mdss_base, ROTTOP_STATUS);
+
+               SDEROT_DBG(
+                       "swts:0x%x, hwts:0x%x, regdma-sts:0x%x, rottop-sts:0x%x\n",
+                               swts, hwts, regdmasts, rotsts);
+               SDEROT_EVTLOG(swts, hwts, regdmasts, rotsts);
+
+               if ((swts != hwts) && ((regdmasts & REGDMA_BUSY) ||
+                                       (rotsts & ROT_STATUS_MASK))) {
+                       SDEROT_ERR(
+                               "Mismatch SWTS with HWTS: swts:0x%x, hwts:0x%x, regdma-sts:0x%x, rottop-sts:0x%x\n",
+                               swts, hwts, regdmasts, rotsts);
+                       SDEROT_EVTLOG_TOUT_HANDLER("rot", "vbif_dbg_bus",
+                                       "panic");
+               }
+
+               /* Turn off rotator clock after checking rotator registers */
+               sde_rotator_clk_ctrl(mgr, false);
+       }
+}
+
+/*
+ * sde_hw_rotator_post_pmevent - SDE rotator core will call this after a
+ *                               PM event occurs
+ * @mgr: Pointer to rotator manager
+ * @pmon: Boolean indicate an on/off power event
+ */
+void sde_hw_rotator_post_pmevent(struct sde_rot_mgr *mgr, bool pmon)
+{
+       struct sde_hw_rotator *rot;
+       u32 l_ts, h_ts, swts;
+
+       /*
+        * After a power on event, the rotator HW is reset to default setting.
+        * It is necessary to synchronize the SW timestamp with the HW.
+        */
+       if (pmon && mgr && mgr->hw_data) {
+               rot = mgr->hw_data;
+               h_ts = atomic_read(&rot->timestamp[ROT_QUEUE_HIGH_PRIORITY]);
+               l_ts = atomic_read(&rot->timestamp[ROT_QUEUE_LOW_PRIORITY]);
+
+               /* contruct the combined timstamp */
+               swts = (h_ts & SDE_REGDMA_SWTS_MASK) |
+                       ((l_ts & SDE_REGDMA_SWTS_MASK) <<
+                        SDE_REGDMA_SWTS_SHIFT);
+
+               SDEROT_DBG("swts:0x%x, h_ts:0x%x, l_ts;0x%x\n",
+                               swts, h_ts, l_ts);
+               SDEROT_EVTLOG(swts, h_ts, l_ts);
+               rot->reset_hw_ts = true;
+               rot->last_hw_ts = swts;
+       }
+}
+
+/*
  * sde_hw_rotator_destroy - Destroy hw rotator and free allocated resources
  * @mgr: Pointer to rotator manager
  */
@@ -1455,6 +1544,15 @@ static int sde_hw_rotator_config(struct sde_rot_hw_resource *hw,
                return -EINVAL;
        }
 
+       if (rot->reset_hw_ts) {
+               SDEROT_EVTLOG(rot->last_hw_ts);
+               SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_TIMESTAMP_REG,
+                               rot->last_hw_ts);
+               /* ensure write is issued to the rotator HW */
+               wmb();
+               rot->reset_hw_ts = false;
+       }
+
        flags = (item->flags & SDE_ROTATION_FLIP_LR) ?
                        SDE_ROT_FLAG_FLIP_LR : 0;
        flags |= (item->flags & SDE_ROTATION_FLIP_UD) ?
@@ -1511,7 +1609,8 @@ static int sde_hw_rotator_config(struct sde_rot_hw_resource *hw,
                                &entry->dst_buf);
        }
 
-       SDEROT_EVTLOG(flags, item->input.width, item->input.height,
+       SDEROT_EVTLOG(ctx->timestamp, flags,
+                       item->input.width, item->input.height,
                        item->output.width, item->output.height,
                        entry->src_buf.p[0].addr, entry->dst_buf.p[0].addr);
 
@@ -1715,6 +1814,7 @@ static int sde_rotator_hw_rev_init(struct sde_hw_rotator *rot)
        mdata->regdump = sde_rot_r3_regdump;
        mdata->regdump_size = ARRAY_SIZE(sde_rot_r3_regdump);
 
+       SDE_ROTREG_WRITE(rot->mdss_base, REGDMA_TIMESTAMP_REG, 0);
        return 0;
 }
 
@@ -2174,6 +2274,8 @@ int sde_rotator_r3_init(struct sde_rot_mgr *mgr)
        mgr->ops_hw_create_debugfs = sde_rotator_r3_create_debugfs;
        mgr->ops_hw_get_pixfmt = sde_hw_rotator_get_pixfmt;
        mgr->ops_hw_is_valid_pixfmt = sde_hw_rotator_is_valid_pixfmt;
+       mgr->ops_hw_pre_pmevent = sde_hw_rotator_pre_pmevent;
+       mgr->ops_hw_post_pmevent = sde_hw_rotator_post_pmevent;
 
        ret = sde_hw_rotator_parse_dt(mgr->hw_data, mgr->pdev);
        if (ret)
index 3267e44..a748b87 100644 (file)
 /* General defines */
 #define ROT_DONE_MASK                   0x1
 #define ROT_DONE_CLEAR                  0x1
-#define ROT_BUSY_BIT                    BIT(1)
+#define ROT_BUSY_BIT                    BIT(0)
 #define ROT_ERROR_BIT                   BIT(8)
 #define ROT_STATUS_MASK                 (ROT_BUSY_BIT | ROT_ERROR_BIT)
+#define REGDMA_BUSY                     BIT(0)
 #define REGDMA_EN                       0x1
 #define REGDMA_SECURE_EN                BIT(8)
 #define REGDMA_HALT                     BIT(16)
index b95f838..91ac3d0 100644 (file)
@@ -270,6 +270,8 @@ struct sde_hw_rotator {
        spinlock_t rotisr_lock;
 
        bool    dbgmem;
+       bool reset_hw_ts;
+       u32 last_hw_ts;
 };
 
 /**