blob: 33c86b87630dcabfc449e06d415a902b1c89dc4c [file] [log] [blame]
/*
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/list.h>
#include <linux/dma-mapping.h>
#include <linux/pm_runtime.h>
#include <linux/pm_qos.h>
#include <linux/suspend.h>
#include <linux/debugfs.h>
#include <soc/samsung/exynos-pm.h>
#include <soc/samsung/bts.h>
#include "cal_bts8890.h"
#include "regs-bts.h"
#define VPP_MAX 9
#define MIF_BLK_NUM 4
#define TREX_SCI_NUM 2
#define BTS_DISP (BTS_TREX_DISP0_0 | BTS_TREX_DISP0_1 | \
BTS_TREX_DISP1_0 | BTS_TREX_DISP1_1)
#define BTS_MFC (BTS_TREX_MFC0 | BTS_TREX_MFC1)
#define BTS_G3D (BTS_TREX_G3D0 | BTS_TREX_G3D1)
#define BTS_RT (BTS_TREX_DISP0_0 | BTS_TREX_DISP0_1 | \
BTS_TREX_DISP1_0 | BTS_TREX_DISP1_1 | \
BTS_TREX_ISP0 | BTS_TREX_CAM0 | BTS_TREX_CP)
#define update_rot_scen(a) (pr_state.rot_scen = a)
#define update_g3d_scen(a) (pr_state.g3d_scen = a)
#define update_urgent_scen(a) (pr_state.urg_scen = a)
#ifdef BTS_DBGGEN
#define BTS_DBG(x...) pr_err(x)
#else
#define BTS_DBG(x...) do {} while (0)
#endif
enum bts_index {
BTS_IDX_TREX_DISP0_0,
BTS_IDX_TREX_DISP0_1,
BTS_IDX_TREX_DISP1_0,
BTS_IDX_TREX_DISP1_1,
BTS_IDX_TREX_ISP0,
BTS_IDX_TREX_CAM0,
BTS_IDX_TREX_CAM1,
BTS_IDX_TREX_CP,
BTS_IDX_TREX_MFC0,
BTS_IDX_TREX_MFC1,
BTS_IDX_TREX_G3D0,
BTS_IDX_TREX_G3D1,
BTS_IDX_TREX_FSYS0,
BTS_IDX_TREX_FSYS1,
BTS_IDX_TREX_MSCL0,
BTS_IDX_TREX_MSCL1,
BTS_MAX,
};
#define BTS_TREX_DISP0_0 ((u64)1 << (u64)BTS_IDX_TREX_DISP0_0)
#define BTS_TREX_DISP0_1 ((u64)1 << (u64)BTS_IDX_TREX_DISP0_1)
#define BTS_TREX_DISP1_0 ((u64)1 << (u64)BTS_IDX_TREX_DISP1_0)
#define BTS_TREX_DISP1_1 ((u64)1 << (u64)BTS_IDX_TREX_DISP1_1)
#define BTS_TREX_ISP0 ((u64)1 << (u64)BTS_IDX_TREX_ISP0)
#define BTS_TREX_CAM0 ((u64)1 << (u64)BTS_IDX_TREX_CAM0)
#define BTS_TREX_CAM1 ((u64)1 << (u64)BTS_IDX_TREX_CAM1)
#define BTS_TREX_CP ((u64)1 << (u64)BTS_IDX_TREX_CP)
#define BTS_TREX_MFC0 ((u64)1 << (u64)BTS_IDX_TREX_MFC0)
#define BTS_TREX_MFC1 ((u64)1 << (u64)BTS_IDX_TREX_MFC1)
#define BTS_TREX_G3D0 ((u64)1 << (u64)BTS_IDX_TREX_G3D0)
#define BTS_TREX_G3D1 ((u64)1 << (u64)BTS_IDX_TREX_G3D1)
#define BTS_TREX_FSYS0 ((u64)1 << (u64)BTS_IDX_TREX_FSYS0)
#define BTS_TREX_FSYS1 ((u64)1 << (u64)BTS_IDX_TREX_FSYS1)
#define BTS_TREX_MSCL0 ((u64)1 << (u64)BTS_IDX_TREX_MSCL0)
#define BTS_TREX_MSCL1 ((u64)1 << (u64)BTS_IDX_TREX_MSCL1)
enum exynos_bts_scenario {
BS_DISABLE,
BS_DEFAULT,
BS_ROTATION,
BS_HIGHPERF,
BS_G3DFREQ,
BS_URGENTOFF,
BS_DEBUG,
BS_MAX,
};
enum exynos_bts_function {
BF_SETQOS,
BF_SETQOS_BW,
BF_SETQOS_MO,
BF_SETQOS_FBMBW,
BF_DISABLE,
BF_SETTREXQOS,
BF_SETTREXQOS_MO,
BF_SETTREXQOS_MO_RT,
BF_SETTREXQOS_MO_CP,
BF_SETTREXQOS_MO_CHANGE,
BF_SETTREXQOS_URGENT_OFF,
BF_SETTREXQOS_BW,
BF_SETTREXQOS_FBMBW,
BF_SETTREXDISABLE,
BF_NOP,
};
enum vpp_state {
VPP_BW,
VPP_ROT_BW,
VPP_STAT,
};
struct bts_table {
enum exynos_bts_function fn;
unsigned int priority;
unsigned int window;
unsigned int token;
unsigned int mo;
unsigned int fbm;
unsigned int mask;
unsigned int timeout;
unsigned int bypass_en;
unsigned int decval;
struct bts_info *next_bts;
int prev_scen;
int next_scen;
};
struct bts_info {
u64 id;
const char *name;
unsigned int pa_base;
void __iomem *va_base;
struct bts_table table[BS_MAX];
const char *pd_name;
bool on;
struct list_head list;
bool enable;
struct clk_info *ct_ptr;
enum exynos_bts_scenario cur_scen;
enum exynos_bts_scenario top_scen;
};
struct bts_scen_status {
bool rot_scen;
bool g3d_scen;
bool urg_scen;
};
struct bts_scenario {
const char *name;
u64 ip;
enum exynos_bts_scenario id;
struct bts_info *head;
};
struct bts_scen_status pr_state = {
.rot_scen = false,
.g3d_scen = false,
.urg_scen = false,
};
struct clk_info {
const char *clk_name;
struct clk *clk;
enum bts_index index;
};
static void __iomem *base_trex[TREX_SCI_NUM];
static DEFINE_MUTEX(media_mutex);
#ifdef CONFIG_EXYNOS8890_BTS_OPTIMIZATION
static unsigned int prev_mo;
static unsigned int vpp_rot[VPP_MAX];
#else
static unsigned int vpp_bw[VPP_STAT][VPP_MAX];
static unsigned int cam_bw, sum_rot_bw, total_bw;
static enum vpp_bw_type vpp_status[VPP_MAX];
static unsigned int mif_freq, int_freq;
#endif
static struct pm_qos_request exynos8_mif_bts_qos;
static struct pm_qos_request exynos8_int_bts_qos;
static struct pm_qos_request exynos8_gpu_mif_bts_qos;
static struct pm_qos_request exynos8_winlayer_mif_bts_qos;
static struct srcu_notifier_head exynos_media_notifier;
static struct clk_info clk_table[0];
static int bts_trex_qosoff(void __iomem *base);
static int bts_trex_qoson(void __iomem *base);
static struct bts_info exynos8_bts[] = {
[BTS_IDX_TREX_DISP0_0] = {
.id = BTS_TREX_DISP0_0,
.name = "disp0_0",
.pd_name = "trex",
.pa_base = EXYNOS8890_PA_BTS_TREX_DISP0_0,
.table[BS_DEFAULT].fn = BF_SETTREXQOS_MO_RT,
.table[BS_DEFAULT].priority = 0x00000008,
.table[BS_DEFAULT].mo = 0x10,
.table[BS_DEFAULT].timeout = 0x40,
.table[BS_DEFAULT].bypass_en = 0,
.table[BS_ROTATION].fn = BF_SETTREXQOS_MO_RT,
.table[BS_ROTATION].priority = 0x00000008,
.table[BS_ROTATION].mo = 0x20,
.table[BS_ROTATION].timeout = 0x40,
.table[BS_ROTATION].bypass_en = 0,
.table[BS_DISABLE].fn = BF_SETTREXDISABLE,
.cur_scen = BS_DISABLE,
.on = false,
.enable = true,
},
[BTS_IDX_TREX_DISP0_1] = {
.id = BTS_TREX_DISP0_1,
.name = "disp0_1",
.pd_name = "trex",
.pa_base = EXYNOS8890_PA_BTS_TREX_DISP0_1,
.table[BS_DEFAULT].fn = BF_SETTREXQOS_MO_RT,
.table[BS_DEFAULT].priority = 0x00000008,
.table[BS_DEFAULT].mo = 0x10,
.table[BS_DEFAULT].timeout = 0x40,
.table[BS_DEFAULT].bypass_en = 0,
.table[BS_ROTATION].fn = BF_SETTREXQOS_MO_RT,
.table[BS_ROTATION].priority = 0x00000008,
.table[BS_ROTATION].mo = 0x20,
.table[BS_ROTATION].timeout = 0x40,
.table[BS_ROTATION].bypass_en = 0,
.table[BS_DISABLE].fn = BF_SETTREXDISABLE,
.cur_scen = BS_DISABLE,
.on = false,
.enable = true,
},
[BTS_IDX_TREX_DISP1_0] = {
.id = BTS_TREX_DISP1_0,
.name = "disp1_0",
.pd_name = "trex",
.pa_base = EXYNOS8890_PA_BTS_TREX_DISP1_0,
.table[BS_DEFAULT].fn = BF_SETTREXQOS_MO_RT,
.table[BS_DEFAULT].priority = 0x00000008,
.table[BS_DEFAULT].mo = 0x10,
.table[BS_DEFAULT].timeout = 0x40,
.table[BS_DEFAULT].bypass_en = 0,
.table[BS_ROTATION].fn = BF_SETTREXQOS_MO_RT,
.table[BS_ROTATION].priority = 0x00000008,
.table[BS_ROTATION].mo = 0x20,
.table[BS_ROTATION].timeout = 0x40,
.table[BS_ROTATION].bypass_en = 0,
.table[BS_DISABLE].fn = BF_SETTREXDISABLE,
.cur_scen = BS_DISABLE,
.on = false,
.enable = true,
},
[BTS_IDX_TREX_DISP1_1] = {
.id = BTS_TREX_DISP1_1,
.name = "disp1_1",
.pd_name = "trex",
.pa_base = EXYNOS8890_PA_BTS_TREX_DISP1_1,
.table[BS_DEFAULT].fn = BF_SETTREXQOS_MO_RT,
.table[BS_DEFAULT].priority = 0x00000008,
.table[BS_DEFAULT].mo = 0x10,
.table[BS_DEFAULT].timeout = 0x40,
.table[BS_DEFAULT].bypass_en = 0,
.table[BS_ROTATION].fn = BF_SETTREXQOS_MO_RT,
.table[BS_ROTATION].priority = 0x00000008,
.table[BS_ROTATION].mo = 0x20,
.table[BS_ROTATION].timeout = 0x40,
.table[BS_ROTATION].bypass_en = 0,
.table[BS_DISABLE].fn = BF_SETTREXDISABLE,
.cur_scen = BS_DISABLE,
.on = false,
.enable = true,
},
[BTS_IDX_TREX_ISP0] = {
.id = BTS_TREX_ISP0,
.name = "isp0",
.pd_name = "trex",
.pa_base = EXYNOS8890_PA_BTS_TREX_ISP0,
.table[BS_DEFAULT].fn = BF_SETTREXQOS_MO_RT,
.table[BS_DEFAULT].priority = 0x0000000A,
.table[BS_DEFAULT].mo = 0x10,
.table[BS_DEFAULT].timeout = 0x10,
.table[BS_DEFAULT].bypass_en = 0,
.table[BS_DISABLE].fn = BF_SETTREXDISABLE,
.cur_scen = BS_DISABLE,
.on = false,
.enable = true,
},
[BTS_IDX_TREX_CAM0] = {
.id = BTS_TREX_CAM0,
.name = "cam0",
.pd_name = "trex",
.pa_base = EXYNOS8890_PA_BTS_TREX_CAM0,
.table[BS_DEFAULT].fn = BF_SETTREXQOS_MO_RT,
.table[BS_DEFAULT].priority = 0x0000000A,
.table[BS_DEFAULT].mo = 0x10,
.table[BS_DEFAULT].timeout = 0x10,
.table[BS_DEFAULT].bypass_en = 0,
.table[BS_DISABLE].fn = BF_SETTREXDISABLE,
.cur_scen = BS_DISABLE,
.on = false,
.enable = true,
},
[BTS_IDX_TREX_CAM1] = {
.id = BTS_TREX_CAM1,
.name = "cam1",
.pd_name = "trex",
.pa_base = EXYNOS8890_PA_BTS_TREX_CAM1,
.table[BS_DEFAULT].fn = BF_SETTREXQOS_MO,
.table[BS_DEFAULT].priority = 0x0000000A,
.table[BS_DEFAULT].mo = 0x10,
.table[BS_DEFAULT].bypass_en = 0,
.table[BS_DISABLE].fn = BF_SETTREXDISABLE,
.cur_scen = BS_DISABLE,
.on = false,
.enable = true,
},
[BTS_IDX_TREX_CP] = {
.id = BTS_TREX_CP,
.name = "cp",
.pd_name = "trex",
.pa_base = EXYNOS8890_PA_BTS_TREX_CP,
.table[BS_DEFAULT].fn = BF_SETTREXQOS_MO_CP,
.table[BS_DEFAULT].priority = 0x0000000C,
.table[BS_DEFAULT].mo = 0x10,
.table[BS_DEFAULT].timeout = 0x10,
.table[BS_DEFAULT].bypass_en = 0,
.table[BS_URGENTOFF].fn = BF_SETTREXQOS_URGENT_OFF,
.table[BS_DISABLE].fn = BF_SETTREXDISABLE,
.cur_scen = BS_DISABLE,
.on = false,
.enable = true,
},
[BTS_IDX_TREX_MFC0] = {
.id = BTS_TREX_MFC0,
.name = "mfc0",
.pd_name = "trex",
.pa_base = EXYNOS8890_PA_BTS_TREX_MFC0,
.table[BS_DEFAULT].fn = BF_SETTREXQOS_MO,
.table[BS_DEFAULT].priority = 0x00000004,
.table[BS_DEFAULT].mo = 0x8,
.table[BS_DEFAULT].bypass_en = 0,
.table[BS_HIGHPERF].fn = BF_SETTREXQOS_MO_CHANGE,
.table[BS_HIGHPERF].mo = 0x400,
.table[BS_DISABLE].fn = BF_SETTREXDISABLE,
.cur_scen = BS_DISABLE,
.on = false,
.enable = true,
},
[BTS_IDX_TREX_MFC1] = {
.id = BTS_TREX_MFC1,
.name = "mfc1",
.pd_name = "trex",
.pa_base = EXYNOS8890_PA_BTS_TREX_MFC1,
.table[BS_DEFAULT].fn = BF_SETTREXQOS_MO,
.table[BS_DEFAULT].priority = 0x00000004,
.table[BS_DEFAULT].mo = 0x8,
.table[BS_DEFAULT].bypass_en = 0,
.table[BS_HIGHPERF].fn = BF_SETTREXQOS_MO_CHANGE,
.table[BS_HIGHPERF].mo = 0x400,
.table[BS_DISABLE].fn = BF_SETTREXDISABLE,
.cur_scen = BS_DISABLE,
.on = false,
.enable = true,
},
[BTS_IDX_TREX_G3D0] = {
.id = BTS_TREX_G3D0,
.name = "g3d0",
.pd_name = "trex",
.pa_base = EXYNOS8890_PA_BTS_TREX_G3D0,
.table[BS_DEFAULT].fn = BF_SETTREXQOS_MO,
.table[BS_DEFAULT].priority = 0x00000004,
.table[BS_DEFAULT].mo = 0x10,
.table[BS_DEFAULT].bypass_en = 0,
.table[BS_G3DFREQ].fn = BF_SETTREXQOS_MO_CHANGE,
.table[BS_G3DFREQ].mo = 0x20,
.table[BS_DISABLE].fn = BF_SETTREXDISABLE,
.cur_scen = BS_DISABLE,
.on = false,
.enable = true,
},
[BTS_IDX_TREX_G3D1] = {
.id = BTS_TREX_G3D1,
.name = "g3d1",
.pd_name = "trex",
.pa_base = EXYNOS8890_PA_BTS_TREX_G3D1,
.table[BS_DEFAULT].fn = BF_SETTREXQOS_MO,
.table[BS_DEFAULT].priority = 0x00000004,
.table[BS_DEFAULT].mo = 0x10,
.table[BS_DEFAULT].bypass_en = 0,
.table[BS_G3DFREQ].fn = BF_SETTREXQOS_MO_CHANGE,
.table[BS_G3DFREQ].mo = 0x20,
.table[BS_DISABLE].fn = BF_SETTREXDISABLE,
.cur_scen = BS_DISABLE,
.on = false,
.enable = true,
},
[BTS_IDX_TREX_FSYS0] = {
.id = BTS_TREX_FSYS0,
.name = "fsys0",
.pd_name = "trex",
.pa_base = EXYNOS8890_PA_BTS_TREX_FSYS0,
.table[BS_DEFAULT].fn = BF_SETTREXQOS_MO,
.table[BS_DEFAULT].priority = 0x00000004,
.table[BS_DEFAULT].mo = 0x4,
.table[BS_DEFAULT].bypass_en = 0,
.table[BS_DISABLE].fn = BF_SETTREXDISABLE,
.cur_scen = BS_DISABLE,
.on = false,
.enable = true,
},
[BTS_IDX_TREX_FSYS1] = {
.id = BTS_TREX_FSYS1,
.name = "fsys1",
.pd_name = "trex",
.pa_base = EXYNOS8890_PA_BTS_TREX_FSYS1,
.table[BS_DEFAULT].fn = BF_SETTREXQOS_MO,
.table[BS_DEFAULT].priority = 0x00000004,
.table[BS_DEFAULT].mo = 0x4,
.table[BS_DEFAULT].bypass_en = 0,
.table[BS_DISABLE].fn = BF_SETTREXDISABLE,
.cur_scen = BS_DISABLE,
.on = false,
.enable = true,
},
[BTS_IDX_TREX_MSCL0] = {
.id = BTS_TREX_MSCL0,
.name = "mscl0",
.pd_name = "trex",
.pa_base = EXYNOS8890_PA_BTS_TREX_MSCL0,
.table[BS_DEFAULT].fn = BF_SETTREXQOS_MO,
.table[BS_DEFAULT].priority = 0x00000004,
.table[BS_DEFAULT].mo = 0x4,
.table[BS_DEFAULT].bypass_en = 0,
.table[BS_DISABLE].fn = BF_SETTREXDISABLE,
.cur_scen = BS_DISABLE,
.on = false,
.enable = true,
},
[BTS_IDX_TREX_MSCL1] = {
.id = BTS_TREX_MSCL1,
.name = "mscl1",
.pd_name = "trex",
.pa_base = EXYNOS8890_PA_BTS_TREX_MSCL1,
.table[BS_DEFAULT].fn = BF_SETTREXQOS_MO,
.table[BS_DEFAULT].priority = 0x00000004,
.table[BS_DEFAULT].mo = 0x4,
.table[BS_DEFAULT].bypass_en = 0,
.table[BS_DISABLE].fn = BF_SETTREXDISABLE,
.cur_scen = BS_DISABLE,
.on = false,
.enable = true,
},
};
static struct bts_scenario bts_scen[] = {
[BS_DISABLE] = {
.name = "bts_disable",
.id = BS_DISABLE,
},
[BS_DEFAULT] = {
.name = "bts_default",
.id = BS_DEFAULT,
},
[BS_ROTATION] = {
.name = "bts_rotation",
.ip = BTS_DISP,
.id = BS_ROTATION,
},
[BS_HIGHPERF] = {
.name = "bts_mfchighperf",
.ip = BTS_MFC,
.id = BS_HIGHPERF,
},
[BS_G3DFREQ] = {
.name = "bts_g3dfreq",
.ip = BTS_G3D,
.id = BS_G3DFREQ,
},
[BS_URGENTOFF] = {
.name = "bts_urgentoff",
.ip = BTS_TREX_CP,
.id = BS_URGENTOFF,
},
[BS_DEBUG] = {
.name = "bts_dubugging_ip",
.id = BS_DEBUG,
},
[BS_MAX] = {
.name = "undefined"
}
};
static DEFINE_SPINLOCK(bts_lock);
static LIST_HEAD(bts_list);
static void is_bts_clk_enabled(struct bts_info *bts)
{
struct clk_info *ptr;
enum bts_index btstable_index;
ptr = bts->ct_ptr;
if (ptr) {
btstable_index = ptr->index;
do {
if(!__clk_is_enabled(ptr->clk))
pr_err("[BTS] CLK is not enabled : %s in %s\n",
ptr->clk_name,
bts->name);
} while (++ptr < clk_table + ARRAY_SIZE(clk_table)
&& ptr->index == btstable_index);
}
}
static void bts_clk_on(struct bts_info *bts)
{
struct clk_info *ptr;
enum bts_index btstable_index;
ptr = bts->ct_ptr;
if (ptr) {
btstable_index = ptr->index;
do {
clk_enable(ptr->clk);
} while (++ptr < clk_table + ARRAY_SIZE(clk_table)
&& ptr->index == btstable_index);
}
}
static void bts_clk_off(struct bts_info *bts)
{
struct clk_info *ptr;
enum bts_index btstable_index;
ptr = bts->ct_ptr;
if (ptr) {
btstable_index = ptr->index;
do {
clk_disable(ptr->clk);
} while (++ptr < clk_table + ARRAY_SIZE(clk_table)
&& ptr->index == btstable_index);
}
}
static void bts_set_ip_table(enum exynos_bts_scenario scen,
struct bts_info *bts)
{
enum exynos_bts_function fn = bts->table[scen].fn;
int i;
is_bts_clk_enabled(bts);
BTS_DBG("[BTS] %s on:%d bts scen: [%s]->[%s]\n", bts->name, bts->on,
bts_scen[bts->cur_scen].name, bts_scen[scen].name);
switch (fn) {
case BF_SETTREXQOS:
bts_settrexqos(bts->va_base, bts->table[scen].priority, 0, 0);
break;
case BF_SETTREXQOS_MO_RT:
bts_settrexqos_mo_rt(bts->va_base, bts->table[scen].priority, bts->table[scen].mo,
0, 0, bts->table[scen].timeout, bts->table[scen].bypass_en);
break;
case BF_SETTREXQOS_MO_CP:
bts_settrexqos_mo_cp(bts->va_base, bts->table[scen].priority, bts->table[scen].mo,
0, 0, bts->table[scen].timeout, bts->table[scen].bypass_en);
bts_settrexqos_urgent_on(bts->va_base);
for (i = 0; i < (unsigned int)ARRAY_SIZE(base_trex); i++)
bts_trex_qoson(base_trex[i]);
break;
case BF_SETTREXQOS_MO:
bts_settrexqos_mo(bts->va_base, bts->table[scen].priority, bts->table[scen].mo, 0, 0);
break;
case BF_SETTREXQOS_MO_CHANGE:
bts_settrexqos_mo_change(bts->va_base, bts->table[scen].mo);
break;
case BF_SETTREXQOS_URGENT_OFF:
bts_settrexqos_urgent_off(bts->va_base);
for (i = 0; i < (unsigned int)ARRAY_SIZE(base_trex); i++)
bts_trex_qosoff(base_trex[i]);
break;
case BF_SETTREXQOS_BW:
bts_settrexqos_bw(bts->va_base, bts->table[scen].priority,
bts->table[scen].decval, 0, 0);
break;
case BF_SETTREXQOS_FBMBW:
bts_settrexqos_fbmbw(bts->va_base, bts->table[scen].priority, 0, 0);
break;
case BF_SETTREXDISABLE:
bts_trexdisable(bts->va_base, 0, 0);
break;
case BF_SETQOS:
bts_setqos(bts->va_base, bts->table[scen].priority, 0);
break;
case BF_SETQOS_BW:
bts_setqos_bw(bts->va_base, bts->table[scen].priority,
bts->table[scen].window, bts->table[scen].token, 0);
break;
case BF_SETQOS_MO:
bts_setqos_mo(bts->va_base, bts->table[scen].priority, bts->table[scen].mo, 0);
break;
case BF_SETQOS_FBMBW:
bts_setqos_fbmbw(bts->va_base, bts->table[scen].priority, bts->table[scen].window,
bts->table[scen].token, bts->table[scen].fbm, 0);
break;
case BF_DISABLE:
bts_disable(bts->va_base, 0);
break;
case BF_NOP:
break;
}
bts->cur_scen = scen;
}
static enum exynos_bts_scenario bts_get_scen(struct bts_info *bts)
{
enum exynos_bts_scenario scen;
scen = BS_DEFAULT;
return scen;
}
static void bts_add_scen(enum exynos_bts_scenario scen, struct bts_info *bts)
{
struct bts_info *first = bts;
int next = 0;
int prev = 0;
if (!bts)
return;
BTS_DBG("[bts %s] scen %s off\n",
bts->name, bts_scen[scen].name);
do {
if (bts->enable) {
if (bts->table[scen].next_scen == 0) {
if (scen >= bts->top_scen) {
bts->table[scen].prev_scen = bts->top_scen;
bts->table[bts->top_scen].next_scen = scen;
bts->top_scen = scen;
bts->table[scen].next_scen = -1;
if(bts->on)
bts_set_ip_table(bts->top_scen, bts);
} else {
for (prev = bts->top_scen; prev > scen; prev = bts->table[prev].prev_scen)
next = prev;
bts->table[scen].prev_scen = bts->table[next].prev_scen;
bts->table[scen].next_scen = bts->table[prev].next_scen;
bts->table[next].prev_scen = scen;
bts->table[prev].next_scen = scen;
}
}
}
bts = bts->table[scen].next_bts;
} while (bts && bts != first);
}
static void bts_del_scen(enum exynos_bts_scenario scen, struct bts_info *bts)
{
struct bts_info *first = bts;
int next = 0;
int prev = 0;
if (!bts)
return;
BTS_DBG("[bts %s] scen %s off\n",
bts->name, bts_scen[scen].name);
do {
if (bts->enable) {
if (bts->table[scen].next_scen != 0) {
if (scen == bts->top_scen) {
prev = bts->table[scen].prev_scen;
bts->top_scen = prev;
bts->table[prev].next_scen = -1;
bts->table[scen].next_scen = 0;
bts->table[scen].prev_scen = 0;
if (bts->on)
bts_set_ip_table(prev, bts);
} else if (scen < bts->top_scen) {
prev = bts->table[scen].prev_scen;
next = bts->table[scen].next_scen;
bts->table[next].prev_scen = bts->table[scen].prev_scen;
bts->table[prev].next_scen = bts->table[scen].next_scen;
bts->table[scen].prev_scen = 0;
bts->table[scen].next_scen = 0;
} else {
BTS_DBG("%s scenario couldn't exist above top_scen\n", bts_scen[scen].name);
}
}
}
bts = bts->table[scen].next_bts;
} while (bts && bts != first);
}
void bts_scen_update(enum bts_scen_type type, unsigned int val)
{
enum exynos_bts_scenario scen = BS_DEFAULT;
struct bts_info *bts = NULL;
bool on;
spin_lock(&bts_lock);
switch (type) {
case TYPE_ROTATION:
on = val ? true : false;
scen = BS_ROTATION;
bts = &exynos8_bts[BTS_IDX_TREX_DISP0_0];
BTS_DBG("[BTS] ROTATION: %s\n", bts_scen[scen].name);
update_rot_scen(val);
break;
case TYPE_HIGHPERF:
on = val ? true : false;
scen = BS_HIGHPERF;
bts = &exynos8_bts[BTS_IDX_TREX_MFC0];
BTS_DBG("[BTS] MFC PERF: %s\n", bts_scen[scen].name);
break;
case TYPE_G3D_FREQ:
on = val ? true : false;
scen = BS_G3DFREQ;
bts = &exynos8_bts[BTS_IDX_TREX_G3D0];
BTS_DBG("[BTS] G3D FREQ: %s\n", bts_scen[scen].name);
update_g3d_scen(val);
break;
case TYPE_URGENT_OFF:
on = val ? true : false;
scen = BS_URGENTOFF;
bts = &exynos8_bts[BTS_IDX_TREX_CP];
BTS_DBG("[BTS] URGENT: %s\n", bts_scen[scen].name);
update_urgent_scen(val);
break;
default:
spin_unlock(&bts_lock);
return;
break;
}
if (on)
bts_add_scen(scen, bts);
else
bts_del_scen(scen, bts);
spin_unlock(&bts_lock);
}
void bts_initialize(const char *pd_name, bool on)
{
struct bts_info *bts;
enum exynos_bts_scenario scen = BS_DISABLE;
spin_lock(&bts_lock);
list_for_each_entry(bts, &bts_list, list) {
BTS_DBG("[BTS] %s on/off:%d->%d\n", bts->name, bts->on, on);
if (!bts->enable) {
bts->on = on;
continue;
}
scen = bts_get_scen(bts);
if (on) {
bts_add_scen(scen, bts);
if (!bts->on) {
bts->on = true;
bts_clk_on(bts);
}
bts_set_ip_table(bts->top_scen, bts);
} else {
if (bts->on) {
bts->on = false;
bts_clk_off(bts);
}
bts_del_scen(scen, bts);
}
}
spin_unlock(&bts_lock);
}
static void scen_chaining(enum exynos_bts_scenario scen)
{
struct bts_info *prev = NULL;
struct bts_info *first = NULL;
struct bts_info *bts;
if (bts_scen[scen].ip) {
list_for_each_entry(bts, &bts_list, list) {
if (bts_scen[scen].ip & bts->id) {
if (!first)
first = bts;
if (prev)
prev->table[scen].next_bts = bts;
prev = bts;
}
}
if (prev)
prev->table[scen].next_bts = first;
bts_scen[scen].head = first;
}
}
static void bts_trex_init(void __iomem *base)
{
__raw_writel(0x0B070000, base + SCI_CTRL);
__raw_writel(0x00200000, base + READ_QURGENT);
__raw_writel(0x00200000, base + WRITE_QURGENT);
__raw_writel(0x2A55A954, base + VC_NUM0);
__raw_writel(0x00000CA0, base + VC_NUM1);
__raw_writel(0x04040404, base + TH_IMM0);
__raw_writel(0x04040404, base + TH_IMM1);
__raw_writel(0x04040404, base + TH_IMM2);
__raw_writel(0x04040404, base + TH_IMM3);
__raw_writel(0x04040404, base + TH_IMM4);
__raw_writel(0x04040004, base + TH_IMM5);
__raw_writel(0x00040404, base + TH_IMM6);
__raw_writel(0x02020202, base + TH_HIGH0);
__raw_writel(0x02020202, base + TH_HIGH1);
__raw_writel(0x02020202, base + TH_HIGH2);
__raw_writel(0x02020202, base + TH_HIGH3);
__raw_writel(0x02020202, base + TH_HIGH4);
__raw_writel(0x02020002, base + TH_HIGH5);
__raw_writel(0x00020202, base + TH_HIGH6);
return;
}
static int bts_trex_qoson(void __iomem *base)
{
__raw_writel(0x00200000, base + READ_QURGENT);
__raw_writel(0x00200000, base + WRITE_QURGENT);
__raw_writel(0x04040004, base + TH_IMM5);
__raw_writel(0x02020002, base + TH_HIGH5);
return 0;
}
static int bts_trex_qosoff(void __iomem *base)
{
__raw_writel(0x00000000, base + READ_QURGENT);
__raw_writel(0x00000000, base + WRITE_QURGENT);
__raw_writel(0x04040404, base + TH_IMM5);
__raw_writel(0x02020202, base + TH_HIGH5);
return 0;
}
static int exynos_bts_notifier_event(struct notifier_block *this,
unsigned long event,
void *ptr)
{
unsigned long i;
switch ((unsigned int)event) {
case PM_POST_SUSPEND:
for (i = 0; i < (unsigned int)ARRAY_SIZE(base_trex); i++)
bts_trex_init(base_trex[i]);
bts_initialize("trex", true);
return NOTIFY_OK;
break;
case PM_SUSPEND_PREPARE:
return NOTIFY_OK;
break;
}
return NOTIFY_DONE;
}
static struct notifier_block exynos_bts_notifier = {
.notifier_call = exynos_bts_notifier_event,
};
int bts_update_gpu_mif(unsigned int freq)
{
int ret = 0;
if (pm_qos_request_active(&exynos8_gpu_mif_bts_qos))
pm_qos_update_request(&exynos8_gpu_mif_bts_qos, freq);
return ret;
}
#if defined(CONFIG_EXYNOS8890_BTS_OPTIMIZATION)
unsigned int ip_sum_bw[IP_NUM];
unsigned int ip_peak_bw[IP_NUM];
void exynos_update_bw(enum bts_media_type ip_type,
unsigned int sum_bw, unsigned int peak_bw)
{
unsigned int ip_sum, ip_peak;
unsigned int int_freq;
unsigned int mif_freq;
int i;
mutex_lock(&media_mutex);
switch (ip_type) {
case TYPE_VPP0:
case TYPE_VPP1:
case TYPE_VPP2:
case TYPE_VPP3:
case TYPE_VPP4:
case TYPE_VPP5:
case TYPE_VPP6:
case TYPE_VPP7:
case TYPE_VPP8:
ip_sum_bw[IP_VPP] = sum_bw;
ip_peak_bw[IP_VPP] = peak_bw;
break;
case TYPE_CAM:
ip_sum_bw[IP_CAM] = sum_bw;
ip_peak_bw[IP_CAM] = peak_bw;
break;
case TYPE_MFC:
ip_sum_bw[IP_MFC] = sum_bw;
ip_peak_bw[IP_MFC] = peak_bw;
break;
default:
pr_err("BTS : unsupported ip type - %u", ip_type);
break;
}
ip_sum = 0;
ip_peak = ip_peak_bw[0];
for (i = 0; i < IP_NUM; i++) {
ip_sum += ip_sum_bw[i];
if (ip_peak < ip_peak_bw[i])
ip_peak = ip_peak_bw[i];
}
BTS_DBG("[BTS BW]: TOTAL SUM: %u, PEAK: %u\n", ip_sum, ip_peak);
mif_freq = (ip_sum * 100000) / (MIF_UTIL * BUS_WIDTH);
int_freq = (ip_peak * 100000) / (INT_UTIL * BUS_WIDTH);
if (mif_freq < 4 * int_freq)
mif_freq = 4 * int_freq;
else
int_freq = mif_freq / 4;
BTS_DBG("[BTS FREQ]: MIF: %uMHz, INT: %uMHz\n", mif_freq, int_freq);
if (pm_qos_request_active(&exynos8_mif_bts_qos))
pm_qos_update_request(&exynos8_mif_bts_qos, mif_freq);
if (pm_qos_request_active(&exynos8_int_bts_qos))
pm_qos_update_request(&exynos8_int_bts_qos, int_freq);
mutex_unlock(&media_mutex);
}
struct bts_vpp_info *exynos_bw_calc(enum bts_media_type ip_type, struct bts_hw *hw)
{
u64 s_ratio_h, s_ratio_v = 0;
u64 s_ratio = 0;
u8 df = 0;
u8 bpl = 0;
struct bts_vpp_info *vpp = to_bts_vpp(hw);
switch (ip_type) {
case TYPE_VPP0:
case TYPE_VPP1:
case TYPE_VPP2:
case TYPE_VPP3:
case TYPE_VPP4:
case TYPE_VPP5:
case TYPE_VPP6:
case TYPE_VPP7:
case TYPE_VPP8:
s_ratio_h = MULTI_FACTOR * vpp->src_w / vpp->dst_w;
s_ratio_v = MULTI_FACTOR * vpp->src_h / vpp->dst_h;
s_ratio = PIXEL_BUFFER * s_ratio_h * s_ratio_v
/ (MULTI_FACTOR * MULTI_FACTOR);
bpl = vpp->bpp * 10 / 8;
if (vpp->bpp == 32)
df = 20;
else if (vpp->bpp == 12)
df = 36;
else if (vpp->bpp == 16)
df = 28;
vpp->cur_bw = vpp->src_h * vpp->src_w * bpl * s_ratio_h * s_ratio_v
* 72 / (MULTI_FACTOR * MULTI_FACTOR * 10)
/ (MULTI_FACTOR * MULTI_FACTOR);
if (vpp->is_rotation)
vpp->peak_bw = (s_ratio + df * vpp->src_h) * bpl * PEAK_FACTOR
* (FPS * vpp->dst_h) / VBI_FACTOR
/ 100 / MULTI_FACTOR;
else
vpp->peak_bw = (s_ratio + 4 * vpp->src_w + 1000) * bpl * PEAK_FACTOR
* (FPS * vpp->dst_h) / VBI_FACTOR
/ 100 / MULTI_FACTOR;
BTS_DBG("%s[%i] cur_bw: %llu peak_bw: %llu\n", __func__, (int)ip_type, vpp->cur_bw, vpp->peak_bw);
break;
case TYPE_CAM:
case TYPE_MFC:
default:
pr_err("%s: BW calculation unsupported - %u", __func__, ip_type);
break;
}
return vpp;
}
void bts_ext_scenario_set(enum bts_media_type ip_type,
enum bts_scen_type scen_type, bool on)
{
int i = 0;
unsigned int cur_mo;
switch (ip_type) {
case TYPE_VPP0:
case TYPE_VPP1:
case TYPE_VPP2:
case TYPE_VPP3:
case TYPE_VPP4:
case TYPE_VPP5:
case TYPE_VPP6:
case TYPE_VPP7:
case TYPE_VPP8:
if (scen_type == TYPE_ROTATION) {
if (on)
vpp_rot[ip_type - TYPE_VPP0] = 16;
else
vpp_rot[ip_type - TYPE_VPP0] = 8;
cur_mo = 8;
for (i = 0; i < VPP_MAX; i++) {
if (cur_mo < vpp_rot[i])
cur_mo = vpp_rot[i];
}
if (cur_mo == 16 && cur_mo != prev_mo) {
bts_scen_update(scen_type, 1);
prev_mo = 16;
} else if (cur_mo == prev_mo) {
return;
} else {
bts_scen_update(scen_type, 0);
prev_mo = 8;
}
}
break;
case TYPE_CAM:
if (scen_type == TYPE_URGENT_OFF) {
if (on)
bts_scen_update(scen_type, 1);
else
bts_scen_update(scen_type, 0);
}
break;
case TYPE_MFC:
if (scen_type == TYPE_HIGHPERF) {
if (on)
bts_scen_update(scen_type, 1);
else
bts_scen_update(scen_type, 0);
}
break;
case TYPE_G3D:
if (scen_type == TYPE_G3D_FREQ) {
if (on)
bts_scen_update(scen_type, 1);
else
bts_scen_update(scen_type, 0);
}
break;
default:
pr_err("BTS : unsupported rotation ip - %u", ip_type);
break;
}
return;
}
int bts_update_winlayer(unsigned int layers)
{
unsigned int freq = 0;
switch (layers) {
case 0:
case 1:
case 2:
case 3:
case 4:
freq = 0;
break;
case 5:
freq = 676000;
break;
case 6:
freq = 845000;
break;
case 7:
case 8:
freq = 1014000;
break;
default:
break;
}
if (pm_qos_request_active(&exynos8_winlayer_mif_bts_qos))
pm_qos_update_request(&exynos8_winlayer_mif_bts_qos, freq);
return 0;
}
#else /* BTS_OPTIMIZATION */
void exynos_update_media_scenario(enum bts_media_type media_type,
unsigned int bw, int bw_type)
{
unsigned int vpp_total_bw = 0;
unsigned int vpp_bus_bw[4];
unsigned int vpp_bus_max_bw;
int utilization;
int i;
int max_status = 0;
static unsigned int is_yuv;
static unsigned int prev_sum_rot_bw;
static unsigned int mif_ud_encoding, mif_ud_decoding;
mutex_lock(&media_mutex);
switch (media_type) {
case TYPE_VPP0:
case TYPE_VPP1:
case TYPE_VPP2:
case TYPE_VPP3:
case TYPE_VPP4:
case TYPE_VPP5:
case TYPE_VPP6:
case TYPE_VPP7:
case TYPE_VPP8:
vpp_bw[VPP_BW][media_type - TYPE_VPP0] = bw;
vpp_status[media_type - TYPE_VPP0] = bw_type;
if (bw_type)
vpp_bw[VPP_ROT_BW][media_type - TYPE_VPP0] = bw;
else
vpp_bw[VPP_ROT_BW][media_type - TYPE_VPP0] = 0;
/*
* VPP0(G0), VPP1(G1), VPP2(VG0), VPP3(VG1),
* VPP4(G2), VPP5(G3), VPP6(VGR0), VPP7(VGR1), VPP8(WB)
* vpp_bus_bw[0] = DISP0_0_BW = G0 + VG0;
* vpp_bus_bw[1] = DISP0_1_BW = G1 + VG1;
* vpp_bus_bw[2] = DISP1_0_BW = G2 + VGR0;
* vpp_bus_bw[3] = DISP1_1_BW = G3 + VGR1 + WB;
*/
vpp_bus_bw[0] = vpp_bw[VPP_BW][0] + vpp_bw[VPP_BW][2];
vpp_bus_bw[1] = vpp_bw[VPP_BW][1] + vpp_bw[VPP_BW][3];
vpp_bus_bw[2] = vpp_bw[VPP_BW][4] + vpp_bw[VPP_BW][6];
vpp_bus_bw[3] = vpp_bw[VPP_BW][5] + vpp_bw[VPP_BW][7] + vpp_bw[VPP_BW][8];
/* sum VPP rotation BW for RT BW */
sum_rot_bw = 0;
for (i = 0; i < VPP_MAX; i++)
sum_rot_bw += vpp_bw[VPP_ROT_BW][i];
/* Such VPP max BW for INT PM QoS */
vpp_bus_max_bw = vpp_bus_bw[0];
for (i = 0; i < 4; i++) {
if (vpp_bus_max_bw < vpp_bus_bw[i])
vpp_bus_max_bw = vpp_bus_bw[i];
}
prev_sum_rot_bw = sum_rot_bw;
break;
case TYPE_CAM:
cam_bw = bw;
break;
case TYPE_YUV:
is_yuv = bw;
break;
case TYPE_UD_ENC:
mif_ud_encoding = bw;
break;
case TYPE_UD_DEC:
mif_ud_decoding = bw;
break;
default:
pr_err("BTS : unsupported media type - %u", media_type);
break;
}
for (i = 0; i < VPP_MAX; i++)
vpp_total_bw += vpp_bw[VPP_BW][i];
total_bw = vpp_total_bw + cam_bw;
int_freq = (total_bw * 100) / (INT_UTIL * BUS_WIDTH);
/* INT_L0 shared between camera and normal scenarioes */
if (int_freq > 468000)
int_freq = 690000;
if (pm_qos_request_active(&exynos8_int_bts_qos))
pm_qos_update_request(&exynos8_int_bts_qos, int_freq);
for (i = 0; i < VPP_MAX; i++) {
if (max_status < vpp_status[i])
max_status = vpp_status[i];
}
switch (max_status) {
case BW_FULLHD_ROT:
SIZE_FACTOR(total_bw);
case BW_ROT:
if (total_bw < 200000)
utilization = 7;
else if (total_bw < 500000)
utilization = 10;
else if (total_bw < 1600000)
utilization = 29;
else if (total_bw < 3000000)
utilization = 48;
else if (total_bw < 3500000)
utilization = 55;
else if (total_bw < 4000000)
utilization = 60;
else if (total_bw < 5000000)
utilization = 70;
else if (total_bw < 7000000)
utilization = 76;
else
utilization = 83;
break;
case BW_DEFAULT:
if (total_bw < (63000 * 2) * 8 * 4)
utilization = MIF_UTIL;
else
utilization = MIF_UTIL2;
break;
default:
pr_err("BTS : unsupported bandwidth type - %u", media_type);
break;
}
mif_freq = (total_bw * 100) / (utilization * BUS_WIDTH);
/* for MFC UHD en/decoding (TBD) */
if (mif_ud_encoding && mif_freq < MIF_ENCODING)
mif_freq = MIF_ENCODING;
if (mif_ud_decoding && mif_freq < MIF_DECODING)
mif_freq = MIF_DECODING;
BTS_DBG("[BTS BW] total: %u, vpp: %u, vpp_rot: %u, cam: %u, yuv: %u, ud_en: %u, ud_de:%u\n",
total_bw, vpp_total_bw, sum_rot_bw, cam_bw, is_yuv,
mif_ud_encoding, mif_ud_decoding);
/*
* VPP0(G0), VPP1(G1), VPP2(VG0), VPP3(VG1),
* VPP4(G2), VPP5(G3), VPP6(VGR0), VPP7(VGR1), VPP8(WB)
* vpp_bus_bw[0] = DISP0_0_BW = G0 + VG0;
* vpp_bus_bw[1] = DISP0_1_BW = G1 + VG1;
* vpp_bus_bw[2] = DISP1_0_BW = G2 + VGR0;
* vpp_bus_bw[3] = DISP1_1_BW = G3 + VGR1 + WB;
*/
BTS_DBG("[BTS VPP] vpp0(G0) vpp1(G1) vpp2(VG0) vpp3(VG1) vpp4(G2) vpp5(G3) "
"vpp6(VGR0) vpp7(VGR1) vpp8(WB)\n");
BTS_DBG("[BTS VPP]");
for (i = 0; i < VPP_MAX; i++)
BTS_DBG(" vpp%d %u,", i, vpp_bw[VPP_BW][i]);
BTS_DBG("\n");
BTS_DBG("[BTS FREQ] MIF freq: %u, util: %u, INT freq: %u\n",
mif_freq, utilization, int_freq);
if (pm_qos_request_active(&exynos8_mif_bts_qos))
pm_qos_update_request(&exynos8_mif_bts_qos, mif_freq);
mutex_unlock(&media_mutex);
}
#endif /* BTS_OPT */
static void __iomem *sci_base;
void exynos_bts_scitoken_setting(bool on)
{
if (on)
__raw_writel(0x10101117, sci_base + CMDTOKEN);
else
__raw_writel(0x10101127, sci_base + CMDTOKEN);
}
#ifdef CONFIG_CPU_IDLE
static int exynos8_bts_lpa_event(struct notifier_block *nb,
unsigned long event, void *data)
{
unsigned long i;
switch (event) {
case LPA_EXIT:
for (i = 0; i < (unsigned int)ARRAY_SIZE(base_trex); i++)
bts_trex_init(base_trex[i]);
bts_initialize("trex", true);
break;
case LPA_ENTER:
break;
default:
break;
}
return NOTIFY_DONE;
}
static struct notifier_block bts_lpa_nb = {
.notifier_call = exynos8_bts_lpa_event,
};
#endif
static int __init exynos8_bts_init(void)
{
signed long i;
int ret;
enum bts_index btstable_index = BTS_MAX - 1;
BTS_DBG("[BTS][%s] bts init\n", __func__);
for (i = 0; i < ARRAY_SIZE(clk_table); i++) {
if (btstable_index != clk_table[i].index) {
btstable_index = clk_table[i].index;
exynos8_bts[btstable_index].ct_ptr = clk_table + i;
}
clk_table[i].clk = clk_get(NULL, clk_table[i].clk_name);
if (IS_ERR(clk_table[i].clk)){
BTS_DBG("failed to get bts clk %s\n",
clk_table[i].clk_name);
exynos8_bts[btstable_index].ct_ptr = NULL;
}
else {
ret = clk_prepare(clk_table[i].clk);
if (ret) {
pr_err("[BTS] failed to prepare bts clk %s\n",
clk_table[i].clk_name);
for (; i >= 0; i--)
clk_put(clk_table[i].clk);
return ret;
}
}
}
for (i = 0; i < ARRAY_SIZE(exynos8_bts); i++) {
exynos8_bts[i].va_base = ioremap(exynos8_bts[i].pa_base, SZ_16K);
list_add(&exynos8_bts[i].list, &bts_list);
}
base_trex[0] = ioremap(EXYNOS8_PA_TREX_CCORE0, SZ_4K);
base_trex[1] = ioremap(EXYNOS8_PA_TREX_CCORE1, SZ_4K);
for (i = BS_DEFAULT + 1; i < BS_MAX; i++)
scen_chaining(i);
for (i = 0; i < ARRAY_SIZE(base_trex); i++)
bts_trex_init(base_trex[i]);
bts_initialize("trex", true);
/* SCI Related settings */
sci_base = ioremap(EXYNOS8_PA_SCI, SZ_4K);
exynos_bts_scitoken_setting(false);
pm_qos_add_request(&exynos8_mif_bts_qos, PM_QOS_BUS_THROUGHPUT, 0);
pm_qos_add_request(&exynos8_int_bts_qos, PM_QOS_DEVICE_THROUGHPUT, 0);
pm_qos_add_request(&exynos8_gpu_mif_bts_qos, PM_QOS_BUS_THROUGHPUT, 0);
pm_qos_add_request(&exynos8_winlayer_mif_bts_qos, PM_QOS_BUS_THROUGHPUT, 0);
register_pm_notifier(&exynos_bts_notifier);
//bts_debugfs();
srcu_init_notifier_head(&exynos_media_notifier);
#ifdef CONFIG_CPU_IDLE
exynos_pm_register_notifier(&bts_lpa_nb);
#endif
return 0;
}
arch_initcall(exynos8_bts_init);