From 22c5e525ac2d6f7e17a8ba4722bf99240ee027a2 Mon Sep 17 00:00:00 2001 From: Yandong Lin Date: Tue, 3 Sep 2024 11:24:07 +0800 Subject: [PATCH] video: rockchip: mpp: add devices load statistics 1.The default statistical time is 0, means the load statistics function is disabled. If want to enable the function, need echo a valid value to load_interval. e.g. 1000ms: $echo 1000 > /proc/mpp_service/load_interval 2.show load info: rk3588_t_evb7:/ # cat /proc/mpp_service/load fdb51000.avsd-plus load: 0.00% utilization: 0.00% fdb50400.vdpu load: 0.00% utilization: 0.00% fdb50000.vepu load: 0.00% utilization: 0.00% fdb90000.jpegd load: 0.00% utilization: 0.00% fdba0000.jpege-core load: 0.00% utilization: 0.00% fdba4000.jpege-core load: 0.00% utilization: 0.00% fdba8000.jpege-core load: 0.00% utilization: 0.00% fdbac000.jpege-core load: 0.00% utilization: 0.00% fdbb0000.iep load: 0.00% utilization: 0.00% fdbd0000.rkvenc-core load: 97.56% utilization: 93.45% fdbe0000.rkvenc-core load: 97.33% utilization: 92.51% fdc38100.rkvdec-core load: 0.00% utilization: 0.00% fdc48100.rkvdec-core load: 0.00% utilization: 0.00% av1d-master load: 0.00% utilization: 0.00% Signed-off-by: Yandong Lin Change-Id: I0b1c6d3efc7cd7708e7f367ad917b865db67a08a --- drivers/video/rockchip/mpp/mpp_common.c | 90 ++++++++++++++++++- drivers/video/rockchip/mpp/mpp_common.h | 17 ++++ drivers/video/rockchip/mpp/mpp_iep2.c | 1 + drivers/video/rockchip/mpp/mpp_jpgdec.c | 1 + drivers/video/rockchip/mpp/mpp_rkvdec.c | 1 + drivers/video/rockchip/mpp/mpp_rkvdec2.c | 3 + drivers/video/rockchip/mpp/mpp_rkvdec2_link.c | 23 +++-- drivers/video/rockchip/mpp/mpp_rkvenc.c | 1 + drivers/video/rockchip/mpp/mpp_rkvenc2.c | 1 + drivers/video/rockchip/mpp/mpp_service.c | 38 ++++++++ drivers/video/rockchip/mpp/mpp_vdpp.c | 1 + drivers/video/rockchip/mpp/mpp_vdpu1.c | 1 + drivers/video/rockchip/mpp/mpp_vdpu2.c | 1 + drivers/video/rockchip/mpp/mpp_vepu1.c | 1 + drivers/video/rockchip/mpp/mpp_vepu2.c | 1 + 15 files changed, 172 insertions(+), 9 deletions(-) diff --git a/drivers/video/rockchip/mpp/mpp_common.c b/drivers/video/rockchip/mpp/mpp_common.c index 0e7261c8dd50..db93061f34b9 100644 --- a/drivers/video/rockchip/mpp/mpp_common.c +++ b/drivers/video/rockchip/mpp/mpp_common.c @@ -761,13 +761,11 @@ static int mpp_task_run(struct mpp_dev *mpp, struct mpp_task *task) { int ret; - u32 timing_en; struct mpp_session *session = task->session; mpp_debug_enter(); - timing_en = mpp->srv->timing_en; - if (timing_en) { + if (mpp->srv->timing_en) { task->on_run = ktime_get(); set_bit(TASK_TIMING_RUN, &task->state); } @@ -819,6 +817,71 @@ static int mpp_task_run(struct mpp_dev *mpp, return 0; } +void mpp_dev_load(struct mpp_dev *mpp, struct mpp_task *mpp_task) +{ + struct mpp_load_info *load_info = &mpp->load_info; + ktime_t now; + s64 time_diff_us; + + if (!mpp->srv->load_interval) { + if (mpp->load_en) { + mpp_dev_load_clear(mpp); + mpp->srv->timing_en = 0; + mpp->load_en = 0; + } + return; + } + + if (!mpp->load_en) { + mpp->srv->timing_en = 1; + mpp->load_en = 1; + load_info->load_time = ktime_get(); + return; + } + + if (!mpp_task->on_run) + return; + + now = ktime_get(); + time_diff_us = ktime_us_delta(now, load_info->load_time); + load_info->busy_time += ktime_us_delta(now, mpp_task->on_run); + if (mpp_task->hw_time) + load_info->hw_busy_time += mpp_task->hw_time; + else + load_info->hw_busy_time += ktime_us_delta(mpp_task->on_irq, + mpp_task->on_sched_timeout); + /* 1s update */ + if (time_diff_us > mpp->srv->load_interval * 1000) { + u32 tmp = div64_s64(load_info->busy_time * 10000, time_diff_us); + u32 load = tmp / 100; + u32 load_frac = tmp % 100; + u32 max_load = 100; + + if (mpp->queue->core_count > 1) + max_load *= mpp->queue->core_count; + + load_info->load = load > max_load ? max_load : load; + load_info->load_frac = load > max_load ? 0 : load_frac; + + tmp = div64_s64(load_info->hw_busy_time * 10000, time_diff_us); + load = tmp / 100; + load_frac = tmp % 100; + load_info->utilization = load > max_load ? max_load : load; + load_info->utilization_frac = load > max_load ? 0 : load_frac; + + load_info->busy_time = 0; + load_info->hw_busy_time = 0; + load_info->load_time = now; + } +} + +void mpp_dev_load_clear(struct mpp_dev *mpp) +{ + struct mpp_load_info *load_info = &mpp->load_info; + + memset(load_info, 0, sizeof(*load_info)); +} + static void try_process_running_task(struct mpp_dev *mpp) { struct mpp_task *mpp_task, *n; @@ -842,7 +905,7 @@ static void try_process_running_task(struct mpp_dev *mpp) if (mpp->auto_freq_en && mpp->hw_ops->reduce_freq && list_empty(&mpp->queue->pending_list)) mpp->hw_ops->reduce_freq(mpp); - + mpp_dev_load(mpp, mpp_task); if (mpp->dev_ops->isr) mpp->dev_ops->isr(mpp); enable_irq(mpp->irq); @@ -2554,6 +2617,25 @@ int mpp_clk_set_rate(struct mpp_clk_info *clk_info, return 0; } +static int __maybe_unused mpp_common_runtime_suspend(struct device *dev) +{ + struct mpp_dev *mpp = dev_get_drvdata(dev); + + mpp_dev_load_clear(mpp); + + return 0; +} + +static int __maybe_unused mpp_common_runtime_resume(struct device *dev) +{ + return 0; +} + +const struct dev_pm_ops mpp_common_pm_ops = { + SET_RUNTIME_PM_OPS(mpp_common_runtime_suspend, mpp_common_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) +}; + #ifdef CONFIG_ROCKCHIP_MPP_PROC_FS static int fops_show_u32(struct seq_file *file, void *v) { diff --git a/drivers/video/rockchip/mpp/mpp_common.h b/drivers/video/rockchip/mpp/mpp_common.h index fdf893c790cc..05dddb4f6835 100644 --- a/drivers/video/rockchip/mpp/mpp_common.h +++ b/drivers/video/rockchip/mpp/mpp_common.h @@ -264,6 +264,15 @@ struct mpp_mem_region { bool is_dup; }; +struct mpp_load_info { + s64 busy_time; + s64 hw_busy_time; + ktime_t load_time; + u32 load; + u32 load_frac; + u32 utilization; + u32 utilization_frac; +}; struct mpp_dev { struct device *dev; @@ -322,6 +331,8 @@ struct mpp_dev { /* common per-device procfs */ u32 disable; u32 timing_check; + u32 load_en; + struct mpp_load_info load_info; }; struct mpp_session { @@ -450,6 +461,7 @@ struct mpp_task { s32 core_id; /* hw cycles */ u32 hw_cycles; + u32 hw_time; }; struct mpp_taskqueue { @@ -534,6 +546,7 @@ struct mpp_service { /* global timing record flag */ u32 timing_en; + u32 load_interval; }; /* @@ -688,6 +701,8 @@ unsigned long mpp_get_clk_info_rate_hz(struct mpp_clk_info *clk_info, enum MPP_CLOCK_MODE mode); int mpp_clk_set_rate(struct mpp_clk_info *clk_info, enum MPP_CLOCK_MODE mode); +void mpp_dev_load(struct mpp_dev *mpp, struct mpp_task *mpp_task); +void mpp_dev_load_clear(struct mpp_dev *mpp); static inline int mpp_write(struct mpp_dev *mpp, u32 reg, u32 val) { @@ -853,4 +868,6 @@ extern struct platform_driver rockchip_av1dec_driver; extern struct platform_driver rockchip_jpgenc_driver; extern struct platform_driver rockchip_vdpp_driver; +extern const struct dev_pm_ops mpp_common_pm_ops; + #endif diff --git a/drivers/video/rockchip/mpp/mpp_iep2.c b/drivers/video/rockchip/mpp/mpp_iep2.c index 275c76524c79..4a9e733c5133 100644 --- a/drivers/video/rockchip/mpp/mpp_iep2.c +++ b/drivers/video/rockchip/mpp/mpp_iep2.c @@ -1129,6 +1129,7 @@ struct platform_driver rockchip_iep2_driver = { .driver = { .name = IEP2_DRIVER_NAME, .of_match_table = of_match_ptr(mpp_iep2_match), + .pm = &mpp_common_pm_ops, }, }; EXPORT_SYMBOL(rockchip_iep2_driver); diff --git a/drivers/video/rockchip/mpp/mpp_jpgdec.c b/drivers/video/rockchip/mpp/mpp_jpgdec.c index b9d415eda21f..719af4116df9 100644 --- a/drivers/video/rockchip/mpp/mpp_jpgdec.c +++ b/drivers/video/rockchip/mpp/mpp_jpgdec.c @@ -654,6 +654,7 @@ struct platform_driver rockchip_jpgdec_driver = { .driver = { .name = JPGDEC_DRIVER_NAME, .of_match_table = of_match_ptr(mpp_jpgdec_dt_match), + .pm = &mpp_common_pm_ops, }, }; EXPORT_SYMBOL(rockchip_jpgdec_driver); diff --git a/drivers/video/rockchip/mpp/mpp_rkvdec.c b/drivers/video/rockchip/mpp/mpp_rkvdec.c index d8628e0db099..89214c2c7e5b 100644 --- a/drivers/video/rockchip/mpp/mpp_rkvdec.c +++ b/drivers/video/rockchip/mpp/mpp_rkvdec.c @@ -1922,6 +1922,7 @@ struct platform_driver rockchip_rkvdec_driver = { .driver = { .name = RKVDEC_DRIVER_NAME, .of_match_table = of_match_ptr(mpp_rkvdec_dt_match), + .pm = &mpp_common_pm_ops, }, }; EXPORT_SYMBOL(rockchip_rkvdec_driver); diff --git a/drivers/video/rockchip/mpp/mpp_rkvdec2.c b/drivers/video/rockchip/mpp/mpp_rkvdec2.c index 09f4607415e9..741924e03ef5 100644 --- a/drivers/video/rockchip/mpp/mpp_rkvdec2.c +++ b/drivers/video/rockchip/mpp/mpp_rkvdec2.c @@ -630,6 +630,7 @@ static int rkvdec_vdpu383_isr(struct mpp_dev *mpp) return IRQ_HANDLED; } mpp_task->hw_cycles = mpp_read(mpp, RKVDEC_PERF_WORKING_CNT); + mpp_task->hw_time = mpp_task->hw_cycles / (dec->cycle_clk->real_rate_hz / 1000000); mpp_time_diff_with_hw_time(mpp_task, dec->cycle_clk->real_rate_hz); mpp->cur_task = NULL; task = to_rkvdec2_task(mpp_task); @@ -2043,6 +2044,8 @@ static int __maybe_unused rkvdec2_runtime_suspend(struct device *dev) if (mpp->hw_ops->clk_off) mpp->hw_ops->clk_off(mpp); + + mpp_dev_load_clear(mpp); } return 0; diff --git a/drivers/video/rockchip/mpp/mpp_rkvdec2_link.c b/drivers/video/rockchip/mpp/mpp_rkvdec2_link.c index 0f1dca216f24..eb02beff8f25 100644 --- a/drivers/video/rockchip/mpp/mpp_rkvdec2_link.c +++ b/drivers/video/rockchip/mpp/mpp_rkvdec2_link.c @@ -1098,6 +1098,8 @@ static void rkvdec2_link_try_dequeue(struct mpp_dev *mpp) task->irq_status = irq_status; mpp_task->hw_cycles = tb_reg[info->tb_reg_cycle]; + mpp_task->hw_time = mpp_task->hw_cycles / + (dec->cycle_clk->real_rate_hz / 1000000); mpp_time_diff_with_hw_time(mpp_task, dec->cycle_clk->real_rate_hz); rkvdec2_link_finish(mpp, mpp_task); @@ -1128,6 +1130,7 @@ static void rkvdec2_link_try_dequeue(struct mpp_dev *mpp) } wake_up(&mpp_task->wait); + mpp_dev_load(mpp, mpp_task); kref_put(&mpp_task->ref, rkvdec2_link_free_task); } @@ -1190,6 +1193,10 @@ static int mpp_task_queue(struct mpp_dev *mpp, struct mpp_task *mpp_task) if (!rkvdec2_link_prepare(mpp, mpp_task)) return -EBUSY; + if (mpp->srv->timing_en) { + mpp_task->on_run = ktime_get(); + set_bit(TASK_TIMING_RUN, &mpp_task->state); + } rkvdec2_link_enqueue(link_dec, mpp_task); set_bit(TASK_STATE_RUNNING, &mpp_task->state); @@ -1649,6 +1656,8 @@ static int rkvdec2_soft_ccu_dequeue(struct mpp_taskqueue *queue) set_bit(TASK_STATE_HANDLE, &mpp_task->state); cancel_delayed_work(&mpp_task->timeout_work); mpp_task->hw_cycles = mpp_read(mpp, RKVDEC_PERF_WORKING_CNT); + mpp_task->hw_time = mpp_task->hw_cycles / + (dec->cycle_clk->real_rate_hz / 1000000); mpp_time_diff_with_hw_time(mpp_task, dec->cycle_clk->real_rate_hz); task->irq_status = irq_status; mpp_debug(DEBUG_IRQ_CHECK, "irq_status=%08x, timeout=%u, abort=%u\n", @@ -1669,6 +1678,7 @@ static int rkvdec2_soft_ccu_dequeue(struct mpp_taskqueue *queue) spin_lock_irqsave(&queue->running_lock, flags); list_del_init(&mpp_task->queue_link); spin_unlock_irqrestore(&queue->running_lock, flags); + mpp_dev_load(mpp, mpp_task); kref_put(&mpp_task->ref, mpp_free_task); } else { /* NOTE: break when meet not finish */ @@ -2024,7 +2034,6 @@ void rkvdec2_soft_ccu_worker(struct kthread_work *work_s) struct mpp_dev *mpp = container_of(work_s, struct mpp_dev, work); struct mpp_taskqueue *queue = mpp->queue; struct rkvdec2_dev *dec = to_rkvdec2_dev(mpp); - u32 timing_en = mpp->srv->timing_en; mpp_debug_enter(); @@ -2068,7 +2077,7 @@ void rkvdec2_soft_ccu_worker(struct kthread_work *work_s) if (!mpp) break; - if (timing_en) { + if (mpp->srv->timing_en) { mpp_task->on_run = ktime_get(); set_bit(TASK_TIMING_RUN, &mpp_task->state); } @@ -2246,6 +2255,8 @@ static int rkvdec2_hard_ccu_dequeue(struct mpp_taskqueue *queue, set_bit(TASK_STATE_HANDLE, &mpp_task->state); cancel_delayed_work(&mpp_task->timeout_work); mpp_task->hw_cycles = tb_reg[hw->tb_reg_cycle]; + mpp_task->hw_time = mpp_task->hw_cycles / + (dec->cycle_clk->real_rate_hz / 1000000); mpp_time_diff_with_hw_time(mpp_task, dec->cycle_clk->real_rate_hz); task->irq_status = irq_status ? irq_status : RKVDEC_ERROR_STA; @@ -2280,7 +2291,7 @@ static int rkvdec2_hard_ccu_dequeue(struct mpp_taskqueue *queue, irq_status, timeout_flag, abort_flag); atomic_inc(&queue->reset_request); } - + mpp_dev_load(mpp_task->session->mpp, mpp_task); kref_put(&mpp_task->ref, mpp_free_task); } else { dequeue_none++; @@ -2644,12 +2655,14 @@ void rkvdec2_hard_ccu_worker(struct kthread_work *work_s) mpp_task = rkvdec2_hard_ccu_prepare(mpp_task, dec->ccu, dec->link_dec->info); if (!mpp_task) break; - + if (mpp->srv->timing_en) { + mpp_task->on_run = ktime_get(); + set_bit(TASK_TIMING_RUN, &mpp_task->state); + } rkvdec2_ccu_power_on(queue, dec->ccu); rkvdec2_hard_ccu_enqueue(dec->ccu, mpp_task, queue, mpp); mpp_taskqueue_pending_to_run(queue, mpp_task); } - /* 4. poweroff when running and pending list are empty */ mutex_lock(&queue->pending_lock); if (list_empty(&queue->running_list) && diff --git a/drivers/video/rockchip/mpp/mpp_rkvenc.c b/drivers/video/rockchip/mpp/mpp_rkvenc.c index f5bf49355165..5ab7441b178f 100644 --- a/drivers/video/rockchip/mpp/mpp_rkvenc.c +++ b/drivers/video/rockchip/mpp/mpp_rkvenc.c @@ -1461,5 +1461,6 @@ struct platform_driver rockchip_rkvenc_driver = { .driver = { .name = RKVENC_DRIVER_NAME, .of_match_table = of_match_ptr(mpp_rkvenc_dt_match), + .pm = &mpp_common_pm_ops, }, }; diff --git a/drivers/video/rockchip/mpp/mpp_rkvenc2.c b/drivers/video/rockchip/mpp/mpp_rkvenc2.c index 08f9c1cce1c9..28affeb862a2 100644 --- a/drivers/video/rockchip/mpp/mpp_rkvenc2.c +++ b/drivers/video/rockchip/mpp/mpp_rkvenc2.c @@ -2973,5 +2973,6 @@ struct platform_driver rockchip_rkvenc2_driver = { .driver = { .name = RKVENC_DRIVER_NAME, .of_match_table = of_match_ptr(mpp_rkvenc_dt_match), + .pm = &mpp_common_pm_ops, }, }; diff --git a/drivers/video/rockchip/mpp/mpp_service.c b/drivers/video/rockchip/mpp/mpp_service.c index e523fd125ed7..882002e20097 100644 --- a/drivers/video/rockchip/mpp/mpp_service.c +++ b/drivers/video/rockchip/mpp/mpp_service.c @@ -309,6 +309,39 @@ static int mpp_show_support_device(struct seq_file *file, void *v) return 0; } +static int mpp_show_device_load(struct seq_file *file, void *v) +{ + u32 i, j; + struct mpp_service *srv = file->private; + + if (!srv->load_interval) { + seq_puts(file, "please set load_interval first!!!\n"); + seq_puts(file, "e.g. set 1000ms to load_interval:\n"); + seq_puts(file, "echo 1000 > /proc/mpp_service/load_interval\n"); + return 0; + } + + for (i = 0; i < MPP_DEVICE_BUTT; i++) { + struct mpp_taskqueue *queue = srv->task_queues[i]; + + if (!queue) + continue; + + for (j = 0; j < MPP_MAX_CORE_NUM; j++) { + struct mpp_dev *mpp = queue->cores[j]; + + if (!mpp) + continue; + seq_printf(file, "%-25s load: %3d.%02d%% utilization: %3d.%02d%%\n", + dev_name(mpp->dev), + mpp->load_info.load, mpp->load_info.load_frac, + mpp->load_info.utilization, mpp->load_info.utilization_frac); + } + } + + return 0; +} + static int mpp_procfs_init(struct mpp_service *srv) { srv->procfs = proc_mkdir(MPP_SERVICE_NAME, NULL); @@ -327,7 +360,12 @@ static int mpp_procfs_init(struct mpp_service *srv) /* show support devices */ proc_create_single_data("supports-device", 0444, srv->procfs, mpp_show_support_device, srv); + srv->timing_en = 1; mpp_procfs_create_u32("timing_en", 0644, srv->procfs, &srv->timing_en); + /* show per device load info */ + proc_create_single_data("load", 0444, srv->procfs, mpp_show_device_load, srv); + srv->load_interval = 0; + mpp_procfs_create_u32("load_interval", 0644, srv->procfs, &srv->load_interval); return 0; } diff --git a/drivers/video/rockchip/mpp/mpp_vdpp.c b/drivers/video/rockchip/mpp/mpp_vdpp.c index e24687c41a74..ca46950b78ed 100644 --- a/drivers/video/rockchip/mpp/mpp_vdpp.c +++ b/drivers/video/rockchip/mpp/mpp_vdpp.c @@ -822,6 +822,7 @@ struct platform_driver rockchip_vdpp_driver = { .driver = { .name = VDPP_DRIVER_NAME, .of_match_table = of_match_ptr(mpp_vdpp_dt_match), + .pm = &mpp_common_pm_ops, }, }; EXPORT_SYMBOL(rockchip_vdpp_driver); diff --git a/drivers/video/rockchip/mpp/mpp_vdpu1.c b/drivers/video/rockchip/mpp/mpp_vdpu1.c index e6a76d230d71..5109bde2c236 100644 --- a/drivers/video/rockchip/mpp/mpp_vdpu1.c +++ b/drivers/video/rockchip/mpp/mpp_vdpu1.c @@ -967,6 +967,7 @@ struct platform_driver rockchip_vdpu1_driver = { .driver = { .name = VDPU1_DRIVER_NAME, .of_match_table = of_match_ptr(mpp_vdpu1_dt_match), + .pm = &mpp_common_pm_ops, }, }; EXPORT_SYMBOL(rockchip_vdpu1_driver); diff --git a/drivers/video/rockchip/mpp/mpp_vdpu2.c b/drivers/video/rockchip/mpp/mpp_vdpu2.c index 0a165fa5d0f6..3d6cd244b04b 100644 --- a/drivers/video/rockchip/mpp/mpp_vdpu2.c +++ b/drivers/video/rockchip/mpp/mpp_vdpu2.c @@ -803,6 +803,7 @@ struct platform_driver rockchip_vdpu2_driver = { .driver = { .name = VDPU2_DRIVER_NAME, .of_match_table = of_match_ptr(mpp_vdpu2_dt_match), + .pm = &mpp_common_pm_ops, }, }; EXPORT_SYMBOL(rockchip_vdpu2_driver); diff --git a/drivers/video/rockchip/mpp/mpp_vepu1.c b/drivers/video/rockchip/mpp/mpp_vepu1.c index 4ebd0a57d74d..59b4f9ffc542 100644 --- a/drivers/video/rockchip/mpp/mpp_vepu1.c +++ b/drivers/video/rockchip/mpp/mpp_vepu1.c @@ -790,6 +790,7 @@ struct platform_driver rockchip_vepu1_driver = { .driver = { .name = VEPU1_DRIVER_NAME, .of_match_table = of_match_ptr(mpp_vepu1_dt_match), + .pm = &mpp_common_pm_ops, }, }; EXPORT_SYMBOL(rockchip_vepu1_driver); diff --git a/drivers/video/rockchip/mpp/mpp_vepu2.c b/drivers/video/rockchip/mpp/mpp_vepu2.c index 1e66dd530b35..d92609ef25ea 100644 --- a/drivers/video/rockchip/mpp/mpp_vepu2.c +++ b/drivers/video/rockchip/mpp/mpp_vepu2.c @@ -1275,6 +1275,7 @@ struct platform_driver rockchip_vepu2_driver = { .driver = { .name = VEPU2_DRIVER_NAME, .of_match_table = of_match_ptr(mpp_vepu2_dt_match), + .pm = &mpp_common_pm_ops, }, }; EXPORT_SYMBOL(rockchip_vepu2_driver);