diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:49:45 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:49:45 +0000 |
commit | 2c3c1048746a4622d8c89a29670120dc8fab93c4 (patch) | |
tree | 848558de17fb3008cdf4d861b01ac7781903ce39 /drivers/gpu/drm/sti/sti_awg_utils.c | |
parent | Initial commit. (diff) | |
download | linux-2c3c1048746a4622d8c89a29670120dc8fab93c4.tar.xz linux-2c3c1048746a4622d8c89a29670120dc8fab93c4.zip |
Adding upstream version 6.1.76.upstream/6.1.76upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/gpu/drm/sti/sti_awg_utils.c')
-rw-r--r-- | drivers/gpu/drm/sti/sti_awg_utils.c | 192 |
1 files changed, 192 insertions, 0 deletions
diff --git a/drivers/gpu/drm/sti/sti_awg_utils.c b/drivers/gpu/drm/sti/sti_awg_utils.c new file mode 100644 index 000000000..5ff87a4a1 --- /dev/null +++ b/drivers/gpu/drm/sti/sti_awg_utils.c @@ -0,0 +1,192 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) STMicroelectronics SA 2014 + * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics. + */ + +#include <drm/drm_print.h> + +#include "sti_awg_utils.h" + +#define AWG_DELAY (-5) + +#define AWG_OPCODE_OFFSET 10 +#define AWG_MAX_ARG 0x3ff + +enum opcode { + SET, + RPTSET, + RPLSET, + SKIP, + STOP, + REPEAT, + REPLAY, + JUMP, + HOLD, +}; + +static int awg_generate_instr(enum opcode opcode, + long int arg, + long int mux_sel, + long int data_en, + struct awg_code_generation_params *fwparams) +{ + u32 instruction = 0; + u32 mux = (mux_sel << 8) & 0x1ff; + u32 data_enable = (data_en << 9) & 0x2ff; + long int arg_tmp = arg; + + /* skip, repeat and replay arg should not exceed 1023. + * If user wants to exceed this value, the instruction should be + * duplicate and arg should be adjust for each duplicated instruction. + * + * mux_sel is used in case of SAV/EAV synchronization. + */ + + while (arg_tmp > 0) { + arg = arg_tmp; + if (fwparams->instruction_offset >= AWG_MAX_INST) { + DRM_ERROR("too many number of instructions\n"); + return -EINVAL; + } + + switch (opcode) { + case SKIP: + /* leave 'arg' + 1 pixel elapsing without changing + * output bus */ + arg--; /* pixel adjustment */ + arg_tmp--; + + if (arg < 0) { + /* SKIP instruction not needed */ + return 0; + } + + if (arg == 0) { + /* SKIP 0 not permitted but we want to skip 1 + * pixel. So we transform SKIP into SET + * instruction */ + opcode = SET; + break; + } + + mux = 0; + data_enable = 0; + arg &= AWG_MAX_ARG; + break; + case REPEAT: + case REPLAY: + if (arg == 0) { + /* REPEAT or REPLAY instruction not needed */ + return 0; + } + + mux = 0; + data_enable = 0; + arg &= AWG_MAX_ARG; + break; + case JUMP: + mux = 0; + data_enable = 0; + arg |= 0x40; /* for jump instruction 7th bit is 1 */ + arg &= AWG_MAX_ARG; + break; + case STOP: + arg = 0; + break; + case SET: + case RPTSET: + case RPLSET: + case HOLD: + arg &= (0x0ff); + break; + default: + DRM_ERROR("instruction %d does not exist\n", opcode); + return -EINVAL; + } + + arg_tmp = arg_tmp - arg; + + arg = ((arg + mux) + data_enable); + + instruction = ((opcode) << AWG_OPCODE_OFFSET) | arg; + fwparams->ram_code[fwparams->instruction_offset] = + instruction & (0x3fff); + fwparams->instruction_offset++; + } + return 0; +} + +static int awg_generate_line_signal( + struct awg_code_generation_params *fwparams, + struct awg_timing *timing) +{ + long int val; + int ret = 0; + + if (timing->trailing_pixels > 0) { + /* skip trailing pixel */ + val = timing->blanking_level; + ret |= awg_generate_instr(RPLSET, val, 0, 0, fwparams); + + val = timing->trailing_pixels - 1 + AWG_DELAY; + ret |= awg_generate_instr(SKIP, val, 0, 0, fwparams); + } + + /* set DE signal high */ + val = timing->blanking_level; + ret |= awg_generate_instr((timing->trailing_pixels > 0) ? SET : RPLSET, + val, 0, 1, fwparams); + + if (timing->blanking_pixels > 0) { + /* skip the number of active pixel */ + val = timing->active_pixels - 1; + ret |= awg_generate_instr(SKIP, val, 0, 1, fwparams); + + /* set DE signal low */ + val = timing->blanking_level; + ret |= awg_generate_instr(SET, val, 0, 0, fwparams); + } + + return ret; +} + +int sti_awg_generate_code_data_enable_mode( + struct awg_code_generation_params *fwparams, + struct awg_timing *timing) +{ + long int val, tmp_val; + int ret = 0; + + if (timing->trailing_lines > 0) { + /* skip trailing lines */ + val = timing->blanking_level; + ret |= awg_generate_instr(RPLSET, val, 0, 0, fwparams); + + val = timing->trailing_lines - 1; + ret |= awg_generate_instr(REPLAY, val, 0, 0, fwparams); + } + + tmp_val = timing->active_lines - 1; + + while (tmp_val > 0) { + /* generate DE signal for each line */ + ret |= awg_generate_line_signal(fwparams, timing); + /* replay the sequence as many active lines defined */ + ret |= awg_generate_instr(REPLAY, + min_t(int, AWG_MAX_ARG, tmp_val), + 0, 0, fwparams); + tmp_val -= AWG_MAX_ARG; + } + + if (timing->blanking_lines > 0) { + /* skip blanking lines */ + val = timing->blanking_level; + ret |= awg_generate_instr(RPLSET, val, 0, 0, fwparams); + + val = timing->blanking_lines - 1; + ret |= awg_generate_instr(REPLAY, val, 0, 0, fwparams); + } + + return ret; +} |