[COMMON] media: scaler: add devfreq setting
authorJanghyuck Kim <janghyuck.kim@samsung.com>
Sat, 15 Jul 2017 10:55:22 +0000 (19:55 +0900)
committerSeungchul Kim <sc377.kim@samsung.com>
Mon, 28 May 2018 05:28:31 +0000 (14:28 +0900)
S_CTRL for framerate setting is added.
If user setting framerate of context, it is considered that pm qos lock
is required to guarantee scaler performance. User can remove pm qos lock
by setting framerate as zero explicitly, or it can be removed
automatically if context is closed.

Change-Id: I8e24e0f7ad24e1ed2fed2cc80a8c35495c32a531
Signed-off-by: Janghyuck Kim <janghyuck.kim@samsung.com>
drivers/media/platform/exynos/scaler/scaler-core.c
drivers/media/platform/exynos/scaler/scaler.h

index bb8f8c5012f2b7204b9b6d18d0117e3727285595..5615daf744479ff916d9d620f5e2b31311937626 100644 (file)
@@ -520,6 +520,101 @@ static const struct sc_variant sc_variant[] = {
        },
 };
 
+#if defined(CONFIG_PM_DEVFREQ) && defined(CONFIG_NEVER_DEFINED_FIXME)
+static void sc_pm_qos_update_devfreq(struct sc_qos_request *qos_req,
+                               int pm_qos_class, u32 freq)
+{
+       struct pm_qos_request *req;
+
+       if (pm_qos_class == PM_QOS_BUS_THROUGHPUT)
+               req = &qos_req->mif_req;
+       else if (pm_qos_class == PM_QOS_DEVICE_THROUGHPUT)
+               req = &qos_req->int_req;
+       else
+               return;
+
+       if (!pm_qos_request_active(req))
+               pm_qos_add_request(req, pm_qos_class, 0);
+
+       pm_qos_update_request(req, freq);
+}
+
+static void sc_pm_qos_remove_devfreq(struct sc_qos_request *qos_req,
+                               int pm_qos_class)
+{
+       struct pm_qos_request *req;
+
+       if (pm_qos_class == PM_QOS_BUS_THROUGHPUT)
+               req = &qos_req->mif_req;
+       else if (pm_qos_class == PM_QOS_DEVICE_THROUGHPUT)
+               req = &qos_req->int_req;
+       else
+               return;
+
+       if (pm_qos_request_active(req))
+               pm_qos_remove_request(req);
+}
+#else
+#define sc_pm_qos_update_devfreq(qos_req, pm_qos_class, freq) do { } while (0)
+#define sc_pm_qos_remove_devfreq(qos_req, pm_qos_class) do { } while (0)
+#endif
+
+void sc_remove_devfreq(struct sc_qos_request *qos_req,
+                       struct sc_qos_table *qos_table)
+{
+       sc_pm_qos_remove_devfreq(qos_req, PM_QOS_BUS_THROUGHPUT);
+       sc_pm_qos_remove_devfreq(qos_req, PM_QOS_DEVICE_THROUGHPUT);
+}
+
+void sc_request_devfreq(struct sc_qos_request *qos_req,
+               struct sc_qos_table *qos_table, unsigned long lv)
+{
+       sc_pm_qos_update_devfreq(qos_req,
+                       PM_QOS_BUS_THROUGHPUT, qos_table[lv].freq_mif);
+       sc_pm_qos_update_devfreq(qos_req,
+                       PM_QOS_DEVICE_THROUGHPUT, qos_table[lv].freq_int);
+}
+
+static bool sc_get_pm_qos_level(struct sc_ctx *ctx, int framerate)
+{
+       struct sc_dev *sc = ctx->sc_dev;
+       struct sc_frame *frame = &ctx->d_frame;
+       struct sc_qos_table *qos_table = sc->qos_table;
+       unsigned int dst_len = 0;
+       int i;
+
+       /* No need to calculate if no qos_table exists. */
+       if (!qos_table)
+               return false;
+
+       for (i = 0; i < frame->sc_fmt->num_planes; i++)
+               dst_len += frame->bytesused[i];
+
+       /*
+        * We don't need accurate size for level selection.
+        * It is divided by 256 to use data size within 32bit value.
+        * It should be done before calculating framerate.
+        */
+       dst_len /= 256;
+       dst_len *= framerate;
+
+       for (i = 0; i < sc->qos_table_cnt; i++)
+               if (qos_table[i].data_size < dst_len)
+                       break;
+
+       /* Set the minimum level */
+       if (i == sc->qos_table_cnt)
+               i--;
+
+       /* No request is required if level is same. */
+       if (ctx->pm_qos_lv == i)
+               return false;
+
+       ctx->pm_qos_lv = i;
+
+       return true;
+}
+
 /* Find the matches format */
 static const struct sc_fmt *sc_find_format(struct sc_dev *sc,
                                                u32 pixfmt, bool output_buf)
@@ -1880,6 +1975,20 @@ static int sc_s_ctrl(struct v4l2_ctrl *ctrl)
        case SC_CID_DNOISE_FT:
                ctx->dnoise_ft.strength = ctrl->val;
                break;
+       case SC_CID_FRAMERATE:
+               if (!ctx->sc_dev->qos_table)
+                       break;
+
+               if (ctrl->val == 0) {
+                       sc_remove_devfreq(&ctx->pm_qos, ctx->sc_dev->qos_table);
+                       ctx->framerate = 0;
+               } else if (ctrl->val != ctx->framerate) {
+                       ctx->framerate = ctrl->val;
+                       if (sc_get_pm_qos_level(ctx, ctx->framerate))
+                               sc_request_devfreq(&ctx->pm_qos,
+                                       ctx->sc_dev->qos_table, ctx->pm_qos_lv);
+               }
+               break;
        }
 
        return ret;
