blob: 2da7d6866d5de3d566bf8b40211730d3f3fc8f19 [file] [log] [blame]
Benjamin Gaignardf32c4c52014-12-30 15:08:16 +01001/*
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
Bich Hemon4d703772016-03-15 17:11:14 +01009#define AWG_DELAY (-5)
10
Benjamin Gaignardf32c4c52014-12-30 15:08:16 +010011#define AWG_OPCODE_OFFSET 10
Bich Hemonb6bb6792016-02-10 10:56:47 +010012#define AWG_MAX_ARG 0x3ff
Benjamin Gaignardf32c4c52014-12-30 15:08:16 +010013
14enum opcode {
15 SET,
16 RPTSET,
17 RPLSET,
18 SKIP,
19 STOP,
20 REPEAT,
21 REPLAY,
22 JUMP,
23 HOLD,
24};
25
26static int awg_generate_instr(enum opcode opcode,
27 long int arg,
28 long int mux_sel,
29 long int data_en,
30 struct awg_code_generation_params *fwparams)
31{
32 u32 instruction = 0;
33 u32 mux = (mux_sel << 8) & 0x1ff;
34 u32 data_enable = (data_en << 9) & 0x2ff;
35 long int arg_tmp = arg;
36
37 /* skip, repeat and replay arg should not exceed 1023.
38 * If user wants to exceed this value, the instruction should be
39 * duplicate and arg should be adjust for each duplicated instruction.
Bich Hemonbfbaf6312016-01-12 09:28:43 +010040 *
41 * mux_sel is used in case of SAV/EAV synchronization.
Benjamin Gaignardf32c4c52014-12-30 15:08:16 +010042 */
43
44 while (arg_tmp > 0) {
45 arg = arg_tmp;
46 if (fwparams->instruction_offset >= AWG_MAX_INST) {
47 DRM_ERROR("too many number of instructions\n");
48 return -EINVAL;
49 }
50
51 switch (opcode) {
52 case SKIP:
53 /* leave 'arg' + 1 pixel elapsing without changing
54 * output bus */
55 arg--; /* pixel adjustment */
56 arg_tmp--;
57
58 if (arg < 0) {
59 /* SKIP instruction not needed */
60 return 0;
61 }
62
63 if (arg == 0) {
64 /* SKIP 0 not permitted but we want to skip 1
65 * pixel. So we transform SKIP into SET
66 * instruction */
67 opcode = SET;
Benjamin Gaignardf32c4c52014-12-30 15:08:16 +010068 break;
69 }
70
71 mux = 0;
72 data_enable = 0;
Bich Hemonb6bb6792016-02-10 10:56:47 +010073 arg &= AWG_MAX_ARG;
Benjamin Gaignardf32c4c52014-12-30 15:08:16 +010074 break;
75 case REPEAT:
76 case REPLAY:
77 if (arg == 0) {
78 /* REPEAT or REPLAY instruction not needed */
79 return 0;
80 }
81
82 mux = 0;
83 data_enable = 0;
Bich Hemonb6bb6792016-02-10 10:56:47 +010084 arg &= AWG_MAX_ARG;
Benjamin Gaignardf32c4c52014-12-30 15:08:16 +010085 break;
86 case JUMP:
87 mux = 0;
88 data_enable = 0;
89 arg |= 0x40; /* for jump instruction 7th bit is 1 */
Bich Hemonb6bb6792016-02-10 10:56:47 +010090 arg &= AWG_MAX_ARG;
Benjamin Gaignardf32c4c52014-12-30 15:08:16 +010091 break;
92 case STOP:
93 arg = 0;
94 break;
95 case SET:
96 case RPTSET:
97 case RPLSET:
98 case HOLD:
Benjamin Gaignardf32c4c52014-12-30 15:08:16 +010099 arg &= (0x0ff);
100 break;
101 default:
102 DRM_ERROR("instruction %d does not exist\n", opcode);
103 return -EINVAL;
104 }
105
106 arg_tmp = arg_tmp - arg;
107
108 arg = ((arg + mux) + data_enable);
109
110 instruction = ((opcode) << AWG_OPCODE_OFFSET) | arg;
111 fwparams->ram_code[fwparams->instruction_offset] =
112 instruction & (0x3fff);
113 fwparams->instruction_offset++;
114 }
115 return 0;
116}
117
Bich Hemonb6bb6792016-02-10 10:56:47 +0100118static int awg_generate_line_signal(
Benjamin Gaignardf32c4c52014-12-30 15:08:16 +0100119 struct awg_code_generation_params *fwparams,
120 struct awg_timing *timing)
121{
122 long int val;
Benjamin Gaignardf32c4c52014-12-30 15:08:16 +0100123 int ret = 0;
124
Benjamin Gaignardf32c4c52014-12-30 15:08:16 +0100125 if (timing->trailing_pixels > 0) {
126 /* skip trailing pixel */
127 val = timing->blanking_level;
Bich Hemonbfbaf6312016-01-12 09:28:43 +0100128 ret |= awg_generate_instr(RPLSET, val, 0, 0, fwparams);
Benjamin Gaignardf32c4c52014-12-30 15:08:16 +0100129
Bich Hemon4d703772016-03-15 17:11:14 +0100130 val = timing->trailing_pixels - 1 + AWG_DELAY;
Bich Hemonbfbaf6312016-01-12 09:28:43 +0100131 ret |= awg_generate_instr(SKIP, val, 0, 0, fwparams);
Benjamin Gaignardf32c4c52014-12-30 15:08:16 +0100132 }
133
134 /* set DE signal high */
135 val = timing->blanking_level;
Benjamin Gaignardf32c4c52014-12-30 15:08:16 +0100136 ret |= awg_generate_instr((timing->trailing_pixels > 0) ? SET : RPLSET,
Bich Hemonbfbaf6312016-01-12 09:28:43 +0100137 val, 0, 1, fwparams);
Benjamin Gaignardf32c4c52014-12-30 15:08:16 +0100138
139 if (timing->blanking_pixels > 0) {
140 /* skip the number of active pixel */
141 val = timing->active_pixels - 1;
Bich Hemonbfbaf6312016-01-12 09:28:43 +0100142 ret |= awg_generate_instr(SKIP, val, 0, 1, fwparams);
Benjamin Gaignardf32c4c52014-12-30 15:08:16 +0100143
144 /* set DE signal low */
145 val = timing->blanking_level;
Bich Hemonbfbaf6312016-01-12 09:28:43 +0100146 ret |= awg_generate_instr(SET, val, 0, 0, fwparams);
Benjamin Gaignardf32c4c52014-12-30 15:08:16 +0100147 }
148
Bich Hemonb6bb6792016-02-10 10:56:47 +0100149 return ret;
150}
151
152int sti_awg_generate_code_data_enable_mode(
153 struct awg_code_generation_params *fwparams,
154 struct awg_timing *timing)
155{
156 long int val, tmp_val;
157 int ret = 0;
158
159 if (timing->trailing_lines > 0) {
160 /* skip trailing lines */
161 val = timing->blanking_level;
162 ret |= awg_generate_instr(RPLSET, val, 0, 0, fwparams);
163
164 val = timing->trailing_lines - 1;
165 ret |= awg_generate_instr(REPLAY, val, 0, 0, fwparams);
166 }
167
168 tmp_val = timing->active_lines - 1;
169
170 while (tmp_val > 0) {
171 /* generate DE signal for each line */
172 ret |= awg_generate_line_signal(fwparams, timing);
173 /* replay the sequence as many active lines defined */
174 ret |= awg_generate_instr(REPLAY,
175 min_t(int, AWG_MAX_ARG, tmp_val),
176 0, 0, fwparams);
177 tmp_val -= AWG_MAX_ARG;
178 }
Benjamin Gaignardf32c4c52014-12-30 15:08:16 +0100179
180 if (timing->blanking_lines > 0) {
181 /* skip blanking lines */
182 val = timing->blanking_level;
Bich Hemonbfbaf6312016-01-12 09:28:43 +0100183 ret |= awg_generate_instr(RPLSET, val, 0, 0, fwparams);
Benjamin Gaignardf32c4c52014-12-30 15:08:16 +0100184
185 val = timing->blanking_lines - 1;
Bich Hemonbfbaf6312016-01-12 09:28:43 +0100186 ret |= awg_generate_instr(REPLAY, val, 0, 0, fwparams);
Benjamin Gaignardf32c4c52014-12-30 15:08:16 +0100187 }
188
189 return ret;
190}