ddr: detect bandwidth except mali and reduce bandwidth [2/3]
authorwenbiao zhang <wenbiao.zhang@amlogic.com>
Fri, 12 Oct 2018 07:28:54 +0000 (15:28 +0800)
committerwenbiao zhang <wenbiao.zhang@amlogic.com>
Mon, 12 Nov 2018 06:47:52 +0000 (14:47 +0800)
PD#SWPL-122

Problem:
ddr bandwidth is tightly in many cases, such as HDR/4K video playing,
and then refresh osd at the same time

Solution:
in most cases the problem is refreshing osd when bandwidth tightly,
so detect ddr bandwidth usage except mali and reduce it when it is tightly

Verify:
verify by p321/r311

Change-Id: Ia594f08dbe9a8a7a8d78b5104c93bddaa8229c0b
Signed-off-by: wenbiao zhang <wenbiao.zhang@amlogic.com>
MAINTAINERS
drivers/amlogic/ddr_tool/ddr_bandwidth.c
include/linux/amlogic/aml_ddr_bandwidth.h
include/linux/amlogic/aml_dmc.h [new file with mode: 0644]

index 66874d3..a13c47b 100644 (file)
@@ -14688,3 +14688,7 @@ AMLOGIC MESON DTS
 M:     Huan Biao <huan.biao@amlogic.com>
 F:     arch/arm64/boot/dts/amlogic/g12a_s905d2_u200_buildroot_vccktest.dts
 F:     arch/arm64/boot/dts/amlogic/g12b_a311d_w400_buildroot_vccktest.dts
+
+AMLOGIC DDR TOOL
+M:     wenbiao zhang <wenbiao.zhang@amlogic.com>
+F:     include/linux/amlogic/aml_dmc.h
index fb49fe8..d15b0eb 100644 (file)
 #include <linux/amlogic/aml_ddr_bandwidth.h>
 #include <linux/io.h>
 #include <linux/slab.h>
+#include <linux/extcon.h>
+#include <linux/amlogic/aml_dmc.h>
 
 static struct ddr_bandwidth *aml_db;