@@ -1969,6 +2078,15 @@ static const struct v4l2_ctrl_config sc_custom_ctrl[] = {
                .min = 0,
                .max = SC_FT_MAX,
                .def = 0,
+       }, {
+               .ops = &sc_ctrl_ops,
+               .id = SC_CID_FRAMERATE,
+               .name = "Frame rate setting",
+               .type = V4L2_CTRL_TYPE_INTEGER,
+               .step = 1,
+               .min = 0,
+               .max = SC_FRAMERATE_MAX,
+               .def = 0,
        }
 };
 
@@ -2159,6 +2277,11 @@ static int sc_release(struct file *file)
 
        destroy_intermediate_frame(ctx);
 
+       if (ctx->framerate) {
+               sc_remove_devfreq(&ctx->pm_qos, ctx->sc_dev->qos_table);
+               ctx->framerate = 0;
+       }
+
        if (!IS_ERR(sc->aclk))
                clk_unprepare(sc->aclk);
        if (!IS_ERR(sc->pclk))
@@ -3432,6 +3555,39 @@ static const struct dev_pm_ops sc_pm_ops = {
        SET_RUNTIME_PM_OPS(NULL, sc_runtime_resume, sc_runtime_suspend)
 };
 
+static int sc_populate_dt(struct sc_dev *sc)
+{
+       struct device *dev = sc->dev;
+       struct sc_qos_table *qos_table;
+       int i, len;
+
+       len = of_property_count_u32_elems(dev->of_node, "mscl_qos_table");
+       if (len < 0) {
+               dev_info(dev, "No qos table for scaler\n");
+               return 0;
+       }
+
+       sc->qos_table_cnt = len / 3;
+
+       qos_table = devm_kzalloc(dev, sizeof(struct sc_qos_table) * sc->qos_table_cnt, GFP_KERNEL);
+       if (!qos_table)
+               return -ENOMEM;
+
+       of_property_read_u32_array(dev->of_node, "mscl_qos_table",
+                                       (unsigned int *)qos_table, len);
+
+       for (i = 0; i < sc->qos_table_cnt; i++) {
+               dev_info(dev, "MSCL QoS Table[%d] mif : %u int : %u [%u]\n", i,
+                       qos_table[i].freq_mif,
+                       qos_table[i].freq_int,
+                       qos_table[i].data_size);
+       }
+
+       sc->qos_table = qos_table;
+
+       return 0;
+}
+
 static int sc_probe(struct platform_device *pdev)
 {
        struct sc_dev *sc;
@@ -3496,6 +3652,10 @@ static int sc_probe(struct platform_device *pdev)
 
        pm_runtime_enable(&pdev->dev);
 
+       ret = sc_populate_dt(sc);
+       if (ret)
+               return ret;
+
        ret = sc_register_m2m_device(sc, sc->dev_id);
        if (ret) {
                dev_err(&pdev->dev, "failed to register m2m device\n");
index af9cab6ab4665db0fbe1acb394856ca8f5044c7f..53194838749313493bd453ae914c9a65bb8b33b7 100644 (file)
@@ -110,6 +110,10 @@ extern int sc_log_level;
 #define V4L2_CID_2D_DITH               (V4L2_CID_EXYNOS_BASE + 105)
 #define V4L2_CID_2D_FMT_PREMULTI       (V4L2_CID_EXYNOS_BASE + 106)
 
+/* for performance */
+#define SC_CID_FRAMERATE               (V4L2_CID_EXYNOS_BASE + 110)
+#define SC_FRAMERATE_MAX               (500)
+
 /* for denoising filter */
 #define SC_CID_DNOISE_FT               (V4L2_CID_EXYNOS_BASE + 150)
 #define SC_M2M1SHOT_OP_FILTER_SHIFT    (28)
@@ -366,6 +370,12 @@ struct sc_qch_dbg {
        u32 log[G2D_QCH_NUM];
 };
 
+struct sc_qos_table {
+       unsigned int freq_mif;
+       unsigned int freq_int;
+       unsigned int data_size;
+};
+
 struct sc_ctx;
 
 /*
@@ -421,6 +431,8 @@ struct sc_dev {
        int                             dbg_idx;
        struct sc_qch_dbg               *qch_dbg;
        struct ion_client               *client;
+       struct sc_qos_table             *qos_table;
+       int qos_table_cnt;
 };
 
 enum SC_CONTEXT_TYPE {
@@ -428,6 +440,11 @@ enum SC_CONTEXT_TYPE {
        SC_CTX_M2M1SHOT_TYPE
 };
 
+struct sc_qos_request {
+       struct pm_qos_request mif_req;
+       struct pm_qos_request int_req;
+};
+
 /*
  * sc_ctx - the abstration for Rotator open context
  * @node:              list to be added to sc_dev.context_list
@@ -479,6 +496,9 @@ struct sc_ctx {
        struct sc_csc                   csc;
        struct sc_init_phase            init_phase;
        struct sc_dnoise_filter         dnoise_ft;
+       struct sc_qos_request           pm_qos;
+       int                             pm_qos_lv;
+       int                             framerate;
 };
 
 static inline struct sc_frame *ctx_get_frame(struct sc_ctx *ctx,