[Feature] add GA346 baseline version

Change-Id: Ic62933698569507dcf98240cdf5d9931ae34348f
diff --git a/src/kernel/linux/v4.19/drivers/interconnect/mediatek/mmqos-hrt.c b/src/kernel/linux/v4.19/drivers/interconnect/mediatek/mmqos-hrt.c
new file mode 100644
index 0000000..98aec4b
--- /dev/null
+++ b/src/kernel/linux/v4.19/drivers/interconnect/mediatek/mmqos-hrt.c
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ * Author: Anthony Huang <anthony.huang@mediatek.com>
+ */
+
+#include <linux/module.h>
+#include "mmqos-mtk.h"
+
+#define MULTIPLY_W_DRAM_WEIGHT(value) ((value)*6/5)
+
+struct mmqos_hrt *mmqos_hrt;
+
+s32 mtk_mmqos_get_avail_hrt_bw(enum hrt_type type)
+{
+	u32 i, used_bw = 0;
+
+	if (!mmqos_hrt)
+		return 0xFFFF;
+
+	for (i = 0; i < HRT_TYPE_NUM; i++) {
+		if (mmqos_hrt->hrt_bw[i] != type)
+			used_bw += mmqos_hrt->hrt_bw[i];
+	}
+
+	if (mmqos_hrt->cam_max_bw)
+		used_bw = used_bw - mmqos_hrt->hrt_bw[HRT_CAM]
+				+ mmqos_hrt->cam_max_bw;
+
+	return (mmqos_hrt->hrt_total_bw - used_bw);
+}
+EXPORT_SYMBOL_GPL(mtk_mmqos_get_avail_hrt_bw);
+
+
+s32 mtk_mmqos_register_bw_throttle_notifier(struct notifier_block *nb)
+{
+	if (!nb || !mmqos_hrt)
+		return -EINVAL;
+	return blocking_notifier_chain_register(
+				&mmqos_hrt->hrt_bw_throttle_notifier,
+				nb);
+}
+EXPORT_SYMBOL_GPL(mtk_mmqos_register_bw_throttle_notifier);
+
+s32 mtk_mmqos_unregister_bw_throttle_notifier(struct notifier_block *nb)
+{
+	if (!nb || !mmqos_hrt)
+		return -EINVAL;
+	return blocking_notifier_chain_unregister(
+				&mmqos_hrt->hrt_bw_throttle_notifier,
+				nb);
+}
+EXPORT_SYMBOL_GPL(mtk_mmqos_unregister_bw_throttle_notifier);
+
+void mtk_mmqos_wait_throttle_done(void)
+{
+	u32 wait_result;
+
+	if (!mmqos_hrt)
+		return;
+
+	if (atomic_read(&mmqos_hrt->lock_count) > 0) {
+		pr_notice("begin to blocking for cam_max_bw=%d\n",
+			mmqos_hrt->cam_max_bw);
+		wait_result = wait_event_timeout(mmqos_hrt->hrt_wait,
+			atomic_read(&mmqos_hrt->lock_count) == 0,
+			msecs_to_jiffies(200));
+		pr_notice("blocking wait_result=%d\n", wait_result);
+	}
+}
+EXPORT_SYMBOL_GPL(mtk_mmqos_wait_throttle_done);
+
+s32 mtk_mmqos_set_hrt_bw(enum hrt_type type, u32 bw)
+{
+	if (type >= HRT_TYPE_NUM) {
+		pr_notice("%s: wrong type:%d\n", __func__, type);
+		return -EINVAL;
+	}
+
+	if (!mmqos_hrt)
+		return -EINVAL;
+
+	mmqos_hrt->hrt_bw[type] = bw;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mtk_mmqos_set_hrt_bw);
+
+static void notify_bw_throttle(u32 bw)
+{
+	u64 start_jiffies = jiffies;
+
+	blocking_notifier_call_chain(&mmqos_hrt->hrt_bw_throttle_notifier,
+		(bw > 0)?BW_THROTTLE_START:BW_THROTTLE_END, NULL);
+
+	pr_notice("%s: notify_time=%u\n", __func__,
+		jiffies_to_msecs(jiffies-start_jiffies));
+}
+
+static void set_camera_max_bw(u32 bw)
+{
+	mmqos_hrt->cam_max_bw = bw;
+	pr_notice("%s: %d\n", __func__, bw);
+
+	if (mmqos_hrt->blocking) {
+		atomic_inc(&mmqos_hrt->lock_count);
+		pr_notice("%s: increase lock_count=%d\n", __func__,
+			atomic_read(&mmqos_hrt->lock_count));
+	}
+	notify_bw_throttle(bw);
+
+	if (mmqos_hrt->blocking) {
+		atomic_dec(&mmqos_hrt->lock_count);
+		wake_up(&mmqos_hrt->hrt_wait);
+		pr_notice("%s: decrease lock_count=%d\n", __func__,
+			atomic_read(&mmqos_hrt->lock_count));
+	}
+}
+
+static void delay_work_handler(struct work_struct *work)
+{
+	mutex_lock(&mmqos_hrt->blocking_lock);
+	set_camera_max_bw(mmqos_hrt->cam_occu_bw);
+	mutex_unlock(&mmqos_hrt->blocking_lock);
+}
+
+static ssize_t camera_max_bw_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	s32 ret;
+	u32 bw = 0;
+
+	ret = kstrtoint(buf, 10, &bw);
+	if (ret) {
+		dev_notice(dev, "wrong camera max bw string:%d\n", ret);
+		return ret;
+	}
+
+	cancel_delayed_work_sync(&mmqos_hrt->work);
+	mmqos_hrt->cam_occu_bw = MULTIPLY_W_DRAM_WEIGHT(bw);
+	mutex_lock(&mmqos_hrt->blocking_lock);
+	if (mmqos_hrt->cam_occu_bw < mmqos_hrt->cam_max_bw) {
+		mmqos_hrt->blocking = false;
+		schedule_delayed_work(&mmqos_hrt->work, 2 * HZ);
+	} else {
+		mmqos_hrt->blocking = true;
+		schedule_delayed_work(&mmqos_hrt->work, 0);
+	}
+	mutex_unlock(&mmqos_hrt->blocking_lock);
+
+	return count;
+}
+static DEVICE_ATTR_WO(camera_max_bw);
+
+void mtk_mmqos_init_hrt(struct mmqos_hrt *hrt)
+{
+	if (!hrt)
+		return;
+	mmqos_hrt = hrt;
+	atomic_set(&mmqos_hrt->lock_count, 0);
+	INIT_DELAYED_WORK(&mmqos_hrt->work, delay_work_handler);
+	BLOCKING_INIT_NOTIFIER_HEAD(&mmqos_hrt->hrt_bw_throttle_notifier);
+	mutex_init(&mmqos_hrt->blocking_lock);
+	init_waitqueue_head(&mmqos_hrt->hrt_wait);
+}
+EXPORT_SYMBOL_GPL(mtk_mmqos_init_hrt);
+
+static struct attribute *mmqos_hrt_sysfs_attrs[] = {
+	&dev_attr_camera_max_bw.attr,
+	NULL
+};
+
+static struct attribute_group mmqos_hrt_sysfs_attr_group = {
+	.name = "mmqos_hrt",
+	.attrs = mmqos_hrt_sysfs_attrs
+};
+
+int mtk_mmqos_register_hrt_sysfs(struct device *dev)
+{
+	return sysfs_create_group(&dev->kobj, &mmqos_hrt_sysfs_attr_group);
+}
+EXPORT_SYMBOL_GPL(mtk_mmqos_register_hrt_sysfs);
+
+void  mtk_mmqos_unregister_hrt_sysfs(struct device *dev)
+{
+	sysfs_remove_group(&dev->kobj, &mmqos_hrt_sysfs_attr_group);
+}
+EXPORT_SYMBOL_GPL(mtk_mmqos_unregister_hrt_sysfs);
+
+
+MODULE_LICENSE("GPL v2");