blob: 98aec4b7e9851599e9dd6d402aacb0fc79cfe46f [file] [log] [blame]
xjb04a4022021-11-25 15:01:52 +08001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2019 MediaTek Inc.
4 * Author: Anthony Huang <anthony.huang@mediatek.com>
5 */
6
7#include <linux/module.h>
8#include "mmqos-mtk.h"
9
10#define MULTIPLY_W_DRAM_WEIGHT(value) ((value)*6/5)
11
12struct mmqos_hrt *mmqos_hrt;
13
14s32 mtk_mmqos_get_avail_hrt_bw(enum hrt_type type)
15{
16 u32 i, used_bw = 0;
17
18 if (!mmqos_hrt)
19 return 0xFFFF;
20
21 for (i = 0; i < HRT_TYPE_NUM; i++) {
22 if (mmqos_hrt->hrt_bw[i] != type)
23 used_bw += mmqos_hrt->hrt_bw[i];
24 }
25
26 if (mmqos_hrt->cam_max_bw)
27 used_bw = used_bw - mmqos_hrt->hrt_bw[HRT_CAM]
28 + mmqos_hrt->cam_max_bw;
29
30 return (mmqos_hrt->hrt_total_bw - used_bw);
31}
32EXPORT_SYMBOL_GPL(mtk_mmqos_get_avail_hrt_bw);
33
34
35s32 mtk_mmqos_register_bw_throttle_notifier(struct notifier_block *nb)
36{
37 if (!nb || !mmqos_hrt)
38 return -EINVAL;
39 return blocking_notifier_chain_register(
40 &mmqos_hrt->hrt_bw_throttle_notifier,
41 nb);
42}
43EXPORT_SYMBOL_GPL(mtk_mmqos_register_bw_throttle_notifier);
44
45s32 mtk_mmqos_unregister_bw_throttle_notifier(struct notifier_block *nb)
46{
47 if (!nb || !mmqos_hrt)
48 return -EINVAL;
49 return blocking_notifier_chain_unregister(
50 &mmqos_hrt->hrt_bw_throttle_notifier,
51 nb);
52}
53EXPORT_SYMBOL_GPL(mtk_mmqos_unregister_bw_throttle_notifier);
54
55void mtk_mmqos_wait_throttle_done(void)
56{
57 u32 wait_result;
58
59 if (!mmqos_hrt)
60 return;
61
62 if (atomic_read(&mmqos_hrt->lock_count) > 0) {
63 pr_notice("begin to blocking for cam_max_bw=%d\n",
64 mmqos_hrt->cam_max_bw);
65 wait_result = wait_event_timeout(mmqos_hrt->hrt_wait,
66 atomic_read(&mmqos_hrt->lock_count) == 0,
67 msecs_to_jiffies(200));
68 pr_notice("blocking wait_result=%d\n", wait_result);
69 }
70}
71EXPORT_SYMBOL_GPL(mtk_mmqos_wait_throttle_done);
72
73s32 mtk_mmqos_set_hrt_bw(enum hrt_type type, u32 bw)
74{
75 if (type >= HRT_TYPE_NUM) {
76 pr_notice("%s: wrong type:%d\n", __func__, type);
77 return -EINVAL;
78 }
79
80 if (!mmqos_hrt)
81 return -EINVAL;
82
83 mmqos_hrt->hrt_bw[type] = bw;
84 return 0;
85}
86EXPORT_SYMBOL_GPL(mtk_mmqos_set_hrt_bw);
87
88static void notify_bw_throttle(u32 bw)
89{
90 u64 start_jiffies = jiffies;
91
92 blocking_notifier_call_chain(&mmqos_hrt->hrt_bw_throttle_notifier,
93 (bw > 0)?BW_THROTTLE_START:BW_THROTTLE_END, NULL);
94
95 pr_notice("%s: notify_time=%u\n", __func__,
96 jiffies_to_msecs(jiffies-start_jiffies));
97}
98
99static void set_camera_max_bw(u32 bw)
100{
101 mmqos_hrt->cam_max_bw = bw;
102 pr_notice("%s: %d\n", __func__, bw);
103
104 if (mmqos_hrt->blocking) {
105 atomic_inc(&mmqos_hrt->lock_count);
106 pr_notice("%s: increase lock_count=%d\n", __func__,
107 atomic_read(&mmqos_hrt->lock_count));
108 }
109 notify_bw_throttle(bw);
110
111 if (mmqos_hrt->blocking) {
112 atomic_dec(&mmqos_hrt->lock_count);
113 wake_up(&mmqos_hrt->hrt_wait);
114 pr_notice("%s: decrease lock_count=%d\n", __func__,
115 atomic_read(&mmqos_hrt->lock_count));
116 }
117}
118
119static void delay_work_handler(struct work_struct *work)
120{
121 mutex_lock(&mmqos_hrt->blocking_lock);
122 set_camera_max_bw(mmqos_hrt->cam_occu_bw);
123 mutex_unlock(&mmqos_hrt->blocking_lock);
124}
125
126static ssize_t camera_max_bw_store(struct device *dev,
127 struct device_attribute *attr, const char *buf, size_t count)
128{
129 s32 ret;
130 u32 bw = 0;
131
132 ret = kstrtoint(buf, 10, &bw);
133 if (ret) {
134 dev_notice(dev, "wrong camera max bw string:%d\n", ret);
135 return ret;
136 }
137
138 cancel_delayed_work_sync(&mmqos_hrt->work);
139 mmqos_hrt->cam_occu_bw = MULTIPLY_W_DRAM_WEIGHT(bw);
140 mutex_lock(&mmqos_hrt->blocking_lock);
141 if (mmqos_hrt->cam_occu_bw < mmqos_hrt->cam_max_bw) {
142 mmqos_hrt->blocking = false;
143 schedule_delayed_work(&mmqos_hrt->work, 2 * HZ);
144 } else {
145 mmqos_hrt->blocking = true;
146 schedule_delayed_work(&mmqos_hrt->work, 0);
147 }
148 mutex_unlock(&mmqos_hrt->blocking_lock);
149
150 return count;
151}
152static DEVICE_ATTR_WO(camera_max_bw);
153
154void mtk_mmqos_init_hrt(struct mmqos_hrt *hrt)
155{
156 if (!hrt)
157 return;
158 mmqos_hrt = hrt;
159 atomic_set(&mmqos_hrt->lock_count, 0);
160 INIT_DELAYED_WORK(&mmqos_hrt->work, delay_work_handler);
161 BLOCKING_INIT_NOTIFIER_HEAD(&mmqos_hrt->hrt_bw_throttle_notifier);
162 mutex_init(&mmqos_hrt->blocking_lock);
163 init_waitqueue_head(&mmqos_hrt->hrt_wait);
164}
165EXPORT_SYMBOL_GPL(mtk_mmqos_init_hrt);
166
167static struct attribute *mmqos_hrt_sysfs_attrs[] = {
168 &dev_attr_camera_max_bw.attr,
169 NULL
170};
171
172static struct attribute_group mmqos_hrt_sysfs_attr_group = {
173 .name = "mmqos_hrt",
174 .attrs = mmqos_hrt_sysfs_attrs
175};
176
177int mtk_mmqos_register_hrt_sysfs(struct device *dev)
178{
179 return sysfs_create_group(&dev->kobj, &mmqos_hrt_sysfs_attr_group);
180}
181EXPORT_SYMBOL_GPL(mtk_mmqos_register_hrt_sysfs);
182
183void mtk_mmqos_unregister_hrt_sysfs(struct device *dev)
184{
185 sysfs_remove_group(&dev->kobj, &mmqos_hrt_sysfs_attr_group);
186}
187EXPORT_SYMBOL_GPL(mtk_mmqos_unregister_hrt_sysfs);
188
189
190MODULE_LICENSE("GPL v2");