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