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