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