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