1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) STMicroelectronics SA 2014
4 * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
5 */
6
7#include <drm/drm_print.h>
8
9#include "sti_awg_utils.h"
10
11#define AWG_DELAY (-5)
12
13#define AWG_OPCODE_OFFSET 10
14#define AWG_MAX_ARG       0x3ff
15
16enum opcode {
17	SET,
18	RPTSET,
19	RPLSET,
20	SKIP,
21	STOP,
22	REPEAT,
23	REPLAY,
24	JUMP,
25	HOLD,
26};
27
28static int awg_generate_instr(enum opcode opcode,
29			      long int arg,
30			      long int mux_sel,
31			      long int data_en,
32			      struct awg_code_generation_params *fwparams)
33{
34	u32 instruction = 0;
35	u32 mux = (mux_sel << 8) & 0x1ff;
36	u32 data_enable = (data_en << 9) & 0x2ff;
37	long int arg_tmp = arg;
38
39	/* skip, repeat and replay arg should not exceed 1023.
40	 * If user wants to exceed this value, the instruction should be
41	 * duplicate and arg should be adjust for each duplicated instruction.
42	 *
43	 * mux_sel is used in case of SAV/EAV synchronization.
44	 */
45
46	while (arg_tmp > 0) {
47		arg = arg_tmp;
48		if (fwparams->instruction_offset >= AWG_MAX_INST) {
49			DRM_ERROR("too many number of instructions\n");
50			return -EINVAL;
51		}
52
53		switch (opcode) {
54		case SKIP:
55			/* leave 'arg' + 1 pixel elapsing without changing
56			 * output bus */
57			arg--; /* pixel adjustment */
58			arg_tmp--;
59
60			if (arg < 0) {
61				/* SKIP instruction not needed */
62				return 0;
63			}
64
65			if (arg == 0) {
66				/* SKIP 0 not permitted but we want to skip 1
67				 * pixel. So we transform SKIP into SET
68				 * instruction */
69				opcode = SET;
70				break;
71			}
72
73			mux = 0;
74			data_enable = 0;
75			arg &= AWG_MAX_ARG;
76			break;
77		case REPEAT:
78		case REPLAY:
79			if (arg == 0) {
80				/* REPEAT or REPLAY instruction not needed */
81				return 0;
82			}
83
84			mux = 0;
85			data_enable = 0;
86			arg &= AWG_MAX_ARG;
87			break;
88		case JUMP:
89			mux = 0;
90			data_enable = 0;
91			arg |= 0x40; /* for jump instruction 7th bit is 1 */
92			arg &= AWG_MAX_ARG;
93			break;
94		case STOP:
95			arg = 0;
96			break;
97		case SET:
98		case RPTSET:
99		case RPLSET:
100		case HOLD:
101			arg &= (0x0ff);
102			break;
103		default:
104			DRM_ERROR("instruction %d does not exist\n", opcode);
105			return -EINVAL;
106		}
107
108		arg_tmp = arg_tmp - arg;
109
110		arg = ((arg + mux) + data_enable);
111
112		instruction = ((opcode) << AWG_OPCODE_OFFSET) | arg;
113		fwparams->ram_code[fwparams->instruction_offset] =
114			instruction & (0x3fff);
115		fwparams->instruction_offset++;
116	}
117	return 0;
118}
119
120static int awg_generate_line_signal(
121		struct awg_code_generation_params *fwparams,
122		struct awg_timing *timing)
123{
124	long int val;
125	int ret = 0;
126
127	if (timing->trailing_pixels > 0) {
128		/* skip trailing pixel */
129		val = timing->blanking_level;
130		ret |= awg_generate_instr(RPLSET, val, 0, 0, fwparams);
131
132		val = timing->trailing_pixels - 1 + AWG_DELAY;
133		ret |= awg_generate_instr(SKIP, val, 0, 0, fwparams);
134	}
135
136	/* set DE signal high */
137	val = timing->blanking_level;
138	ret |= awg_generate_instr((timing->trailing_pixels > 0) ? SET : RPLSET,
139			val, 0, 1, fwparams);
140
141	if (timing->blanking_pixels > 0) {
142		/* skip the number of active pixel */
143		val = timing->active_pixels - 1;
144		ret |= awg_generate_instr(SKIP, val, 0, 1, fwparams);
145
146		/* set DE signal low */
147		val = timing->blanking_level;
148		ret |= awg_generate_instr(SET, val, 0, 0, fwparams);
149	}
150
151	return ret;
152}
153
154int sti_awg_generate_code_data_enable_mode(
155		struct awg_code_generation_params *fwparams,
156		struct awg_timing *timing)
157{
158	long int val, tmp_val;
159	int ret = 0;
160
161	if (timing->trailing_lines > 0) {
162		/* skip trailing lines */
163		val = timing->blanking_level;
164		ret |= awg_generate_instr(RPLSET, val, 0, 0, fwparams);
165
166		val = timing->trailing_lines - 1;
167		ret |= awg_generate_instr(REPLAY, val, 0, 0, fwparams);
168	}
169
170	tmp_val = timing->active_lines - 1;
171
172	while (tmp_val > 0) {
173		/* generate DE signal for each line */
174		ret |= awg_generate_line_signal(fwparams, timing);
175		/* replay the sequence as many active lines defined */
176		ret |= awg_generate_instr(REPLAY,
177					  min_t(int, AWG_MAX_ARG, tmp_val),
178					  0, 0, fwparams);
179		tmp_val -= AWG_MAX_ARG;
180	}
181
182	if (timing->blanking_lines > 0) {
183		/* skip blanking lines */
184		val = timing->blanking_level;
185		ret |= awg_generate_instr(RPLSET, val, 0, 0, fwparams);
186
187		val = timing->blanking_lines - 1;
188		ret |= awg_generate_instr(REPLAY, val, 0, 0, fwparams);
189	}
190
191	return ret;
192}
193