blob: a3ff56b7e820f699717a4d79d5d5d32884410fbb [file] [log] [blame]
/*
* Copyright (c) 2016 Samsung Electronics Co., Ltd.
* http://www.samsung.com
*
* BTS file for Samsung EXYNOS DPU driver
*
* 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 "decon.h"
#include <soc/samsung/bts.h>
#include <media/v4l2-subdev.h>
#define DISP_FACTOR 100
#define PPC 2
#define LCD_REFRESH_RATE 63
#define MULTI_FACTOR (1 << 10)
u64 dpu_bts_calc_aclk_disp(struct decon_device *decon,
struct decon_win_config *config)
{
u32 resol_clock;
u64 s_ratio_h, s_ratio_v;
u64 aclk_disp;
struct decon_frame *src = &config->src;
struct decon_frame *dst = &config->dst;
/* 1.1: 10% margin, 1000: for KHZ, 1: for raising to a unit */
resol_clock = decon->lcd_info->xres * decon->lcd_info->yres *
LCD_REFRESH_RATE * 11 / 10 / 1000 + 1;
s_ratio_h = (src->w <= dst->w) ? MULTI_FACTOR : MULTI_FACTOR * src->w / dst->w;
s_ratio_v = (src->h <= dst->h) ? MULTI_FACTOR : MULTI_FACTOR * src->h / dst->h;
aclk_disp = resol_clock * s_ratio_h * s_ratio_v * DISP_FACTOR / 100
/ PPC * (MULTI_FACTOR * dst->w / decon->lcd_info->xres)
/ (MULTI_FACTOR * MULTI_FACTOR * MULTI_FACTOR);
return aclk_disp;
}
/* bus utilization 75% */
#define BUS_UTIL 75
static void dpu_bts_find_max_disp_freq(struct decon_device *decon,
struct decon_reg_data *regs)
{
int i, idx;
u32 disp_ch_bw[BTS_DPU_MAX];
u32 max_disp_ch_bw;
u32 disp_op_freq = 0, freq = 0;
struct decon_win_config *config = regs->dpp_config;
memset(disp_ch_bw, 0, sizeof(disp_ch_bw));
disp_ch_bw[BTS_DPU0] = decon->bts.bw[BTS_DPP4] + decon->bts.bw[BTS_DPP5];
disp_ch_bw[BTS_DPU1] = decon->bts.bw[BTS_DPP0] + decon->bts.bw[BTS_DPP2];
disp_ch_bw[BTS_DPU2] = decon->bts.bw[BTS_DPP1] + decon->bts.bw[BTS_DPP3];
for (i = 0; i < BTS_DPU_MAX; ++i)
DPU_DEBUG_BTS("CH%d = %d\n", i, disp_ch_bw[i]);
max_disp_ch_bw = disp_ch_bw[0];
for (i = 1; i < BTS_DPU_MAX; ++i)
if (max_disp_ch_bw < disp_ch_bw[i])
max_disp_ch_bw = disp_ch_bw[i];
decon->bts.max_disp_freq = max_disp_ch_bw * 100 / (16 * BUS_UTIL) + 1;
for (i = 0; i < MAX_DECON_WIN; ++i) {
idx = config[i].idma_type;
if (config[i].state != DECON_WIN_STATE_BUFFER)
continue;
if (idx != BTS_DPP4 && idx != BTS_DPP5)
continue;
if (!is_scaling(&config[i]))
continue;
freq = dpu_bts_calc_aclk_disp(decon, &config[i]);
if (disp_op_freq < freq)
disp_op_freq = freq;
}
DPU_DEBUG_BTS("DISP bus freq(%d), operating freq(%d)\n",
decon->bts.max_disp_freq, disp_op_freq);
if (decon->bts.max_disp_freq < disp_op_freq)
decon->bts.max_disp_freq = disp_op_freq;
DPU_DEBUG_BTS("MAX DISP CH FREQ = %d\n", decon->bts.max_disp_freq);
}
void dpu_bts_calc_bw(struct decon_device *decon, struct decon_reg_data *regs)
{
struct decon_win_config *config = regs->dpp_config;
struct bts_decon_info bts_info;
int idx, i;
memset(&bts_info, 0, sizeof(struct bts_decon_info));
for (i = 0; i < MAX_DECON_WIN; ++i) {
idx = config[i].idma_type;
if (config[i].state == DECON_WIN_STATE_BUFFER) {
bts_info.dpp[idx].used = true;
} else {
bts_info.dpp[idx].used = false;
continue;
}
bts_info.dpp[idx].bpp = dpu_get_bpp(config[i].format);
bts_info.dpp[idx].src_w = config[i].src.w;
bts_info.dpp[idx].src_h = config[i].src.h;
bts_info.dpp[idx].dst.x1 = config[i].dst.x;
bts_info.dpp[idx].dst.x2 = config[i].dst.x + config[i].dst.w;
bts_info.dpp[idx].dst.y1 = config[i].dst.y;
bts_info.dpp[idx].dst.y2 = config[i].dst.y + config[i].dst.h;
DPU_DEBUG_BTS("%s:used(%d), bpp(%d), src_w(%d), src_h(%d)\n",
__func__,
bts_info.dpp[idx].used, bts_info.dpp[idx].bpp,
bts_info.dpp[idx].src_w, bts_info.dpp[idx].src_h);
DPU_DEBUG_BTS("\t\t\t\tdst x(%d), right(%d), y(%d), bottom(%d)\n",
bts_info.dpp[idx].dst.x1, bts_info.dpp[idx].dst.x2,
bts_info.dpp[idx].dst.y1, bts_info.dpp[idx].dst.y2);
}
bts_info.vclk = decon->bts.resol_clk;
decon->bts.total_bw = bts_calc_bw(decon->bts.type, &bts_info);
for (i = 0; i < BTS_DPP_MAX; ++i) {
decon->bts.bw[i] = bts_info.dpp[i].bw;
DPU_DEBUG_BTS("DPP%d bandwidth = %d\n", i, decon->bts.bw[i]);
}
DPU_DEBUG_BTS("DECON%d total bandwidth = %d\n", decon->id,
decon->bts.total_bw);
dpu_bts_find_max_disp_freq(decon, regs);
}
void dpu_bts_update_bw(struct decon_device *decon, struct decon_reg_data *regs,
u32 is_after)
{
DPU_DEBUG_BTS("%s +\n", __func__);
if (is_after) { /* after DECON h/w configuration */
if (decon->bts.total_bw <= decon->bts.prev_total_bw)
bts_update_bw(decon->bts.type, decon->bts.total_bw);
if (decon->bts.max_disp_freq <= decon->bts.prev_max_disp_freq)
pm_qos_update_request(&decon->bts.disp_qos,
decon->bts.max_disp_freq);
decon->bts.prev_total_bw = decon->bts.total_bw;
decon->bts.prev_max_disp_freq = decon->bts.max_disp_freq;
} else {
if (decon->bts.total_bw > decon->bts.prev_total_bw)
bts_update_bw(decon->bts.type, decon->bts.total_bw);
if (decon->bts.max_disp_freq > decon->bts.prev_max_disp_freq)
pm_qos_update_request(&decon->bts.disp_qos,
decon->bts.max_disp_freq);
}
DPU_DEBUG_BTS("%s -\n", __func__);
}
void dpu_bts_release_bw(struct decon_device *decon)
{
DPU_DEBUG_BTS("%s +\n", __func__);
bts_update_bw(decon->bts.type, 0);
decon->bts.prev_total_bw = 0;
pm_qos_update_request(&decon->bts.disp_qos, 0);
decon->bts.prev_max_disp_freq = 0;
DPU_DEBUG_BTS("%s -\n", __func__);
}
void dpu_bts_init(struct decon_device *decon)
{
int comp_ratio;
DPU_DEBUG_BTS("%s +\n", __func__);
if (decon->id == 1)
decon->bts.type = BTS_BW_DECON1;
else if (decon->id == 2)
decon->bts.type = BTS_BW_DECON2;
else
decon->bts.type = BTS_BW_DECON0;
DPU_DEBUG_BTS("BTS_BW_TYPE(%d) -\n", decon->bts.type);
if (decon->lcd_info->dsc_enabled)
comp_ratio = 3;
else
comp_ratio = 1;
/*
* Resol clock(KHZ) = lcd width x lcd height x 63(refresh rate) x
* 1.1(10% margin) x comp_ratio(1/3 DSC) / 2(2PPC) /
* 1000(for KHZ) + 1(for raising to a unit)
*/
decon->bts.resol_clk = decon->lcd_info->xres * decon->lcd_info->yres *
63 * 11 / 10 / 1000 + 1;
DPU_DEBUG_BTS("resol clock = %d Khz\n", decon->bts.resol_clk);
pm_qos_add_request(&decon->bts.disp_qos, PM_QOS_DISPLAY_THROUGHPUT, 0);
}
void dpu_bts_deinit(struct decon_device *decon)
{
DPU_DEBUG_BTS("%s +\n", __func__);
pm_qos_remove_request(&decon->bts.disp_qos);
DPU_DEBUG_BTS("%s -\n", __func__);
}
struct decon_bts_ops decon_bts_control = {
.bts_init = dpu_bts_init,
.bts_calc_bw = dpu_bts_calc_bw,
.bts_update_bw = dpu_bts_update_bw,
.bts_release_bw = dpu_bts_release_bw,
.bts_deinit = dpu_bts_deinit,
};