+struct extcon_dev *ddr_extcon_bandwidth;
+static const unsigned int bandwidth_cable[] = {
+       EXTCON_TYPE_DISP,
+       EXTCON_NONE,
+};
 
 static void cal_ddr_usage(struct ddr_bandwidth *db, struct ddr_grant *dg)
 {
        u64 mul; /* avoid overflow */
        unsigned long i, cnt, freq = 0;
 
+       if (db->mode == MODE_AUTODETECT) { /* ignore mali bandwidth */
+               static int count;
+               unsigned int grant = dg->all_grant;
+
+               if (db->mali_port[0] >= 0)
+                       grant -= dg->channel_grant[0];
+               if (db->mali_port[1] >= 0)
+                       grant -= dg->channel_grant[1];
+               if (grant > db->threshold) {
+                       if (count >= 2) {
+                               if (db->busy == 0) {
+                                       db->busy = 1;
+                                       schedule_work(&db->work_bandwidth);
+                               }
+                       } else
+                               count++;
+               } else if (count > 0) {
+                       if (count >= 2) {
+                               db->busy = 0;
+                               schedule_work(&db->work_bandwidth);
+                       }
+                       count = 0;
+               }
+               return;
+       }
        if (db->ops && db->ops->get_freq)
                freq = db->ops->get_freq(db);
        mul  = dg->all_grant;
@@ -139,6 +171,91 @@ static ssize_t ddr_channel_store(struct class *cla,
        return count;
 }
 
+static ssize_t busy_show(struct class *cla,
+       struct class_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%d\n", aml_db->busy);
+}
+
+static ssize_t threshold_show(struct class *cla,
+       struct class_attribute *attr, char *buf)
+{
+       return sprintf(buf, "%d\n",
+               aml_db->threshold / 16 / (aml_db->clock_count / 10000));
+}
+
+static ssize_t threshold_store(struct class *cla,
+       struct class_attribute *attr, const char *buf, size_t count)
+{
+       long val = 0;
+
+       if (kstrtoul(buf, 10, &val)) {
+               pr_info("invalid input:%s\n", buf);
+               return 0;
+       }
+
+       if (val > 10000)
+               val = 10000;
+       aml_db->threshold = val * 16 * (aml_db->clock_count / 10000);
+       return count;
+}
+
+static ssize_t mode_show(struct class *cla,
+       struct class_attribute *attr, char *buf)
+{
+       if (aml_db->mode == MODE_DISABLE)
+               return sprintf(buf, "0: disable\n");
+       else if (aml_db->mode == MODE_ENABLE)
+               return sprintf(buf, "1: enable\n");
+       else if (aml_db->mode == MODE_AUTODETECT)
+               return sprintf(buf, "2: auto detect\n");
+       return sprintf(buf, "\n");
+}
+
+static ssize_t mode_store(struct class *cla,
+       struct class_attribute *attr, const char *buf, size_t count)
+{
+       long val = 0;
+
+       if (kstrtoul(buf, 10, &val)) {
+               pr_info("invalid input:%s\n", buf);
+               return 0;
+       }
+       if ((val > MODE_AUTODETECT) || (val < MODE_DISABLE))
+               val = MODE_AUTODETECT;
+
+       if (val == MODE_AUTODETECT && aml_db->ops && aml_db->ops->config_port) {
+               if (aml_db->mali_port[0] >= 0) {
+                       aml_db->port[0] = aml_db->mali_port[0];
+                       aml_db->ops->config_port(aml_db, 0, aml_db->port[0]);
+               }
+               if (aml_db->mali_port[1] >= 0) {
+                       aml_db->port[1] = aml_db->mali_port[1];
+                       aml_db->ops->config_port(aml_db, 1, aml_db->port[1]);
+               }
+       }
+       if ((aml_db->mode == MODE_DISABLE) && (val != MODE_DISABLE)) {
+               int r = request_irq(aml_db->irq_num, dmc_irq_handler,
+                               IRQF_SHARED, "ddr_bandwidth", (void *)aml_db);
+               if (r < 0) {
+                       pr_info("ddrbandwidth request irq failed\n");
+                       return count;
+               }
+
+               if (aml_db->ops->init)
+                       aml_db->ops->init(aml_db);
+       } else if ((aml_db->mode != MODE_DISABLE) && (val == MODE_DISABLE)) {
+               free_irq(aml_db->irq_num, (void *)aml_db);
+               aml_db->total_usage = 0;
+               aml_db->total_bandwidth = 0;
+               aml_db->busy = 0;
+       }
+       aml_db->mode = val;
+
+       return count;
+}
+
+
 static ssize_t clock_count_show(struct class *cla,
        struct class_attribute *attr, char *buf)
 {
@@ -154,10 +271,11 @@ static ssize_t clock_count_store(struct class *cla,
                pr_info("invalid input:%s\n", buf);
                return 0;
        }
+       aml_db->threshold /= (aml_db->clock_count / 10000);
+       aml_db->threshold *= (val / 10000);
        aml_db->clock_count = val;
        if (aml_db->ops && aml_db->ops->init)
                aml_db->ops->init(aml_db);
-
        return count;
 }
 
@@ -166,7 +284,10 @@ static ssize_t bandwidth_show(struct class *cla,
 {
        size_t s = 0, i;
        int percent, rem;
-#define BANDWIDTH_PREFIX "Total bandwidth:%8d KB/s, usage:%2d.%02d%%\n"
+#define BANDWIDTH_PREFIX "Total bandwidth: %8d KB/s, usage: %2d.%02d%%\n"
+
+       if (aml_db->mode != MODE_ENABLE)
+               return sprintf(buf, "set mode to enable(1) first.\n");
 
        percent = aml_db->total_usage / 100;
        rem     = aml_db->total_usage % 100;
@@ -174,8 +295,8 @@ static ssize_t bandwidth_show(struct class *cla,
                        aml_db->total_bandwidth, percent, rem);
 
        for (i = 0; i < aml_db->channels; i++) {
-               s += sprintf(buf + s, "Channel %zu, bandwidth:%8d KB/s\n",
-                               i, aml_db->bandwidth[i]);
+               s += sprintf(buf + s, "port%d: %8d KB/s\n",
+                               aml_db->port[i], aml_db->bandwidth[i]);
        }
        return s;
 }
@@ -190,6 +311,94 @@ static ssize_t freq_show(struct class *cla,
        return sprintf(buf, "%ld MHz\n", clk / 1000000);
 }
 
+void dmc_set_urgent(unsigned int port, unsigned int type)
+{
+       unsigned int val = 0, addr = 0;
+
+       if (aml_db->cpu_type < MESON_CPU_MAJOR_ID_G12A) {
+               unsigned int port_reg[16] = {
+                       DMC_AXI0_CHAN_CTRL, DMC_AXI1_CHAN_CTRL,
+                       DMC_AXI2_CHAN_CTRL, DMC_AXI3_CHAN_CTRL,
+                       DMC_AXI4_CHAN_CTRL, DMC_AXI5_CHAN_CTRL,
+                       DMC_AXI6_CHAN_CTRL, DMC_AXI7_CHAN_CTRL,
+                       DMC_AM0_CHAN_CTRL, DMC_AM1_CHAN_CTRL,
+                       DMC_AM2_CHAN_CTRL, DMC_AM3_CHAN_CTRL,
+                       DMC_AM4_CHAN_CTRL, DMC_AM5_CHAN_CTRL,
+                       DMC_AM6_CHAN_CTRL, DMC_AM7_CHAN_CTRL,};
+
+               if (port >= 16)
+                       return;
+               addr = port_reg[port];
+       } else {
+               unsigned int port_reg[24] = {
+                       DMC_AXI0_G12_CHAN_CTRL, DMC_AXI1_G12_CHAN_CTRL,
+                       DMC_AXI2_G12_CHAN_CTRL, DMC_AXI3_G12_CHAN_CTRL,
+                       DMC_AXI4_G12_CHAN_CTRL, DMC_AXI5_G12_CHAN_CTRL,
+                       DMC_AXI6_G12_CHAN_CTRL, DMC_AXI7_G12_CHAN_CTRL,
+                       DMC_AXI8_G12_CHAN_CTRL, DMC_AXI9_G12_CHAN_CTRL,
+                       DMC_AXI10_G12_CHAN_CTRL, DMC_AXI1_G12_CHAN_CTRL,
+                       DMC_AXI12_G12_CHAN_CTRL, 0, 0, 0,
+                       DMC_AM0_G12_CHAN_CTRL, DMC_AM1_G12_CHAN_CTRL,
+                       DMC_AM2_G12_CHAN_CTRL, DMC_AM3_G12_CHAN_CTRL,
+                       DMC_AM4_G12_CHAN_CTRL, DMC_AM5_G12_CHAN_CTRL,
+                       DMC_AM6_G12_CHAN_CTRL, DMC_AM7_G12_CHAN_CTRL,};
+
+               if ((port >= 24) || (port_reg[port] == 0))
+                       return;
+               addr = port_reg[port];
+       }
+
+       /**
+        *bit 18. force this channel all request to be super urgent request.
+        *bit 17. force this channel all request to be urgent request.
+        *bit 16. force this channel all request to be non urgent request.
+        */
+       val = readl(aml_db->ddr_reg + addr);
+       val &= (~(0x7 << 16));
+       val |= ((type & 0x7) << 16);
+       writel(val, aml_db->ddr_reg + addr);
+}
+EXPORT_SYMBOL(dmc_set_urgent);
+
+static ssize_t urgent_show(struct class *cla,
+       struct class_attribute *attr, char *buf)
+{
+       int i, s = 0;
+
+       if (!aml_db->real_ports || !aml_db->port_desc)
+               return -EINVAL;
+
+       s += sprintf(buf + s, "echo port val > /sys/class/aml_ddr/urgent\n"
+                       "val:\n\t1: non urgent;\n\t2: urgent;\n\t4: super urgent;\n"
+                       "port:   (hex integer)\n");
+       for (i = 0; i < aml_db->real_ports; i++) {
+               if (aml_db->port_desc[i].port_id >= 24)
+                       break;
+               s += sprintf(buf + s, "\tbit%d: %s\n",
+                               aml_db->port_desc[i].port_id,
+                               aml_db->port_desc[i].port_name);
+       }
+
+       return s;
+}
+
+static ssize_t urgent_store(struct class *cla,
+       struct class_attribute *attr, const char *buf, size_t count)
+{
+       unsigned int port = 0, val, i;
+
+       if (sscanf(buf, "%x %d", &port, &val) != 2) {
+               pr_info("invalid input:%s\n", buf);
+               return -EINVAL;
+       }
+       for (i = 0; i < 24; i++) {
+               if (port & 1)
+                       dmc_set_urgent(i, val);
+               port >>= 1;
+       }
+       return count;
+}
+
 #if DDR_BANDWIDTH_DEBUG
 static ssize_t dump_reg_show(struct class *cla,
        struct class_attribute *attr, char *buf)
@@ -232,6 +441,10 @@ static ssize_t name_of_ports_show(struct class *cla,
 static struct class_attribute aml_ddr_tool_attr[] = {
        __ATTR(port, 0664, ddr_channel_show, ddr_channel_store),
        __ATTR(irq_clock, 0664, clock_count_show, clock_count_store),
+       __ATTR(urgent, 0664, urgent_show, urgent_store),
+       __ATTR(threshold, 0664, threshold_show, threshold_store),
+       __ATTR(mode, 0664, mode_show, mode_store),
+       __ATTR_RO(busy),
        __ATTR_RO(bandwidth),
        __ATTR_RO(freq),
        __ATTR_RO(cpu_type),
@@ -247,6 +460,41 @@ static struct class aml_ddr_class = {
        .class_attrs = aml_ddr_tool_attr,
 };
 
+static void bandwidth_work_func(struct work_struct *work)
+{
+       extcon_set_state_sync(ddr_extcon_bandwidth, EXTCON_TYPE_DISP,
+               (aml_db->busy == 1) ? true : false);
+}
+
+void ddr_extcon_register(struct platform_device *pdev)
+{
+       struct extcon_dev *edev;
+       int ret;
+
+       edev = extcon_dev_allocate(bandwidth_cable);
+       if (IS_ERR(edev)) {
+               pr_info("failed to allocate ddr extcon bandwidth\n");
+               return;
+       }
+       edev->dev.parent = &pdev->dev;
+       edev->name = "ddr_extcon_bandwidth";
+       dev_set_name(&edev->dev, "bandwidth");
+       ret = extcon_dev_register(edev);
+       if (ret < 0) {
+               pr_info("failed to register ddr extcon bandwidth\n");
+               return;
+       }
+       ddr_extcon_bandwidth = edev;
+
+       INIT_WORK(&aml_db->work_bandwidth, bandwidth_work_func);
+}
+
+static void ddr_extcon_free(void)
+{
+       extcon_dev_free(ddr_extcon_bandwidth);
+       ddr_extcon_bandwidth = NULL;
+}
+
 /*
  *    ddr_bandwidth_probe only executes before the init process starts
  * to run, so add __ref to indicate it is okay to call __init function
@@ -257,7 +505,6 @@ static int __ref ddr_bandwidth_probe(struct platform_device *pdev)
        int r = 0;
 #ifdef CONFIG_OF
        struct device_node *node = pdev->dev.of_node;
-       const char *irq_name = NULL;
        /*struct pinctrl *p;*/
        struct resource *res;
        resource_size_t *base;
@@ -277,10 +524,24 @@ static int __ref ddr_bandwidth_probe(struct platform_device *pdev)
        }
 
        /* set channel */
-       if (aml_db->cpu_type < MESON_CPU_MAJOR_ID_GXTVBB)
+       if (aml_db->cpu_type < MESON_CPU_MAJOR_ID_GXTVBB) {
                aml_db->channels = 1;
-       else
+               aml_db->mali_port[0] = 2;
+               aml_db->mali_port[1] = -1;
+       } else {
                aml_db->channels = 4;
+               if ((aml_db->cpu_type == MESON_CPU_MAJOR_ID_GXM)
+                       || (aml_db->cpu_type >= MESON_CPU_MAJOR_ID_G12A)) {
+                       aml_db->mali_port[0] = 1; /* port1: mali */
+                       aml_db->mali_port[1] = -1;
+               } else if (aml_db->cpu_type == MESON_CPU_MAJOR_ID_AXG) {
+                       aml_db->mali_port[0] = -1; /* no mali */
+                       aml_db->mali_port[1] = -1;
+               } else {
+                       aml_db->mali_port[0] = 1; /* port1: mali0 */
+                       aml_db->mali_port[1] = 2; /* port2: mali1 */
+               }
+       }
 
        /* find and configure port description */
        pcnt = ddr_find_port_desc(aml_db->cpu_type, &desc);
@@ -313,19 +574,11 @@ static int __ref ddr_bandwidth_probe(struct platform_device *pdev)
        }
 
        aml_db->irq_num = of_irq_get(node, 0);
-       if (of_get_property(node, "interrupt-names", NULL)) {
-               r = of_property_read_string(node, "interrupt-names", &irq_name);
-               if (!r) {
-                       r = request_irq(aml_db->irq_num, dmc_irq_handler,
-                                       IRQF_SHARED, irq_name, (void *)aml_db);
-               }
-       }
-       if (r < 0) {
-               pr_info("request irq failed:%d\n", aml_db->irq_num);
-               goto inval;
-       }
 #endif
        aml_db->clock_count = DEFAULT_CLK_CNT;
+       aml_db->mode = MODE_DISABLE;
+       aml_db->threshold = DEFAULT_THRESHOLD * 16 *
+                       (aml_db->clock_count / 10000);
        if (aml_db->cpu_type <= MESON_CPU_MAJOR_ID_GXTVBB)
                aml_db->ops = &gx_ddr_bw_ops;
        else if ((aml_db->cpu_type <= MESON_CPU_MAJOR_ID_TXHD) &&
@@ -339,11 +592,11 @@ static int __ref ddr_bandwidth_probe(struct platform_device *pdev)
                goto inval;
        }
 
-       if (aml_db->ops->init)
-               aml_db->ops->init(aml_db);
        r = class_register(&aml_ddr_class);
        if (r)
                pr_info("%s, class regist failed\n", __func__);
+
+       ddr_extcon_register(pdev);
        return 0;
 inval:
        kfree(aml_db->port_desc);
@@ -364,6 +617,7 @@ static int ddr_bandwidth_remove(struct platform_device *pdev)
                kfree(aml_db);
                aml_db = NULL;
        }
+       ddr_extcon_free();
 
        return 0;
 }
