// SPDX-License-Identifier: GPL-2.0-only /* * linux/drivers/video/omap2/dss/dispc.c * * Copyright (C) 2009 Nokia Corporation * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> * * Some code and ideas taken from drivers/video/omap/ driver * by Imre Deak.
*/
enum omap_color_component { /* used for all color formats for OMAP3 and earlier * and for RGB and Y color component on OMAP4
*/
DISPC_COLOR_COMPONENT_RGB_Y = 1 << 0, /* used for UV component for * OMAP_DSS_COLOR_YUV2, OMAP_DSS_COLOR_UYVY, OMAP_DSS_COLOR_NV12 * color formats on OMAP4
*/
DISPC_COLOR_COMPONENT_UV = 1 << 1,
};
enum mgr_reg_fields {
DISPC_MGR_FLD_ENABLE,
DISPC_MGR_FLD_STNTFT,
DISPC_MGR_FLD_GO,
DISPC_MGR_FLD_TFTDATALINES,
DISPC_MGR_FLD_STALLMODE,
DISPC_MGR_FLD_TCKENABLE,
DISPC_MGR_FLD_TCKSELECTION,
DISPC_MGR_FLD_CPR,
DISPC_MGR_FLD_FIFOHANDCHECK, /* used to maintain a count of the above fields */
DISPC_MGR_FLD_NUM,
};
SR(IRQENABLE);
SR(CONTROL);
SR(CONFIG);
SR(LINE_NUMBER); if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER) ||
dss_has_feature(FEAT_ALPHA_FREE_ZORDER))
SR(GLOBAL_ALPHA); if (dss_has_feature(FEAT_MGR_LCD2)) {
SR(CONTROL2);
SR(CONFIG2);
} if (dss_has_feature(FEAT_MGR_LCD3)) {
SR(CONTROL3);
SR(CONFIG3);
}
for (i = 0; i < dss_feat_get_num_mgrs(); i++) {
SR(DEFAULT_COLOR(i));
SR(TRANS_COLOR(i));
SR(SIZE_MGR(i)); if (i == OMAP_DSS_CHANNEL_DIGIT) continue;
SR(TIMING_H(i));
SR(TIMING_V(i));
SR(POL_FREQ(i));
SR(DIVISORo(i));
if (dss_has_feature(FEAT_CPR)) {
SR(CPR_COEF_R(i));
SR(CPR_COEF_G(i));
SR(CPR_COEF_B(i));
}
}
for (i = 0; i < dss_feat_get_num_ovls(); i++) {
SR(OVL_BA0(i));
SR(OVL_BA1(i));
SR(OVL_POSITION(i));
SR(OVL_SIZE(i));
SR(OVL_ATTRIBUTES(i));
SR(OVL_FIFO_THRESHOLD(i));
SR(OVL_ROW_INC(i));
SR(OVL_PIXEL_INC(i)); if (dss_has_feature(FEAT_PRELOAD))
SR(OVL_PRELOAD(i)); if (i == OMAP_DSS_GFX) {
SR(OVL_WINDOW_SKIP(i));
SR(OVL_TABLE_BA(i)); continue;
}
SR(OVL_FIR(i));
SR(OVL_PICTURE_SIZE(i));
SR(OVL_ACCU0(i));
SR(OVL_ACCU1(i));
for (j = 0; j < 8; j++)
SR(OVL_FIR_COEF_H(i, j));
for (j = 0; j < 8; j++)
SR(OVL_FIR_COEF_HV(i, j));
for (j = 0; j < 5; j++)
SR(OVL_CONV_COEF(i, j));
if (dss_has_feature(FEAT_FIR_COEF_V)) { for (j = 0; j < 8; j++)
SR(OVL_FIR_COEF_V(i, j));
}
if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
SR(OVL_BA0_UV(i));
SR(OVL_BA1_UV(i));
SR(OVL_FIR2(i));
SR(OVL_ACCU2_0(i));
SR(OVL_ACCU2_1(i));
for (j = 0; j < 8; j++)
SR(OVL_FIR_COEF_H2(i, j));
for (j = 0; j < 8; j++)
SR(OVL_FIR_COEF_HV2(i, j));
for (j = 0; j < 8; j++)
SR(OVL_FIR_COEF_V2(i, j));
} if (dss_has_feature(FEAT_ATTR2))
SR(OVL_ATTRIBUTES2(i));
}
if (dss_has_feature(FEAT_CORE_CLK_DIV))
SR(DIVISOR);
dispc.ctx_valid = true;
DSSDBG("context saved\n");
}
staticvoid dispc_restore_context(void)
{ int i, j;
DSSDBG("dispc_restore_context\n");
if (!dispc.ctx_valid) return;
/*RR(IRQENABLE);*/ /*RR(CONTROL);*/
RR(CONFIG);
RR(LINE_NUMBER); if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER) ||
dss_has_feature(FEAT_ALPHA_FREE_ZORDER))
RR(GLOBAL_ALPHA); if (dss_has_feature(FEAT_MGR_LCD2))
RR(CONFIG2); if (dss_has_feature(FEAT_MGR_LCD3))
RR(CONFIG3);
for (i = 0; i < dss_feat_get_num_mgrs(); i++) {
RR(DEFAULT_COLOR(i));
RR(TRANS_COLOR(i));
RR(SIZE_MGR(i)); if (i == OMAP_DSS_CHANNEL_DIGIT) continue;
RR(TIMING_H(i));
RR(TIMING_V(i));
RR(POL_FREQ(i));
RR(DIVISORo(i));
if (dss_has_feature(FEAT_CPR)) {
RR(CPR_COEF_R(i));
RR(CPR_COEF_G(i));
RR(CPR_COEF_B(i));
}
}
for (i = 0; i < dss_feat_get_num_ovls(); i++) {
RR(OVL_BA0(i));
RR(OVL_BA1(i));
RR(OVL_POSITION(i));
RR(OVL_SIZE(i));
RR(OVL_ATTRIBUTES(i));
RR(OVL_FIFO_THRESHOLD(i));
RR(OVL_ROW_INC(i));
RR(OVL_PIXEL_INC(i)); if (dss_has_feature(FEAT_PRELOAD))
RR(OVL_PRELOAD(i)); if (i == OMAP_DSS_GFX) {
RR(OVL_WINDOW_SKIP(i));
RR(OVL_TABLE_BA(i)); continue;
}
RR(OVL_FIR(i));
RR(OVL_PICTURE_SIZE(i));
RR(OVL_ACCU0(i));
RR(OVL_ACCU1(i));
for (j = 0; j < 8; j++)
RR(OVL_FIR_COEF_H(i, j));
for (j = 0; j < 8; j++)
RR(OVL_FIR_COEF_HV(i, j));
for (j = 0; j < 5; j++)
RR(OVL_CONV_COEF(i, j));
if (dss_has_feature(FEAT_FIR_COEF_V)) { for (j = 0; j < 8; j++)
RR(OVL_FIR_COEF_V(i, j));
}
if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
RR(OVL_BA0_UV(i));
RR(OVL_BA1_UV(i));
RR(OVL_FIR2(i));
RR(OVL_ACCU2_0(i));
RR(OVL_ACCU2_1(i));
for (j = 0; j < 8; j++)
RR(OVL_FIR_COEF_H2(i, j));
for (j = 0; j < 8; j++)
RR(OVL_FIR_COEF_HV2(i, j));
for (j = 0; j < 8; j++)
RR(OVL_FIR_COEF_V2(i, j));
} if (dss_has_feature(FEAT_ATTR2))
RR(OVL_ATTRIBUTES2(i));
}
if (dss_has_feature(FEAT_CORE_CLK_DIV))
RR(DIVISOR);
/* enable last, because LCD & DIGIT enable are here */
RR(CONTROL); if (dss_has_feature(FEAT_MGR_LCD2))
RR(CONTROL2); if (dss_has_feature(FEAT_MGR_LCD3))
RR(CONTROL3); /* clear spurious SYNC_LOST_DIGIT interrupts */
dispc_clear_irqstatus(DISPC_IRQ_SYNC_LOST_DIGIT);
/* * enable last so IRQs won't trigger before * the context is fully restored
*/
RR(IRQENABLE);
DSSDBG("context restored\n");
}
#undef SR #undef RR
int dispc_runtime_get(void)
{ int r;
DSSDBG("dispc_runtime_get\n");
r = pm_runtime_resume_and_get(&dispc.pdev->dev); if (WARN_ON(r < 0)) return r; return 0;
}
EXPORT_SYMBOL(dispc_runtime_get);
void dispc_runtime_put(void)
{ int r;
DSSDBG("dispc_runtime_put\n");
r = pm_runtime_put_sync(&dispc.pdev->dev);
WARN_ON(r < 0 && r != -ENOSYS);
}
EXPORT_SYMBOL(dispc_runtime_put);
staticvoid dispc_ovl_set_scale_coef(enum omap_plane plane, int fir_hinc, int fir_vinc, int five_taps, enum omap_color_component color_comp)
{ conststruct dispc_coef *h_coef, *v_coef; int i;
if (color_comp == DISPC_COLOR_COMPONENT_RGB_Y) {
dispc_ovl_write_firh_reg(plane, i, h);
dispc_ovl_write_firhv_reg(plane, i, hv);
} else {
dispc_ovl_write_firh2_reg(plane, i, h);
dispc_ovl_write_firhv2_reg(plane, i, hv);
}
}
if (five_taps) { for (i = 0; i < 8; i++) {
u32 v;
v = FLD_VAL(v_coef[i].hc0_vc00, 7, 0)
| FLD_VAL(v_coef[i].hc4_vc22, 15, 8); if (color_comp == DISPC_COLOR_COMPONENT_RGB_Y)
dispc_ovl_write_firv_reg(plane, i, v); else
dispc_ovl_write_firv2_reg(plane, i, v);
}
}
}
staticvoid dispc_ovl_set_color_mode(enum omap_plane plane, enum omap_color_mode color_mode)
{
u32 m = 0; if (plane != OMAP_DSS_GFX) { switch (color_mode) { case OMAP_DSS_COLOR_NV12:
m = 0x0; break; case OMAP_DSS_COLOR_RGBX16:
m = 0x1; break; case OMAP_DSS_COLOR_RGBA16:
m = 0x2; break; case OMAP_DSS_COLOR_RGB12U:
m = 0x4; break; case OMAP_DSS_COLOR_ARGB16:
m = 0x5; break; case OMAP_DSS_COLOR_RGB16:
m = 0x6; break; case OMAP_DSS_COLOR_ARGB16_1555:
m = 0x7; break; case OMAP_DSS_COLOR_RGB24U:
m = 0x8; break; case OMAP_DSS_COLOR_RGB24P:
m = 0x9; break; case OMAP_DSS_COLOR_YUV2:
m = 0xa; break; case OMAP_DSS_COLOR_UYVY:
m = 0xb; break; case OMAP_DSS_COLOR_ARGB32:
m = 0xc; break; case OMAP_DSS_COLOR_RGBA32:
m = 0xd; break; case OMAP_DSS_COLOR_RGBX32:
m = 0xe; break; case OMAP_DSS_COLOR_XRGB16_1555:
m = 0xf; break; default:
BUG(); return;
}
} else { switch (color_mode) { case OMAP_DSS_COLOR_CLUT1:
m = 0x0; break; case OMAP_DSS_COLOR_CLUT2:
m = 0x1; break; case OMAP_DSS_COLOR_CLUT4:
m = 0x2; break; case OMAP_DSS_COLOR_CLUT8:
m = 0x3; break; case OMAP_DSS_COLOR_RGB12U:
m = 0x4; break; case OMAP_DSS_COLOR_ARGB16:
m = 0x5; break; case OMAP_DSS_COLOR_RGB16:
m = 0x6; break; case OMAP_DSS_COLOR_ARGB16_1555:
m = 0x7; break; case OMAP_DSS_COLOR_RGB24U:
m = 0x8; break; case OMAP_DSS_COLOR_RGB24P:
m = 0x9; break; case OMAP_DSS_COLOR_RGBX16:
m = 0xa; break; case OMAP_DSS_COLOR_RGBA16:
m = 0xb; break; case OMAP_DSS_COLOR_ARGB32:
m = 0xc; break; case OMAP_DSS_COLOR_RGBA32:
m = 0xd; break; case OMAP_DSS_COLOR_RGBX32:
m = 0xe; break; case OMAP_DSS_COLOR_XRGB16_1555:
m = 0xf; break; default:
BUG(); return;
}
}
REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), m, 4, 1);
}
staticvoid dispc_configure_burst_sizes(void)
{ int i; constint burst_size = BURST_SIZE_X8;
/* Configure burst size always to maximum size */ for (i = 0; i < dss_feat_get_num_ovls(); ++i)
dispc_ovl_set_burst_size(i, burst_size); if (dispc.feat->has_writeback)
dispc_ovl_set_burst_size(OMAP_DSS_WB, burst_size);
}
static u32 dispc_ovl_get_burst_size(enum omap_plane plane)
{ unsigned unit = dss_feat_get_burst_size_unit(); /* burst multiplier is always x8 (see dispc_configure_burst_sizes()) */ return unit * 8;
}
void dispc_enable_gamma_table(bool enable)
{ /* * This is partially implemented to support only disabling of * the gamma table.
*/ if (enable) {
DSSWARN("Gamma table enabling for TV not yet supported"); return;
}
/* * By default fifos are mapped directly to overlays, fifo 0 to * ovl 0, fifo 1 to ovl 1, etc.
*/
dispc.fifo_assignment[fifo] = fifo;
}
/* * The GFX fifo on OMAP4 is smaller than the other fifos. The small fifo * causes problems with certain use cases, like using the tiler in 2D * mode. The below hack swaps the fifos of GFX and WB planes, thus * giving GFX plane a larger fifo. WB but should work fine with a * smaller fifo.
*/ if (dispc.feat->gfx_fifo_workaround) {
u32 v;
v = dispc_read_reg(DISPC_GLOBAL_BUFFER);
v = FLD_MOD(v, 4, 2, 0); /* GFX BUF top to WB */
v = FLD_MOD(v, 4, 5, 3); /* GFX BUF bottom to WB */
v = FLD_MOD(v, 0, 26, 24); /* WB BUF top to GFX */
v = FLD_MOD(v, 0, 29, 27); /* WB BUF bottom to GFX */
DSSDBG("fifo(%d) threshold (bytes), old %u/%u, new %u/%u\n",
plane,
REG_GET(DISPC_OVL_FIFO_THRESHOLD(plane),
lo_start, lo_end) * unit,
REG_GET(DISPC_OVL_FIFO_THRESHOLD(plane),
hi_start, hi_end) * unit,
low * unit, high * unit);
/* * configure the preload to the pipeline's high threhold, if HT it's too * large for the preload field, set the threshold to the maximum value * that can be held by the preload register
*/ if (dss_has_feature(FEAT_PRELOAD) && dispc.feat->set_max_preload &&
plane != OMAP_DSS_WB)
dispc_write_reg(DISPC_OVL_PRELOAD(plane), min(high, 0xfffu));
}
void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane,
u32 *fifo_low, u32 *fifo_high, bool use_fifomerge, bool manual_update)
{ /* * All sizes are in bytes. Both the buffer and burst are made of * buffer_units, and the fifo thresholds must be buffer_unit aligned.
*/
unsigned buf_unit = dss_feat_get_buffer_size_unit(); unsigned ovl_fifo_size, total_fifo_size, burst_size; int i;
if (use_fifomerge) {
total_fifo_size = 0; for (i = 0; i < dss_feat_get_num_ovls(); ++i)
total_fifo_size += dispc_ovl_get_fifo_size(i);
} else {
total_fifo_size = ovl_fifo_size;
}
/* * We use the same low threshold for both fifomerge and non-fifomerge * cases, but for fifomerge we calculate the high threshold using the * combined fifo size
*/
if (manual_update && dss_has_feature(FEAT_OMAP3_DSI_FIFO_BUG)) {
*fifo_low = ovl_fifo_size - burst_size * 2;
*fifo_high = total_fifo_size - burst_size;
} elseif (plane == OMAP_DSS_WB) { /* * Most optimal configuration for writeback is to push out data * to the interconnect the moment writeback pushes enough pixels * in the FIFO to form a burst
*/
*fifo_low = 0;
*fifo_high = burst_size;
} else {
*fifo_low = ovl_fifo_size - burst_size;
*fifo_high = total_fifo_size - buf_unit;
}
}
staticvoid dispc_ovl_set_mflag(enum omap_plane plane, bool enable)
{ int bit;
if (plane == OMAP_DSS_GFX)
bit = 14; else
bit = 23;
staticvoid dispc_ovl_set_mflag_threshold(enum omap_plane plane, int low, int high)
{
dispc_write_reg(DISPC_OVL_MFLAG_THRESHOLD(plane),
FLD_VAL(high, 31, 16) | FLD_VAL(low, 15, 0));
}
staticvoid dispc_init_mflag(void)
{ int i;
/* * HACK: NV12 color format and MFLAG seem to have problems working * together: using two displays, and having an NV12 overlay on one of * the displays will cause underflows/synclosts when MFLAG_CTRL=2. * Changing MFLAG thresholds and PRELOAD to certain values seem to * remove the errors, but there doesn't seem to be a clear logic on * which values work and which not. * * As a work-around, set force MFLAG to always on.
*/
dispc_write_reg(DISPC_GLOBAL_MFLAG_ATTRIBUTE,
(1 << 0) | /* MFLAG_CTRL = force always on */
(0 << 2)); /* MFLAG_START = disable */
for (i = 0; i < dss_feat_get_num_ovls(); ++i) {
u32 size = dispc_ovl_get_fifo_size(i);
u32 unit = dss_feat_get_buffer_size_unit();
u32 low, high;
switch (color_mode) { case OMAP_DSS_COLOR_NV12: if (chroma_upscale) { /* UV is subsampled by 2 horizontally and vertically */
orig_height >>= 1;
orig_width >>= 1;
} else { /* UV is downsampled by 2 horizontally and vertically */
orig_height <<= 1;
orig_width <<= 1;
}
break; case OMAP_DSS_COLOR_YUV2: case OMAP_DSS_COLOR_UYVY: /* For YUV422 with 90/270 rotation, we don't upsample chroma */ if (rotation == OMAP_DSS_ROT_0 ||
rotation == OMAP_DSS_ROT_180) { if (chroma_upscale) /* UV is subsampled by 2 horizontally */
orig_width >>= 1; else /* UV is downsampled by 2 horizontally */
orig_width <<= 1;
}
/* must use FIR for YUV422 if rotated */ if (rotation != OMAP_DSS_ROT_0)
scale_x = scale_y = true;
break; default:
BUG(); return;
}
if (out_width != orig_width)
scale_x = true; if (out_height != orig_height)
scale_y = true;
/* * OMAP4/5 Errata i631: * NV12 in 1D mode must use ROTATION=1. Otherwise DSS will fetch extra * rows beyond the framebuffer, which may cause OCP error.
*/ if (color_mode == OMAP_DSS_COLOR_NV12 &&
rotation_type != OMAP_DSS_ROT_TILER)
vidrot = 1;
staticint color_mode_to_bpp(enum omap_color_mode color_mode)
{ switch (color_mode) { case OMAP_DSS_COLOR_CLUT1: return 1; case OMAP_DSS_COLOR_CLUT2: return 2; case OMAP_DSS_COLOR_CLUT4: return 4; case OMAP_DSS_COLOR_CLUT8: case OMAP_DSS_COLOR_NV12: return 8; case OMAP_DSS_COLOR_RGB12U: case OMAP_DSS_COLOR_RGB16: case OMAP_DSS_COLOR_ARGB16: case OMAP_DSS_COLOR_YUV2: case OMAP_DSS_COLOR_UYVY: case OMAP_DSS_COLOR_RGBA16: case OMAP_DSS_COLOR_RGBX16: case OMAP_DSS_COLOR_ARGB16_1555: case OMAP_DSS_COLOR_XRGB16_1555: return 16; case OMAP_DSS_COLOR_RGB24P: return 24; case OMAP_DSS_COLOR_RGB24U: case OMAP_DSS_COLOR_ARGB32: case OMAP_DSS_COLOR_RGBA32: case OMAP_DSS_COLOR_RGBX32: return 32; default:
BUG(); return 0;
}
}
/* * field 0 = even field = bottom field * field 1 = odd field = top field
*/ switch (rotation + mirror * 4) { case OMAP_DSS_ROT_0: case OMAP_DSS_ROT_180: /* * If the pixel format is YUV or UYVY divide the width * of the image by 2 for 0 and 180 degree rotation.
*/ if (color_mode == OMAP_DSS_COLOR_YUV2 ||
color_mode == OMAP_DSS_COLOR_UYVY)
width = width >> 1;
fallthrough; case OMAP_DSS_ROT_90: case OMAP_DSS_ROT_270:
*offset1 = 0; if (field_offset)
*offset0 = field_offset * screen_width * ps; else
*offset0 = 0;
/* * field 0 = even field = bottom field * field 1 = odd field = top field
*/
*offset1 = 0; if (field_offset)
*offset0 = *offset1 + field_offset * screen_width * ps; else
*offset0 = *offset1;
*row_inc = pixinc(1 + (y_predecim * screen_width - width * x_predecim) +
(fieldmode ? screen_width : 0), ps); if (color_mode == OMAP_DSS_COLOR_YUV2 ||
color_mode == OMAP_DSS_COLOR_UYVY)
*pix_inc = pixinc(x_predecim, 2 * ps); else
*pix_inc = pixinc(x_predecim, ps);
}
/* * This function is used to avoid synclosts in OMAP3, because of some * undocumented horizontal position and timing related limitations.
*/ staticint check_horiz_timing_omap3(unsignedlong pclk, unsignedlong lclk, conststruct omap_video_timings *t, u16 pos_x,
u16 width, u16 height, u16 out_width, u16 out_height, bool five_taps)
{ constint ds = DIV_ROUND_UP(height, out_height); unsignedlong nonactive; staticconst u8 limits[3] = { 8, 10, 20 };
u64 val, blank; int i;
i = 0; if (out_height < height)
i++; if (out_width < width)
i++;
blank = div_u64((u64)(t->hbp + t->hsw + t->hfp) * lclk, pclk);
DSSDBG("blanking period + ppl = %llu (limit = %u)\n", blank, limits[i]); if (blank <= limits[i]) return -EINVAL;
/* FIXME add checks for 3-tap filter once the limitations are known */ if (!five_taps) return 0;
/* * Pixel data should be prepared before visible display point starts. * So, atleast DS-2 lines must have already been fetched by DISPC * during nonactive - pos_x period.
*/
val = div_u64((u64)(nonactive - pos_x) * lclk, pclk);
DSSDBG("(nonactive - pos_x) * pcd = %llu max(0, DS - 2) * width = %d\n",
val, max(0, ds - 2) * width); if (val < max(0, ds - 2) * width) return -EINVAL;
/* * All lines need to be refilled during the nonactive period of which * only one line can be loaded during the active period. So, atleast * DS - 1 lines should be loaded during nonactive period.
*/
val = div_u64((u64)nonactive * lclk, pclk);
DSSDBG("nonactive * pcd = %llu, max(0, DS - 1) * width = %d\n",
val, max(0, ds - 1) * width); if (val < max(0, ds - 1) * width) return -EINVAL;
staticunsignedlong calc_core_clk_44xx(unsignedlong pclk, u16 width,
u16 height, u16 out_width, u16 out_height, bool mem_to_mem)
{ /* * If the overlay/writeback is in mem to mem mode, there are no * downscaling limitations with respect to pixel clock, return 1 as * required core clock to represent that we have sufficient enough * core clock to do maximum downscaling
*/ if (mem_to_mem) return 1;
if (paddr == 0 && rotation_type != OMAP_DSS_ROT_TILER) return -EINVAL;
switch (color_mode) { case OMAP_DSS_COLOR_YUV2: case OMAP_DSS_COLOR_UYVY: case OMAP_DSS_COLOR_NV12: if (in_width & 1) {
DSSERR("input width %d is not even for YUV format\n",
in_width); return -EINVAL;
} break;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.