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