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
11enum opcode {
12	SET,
13	RPTSET,
14	RPLSET,
15	SKIP,
16	STOP,
17	REPEAT,
18	REPLAY,
19	JUMP,
20	HOLD,
21};
22
23static 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
117int 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