index 5e3113b..1235d1e 100644 (file)
 #ifndef __AML_DDR_BANDWIDTH_H__
 #define __AML_DDR_BANDWIDTH_H__
 
+#define MODE_DISABLE                   0
+#define MODE_ENABLE                    1
+#define MODE_AUTODETECT                        2
 
-#define DEFAULT_CLK_CNT                        12000000
+#define DEFAULT_THRESHOLD              5000
+
+#define DEFAULT_CLK_CNT                        48000000
 #define DEFAULT_XTAL_FREQ              24000000UL
 
 #define DMC_QOS_IRQ                    (1 << 30)
 #define DMC_MON_CTRL5                  (0x19 << 2)
 #define DMC_MON_CTRL6                  (0x1a << 2)
 
+#define DMC_AM0_CHAN_CTRL              (0x60 << 2)
+#define DMC_AM1_CHAN_CTRL              (0x6a << 2)
+#define DMC_AM2_CHAN_CTRL              (0x74 << 2)
+#define DMC_AM3_CHAN_CTRL              (0x7e << 2)
+#define DMC_AM4_CHAN_CTRL              (0x88 << 2)
+#define DMC_AM5_CHAN_CTRL              (0x92 << 2)
+#define DMC_AM6_CHAN_CTRL              (0x9c << 2)
+#define DMC_AM7_CHAN_CTRL              (0xa6 << 2)
+#define DMC_AXI0_CHAN_CTRL             (0xb0 << 2)
+#define DMC_AXI1_CHAN_CTRL             (0xba << 2)
+#define DMC_AXI2_CHAN_CTRL             (0xc4 << 2)
+#define DMC_AXI3_CHAN_CTRL             (0xce << 2)
+#define DMC_AXI4_CHAN_CTRL             (0xd8 << 2)
+#define DMC_AXI5_CHAN_CTRL             (0xe2 << 2)
+#define DMC_AXI6_CHAN_CTRL             (0xec << 2)
+#define DMC_AXI7_CHAN_CTRL             (0xf6 << 2)
+
 /*
  * register offset for g12a
  */
 #define DMC_MON_G12_FOR_GRANT_CNT      (0x2e  << 2)
 #define DMC_MON_G12_TIMER              (0x2f  << 2)
 
