1409 lines
34 KiB
C
1409 lines
34 KiB
C
|
/*
|
||
|
* 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);
|