xref: /linux/drivers/gpu/drm/sti/sti_awg_utils.c (revision 95298d63c67673c654c08952672d016212b26054)
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 
16 enum opcode {
17 	SET,
18 	RPTSET,
19 	RPLSET,
20 	SKIP,
21 	STOP,
22 	REPEAT,
23 	REPLAY,
24 	JUMP,
25 	HOLD,
26 };
27 
28 static 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 
120 static 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 
154 int 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