tegrakernel/kernel/nvidia/drivers/video/tegra/dc/nvdisp/nvdisp_win.c

1366 lines
39 KiB
C

/*
* drivers/video/tegra/dc/nvdisplay/nvdis_win.c
*
* Copyright (c) 2014-2019, NVIDIA CORPORATION, All rights reserved.
*
* 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/delay.h>
#include <uapi/video/tegra_dc_ext.h>
#include "dc.h"
#include "nvdisp.h"
#include "nvdisp_priv.h"
#include "hw_nvdisp_nvdisp.h"
#include "dc_common.h"
#include "dc_config.h"
#define NVDISP_ODD_VAL(x) ((x) % (2))
/* Num Fractional Bits in 8.24 fixed point phase and phase increment values */
#define NFB 24
/*
* Generated from gen_vic_filter.py and taking only
* the data needed for 3 scaling ratios - 1x,2x,4x for 5 taps.
* 192 entries are needed to generate the 1x,2x and 4x coeffs
* For supporting the normal scaler will use only the last
* 10 bits
*/
static unsigned int vic_filter_coeffs[192] = {
0x00000000, 0x3c70e400, 0x3bb037e4, 0x0c51cc9c,
0x00100001, 0x3bf0dbfa, 0x3d00f406, 0x3fe003ff,
0x00300002, 0x3b80cbf5, 0x3da1040d, 0x3fb003fe,
0x00400002, 0x3b20bff1, 0x3e511015, 0x3f9003fc,
0x00500002, 0x3ad0b3ed, 0x3f21201d, 0x3f5003fb,
0x00500003, 0x3aa0a3e9, 0x3ff13026, 0x3f2007f9,
0x00500403, 0x3a7097e6, 0x00e1402f, 0x3ee007f7,
0x00500403, 0x3a608be4, 0x01d14c38, 0x3ea00bf6,
0x00500403, 0x3a507fe2, 0x02e15c42, 0x3e500ff4,
0x00500402, 0x3a6073e1, 0x03f16c4d, 0x3e000ff2,
0x00400402, 0x3a706be0, 0x05117858, 0x3db013f0,
0x00300402, 0x3a905fe0, 0x06318863, 0x3d6017ee,
0x00300402, 0x3ab057e0, 0x0771986e, 0x3d001beb,
0x00200001, 0x3af04fe1, 0x08a1a47a, 0x3cb023e9,
0x00100001, 0x3b2047e2, 0x09e1b485, 0x3c6027e7,
0x00100000, 0x3b703fe2, 0x0b11c091, 0x3c002fe6,
0x3f203800, 0x0391103f, 0x3ff0a014, 0x0811606c,
0x3f2037ff, 0x0351083c, 0x03e11842, 0x3f203c00,
0x3f302fff, 0x03010439, 0x04311c45, 0x3f104401,
0x3f302fff, 0x02c0fc35, 0x04812448, 0x3f104802,
0x3f4027ff, 0x0270f832, 0x04c1284b, 0x3f205003,
0x3f4023ff, 0x0230f030, 0x0511304e, 0x3f205403,
0x3f601fff, 0x01f0e82d, 0x05613451, 0x3f205c04,
0x3f701bfe, 0x01b0e02a, 0x05a13c54, 0x3f306006,
0x3f7017fe, 0x0170d827, 0x05f14057, 0x3f406807,
0x3f8017ff, 0x0140d424, 0x0641445a, 0x3f406c08,
0x3fa013ff, 0x0100cc22, 0x0681485d, 0x3f507409,
0x3fa00fff, 0x00d0c41f, 0x06d14c60, 0x3f607c0b,
0x3fc00fff, 0x0090bc1c, 0x07115063, 0x3f80840c,
0x3fd00bff, 0x0070b41a, 0x07515465, 0x3f908c0e,
0x3fe007ff, 0x0040b018, 0x07915868, 0x3fb0900f,
0x3ff00400, 0x0010a816, 0x07d15c6a, 0x3fd09811,
0x00a04c0e, 0x0460f442, 0x0240a827, 0x05c15859,
0x0090440d, 0x0440f040, 0x0480fc43, 0x00b05010,
0x0080400c, 0x0410ec3e, 0x04910044, 0x00d05411,
0x0070380b, 0x03f0e83d, 0x04b10846, 0x00e05812,
0x0060340a, 0x03d0e43b, 0x04d10c48, 0x00f06013,
0x00503009, 0x03b0e039, 0x04e11449, 0x01106415,
0x00402c08, 0x0390d838, 0x05011c4b, 0x01206c16,
0x00302807, 0x0370d436, 0x0511204c, 0x01407018,
0x00302406, 0x0340d034, 0x0531244e, 0x01507419,
0x00202005, 0x0320cc32, 0x05412c50, 0x01707c1b,
0x00101c04, 0x0300c431, 0x05613451, 0x0180801d,
0x00101803, 0x02e0c02f, 0x05713853, 0x01a0881e,
0x00101002, 0x02b0bc2d, 0x05814054, 0x01c08c20,
0x00000c02, 0x02a0b82c, 0x05914455, 0x01e09421,
0x00000801, 0x0280b02a, 0x05a14c57, 0x02009c23,
0x00000400, 0x0260ac28, 0x05b15458, 0x0220a025,
};
static int tegra_nvdisp_blend(struct tegra_dc_win *win)
{
bool update_blend_seq = false;
int idx = win->idx;
u32 blend_ctrl = 0;
struct tegra_dc *dc = win->dc;
struct tegra_dc_blend *blend = &dc->blend;
/* Update blender */
if ((win->z != blend->z[idx]) ||
((win->flags & TEGRA_WIN_BLEND_FLAGS_MASK) !=
blend->flags[idx])) {
blend->z[win->idx] = win->z;
blend->flags[win->idx] =
win->flags & TEGRA_WIN_BLEND_FLAGS_MASK;
if (tegra_dc_feature_is_gen2_blender(dc, idx))
update_blend_seq = true;
}
/*
* For gen2 blending, change in the global alpha needs rewrite
* to blending regs.
*/
if ((blend->alpha[idx] != win->global_alpha) &&
(tegra_dc_feature_is_gen2_blender(dc, idx)))
update_blend_seq = true;
/* Cache the global alpha of each window here. It is necessary
* for in-order blending settings. */
dc->blend.alpha[win->idx] = win->global_alpha;
blend_ctrl = win_blend_layer_control_depth_f(blend->z[idx]);
if (update_blend_seq) {
if (blend->flags[idx] & TEGRA_WIN_FLAG_BLEND_COVERAGE) {
blend_ctrl |=
(win_blend_layer_control_k2_f(0xff) |
win_blend_layer_control_k1_f(blend->alpha[idx]) |
win_blend_layer_control_blend_enable_enable_f());
nvdisp_win_write(win,
WIN_BLEND_FACT_SRC_COLOR_MATCH_SEL_K1_TIMES_SRC |
WIN_BLEND_FACT_DST_COLOR_MATCH_SEL_NEG_K1_TIMES_SRC |
WIN_BLEND_FACT_SRC_ALPHA_MATCH_SEL_K2 |
WIN_BLEND_FACT_DST_ALPHA_MATCH_SEL_ZERO,
win_blend_match_select_r());
nvdisp_win_write(win,
WIN_BLEND_FACT_SRC_COLOR_NOMATCH_SEL_K1_TIMES_SRC |
WIN_BLEND_FACT_DST_COLOR_NOMATCH_SEL_NEG_K1_TIMES_SRC |
WIN_BLEND_FACT_SRC_ALPHA_NOMATCH_SEL_K2 |
WIN_BLEND_FACT_DST_ALPHA_NOMATCH_SEL_ZERO,
win_blend_nomatch_select_r());
} else if (blend->flags[idx] & TEGRA_WIN_FLAG_BLEND_PREMULT) {
blend_ctrl |=
(win_blend_layer_control_k2_f(0xff) |
win_blend_layer_control_k1_f(blend->alpha[idx]) |
win_blend_layer_control_blend_enable_enable_f());
nvdisp_win_write(win,
WIN_BLEND_FACT_SRC_COLOR_MATCH_SEL_K1 |
WIN_BLEND_FACT_DST_COLOR_MATCH_SEL_NEG_K1_TIMES_SRC |
WIN_BLEND_FACT_SRC_ALPHA_MATCH_SEL_K2 |
WIN_BLEND_FACT_DST_ALPHA_MATCH_SEL_ZERO,
win_blend_match_select_r());
nvdisp_win_write(win,
WIN_BLEND_FACT_SRC_COLOR_NOMATCH_SEL_NEG_K1_TIMES_DST |
WIN_BLEND_FACT_DST_COLOR_NOMATCH_SEL_K1 |
WIN_BLEND_FACT_SRC_ALPHA_NOMATCH_SEL_K2 |
WIN_BLEND_FACT_DST_ALPHA_NOMATCH_SEL_ZERO,
win_blend_nomatch_select_r());
} else if (blend->flags[idx] & TEGRA_WIN_FLAG_BLEND_ADD) {
blend_ctrl |=
(win_blend_layer_control_k2_f(0xff) |
win_blend_layer_control_k1_f(0xff) |
win_blend_layer_control_blend_enable_enable_f());
nvdisp_win_write(win,
/* note: WIN_BLEND_FACT_SRC_COLOR_MATCH_SEL_ONE is not
* supported. Use K1 set to one instead. */
WIN_BLEND_FACT_SRC_COLOR_MATCH_SEL_K1 |
WIN_BLEND_FACT_DST_COLOR_MATCH_SEL_ONE|
WIN_BLEND_FACT_SRC_ALPHA_MATCH_SEL_ZERO |
WIN_BLEND_FACT_DST_ALPHA_MATCH_SEL_ZERO,
win_blend_match_select_r());
nvdisp_win_write(win,
WIN_BLEND_FACT_SRC_COLOR_NOMATCH_SEL_ZERO |
WIN_BLEND_FACT_DST_COLOR_NOMATCH_SEL_ZERO |
WIN_BLEND_FACT_SRC_ALPHA_NOMATCH_SEL_ZERO |
WIN_BLEND_FACT_DST_ALPHA_NOMATCH_SEL_ZERO,
win_blend_nomatch_select_r());
} else {
blend_ctrl |=
win_blend_layer_control_blend_enable_bypass_f();
}
nvdisp_win_write(win, blend_ctrl,
win_blend_layer_control_r());
}
return 0;
}
static void tegra_nvdisp_set_scaler_coeff(struct tegra_dc_win *win)
{
unsigned int ratio, row, col, index, coeff;
index = 0;
for (ratio = 0; ratio <= 2; ratio++) {
for (row = 0; row <= 15; row++) {
for (col = 0; col <= 3; col++) {
index = (ratio << 6) + (row << 2) + (col);
/* coeff value is bit 9:0 of each entry in
vic_filer_coeffs array */
/* index field is from 22:15 */
coeff = win_scaler_set_coeff_index_f(index) |
win_scaler_set_coeff_data_f(
vic_filter_coeffs[index]);
nvdisp_win_write(win, coeff,
win_scaler_set_coeff_r());
}
}
}
}
/* Returns phase_incr in fixed20.12 format */
static inline u32 compute_phase_incr(fixed20_12 in, unsigned out_int)
{
u32 phase_incr = 0;
u64 tmp = (u64) dfixed_trunc(in);
u64 tmp2 = (u64) out_int;
u64 tmp1 = (tmp << NFB) + (tmp2 >> 1);
tmp1 = tmp1 / tmp2;
phase_incr = lower_32_bits(tmp1);
return phase_incr;
}
static int tegra_nvdisp_scaling(struct tegra_dc_win *win)
{
u32 hbypass, vbypass, hphase_incr, h_init_phase;
u32 vphase_incr, v_init_phase;
bool is_scalar_column = win->flags & TEGRA_WIN_FLAG_SCAN_COLUMN;
fixed20_12 hscalar = win->w;
fixed20_12 vscalar = win->h;
if (is_scalar_column) {
hscalar = win->h;
vscalar = win->w;
}
hbypass = (dfixed_trunc(hscalar) == win->out_w);
vbypass = (dfixed_trunc(vscalar) == win->out_h);
if (!(hbypass && vbypass)) {
u32 min_width, win_capc, win_cape;
/* Select 2 taps vs 5 taps */
min_width = (dfixed_trunc(hscalar) < win->out_w) ?
dfixed_trunc(hscalar) : win->out_w;
win_capc = nvdisp_win_read(win, win_precomp_wgrp_capc_r());
win_cape = nvdisp_win_read(win, win_precomp_wgrp_cape_r());
if (min_width <
win_precomp_wgrp_capc_max_pixels_5tap444_v(win_capc)) {
nvdisp_win_write(win, win_scaler_input_h_taps_5_f() |
win_scaler_input_v_taps_5_f(),
win_scaler_input_r());
} else if (min_width <
win_precomp_wgrp_cape_max_pixels_2tap444_v(win_cape)) {
nvdisp_win_write(win, win_scaler_input_h_taps_2_f() |
win_scaler_input_v_taps_2_f(),
win_scaler_input_r());
} else {
dev_err(&win->dc->ndev->dev,
"Scaler error. min_w=%d 5tap444_w=%d 2tap444_w=%d\n",
min_width,
win_precomp_wgrp_capc_max_pixels_5tap444_v(win_capc),
win_precomp_wgrp_cape_max_pixels_2tap444_v(win_cape));
return -EINVAL;
}
}
nvdisp_win_write(win, win_scaler_usage_hbypass_f(hbypass) |
win_scaler_usage_vbypass_f(vbypass) |
win_scaler_usage_use422_disable_f(), win_scaler_usage_r());
/* scaling is needed in horizontal direction */
if (!hbypass) {
/* Convert phase_incr from fixed20.12 to fixed 8.24 */
hphase_incr = compute_phase_incr(hscalar, win->out_w) & ~0x1;
dev_dbg(&win->dc->ndev->dev,
"hphase_incr: 0x%x.\n", hphase_incr);
h_init_phase = (1 << (NFB - 1)) + (hphase_incr >> 1);
dev_dbg(&win->dc->ndev->dev,
"h_init_phase: 0x%x.\n", h_init_phase);
nvdisp_win_write(win,
win_scaler_hphase_incr_incr_f(hphase_incr),
win_scaler_hphase_incr_r());
nvdisp_win_write(win,
win_scaler_hstart_phase_phase_f(h_init_phase),
win_scaler_hstart_phase_r());
}
/* scaling is needed in vertical direction */
if (!vbypass) {
/* Convert phase_incr from fixed20.12 to fixed8.24 */
vphase_incr = compute_phase_incr(vscalar, win->out_h)& ~0x1;
dev_dbg(&win->dc->ndev->dev,
"vphase_incr: 0x%x.\n", vphase_incr);
v_init_phase = (1 << (NFB - 1)) + (vphase_incr >> 1);
dev_dbg(&win->dc->ndev->dev,
"v_init_phase: 0x%x.\n", v_init_phase);
nvdisp_win_write(win,
win_scaler_vphase_incr_incr_f(vphase_incr),
win_scaler_vphase_incr_r());
nvdisp_win_write(win,
win_scaler_vstart_phase_phase_f(v_init_phase),
win_scaler_vstart_phase_r());
}
return 0;
}
int tegra_nvdisp_verify_win_properties(struct tegra_dc *dc,
struct tegra_dc_ext_flip_windowattr *win)
{
fixed20_12 win_in_width, win_in_height;
fixed20_12 win_out_width, win_out_height;
fixed20_12 h_ds_ratio, v_ds_ratio;
u64 h_v_combined_ratio = 0;
/*
* Following are the downscaling constraints in nvdisplay:
* Maximum downscaling in any direction is 4x.
* Up-scaling is not limited, but only verified up to 16x.
* For performance reasons, vertical down-scaling should not
* exceed 2x.
* For performance reasons, total down-scaling (H x V) should
* not exceed 4x.
*/
if (!win->out_w || !win->out_h)
return 0;
/* win->w/h are of fixed20_12 type */
win_in_width.full = win->w;
win_in_height.full = win->h;
win_out_width.full = dfixed_const(win->out_w);
win_out_height.full = dfixed_const(win->out_h);
if (win->flags & TEGRA_DC_EXT_FLIP_FLAG_SCAN_COLUMN) {
/* Rotated 90 degrees or 270 degrees */
v_ds_ratio.full =
dfixed_div(win_in_width, win_out_height);
h_ds_ratio.full =
dfixed_div(win_in_height, win_out_width);
} else {
/* Either no rotation or 180 degrees rotation */
v_ds_ratio.full =
dfixed_div(win_in_height, win_out_height);
h_ds_ratio.full =
dfixed_div(win_in_width, win_out_width);
}
/*
* Most dfixed functions return u32 values with msb 20 bits
* having the actual value. Hence, need to shift them right
* by 12. Except for dfixed_mul() which returns u64 type.
*/
if ((dfixed_ceil(v_ds_ratio) >> 12) > 2) {
dev_err(&dc->ndev->dev,
"Vertical downscale ratio %ux greater than 2x\n",
(dfixed_ceil(v_ds_ratio) >> 12));
return -EINVAL;
}
if ((dfixed_ceil(h_ds_ratio) >> 12) > 4) {
dev_err(&dc->ndev->dev,
"Horizontal downscale ratio %ux greater than 4x\n",
(dfixed_ceil(h_ds_ratio) >> 12));
return -EINVAL;
}
/*
* Checking combined ratio. If either h or v is upscaled, then
* the downscale ratio will be < 1. Multiplication in such cases
* will not yield desired results. But, that should not affect
* us as we have already checked h and v downscale ratios above
*/
h_v_combined_ratio = dfixed_mul(h_ds_ratio, v_ds_ratio);
if ((h_v_combined_ratio >> 12) > 4) {
dev_err(&dc->ndev->dev,
"Total (H x V) downscale ratio %llux greater than 4x\n",
(h_v_combined_ratio >> 12));
return -EINVAL;
}
return 0;
}
static int tegra_nvdisp_enable_cde(struct tegra_dc_win *win)
{
if (win->cde.cde_addr) {
nvdisp_win_write(win, tegra_dc_reg_l32(win->cde.cde_addr),
win_cde_base_r());
nvdisp_win_write(win, tegra_dc_reg_h32(win->cde.cde_addr),
win_cde_base_hi_r());
nvdisp_win_write(win,
win_cde_zbc_color_f(win->cde.zbc_color),
win_cde_zbc_r());
nvdisp_win_write(win,
win_cde_ctb_entry_f(win->cde.ctb_entry),
win_cde_ctb_r());
nvdisp_win_write(win,
win_cde_ctrl_surface_enable_f(),
win_cde_ctrl_r());
} else {
nvdisp_win_write(win,
0,
win_cde_ctrl_r());
}
return 0;
}
static inline u32 tegra_nvdisp_win_swap_uv(struct tegra_dc_win *win)
{
u32 swap_uv = 0;
switch (tegra_dc_fmt(win->fmt)) {
case TEGRA_DC_EXT_FMT_T_Y8___V8U8_N420:
swap_uv = 1;
break;
case TEGRA_DC_EXT_FMT_T_Y8___U8V8_N420:
win->fmt = TEGRA_DC_EXT_FMT_T_Y8___V8U8_N420;
break;
case TEGRA_DC_EXT_FMT_T_Y8___U8V8_N422:
swap_uv = 1;
break;
case TEGRA_DC_EXT_FMT_T_Y8___V8U8_N422:
win->fmt = TEGRA_DC_EXT_FMT_T_Y8___U8V8_N422;
break;
case TEGRA_DC_EXT_FMT_T_Y8___U8V8_N422R:
swap_uv = 1;
break;
case TEGRA_DC_EXT_FMT_T_Y8___V8U8_N422R:
win->fmt = TEGRA_DC_EXT_FMT_T_Y8___U8V8_N422R;
break;
case TEGRA_DC_EXT_FMT_T_Y8___U8V8_N444:
swap_uv = 1;
break;
case TEGRA_DC_EXT_FMT_T_Y8___V8U8_N444:
win->fmt = TEGRA_DC_EXT_FMT_T_Y8___U8V8_N444;
break;
case TEGRA_DC_EXT_FMT_T_Y10___U10V10_N420:
win->fmt = TEGRA_DC_EXT_FMT_T_Y10___V10U10_N420;
break;
case TEGRA_DC_EXT_FMT_T_Y10___V10U10_N422:
win->fmt = TEGRA_DC_EXT_FMT_T_Y10___U10V10_N422;
break;
case TEGRA_DC_EXT_FMT_T_Y10___V10U10_N422R:
win->fmt = TEGRA_DC_EXT_FMT_T_Y10___U10V10_N422R;
break;
case TEGRA_DC_EXT_FMT_T_Y10___V10U10_N444:
win->fmt = TEGRA_DC_EXT_FMT_T_Y10___U10V10_N444;
break;
case TEGRA_DC_EXT_FMT_T_Y12___U12V12_N420:
win->fmt = TEGRA_DC_EXT_FMT_T_Y12___V12U12_N420;
break;
case TEGRA_DC_EXT_FMT_T_Y12___V12U12_N422:
win->fmt = TEGRA_DC_EXT_FMT_T_Y12___U12V12_N422;
break;
case TEGRA_DC_EXT_FMT_T_Y12___V12U12_N422R:
win->fmt = TEGRA_DC_EXT_FMT_T_Y12___U12V12_N422R;
break;
case TEGRA_DC_EXT_FMT_T_Y12___V12U12_N444:
win->fmt = TEGRA_DC_EXT_FMT_T_Y12___U12V12_N444;
break;
case TEGRA_DC_EXT_FMT_T_Y10___V10U10_N420:
case TEGRA_DC_EXT_FMT_T_Y10___U10V10_N422:
case TEGRA_DC_EXT_FMT_T_Y10___U10V10_N422R:
case TEGRA_DC_EXT_FMT_T_Y10___U10V10_N444:
case TEGRA_DC_EXT_FMT_T_Y12___V12U12_N420:
case TEGRA_DC_EXT_FMT_T_Y12___U12V12_N422:
case TEGRA_DC_EXT_FMT_T_Y12___U12V12_N422R:
case TEGRA_DC_EXT_FMT_T_Y12___U12V12_N444:
swap_uv = 1;
break;
}
return swap_uv;
}
int tegra_nvdisp_get_degamma_config(struct tegra_dc *dc,
struct tegra_dc_win *win)
{
int ret = 0;
if (win->flags & TEGRA_WIN_FLAG_DEGAMMA_SRGB) {
ret |= win_win_set_params_degamma_range_srgb_f();
} else if (win->flags & TEGRA_WIN_FLAG_DEGAMMA_YUV_8_10) {
ret |= win_win_set_params_degamma_range_yuv8_10_f();
} else if (win->flags & TEGRA_WIN_FLAG_DEGAMMA_YUV_12) {
ret |= win_win_set_params_degamma_range_yuv12_f();
} else if (win->flags & TEGRA_WIN_FLAG_DEGAMMA_NONE) {
ret |= win_win_set_params_degamma_range_none_f();
} else {
/* If degamma is not programmed through window flip flags,
* set degamma based on window format.
*/
if (tegra_dc_is_yuv(win->fmt)) {
/* yuv8_10 for rec601/709/2020-10 and
* yuv12 for rec2020-12 yuv inputs
*/
if (tegra_dc_is_yuv_12bpc(win->fmt))
ret |=
win_win_set_params_degamma_range_yuv12_f();
else
ret |=
win_win_set_params_degamma_range_yuv8_10_f();
} else {
/* srgb for rgb, none for I8 */
if (win->fmt == TEGRA_DC_EXT_FMT_T_P8)
ret |=
win_win_set_params_degamma_range_none_f();
else
ret |=
win_win_set_params_degamma_range_srgb_f();
}
}
return ret;
}
/* Degamma is programmed out-of-order here (input from debugfs).
* In-order degamma programming is done in tegra_nvdisp_win_attribute.
*/
int tegra_nvdisp_set_degamma_user_config(struct tegra_dc_win *win,
long degamma_flag)
{
u32 reg_val = 0;
u32 mask = 0;
u32 act_req_mask = nvdisp_cmd_state_ctrl_general_act_req_enable_f();
int ret = 0;
struct tegra_dc *dc = win->dc;
if (!WIN_IS_ENABLED(win))
return 0;
mutex_lock(&dc->lock);
if (!dc->enabled) {
mutex_unlock(&dc->lock);
return 0;
}
tegra_dc_get(win->dc);
reg_val = nvdisp_win_read(win, win_win_set_params_r());
mask = win_win_set_params_degamma_range_mask_f();
reg_val &= ~mask;
switch (degamma_flag) {
case NVDISP_DEGAMMA_NONE:
reg_val |= win_win_set_params_degamma_range_none_f();
break;
case NVDISP_DEGAMMA_SRGB:
reg_val |= win_win_set_params_degamma_range_srgb_f();
break;
case NVDISP_DEGAMMA_YUV_8_10:
reg_val |= win_win_set_params_degamma_range_yuv8_10_f();
break;
case NVDISP_DEGAMMA_YUV_12:
reg_val |= win_win_set_params_degamma_range_yuv12_f();
break;
default:
dev_err(&win->dc->ndev->dev,
"Invalid degamma setting.\n");
ret = -EINVAL;
goto err;
}
nvdisp_win_write(win, reg_val, win_win_set_params_r());
act_req_mask |= nvdisp_cmd_state_ctrl_a_act_req_enable_f() << win->idx;
tegra_dc_writel(win->dc, act_req_mask, nvdisp_cmd_state_ctrl_r());
tegra_dc_readl(dc, nvdisp_cmd_state_ctrl_r());
if (tegra_dc_in_cmode(dc)) {
/* wait for ACT_REQ to complete or time out */
if (tegra_dc_poll_register(dc, nvdisp_cmd_state_ctrl_r(),
act_req_mask, 0, 1,
NVDISP_TEGRA_POLL_TIMEOUT_MS))
dev_err(&dc->ndev->dev,
"dc timeout waiting to clear ACT_REQ");
}
err:
tegra_dc_put(dc);
mutex_unlock(&dc->lock);
return ret;
}
int tegra_nvdisp_get_degamma_user_config(struct tegra_dc_win *win)
{
u32 reg_val = 0;
u32 mask = 0;
long degamma_flag = 0;
if (!WIN_IS_ENABLED(win))
return 0;
reg_val = nvdisp_win_read(win, win_win_set_params_r());
mask = win_win_set_params_degamma_range_mask_f();
reg_val &= mask;
if (reg_val == win_win_set_params_degamma_range_none_f())
degamma_flag = NVDISP_DEGAMMA_NONE;
else if (reg_val == win_win_set_params_degamma_range_srgb_f())
degamma_flag = NVDISP_DEGAMMA_SRGB;
else if (reg_val == win_win_set_params_degamma_range_yuv8_10_f())
degamma_flag = NVDISP_DEGAMMA_YUV_8_10;
else if (reg_val == win_win_set_params_degamma_range_yuv12_f())
degamma_flag = NVDISP_DEGAMMA_YUV_12;
else
degamma_flag = -EINVAL;
return degamma_flag;
}
/*
* Wait/sleep for until start fetch occurs if scan line present between
* loadv line and start fetch, for more info refer to Bug 200293633.
*/
static void tegra_nvdisp_wait_for_start_fetch(struct tegra_dc *dc)
{
#define NVDISP_CRITICAL_REGION_IN_LINES 3
int loadv_line = 0, rg_elv = 0;
int line_in_usec = 0, cur_sline = 0;
int lines = NVDISP_CRITICAL_REGION_IN_LINES;
rg_elv = tegra_dc_readl(dc, nvdisp_rg_elv_0_r());
/* calculate loadv line */
loadv_line = dc->mode_metadata.vtotal_lines
- dc->mode.v_front_porch + rg_elv;
/* time required to scan one line in micro seconds */
line_in_usec = (u64)dc->mode_metadata.line_in_nsec / (u64)1000;
/*
* if cur_sline in b/w loadvline and start fetch then sleep
* till startFecth completed
*/
do {
/* read current scan line */
cur_sline = tegra_dc_get_v_count(dc);
} while ((cur_sline >= (loadv_line - lines)) &&
(cur_sline <= (loadv_line + lines)) &&
(udelay((loadv_line + lines - cur_sline) * line_in_usec), 1));
#undef NVDISP_CRITICAL_REGION_IN_LINES
}
static int tegra_nvdisp_win_attribute(struct tegra_dc_win *win,
bool wait_for_vblank)
{
u32 win_options, win_params, swap_uv;
fixed20_12 h_offset, v_offset;
int err = 0;
uint64_t addr_flag = 0x0;
bool yuv = tegra_dc_is_yuv(win->fmt);
bool yuvp = tegra_dc_is_yuv_planar(win->fmt);
bool yuvsp = tegra_dc_is_yuv_semi_planar(win->fmt);
struct tegra_dc *dc = win->dc;
/* Check the cropped size width and height are even for YUV formats
* Fail the call if not even
*/
if (yuv && (NVDISP_ODD_VAL(dfixed_trunc(win->h)) ||
NVDISP_ODD_VAL(dfixed_trunc(win->w)))) {
dev_err(&dc->ndev->dev,
"Window HEIGHT and WIDTH must be EVEN for YUV formats\n");
err = -EINVAL;
goto attr_fail;
}
h_offset.full = dfixed_floor(win->x);
v_offset = win->y;
/* For YUV format X & Y must be even */
if (yuv && (NVDISP_ODD_VAL(dfixed_trunc(h_offset))
|| NVDISP_ODD_VAL(dfixed_trunc(v_offset)))) {
dev_err(&dc->ndev->dev,
"X and Y offsets must be EVEN for YUV formats\n");
err = -EINVAL;
goto attr_fail;
}
if (tegra_dc_is_t19x())
addr_flag = nvdisp_t19x_get_addr_flag(win);
swap_uv = tegra_nvdisp_win_swap_uv(win);
nvdisp_win_write(win, tegra_dc_fmt(win->fmt), win_color_depth_r());
nvdisp_win_write(win, win_wgrp_params_swap_uv_f(swap_uv),
win_wgrp_params_r());
nvdisp_win_write(win,
win_position_v_position_f(win->out_y) |
win_position_h_position_f(win->out_x),
win_position_r());
if (tegra_dc_feature_has_interlace(dc, win->idx) &&
(dc->mode.vmode == FB_VMODE_INTERLACED)) {
nvdisp_win_write(win,
win_size_v_size_f((win->out_h) >> 1) |
win_size_h_size_f(win->out_w),
win_size_r());
} else {
nvdisp_win_write(win, win_size_v_size_f(win->out_h) |
win_size_h_size_f(win->out_w),
win_size_r());
}
win_options = win_options_win_enable_enable_f();
if (win->flags & TEGRA_WIN_FLAG_SCAN_COLUMN)
win_options |= win_options_scan_column_enable_f();
if (win->flags & TEGRA_WIN_FLAG_INVERT_H)
win_options |= win_options_h_direction_decrement_f();
if (win->flags & TEGRA_WIN_FLAG_INVERT_V)
win_options |= win_options_v_direction_decrement_f();
if ((!dc->yuv_bypass) && win->color_expand_enable)
win_options |= win_options_color_expand_enable_f();
if (win->ppflags & TEGRA_WIN_PPFLAG_CP_ENABLE)
win_options |= win_options_cp_enable_enable_f();
if (dc->yuv_bypass) {
win_options &= ~win_options_cp_enable_enable_f();
}
if (win_options & win_options_cp_enable_enable_f())
tegra_dc_set_nvdisp_lut(dc, win);
nvdisp_win_write(win, win_options, win_options_r());
nvdisp_win_write(win,
win_set_cropped_size_in_height_f(dfixed_trunc(win->h)) |
win_set_cropped_size_in_width_f(dfixed_trunc(win->w)),
win_set_cropped_size_in_r());
win->phys_addr |= addr_flag;
nvdisp_win_write(win, tegra_dc_reg_l32(win->phys_addr),
win_start_addr_r());
nvdisp_win_write(win, tegra_dc_reg_h32(win->phys_addr),
win_start_addr_hi_r());
/* pitch is in 64B chunks */
nvdisp_win_write(win, (win->stride>>6),
win_set_planar_storage_r());
if (yuvp) {
win->phys_addr_u |= addr_flag;
win->phys_addr_v |= addr_flag;
nvdisp_win_write(win, tegra_dc_reg_l32(win->phys_addr_u),
win_start_addr_u_r());
nvdisp_win_write(win, tegra_dc_reg_h32(win->phys_addr_u),
win_start_addr_hi_u_r());
nvdisp_win_write(win, tegra_dc_reg_l32(win->phys_addr_v),
win_start_addr_v_r());
nvdisp_win_write(win, tegra_dc_reg_h32(win->phys_addr_v),
win_start_addr_hi_v_r());
nvdisp_win_write(win,
win_set_planar_storage_uv_uv0_f(win->stride_uv>>6) |
win_set_planar_storage_uv_uv1_f(win->stride_uv>>6),
win_set_planar_storage_uv_r());
} else if (yuvsp) {
win->phys_addr_u |= addr_flag;
nvdisp_win_write(win, tegra_dc_reg_l32(win->phys_addr_u),
win_start_addr_u_r());
nvdisp_win_write(win, tegra_dc_reg_h32(win->phys_addr_u),
win_start_addr_hi_u_r());
nvdisp_win_write(win,
win_set_planar_storage_uv_uv0_f(win->stride_uv>>6),
win_set_planar_storage_uv_r());
}
/* setting full range as default */
win_params = win_win_set_params_in_range_full_f();
if (win->clamp_before_blend)
win_params |= win_win_set_params_clamp_before_blend_enable_f();
/* setting input-range */
if (win->flags & TEGRA_WIN_FLAG_INPUT_RANGE_BYPASS)
win_params = win_win_set_params_in_range_bypass_f();
else if (win->flags & TEGRA_WIN_FLAG_INPUT_RANGE_LIMITED)
win_params = win_win_set_params_in_range_limited_f();
if (!win->force_user_degamma)
win_params |= tegra_nvdisp_get_degamma_config(dc, win);
/* color_space settings */
if (yuv) {
if (win->flags & TEGRA_WIN_FLAG_CS_REC601)
win_params |= win_win_set_params_cs_range_yuv_601_f();
else if (win->flags & TEGRA_WIN_FLAG_CS_REC2020)
win_params |= win_win_set_params_cs_range_yuv_2020_f();
else
win_params |= win_win_set_params_cs_range_yuv_709_f();
} else {
win_params |= win_win_set_params_cs_range_rgb_f();
}
/* over-ride the win params for yuv bypass */
if (dc->yuv_bypass) {
win_params = win_win_set_params_in_range_bypass_f();
win_params |=
win_win_set_params_degamma_range_none_f();
}
nvdisp_win_write(win, win_params, win_win_set_params_r());
nvdisp_win_write(win,
win_cropped_point_h_offset_f(dfixed_trunc(h_offset))|
win_cropped_point_v_offset_f(dfixed_trunc(v_offset)),
win_cropped_point_r());
if ((dc->mode.vmode == FB_VMODE_INTERLACED) && WIN_IS_FB(win)) {
if (!WIN_IS_INTERLACE(win))
win->phys_addr2 = win->phys_addr;
}
if (tegra_dc_feature_has_interlace(dc, win->idx) &&
(dc->mode.vmode == FB_VMODE_INTERLACED)) {
win->phys_addr2 |= addr_flag;
nvdisp_win_write(win,
tegra_dc_reg_l32(win->phys_addr2),
win_start_addr_fld2_r());
nvdisp_win_write(win,
tegra_dc_reg_h32(win->phys_addr2),
win_start_addr_fld2_hi_r());
if (yuvp) {
win->phys_addr_u2 |= addr_flag;
win->phys_addr_v2 |= addr_flag;
nvdisp_win_write(win,
tegra_dc_reg_l32(win->phys_addr_u2),
win_start_addr_fld2_u_r());
nvdisp_win_write(win,
tegra_dc_reg_h32(win->phys_addr_u2),
win_start_addr_fld2_hi_u_r());
nvdisp_win_write(win,
tegra_dc_reg_l32(win->phys_addr_v2),
win_start_addr_fld2_v_r());
nvdisp_win_write(win,
tegra_dc_reg_h32(win->phys_addr_v2),
win_start_addr_fld2_hi_v_r());
} else if (yuvsp) {
win->phys_addr_u2 |= addr_flag;
nvdisp_win_write(win,
tegra_dc_reg_l32(win->phys_addr_u2),
win_start_addr_fld2_u_r());
nvdisp_win_write(win,
tegra_dc_reg_h32(win->phys_addr_u2),
win_start_addr_fld2_hi_u_r());
}
nvdisp_win_write(win,
win_cropped_point_fld2_h_f(dfixed_trunc(h_offset)),
win_cropped_point_fld2_r());
if (WIN_IS_INTERLACE(win)) {
nvdisp_win_write(win,
win_cropped_point_fld2_v_f(
dfixed_trunc(v_offset)),
win_cropped_point_fld2_r());
} else {
v_offset.full += dfixed_const(1);
nvdisp_win_write(win,
win_cropped_point_fld2_v_f(
dfixed_trunc(v_offset)),
win_cropped_point_fld2_r());
}
}
if (WIN_IS_BLOCKLINEAR(win)) {
nvdisp_win_write(win, win_surface_kind_kind_bl_f() |
win_surface_kind_block_height_f(win->block_height_log2),
win_surface_kind_r());
} else if (WIN_IS_TILED(win)) {
nvdisp_win_write(win, win_surface_kind_kind_tiled_f(),
win_surface_kind_r());
} else {
nvdisp_win_write(win, win_surface_kind_kind_pitch_f(),
win_surface_kind_r());
}
attr_fail:
return err;
}
int tegra_nvdisp_get_linestride(struct tegra_dc *dc, int win)
{
return nvdisp_win_read(tegra_dc_get_window(dc, win),
win_set_planar_storage_r()) << 6;
}
int tegra_nvdisp_update_windows(struct tegra_dc *dc,
struct tegra_dc_win *windows[], int n,
u16 *dirty_rect, bool wait_for_vblank, bool lock_flip)
{
int i;
u32 update_mask = nvdisp_cmd_state_ctrl_general_update_enable_f();
u32 act_req_mask = nvdisp_cmd_state_ctrl_general_act_req_enable_f();
u32 act_control = 0;
mutex_lock(&tegra_nvdisp_lock);
/* If any of the window updates requires vsync to program the window
update safely, vsync all windows in this flip. Safety overrides both
the requested wait_for_vblank, and also the no_vsync global.
The HSync Flip has some restrictions on changes from previous frame.
The update_is_hsync_safe() call is used to filter out flips to force
the VSync instead. */
for (i = 0; i < n; i++) {
struct tegra_dc_win *win = windows[i];
if ((!wait_for_vblank
&& !update_is_hsync_safe(
&dc->shadow_windows[win->idx], win))
/*|| do_partial_update*/)
wait_for_vblank = true;
memcpy(&dc->shadow_windows[win->idx], win, sizeof(*win));
}
for (i = 0; i < n; i++) {
struct tegra_dc_win *win = windows[i];
struct tegra_dc_win *dc_win;
if (!win) {
dev_err(&dc->ndev->dev,
"Invalid window %d to update\n", i);
mutex_unlock(&tegra_nvdisp_lock);
return -EINVAL;
}
dc_win = tegra_dc_get_window(dc, win->idx);
if (!dc_win) {
dev_err(&dc->ndev->dev,
"Cannot get window %d to update\n", i);
mutex_unlock(&tegra_nvdisp_lock);
return -EINVAL;
}
if (!WIN_IS_ENABLED(win)) {
u32 win_options;
update_mask |=
nvdisp_cmd_state_ctrl_win_a_update_enable_f()
<< win->idx;
act_req_mask |=
nvdisp_cmd_state_ctrl_a_act_req_enable_f()
<< win->idx;
/* detach the window from the head
* this must be written before other window regs */
nvdisp_win_write(win, win_set_control_owner_none_f(),
win_set_control_r());
/* disable scaler (bypass mode) */
nvdisp_win_write(win, win_scaler_usage_hbypass_f(1) |
win_scaler_usage_vbypass_f(1) |
win_scaler_usage_use422_disable_f(),
win_scaler_usage_r());
/* disable csc */
tegra_nvdisp_set_win_csc(win, &win->nvdisp_win_csc);
/*
* mark csc_dirty so that next time when window is
* enabled, CSC can be programmed.
*/
dc_win->csc_dirty = true;
/* disable cde */
nvdisp_win_write(win, 0, win_cde_ctrl_r());
/* disable window */
win_options = win_options_win_enable_disable_f();
nvdisp_win_write(win, win_options, win_options_r());
/* disable HSync Flip */
wait_for_vblank = true;
nvdisp_win_write(win,
win_act_control_ctrl_sel_vcounter_f(),
win_act_control_r());
dc_win->dirty = no_vsync ? 0 : 1;
} else {
/* attach window to the head */
nvdisp_win_write(win, dc->ctrl_num,
win_set_control_r());
update_mask |= nvdisp_cmd_state_ctrl_win_a_update_enable_f()
<< win->idx;
act_req_mask |= nvdisp_cmd_state_ctrl_a_act_req_enable_f()
<< win->idx;
if (wait_for_vblank)
act_control = win_act_control_ctrl_sel_vcounter_f();
else
act_control = win_act_control_ctrl_sel_hcounter_f();
nvdisp_win_write(win, act_control, win_act_control_r());
tegra_nvdisp_blend(win);
tegra_nvdisp_scaling(win);
tegra_nvdisp_enable_cde(win);
/* if (do_partial_update) { */
/* /\* calculate the xoff, yoff etc values *\/ */
/* tegra_dc_win_partial_update(dc, win, xoff, yoff, */
/* width, height); */
/* } */
if (tegra_nvdisp_win_attribute(win, wait_for_vblank)) {
dev_err(&dc->ndev->dev,
"win_attribute settings failed\n");
mutex_unlock(&tegra_nvdisp_lock);
return -EINVAL;
}
if (dc_win->csc_dirty) {
tegra_nvdisp_set_win_csc(win,
&dc_win->nvdisp_win_csc);
dc_win->csc_dirty = false;
}
dc_win->dirty = 1;
}
trace_window_update(dc, win);
}
/* If new IMP results are pending, go ahead and program them. */
if (dc->imp_dirty) {
tegra_nvdisp_program_imp_settings(dc);
/*
* The ihub registers live on the COMMON channel. Update the
* masks accordingly so that the COMMON channel state is
* promoted along with the WINDOW channel state.
*/
update_mask |=
nvdisp_cmd_state_ctrl_common_act_update_enable_f();
act_req_mask |= nvdisp_cmd_state_ctrl_common_act_req_enable_f();
/*
* We can safely set the pending flag here since we're holding
* the global nvdisp lock.
*/
tegra_nvdisp_set_common_channel_pending(dc);
dc->imp_dirty = false;
}
if (wait_for_vblank) {
/* Use the interrupt handler. ISR will clear the dirty flags
when the flip is completed */
set_bit(V_BLANK_FLIP, &dc->vblank_ref_count);
tegra_dc_unmask_interrupt(dc,
(nvdisp_cmd_int_status_frame_end_f(1) |
nvdisp_cmd_int_status_v_blank_f(1) |
nvdisp_cmd_int_status_uf_f(1)));
}
if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE) {
schedule_delayed_work(&dc->one_shot_work,
msecs_to_jiffies(dc->one_shot_delay_ms));
}
dc->crc_pending = true;
tegra_dc_program_bandwidth(dc, false);
if (dc->out->flags & TEGRA_DC_OUT_ONE_SHOT_MODE)
act_req_mask |= nvdisp_cmd_state_ctrl_host_trig_enable_f();
/*
* For immediate flips on t186 wait for start fetch, for more info
* refer to Bug 200293633.
*/
if (!wait_for_vblank && tegra_dc_is_t18x())
tegra_nvdisp_wait_for_start_fetch(dc);
/* cannot set fields related to UPDATE and ACT_REQ in the same write */
tegra_dc_writel(dc, update_mask, nvdisp_cmd_state_ctrl_r());
tegra_dc_readl(dc, nvdisp_cmd_state_ctrl_r()); /* flush */
if (act_req_mask) {
if (lock_flip) {
dc->act_req_mask = act_req_mask;
} else {
tegra_dc_writel(dc, act_req_mask,
nvdisp_cmd_state_ctrl_r());
tegra_dc_readl(dc,
nvdisp_cmd_state_ctrl_r()); /* flush */
}
}
if (!wait_for_vblank) {
/* Don't use a interrupt handler for the update, but leave
vblank interrupts unmasked since they could be used by other
windows. One window could flip on HBLANK while others flip
on VBLANK. Poll HW until this window update is completed
which could take up to 16 scan lines for T18x or time out
about a frame period. */
unsigned int winmask = act_req_mask &
((0x3f & dc->pdata->win_mask) << 1)/*WIN_ALL_ACT_REQ*/;
int i = 17000; /* 60Hz frame period in uSec */
while (tegra_dc_windows_are_dirty(dc, winmask) && --i)
udelay(1);
if (i) {
struct tegra_dc_win *dc_win;
for (i = 0; i < n; i++) {
dc_win = tegra_dc_get_window(dc,
windows[i]->idx);
dc_win->dirty = 0;
}
} else { /* time out */
dev_warn(&dc->ndev->dev, "winmask:0x%x: HSync timeout\n",
winmask);
}
}
mutex_unlock(&tegra_nvdisp_lock);
return 0;
}
/* detach window idx from current head */
int tegra_nvdisp_detach_win(struct tegra_dc *dc, unsigned idx)
{
struct tegra_dc_win *win = tegra_dc_get_window(dc, idx);
if (!win || win->dc != dc) {
dev_err(&dc->ndev->dev,
"%s: window %d does not belong to head %d\n",
__func__, idx, dc->ctrl_num);
return -EINVAL;
}
mutex_lock(&tegra_nvdisp_lock);
/* detach window idx */
nvdisp_win_write(win,
win_set_control_owner_none_f(), win_set_control_r());
win->dc = NULL;
win->is_scaler_coeff_set = false;
mutex_unlock(&tegra_nvdisp_lock);
return 0;
}
/* Assign window idx to head dc */
int tegra_nvdisp_assign_win(struct tegra_dc *dc, unsigned idx)
{
struct tegra_dc_win *win = tegra_dc_get_window(dc, idx);
if (win == NULL)
return -EINVAL;
if (win->dc == dc) { /* already assigned to current head */
/* If Reset happens without detach_win call
* call attach idx for safety. Can remove this
* once make sure detach_win is called before attach
*/
nvdisp_win_write(win, dc->ctrl_num, win_set_control_r());
return 0;
}
if (win->dc) { /* window is owned by another head */
dev_err(&dc->ndev->dev,
"%s: cannot assign win %d to head %d, it owned by %d\n",
__func__, idx, dc->ctrl_num, win->dc->ctrl_num);
dc->valid_windows &= ~(0x1 << idx);
mutex_unlock(&tegra_nvdisp_lock);
return -EINVAL;
}
win->dc = dc;
/* attach window idx */
nvdisp_win_write(win, dc->ctrl_num, win_set_control_r());
/* cache the values of the CAPC and CAPE registers */
if (!win->precomp_caps_read) {
win->precomp_capc = nvdisp_win_read(win,
win_precomp_wgrp_capc_r());
win->precomp_cape = nvdisp_win_read(win,
win_precomp_wgrp_cape_r());
win->precomp_caps_read = true;
}
/* Configure some IHUB related settings.
* Set linebuf config to BLx4 (4-line buffering) by default always.
*/
nvdisp_win_write(win,
win_ihub_linebuf_config_mode_four_lines_f(),
win_ihub_linebuf_config_r());
/* set the windows scaler coeff value */
if (!win->is_scaler_coeff_set) {
tegra_nvdisp_set_scaler_coeff(win);
win->is_scaler_coeff_set = true;
}
if (!win->force_user_csc) {
win->nvdisp_win_csc = dc->default_csc;
win->csc_dirty = true;
}
return 0;
}
int tegra_nvdisp_disable_wins(struct tegra_dc *dc,
struct tegra_dc_win_detach_state *win_state_arr)
{
u32 update_mask = 0;
u32 act_req_mask = 0;
int i, ret;
for_each_set_bit(i, &dc->valid_windows,
tegra_dc_get_numof_dispwindows()) {
struct tegra_dc_win *win = tegra_dc_get_window(dc, i);
struct tegra_dc_win_detach_state *win_state;
if (!win || !win->dc)
continue;
/*
* Cache the relevant register state for this window.
*
* If the window is already detached, there's nothing to cache.
* We want to avoid reading any additional window registers
* since the window owner must at least be set in assembly.
*/
win_state = &win_state_arr[i];
win_state->win_set_control_reg =
nvdisp_win_read(win, win_set_control_r());
if (win_state->win_set_control_reg ==
win_set_control_owner_none_f())
continue;
win_state->win_scaler_usage_reg =
nvdisp_win_read(win, win_scaler_usage_r());
win_state->win_options_reg =
nvdisp_win_read(win, win_options_r());
/* Detach and disable this window. */
nvdisp_win_write(win, win_set_control_owner_none_f(),
win_set_control_r());
nvdisp_win_write(win, win_scaler_usage_hbypass_f(1) |
win_scaler_usage_vbypass_f(1) |
win_scaler_usage_use422_disable_f(),
win_scaler_usage_r());
nvdisp_win_write(win, win_options_win_enable_disable_f(),
win_options_r());
update_mask |=
nvdisp_cmd_state_ctrl_win_a_update_enable_f() << i;
act_req_mask |=
nvdisp_cmd_state_ctrl_a_act_req_enable_f() << i;
}
ret = tegra_dc_enable_update_and_act(dc, update_mask, act_req_mask);
if (ret)
dev_err(&dc->ndev->dev,
"%s: DC timeout when disabling windows\n", __func__);
return ret;
}
int tegra_nvdisp_restore_wins(struct tegra_dc *dc,
struct tegra_dc_win_detach_state *win_state_arr)
{
u32 update_mask = 0;
u32 act_req_mask = 0;
int i, ret;
for_each_set_bit(i, &dc->valid_windows,
tegra_dc_get_numof_dispwindows()) {
struct tegra_dc_win *win = tegra_dc_get_window(dc, i);
struct tegra_dc_win_detach_state *win_state;
if (!win || !win->dc)
continue;
/*
* If this window was already detached before disable, there's
* nothing to restore.
*/
win_state = &win_state_arr[i];
if (win_state->win_set_control_reg ==
win_set_control_owner_none_f())
continue;
nvdisp_win_write(win, win_state->win_set_control_reg,
win_set_control_r());
nvdisp_win_write(win, win_state->win_scaler_usage_reg,
win_scaler_usage_r());
nvdisp_win_write(win, win_state->win_options_reg,
win_options_r());
update_mask |=
nvdisp_cmd_state_ctrl_win_a_update_enable_f() << i;
act_req_mask |=
nvdisp_cmd_state_ctrl_a_act_req_enable_f() << i;
}
ret = tegra_dc_enable_update_and_act(dc, update_mask, act_req_mask);
if (ret)
dev_err(&dc->ndev->dev,
"%s: DC timeout when restoring windows\n", __func__);
return ret;
}