summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/sti/sti_awg_utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/sti/sti_awg_utils.c')
-rw-r--r--drivers/gpu/drm/sti/sti_awg_utils.c192
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;
+}