blob: a8f696627357c193596f88d84771874708914cb9 [file] [log] [blame]
/*
* drivers/video/tegra/dc/bandwidth.c
*
* Copyright (c) 2010-2013, NVIDIA CORPORATION, All rights reserved.
*
* Author: Jon Mayo <jmayo@nvidia.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* 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.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/clk.h>
#include <linux/clk/tegra.h>
#include <mach/dc.h>
#include <mach/fb.h>
#include <mach/mc.h>
#include <linux/nvhost.h>
#include <mach/latency_allowance.h>
#include <trace/events/display.h>
#include "dc_reg.h"
#include "dc_config.h"
#include "dc_priv.h"
static int use_dynamic_emc = 1;
module_param_named(use_dynamic_emc, use_dynamic_emc, int, S_IRUGO | S_IWUSR);
static unsigned int tegra_dcs_total_bw[TEGRA_MAX_DC] = {0};
DEFINE_MUTEX(tegra_dcs_total_bw_lock);
/* windows A, B, C for first and second display */
static const enum tegra_la_id la_id_tab[2][DC_N_WINDOWS] = {
/* first display */
{
TEGRA_LA_DISPLAY_0A,
TEGRA_LA_DISPLAY_0B,
TEGRA_LA_DISPLAY_0C,
#if defined(CONFIG_ARCH_TEGRA_14x_SOC) && defined(CONFIG_ARCH_TEGRA_12x_SOC)
TEGRA_LA_DISPLAYD,
#endif
#if defined(CONFIG_ARCH_TEGRA_14x_SOC)
TEGRA_LA_DISPLAY_HC,
#endif
#if defined(CONFIG_ARCH_TEGRA_12x_SOC)
TEGRA_LA_DISPLAY_T,
#endif
},
/* second display */
{
TEGRA_LA_DISPLAY_0AB,
TEGRA_LA_DISPLAY_0BB,
TEGRA_LA_DISPLAY_0CB,
#if defined(CONFIG_ARCH_TEGRA_14x_SOC)
0,
TEGRA_LA_DISPLAY_HCB,
#endif
},
};
bool is_internal_win(enum tegra_la_id id)
{
return ((id == TEGRA_LA_DISPLAY_0A) || (id == TEGRA_LA_DISPLAY_0B) ||
(id == TEGRA_LA_DISPLAY_0C) || (id == TEGRA_LA_DISPLAYD) ||
(id == TEGRA_LA_DISPLAY_HC) || (id == TEGRA_LA_DISPLAY_T));
}
static unsigned int num_active_internal_wins(struct tegra_dc *dc)
{
unsigned int num_active_internal_wins = 0;
int i = 0;
for (i = 0; i < DC_N_WINDOWS; i++) {
struct tegra_dc_win *curr_win = &dc->windows[i];
enum tegra_la_id curr_win_la_id =
la_id_tab[dc->ndev->id][curr_win->idx];
if (!is_internal_win(curr_win_la_id))
continue;
if (WIN_IS_ENABLED(curr_win))
num_active_internal_wins++;
}
return num_active_internal_wins;
}
static unsigned int num_active_external_wins(struct tegra_dc *dc)
{
unsigned int num_active_external_wins = 0;
int i = 0;
for (i = 0; i < DC_N_WINDOWS; i++) {
struct tegra_dc_win *curr_win = &dc->windows[i];
enum tegra_la_id curr_win_la_id =
la_id_tab[dc->ndev->id][curr_win->idx];
if (is_internal_win(curr_win_la_id))
continue;
if (WIN_IS_ENABLED(curr_win))
num_active_external_wins++;
}
return num_active_external_wins;
}
#ifdef CONFIG_ARCH_TEGRA_12x_SOC
/*
* Note about fixed point arithmetic:
* ----------------------------------
* calc_disp_params(...) contains fixed point values and arithmetic due to the
* need to use floating point values. All fixed point values have the "_fp" or
* "_FP" suffix in their name. Functions/values used to convert between real and
* fixed point values are listed below:
* - la_params.fp_factor
* - la_params.la_real_to_fp(real_val)
* - la_params.la_fp_to_real(fp_val)
*/
#define T12X_LA_BW_DISRUPTION_TIME_EMCCLKS_FP 1342000
#define T12X_LA_STATIC_LA_SNAP_ARB_TO_ROW_SRT_EMCCLKS_FP 54000
#define T12X_LA_CONS_MEM_EFFICIENCY_FP 500
#define T12X_LA_ROW_SRT_SZ_BYTES (64 * (T12X_LA_MC_EMEM_NUM_SLOTS + 1))
#define T12X_LA_MC_EMEM_NUM_SLOTS 63
#define T12X_LA_MAX_DRAIN_TIME_USEC 10
/*
* Function outputs:
* - disp_params->thresh_lwm_bytes
* - disp_params->spool_up_buffering_adj_bytes
* - disp_params->total_dc0_bw
* - disp_params->total_dc1_bw
*/
static void calc_disp_params(struct tegra_dc *dc,
struct tegra_dc_win *w,
enum tegra_la_id la_id,
unsigned int bw_mbps,
struct dc_to_la_params *disp_params) {
const struct disp_client *disp_clients_info =
tegra_la_disp_clients_info;
struct la_to_dc_params la_params = tegra_get_la_to_dc_params();
unsigned int bw_mbps_fp = la_params.la_real_to_fp(bw_mbps);
bool active = WIN_IS_ENABLED(w);
bool win_rotated = false;
unsigned int window_width = dfixed_trunc(w->w);
unsigned int surface_width = 0;
bool vertical_scaling_enabled = false;
bool pitch = !WIN_IS_BLOCKLINEAR(w) && !WIN_IS_TILED(w);
bool planar = tegra_dc_is_yuv_planar(w->fmt);
bool packed_yuv422 = ((w->fmt == TEGRA_WIN_FMT_YCbCr422) ||
(w->fmt == TEGRA_WIN_FMT_YUV422));
/* all of tegra's YUV formats(420 and 422) fetch 2 bytes per pixel,
* but the size reported by tegra_dc_fmt_bpp for the planar version
* is of the luma plane's size only. */
unsigned int bytes_per_pixel = tegra_dc_is_yuv_planar(w->fmt) ?
2 * tegra_dc_fmt_bpp(w->fmt) / 8 :
tegra_dc_fmt_bpp(w->fmt) / 8;
struct tegra_dc_mode mode = dc->mode;
unsigned int total_h = mode.h_active +
mode.h_front_porch +
mode.h_back_porch +
mode.h_sync_width;
unsigned int total_v = mode.v_active +
mode.v_front_porch +
mode.v_back_porch +
mode.v_sync_width;
unsigned int total_screen_area = total_h * total_v;
unsigned int total_active_area = mode.h_active * mode.v_active;
unsigned int total_blank_area = total_screen_area - total_active_area;
unsigned int reqd_buffering_thresh_disp_bytes_fp = 0;
unsigned int latency_buffering_available_in_reqd_buffering_fp = 0;
struct clk *emc_clk = clk_get(NULL, "emc");
unsigned long emc_freq_mhz = clk_get_rate(emc_clk)/1000000;
unsigned int bw_disruption_time_usec_fp =
T12X_LA_BW_DISRUPTION_TIME_EMCCLKS_FP /
emc_freq_mhz;
unsigned int effective_row_srt_sz_bytes_fp =
min(la_params.la_real_to_fp(min(
T12X_LA_ROW_SRT_SZ_BYTES,
16 * min(emc_freq_mhz + 50,
400))),
((T12X_LA_MAX_DRAIN_TIME_USEC *
emc_freq_mhz -
la_params.la_fp_to_real(
T12X_LA_STATIC_LA_SNAP_ARB_TO_ROW_SRT_EMCCLKS_FP) *
2 *
la_params.dram_width_bits /
8 *
T12X_LA_CONS_MEM_EFFICIENCY_FP)));
unsigned int drain_time_usec_fp =
effective_row_srt_sz_bytes_fp *
la_params.fp_factor /
(emc_freq_mhz *
la_params.dram_width_bits /
4 *
T12X_LA_CONS_MEM_EFFICIENCY_FP) +
T12X_LA_STATIC_LA_SNAP_ARB_TO_ROW_SRT_EMCCLKS_FP /
emc_freq_mhz;
unsigned int total_latency_usec_fp =
drain_time_usec_fp +
la_params.static_la_minus_snap_arb_to_row_srt_emcclks_fp /
emc_freq_mhz;
unsigned int bw_disruption_buffering_bytes_fp =
bw_mbps *
max(bw_disruption_time_usec_fp,
total_latency_usec_fp) +
la_params.la_real_to_fp(1);
unsigned int reqd_lines = 0;
unsigned int lines_of_latency = 0;
unsigned int thresh_lwm_bytes = 0;
unsigned int total_buf_sz_bytes =
disp_clients_info[DISP_CLIENT_LA_ID(la_id)].line_buf_sz_bytes +
disp_clients_info[DISP_CLIENT_LA_ID(la_id)].mccif_size_bytes;
unsigned int num_active_wins_to_use = is_internal_win(la_id) ?
num_active_internal_wins(dc) :
num_active_external_wins(dc);
unsigned int total_active_space_bw = 0;
unsigned int total_vblank_bw = 0;
unsigned int bw_other_wins = 0;
unsigned int bw_display_fp = 0;
unsigned int bw_delta_fp = 0;
unsigned int fill_rate_other_wins_fp = 0;
unsigned int dvfs_time_nsec = tegra_get_dvfs_time_nsec(emc_freq_mhz);
unsigned int data_shortfall_other_wins_fp = 0;
unsigned int duration_usec_fp = 0;
unsigned int spool_up_buffering_adj_bytes = 0;
unsigned int curr_dc_head_bw = 0;
if (w->flags & TEGRA_WIN_FLAG_SCAN_COLUMN) {
win_rotated = true;
surface_width = dfixed_trunc(w->h);
vertical_scaling_enabled = (dfixed_trunc(w->w) == w->out_w) ?
false : true;
} else {
win_rotated = false;
surface_width = dfixed_trunc(w->w);
vertical_scaling_enabled = (dfixed_trunc(w->h) == w->out_h) ?
false : true;
}
if ((disp_clients_info[DISP_CLIENT_LA_ID(la_id)].line_buf_sz_bytes
== 0) ||
(pitch == true)) {
reqd_lines = 0;
} else if (win_rotated && planar) {
if (vertical_scaling_enabled)
reqd_lines = 17;
else
reqd_lines = 16;
} else {
if (win_rotated) {
if (vertical_scaling_enabled)
reqd_lines = 16 / bytes_per_pixel + 1;
else
reqd_lines = 16 / bytes_per_pixel;
} else {
if (vertical_scaling_enabled)
reqd_lines = 3;
else
reqd_lines = 1;
}
}
if (reqd_lines > 0 && !vertical_scaling_enabled && win_rotated)
lines_of_latency = 1;
else
lines_of_latency = 0;
if ((DISP_CLIENT_LA_ID(la_id) == TEGRA_LA_DISPLAY_0A) ||
(DISP_CLIENT_LA_ID(la_id) == TEGRA_LA_DISPLAY_0B) ||
(DISP_CLIENT_LA_ID(la_id) == TEGRA_LA_DISPLAY_0C)) {
unsigned int c1 =
((w->fmt == TEGRA_WIN_FMT_YUV420P &&
win_rotated == false) ||
(tegra_dc_is_yuv(w->fmt) && win_rotated == true)) ?
3 : bytes_per_pixel;
unsigned int c2 = (w->fmt == packed_yuv422 &&
win_rotated == true) ? 2 : 1;
reqd_buffering_thresh_disp_bytes_fp =
la_params.la_real_to_fp(active *
surface_width *
reqd_lines *
c1 *
c2);
latency_buffering_available_in_reqd_buffering_fp =
la_params.la_real_to_fp(active *
surface_width *
lines_of_latency *
c1 *
c2);
if ((w->fmt == TEGRA_WIN_FMT_YUV422R) &&
(win_rotated == false)) {
reqd_buffering_thresh_disp_bytes_fp =
reqd_buffering_thresh_disp_bytes_fp * 5 / 2;
latency_buffering_available_in_reqd_buffering_fp =
latency_buffering_available_in_reqd_buffering_fp *
5 /
2;
}
} else if ((DISP_CLIENT_LA_ID(la_id) == TEGRA_LA_DISPLAYD) ||
(DISP_CLIENT_LA_ID(la_id) == TEGRA_LA_DISPLAY_T) ||
(DISP_CLIENT_LA_ID(la_id) == TEGRA_LA_DISPLAY_HC)) {
reqd_buffering_thresh_disp_bytes_fp =
la_params.la_real_to_fp(active *
bytes_per_pixel *
window_width *
reqd_lines);
latency_buffering_available_in_reqd_buffering_fp =
la_params.la_real_to_fp(active *
bytes_per_pixel *
window_width *
lines_of_latency);
} else if ((DISP_CLIENT_LA_ID(la_id) == TEGRA_LA_DISPLAY_0AB) ||
(DISP_CLIENT_LA_ID(la_id) == TEGRA_LA_DISPLAY_0BB) ||
(DISP_CLIENT_LA_ID(la_id) == TEGRA_LA_DISPLAY_0CB)) {
unsigned int c = (w->fmt == TEGRA_WIN_FMT_YUV420P) ?
3 : bytes_per_pixel;
reqd_buffering_thresh_disp_bytes_fp =
la_params.la_real_to_fp(c *
window_width *
reqd_lines *
active);
latency_buffering_available_in_reqd_buffering_fp =
la_params.la_real_to_fp(c *
window_width *
lines_of_latency *
active);
if (w->fmt == TEGRA_WIN_FMT_YUV422R) {
reqd_buffering_thresh_disp_bytes_fp =
reqd_buffering_thresh_disp_bytes_fp * 5 / 2;
latency_buffering_available_in_reqd_buffering_fp =
latency_buffering_available_in_reqd_buffering_fp *
5 /
2;
}
}
thresh_lwm_bytes =
la_params.la_fp_to_real(
reqd_buffering_thresh_disp_bytes_fp +
bw_disruption_buffering_bytes_fp -
latency_buffering_available_in_reqd_buffering_fp);
disp_params->thresh_lwm_bytes = thresh_lwm_bytes;
if (is_internal_win(la_id)) {
int i = 0;
for (i = 0; i < DC_N_WINDOWS; i++) {
struct tegra_dc_win *curr_win = &dc->windows[i];
enum tegra_la_id curr_win_la_id =
la_id_tab[dc->ndev->id][curr_win->idx];
unsigned int curr_win_bw = 0;
if (!is_internal_win(curr_win_la_id))
continue;
curr_win_bw = max(curr_win->bandwidth,
curr_win->new_bandwidth);
/* our bandwidth is in kbytes/sec, but LA takes MBps.
* round up bandwidth to next 1MBps */
if (curr_win_bw != UINT_MAX)
curr_win_bw = curr_win_bw / 1000 + 1;
total_active_space_bw += curr_win_bw;
}
} else {
int i = 0;
for (i = 0; i < DC_N_WINDOWS; i++) {
struct tegra_dc_win *curr_win = &dc->windows[i];
enum tegra_la_id curr_win_la_id =
la_id_tab[dc->ndev->id][curr_win->idx];
unsigned int curr_win_bw = 0;
if (is_internal_win(curr_win_la_id))
continue;
curr_win_bw = max(curr_win->bandwidth,
curr_win->new_bandwidth);
/* our bandwidth is in kbytes/sec, but LA takes MBps.
* round up bandwidth to next 1MBps */
if (curr_win_bw != UINT_MAX)
curr_win_bw = curr_win_bw / 1000 + 1;
total_active_space_bw += curr_win_bw;
}
}
if ((disp_clients_info[DISP_CLIENT_LA_ID(la_id)].win_type ==
TEGRA_LA_DISP_WIN_TYPE_FULL) ||
(disp_clients_info[DISP_CLIENT_LA_ID(la_id)].win_type ==
TEGRA_LA_DISP_WIN_TYPE_FULLA) ||
(disp_clients_info[DISP_CLIENT_LA_ID(la_id)].win_type ==
TEGRA_LA_DISP_WIN_TYPE_FULLB)) {
total_vblank_bw = total_buf_sz_bytes / total_blank_area;
} else {
total_vblank_bw = 0;
}
bw_display_fp = la_params.disp_catchup_factor_fp *
max(total_active_space_bw,
total_vblank_bw);
if (active)
bw_delta_fp = bw_mbps_fp -
(bw_display_fp /
num_active_wins_to_use);
bw_other_wins = total_active_space_bw - bw_mbps;
if (num_active_wins_to_use > 0) {
fill_rate_other_wins_fp =
bw_display_fp *
(num_active_wins_to_use - active) /
num_active_wins_to_use -
la_params.la_real_to_fp(bw_other_wins);
} else {
fill_rate_other_wins_fp = 0;
}
data_shortfall_other_wins_fp = dvfs_time_nsec *
bw_other_wins *
la_params.fp_factor /
1000;
duration_usec_fp = (fill_rate_other_wins_fp == 0) ? 0 :
data_shortfall_other_wins_fp *
la_params.fp_factor /
fill_rate_other_wins_fp;
spool_up_buffering_adj_bytes = (bw_delta_fp > 0) ?
(bw_delta_fp *
duration_usec_fp /
(la_params.fp_factor *
la_params.fp_factor)) :
0;
disp_params->spool_up_buffering_adj_bytes =
spool_up_buffering_adj_bytes;
mutex_lock(&tegra_dcs_total_bw_lock);
curr_dc_head_bw = max(dc->new_bw_kbps, dc->bw_kbps);
/* our bandwidth is in kbytes/sec, but LA takes MBps.
* round up bandwidth to next 1MBps */
if (curr_dc_head_bw != ULONG_MAX)
curr_dc_head_bw = curr_dc_head_bw / 1000 + 1;
tegra_dcs_total_bw[dc->ndev->id] = curr_dc_head_bw;
disp_params->total_dc0_bw = tegra_dcs_total_bw[0];
disp_params->total_dc1_bw = tegra_dcs_total_bw[1];
mutex_unlock(&tegra_dcs_total_bw_lock);
}
#endif
/* uses the larger of w->bandwidth or w->new_bandwidth */
static void tegra_dc_set_latency_allowance(struct tegra_dc *dc,
struct tegra_dc_win *w)
{
unsigned long bw;
struct dc_to_la_params disp_params;
#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
/* window B V-filter tap for first and second display. */
static const enum tegra_la_id vfilter_tab[2] = {
TEGRA_LA_DISPLAY_1B, TEGRA_LA_DISPLAY_1BB,
};
#endif
BUG_ON(dc->ndev->id >= ARRAY_SIZE(la_id_tab));
#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
BUG_ON(dc->ndev->id >= ARRAY_SIZE(vfilter_tab));
#endif
BUG_ON(w->idx >= ARRAY_SIZE(*la_id_tab));
bw = max(w->bandwidth, w->new_bandwidth);
#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
/* tegra_dc_get_bandwidth() treats V filter windows as double
* bandwidth, but LA has a seperate client for V filter */
if (w->idx == 1 && win_use_v_filter(dc, w))
bw /= 2;
#endif
/* our bandwidth is in kbytes/sec, but LA takes MBps.
* round up bandwidth to next 1MBps */
if (bw != ULONG_MAX)
bw = bw / 1000 + 1;
#ifdef CONFIG_ARCH_TEGRA_12x_SOC
calc_disp_params(dc,
w,
la_id_tab[dc->ndev->id][w->idx],
bw,
&disp_params);
#endif
tegra_set_disp_latency_allowance(la_id_tab[dc->ndev->id][w->idx],
bw,
disp_params);
#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
/* if window B, also set the 1B client for the 2-tap V filter. */
if (w->idx == 1)
tegra_set_latency_allowance(vfilter_tab[dc->ndev->id], bw);
#endif
}
static int tegra_dc_windows_is_overlapped(struct tegra_dc_win *a,
struct tegra_dc_win *b)
{
if (a == b)
return 0;
if (!WIN_IS_ENABLED(a) || !WIN_IS_ENABLED(b))
return 0;
/* because memory access to load the fifo can overlap, only care
* if windows overlap vertically */
return ((a->out_y + a->out_h > b->out_y) && (a->out_y <= b->out_y)) ||
((b->out_y + b->out_h > a->out_y) && (b->out_y <= a->out_y));
}
/* check overlapping window combinations to find the max bandwidth. */
static unsigned long tegra_dc_find_max_bandwidth(struct tegra_dc_win *wins[],
unsigned n)
{
unsigned i;
unsigned j;
unsigned long bw;
unsigned long max = 0;
for (i = 0; i < n; i++) {
bw = wins[i]->new_bandwidth;
for (j = 0; j < n; j++)
if (tegra_dc_windows_is_overlapped(wins[i], wins[j]))
bw += wins[j]->new_bandwidth;
if (max < bw)
max = bw;
}
return max;
}
/*
* Calculate peak EMC bandwidth for each enabled window =
* pixel_clock * win_bpp * (use_v_filter ? 2 : 1)) * H_scale_factor *
* (windows_tiling ? 2 : 1)
*
* note:
* (*) We use 2 tap V filter on T2x/T3x, so need double BW if use V filter
* (*) Tiling mode on T30 and DDR3 requires double BW
*
* return:
* bandwidth in kBps
*/
static unsigned long tegra_dc_calc_win_bandwidth(struct tegra_dc *dc,
struct tegra_dc_win *w)
{
unsigned long ret;
int tiled_windows_bw_multiplier;
unsigned long bpp;
unsigned in_w;
if (!WIN_IS_ENABLED(w))
return 0;
if (dfixed_trunc(w->w) == 0 || dfixed_trunc(w->h) == 0 ||
w->out_w == 0 || w->out_h == 0)
return 0;
if (w->flags & TEGRA_WIN_FLAG_SCAN_COLUMN)
/* rotated: PRESCALE_SIZE swapped, but WIN_SIZE is unchanged */
in_w = dfixed_trunc(w->h);
else
in_w = dfixed_trunc(w->w); /* normal output, not rotated */
tiled_windows_bw_multiplier =
tegra_mc_get_tiled_memory_bandwidth_multiplier();
/* all of tegra's YUV formats(420 and 422) fetch 2 bytes per pixel,
* but the size reported by tegra_dc_fmt_bpp for the planar version
* is of the luma plane's size only. */
bpp = tegra_dc_is_yuv_planar(w->fmt) ?
2 * tegra_dc_fmt_bpp(w->fmt) : tegra_dc_fmt_bpp(w->fmt);
ret = dc->mode.pclk / 1000UL * bpp / 8 *
#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
(win_use_v_filter(dc, w) ? 2 : 1) *
#endif
in_w / w->out_w * (WIN_IS_TILED(w) ?
tiled_windows_bw_multiplier : 1);
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
/*
* Assuming 60% efficiency: i.e. if we calculate we need 70MBps, we
* will request 117MBps from EMC.
*/
ret = ret + (17 * ret / 25);
#endif
return ret;
}
static unsigned long tegra_dc_get_bandwidth(
struct tegra_dc_win *windows[], int n)
{
int i;
BUG_ON(n > DC_N_WINDOWS);
/* emc rate and latency allowance both need to know per window
* bandwidths */
for (i = 0; i < n; i++) {
struct tegra_dc_win *w = windows[i];
if (w)
w->new_bandwidth =
tegra_dc_calc_win_bandwidth(w->dc, w);
}
return tegra_dc_find_max_bandwidth(windows, n);
}
#ifdef CONFIG_TEGRA_ISOMGR
/* to save power, call when display memory clients would be idle */
void tegra_dc_clear_bandwidth(struct tegra_dc *dc)
{
int latency;
trace_clear_bandwidth(dc);
latency = tegra_isomgr_reserve(dc->isomgr_handle, 0, 1000);
WARN_ONCE(!latency, "tegra_isomgr_reserve failed\n");
if (latency) {
latency = tegra_isomgr_realize(dc->isomgr_handle);
WARN_ONCE(!latency, "tegra_isomgr_realize failed\n");
} else {
tegra_dc_ext_process_bandwidth_renegotiate(dc->ndev->id);
}
dc->bw_kbps = 0;
}
#else
/* to save power, call when display memory clients would be idle */
void tegra_dc_clear_bandwidth(struct tegra_dc *dc)
{
trace_clear_bandwidth(dc);
if (tegra_is_clk_enabled(dc->emc_clk))
clk_disable_unprepare(dc->emc_clk);
dc->bw_kbps = 0;
}
/* bw in kByte/second. returns Hz for EMC frequency */
static inline unsigned long tegra_dc_kbps_to_emc(unsigned long bw)
{
unsigned long freq;
if (bw == ULONG_MAX)
return ULONG_MAX;
freq = tegra_emc_bw_to_freq_req(bw);
if (freq >= (ULONG_MAX / 1000))
return ULONG_MAX; /* freq too big - clamp at max */
if (WARN_ONCE((freq * 1000) < freq, "Bandwidth Overflow"))
return ULONG_MAX; /* should never occur because of above. */
return freq * 1000;
}
#endif
/* use the larger of dc->bw_kbps or dc->new_bw_kbps, and copies
* dc->new_bw_kbps into dc->bw_kbps.
* calling this function both before and after a flip is sufficient to select
* the best possible frequency and latency allowance.
* set use_new to true to force dc->new_bw_kbps programming.
*/
void tegra_dc_program_bandwidth(struct tegra_dc *dc, bool use_new)
{
unsigned i;
if (use_new || dc->bw_kbps != dc->new_bw_kbps) {
long bw = max(dc->bw_kbps, dc->new_bw_kbps);
#ifdef CONFIG_TEGRA_ISOMGR
int latency;
latency = tegra_isomgr_reserve(dc->isomgr_handle, bw, 1000);
if (latency) {
latency = tegra_isomgr_realize(dc->isomgr_handle);
WARN_ONCE(!latency, "tegra_isomgr_realize failed\n");
} else {
tegra_dc_ext_process_bandwidth_renegotiate(
dc->ndev->id);
}
#else /* EMC version */
int emc_freq;
/* going from 0 to non-zero */
if (!dc->bw_kbps && dc->new_bw_kbps &&
!tegra_is_clk_enabled(dc->emc_clk))
clk_prepare_enable(dc->emc_clk);
emc_freq = tegra_dc_kbps_to_emc(bw);
clk_set_rate(dc->emc_clk, emc_freq);
/* going from non-zero to 0 */
if (dc->bw_kbps && !dc->new_bw_kbps &&
tegra_is_clk_enabled(dc->emc_clk))
clk_disable_unprepare(dc->emc_clk);
#endif
dc->bw_kbps = dc->new_bw_kbps;
}
for (i = 0; i < DC_N_WINDOWS; i++) {
struct tegra_dc_win *w = &dc->windows[i];
if ((use_new || w->bandwidth != w->new_bandwidth) &&
w->new_bandwidth != 0)
tegra_dc_set_latency_allowance(dc, w);
trace_program_bandwidth(dc);
w->bandwidth = w->new_bandwidth;
}
}
int tegra_dc_set_dynamic_emc(struct tegra_dc_win *windows[], int n)
{
unsigned long new_rate;
struct tegra_dc *dc;
if (!use_dynamic_emc)
return 0;
dc = windows[0]->dc;
#ifdef CONFIG_TEGRA_ISOMGR
new_rate = tegra_dc_get_bandwidth(windows, n);
#else
if (tegra_dc_has_multiple_dc())
new_rate = ULONG_MAX;
else
new_rate = tegra_dc_get_bandwidth(windows, n);
#endif
dc->new_bw_kbps = new_rate;
trace_set_dynamic_emc(dc);
return 0;
}
/* return the minimum bandwidth in kbps for display to function */
long tegra_dc_calc_min_bandwidth(struct tegra_dc *dc)
{
unsigned pclk = tegra_dc_get_out_max_pixclock(dc);
if (WARN_ONCE(!dc, "dc is NULL") ||
WARN_ONCE(!dc->out, "dc->out is NULL!"))
return 0;
if (!pclk && dc->out->type == TEGRA_DC_OUT_HDMI) {
pclk = tegra_dc_get_out_max_pixclock(dc);
if (!pclk) {
#if defined(CONFIG_ARCH_TEGRA_11x_SOC)
pclk = 300000000; /* 300MHz max */
#else
pclk = 150000000; /* 150MHz max */
#endif
}
} else {
pclk = dc->mode.pclk;
}
return pclk / 1000 * 4; /* support a single 32bpp window */
}
#ifdef CONFIG_TEGRA_ISOMGR
void tegra_dc_bandwidth_renegotiate(void *p, u32 avail_bw)
{
struct tegra_dc *dc = p;
unsigned long bw;
if (WARN_ONCE(!dc, "dc is NULL!"))
return;
tegra_dc_ext_process_bandwidth_renegotiate(dc->ndev->id);
/* a bit of a hack, report the change in bandwidth before it
* really happens.
*/
bw = tegra_dc_calc_min_bandwidth(dc);
if (tegra_isomgr_reserve(dc->isomgr_handle, 0, 1000))
tegra_isomgr_realize(dc->isomgr_handle);
}
#endif