+#define DMC_AM0_G12_CHAN_CTRL          (0x60 << 2)
+#define DMC_AM1_G12_CHAN_CTRL          (0x64 << 2)
+#define DMC_AM2_G12_CHAN_CTRL          (0x68 << 2)
+#define DMC_AM3_G12_CHAN_CTRL          (0x6c << 2)
+#define DMC_AM4_G12_CHAN_CTRL          (0x70 << 2)
+#define DMC_AM5_G12_CHAN_CTRL          (0x74 << 2)
+#define DMC_AM6_G12_CHAN_CTRL          (0x78 << 2)
+#define DMC_AM7_G12_CHAN_CTRL          (0x7c << 2)
+#define DMC_AXI0_G12_CHAN_CTRL         (0x80 << 2)
+#define DMC_AXI1_G12_CHAN_CTRL         (0x84 << 2)
+#define DMC_AXI2_G12_CHAN_CTRL         (0x88 << 2)
+#define DMC_AXI3_G12_CHAN_CTRL         (0x8c << 2)
+#define DMC_AXI4_G12_CHAN_CTRL         (0x90 << 2)
+#define DMC_AXI5_G12_CHAN_CTRL         (0x94 << 2)
+#define DMC_AXI6_G12_CHAN_CTRL         (0x98 << 2)
+#define DMC_AXI7_G12_CHAN_CTRL         (0x9c << 2)
+#define DMC_AXI8_G12_CHAN_CTRL         (0xa0 << 2)
+#define DMC_AXI9_G12_CHAN_CTRL         (0xa4 << 2)
+#define DMC_AXI10_G12_CHAN_CTRL                (0xa8 << 2)
+#define DMC_AXI11_G12_CHAN_CTRL                (0xac << 2)
+#define DMC_AXI12_G12_CHAN_CTRL                (0xb0 << 2)
+
 /* data structure */
 #define DDR_BANDWIDTH_DEBUG            1
 
@@ -91,6 +135,11 @@ struct ddr_bandwidth {
        struct class *class;
        unsigned short cpu_type;
        unsigned short real_ports;
+       char busy;
+       char mode;
+       int mali_port[2];
+       unsigned int threshold;
+       struct work_struct work_bandwidth;
        unsigned int irq_num;
        unsigned int clock_count;
        unsigned int channels;
diff --git a/include/linux/amlogic/aml_dmc.h b/include/linux/amlogic/aml_dmc.h
new file mode 100644 (file)
index 0000000..0925848
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * include/linux/amlogic/aml_dmc.h
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef AML_DMC_HEADERS
+#define AML_DMC_HEADERS
+
+extern void dmc_set_urgent(unsigned int port, unsigned int type);
+
+#endif
+