GNU Linux-libre 4.19.264-gnu1
[releases.git] / drivers / gpu / drm / sti / sti_awg_utils.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) STMicroelectronics SA 2014
4  * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
5  */
6
7 #include "sti_awg_utils.h"
8
9 #define AWG_DELAY (-5)
10
11 #define AWG_OPCODE_OFFSET 10
12 #define AWG_MAX_ARG       0x3ff
13
14 enum opcode {
15         SET,
16         RPTSET,
17         RPLSET,
18         SKIP,
19         STOP,
20         REPEAT,
21         REPLAY,
22         JUMP,
23         HOLD,
24 };
25
26 static 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.
40          *
41          * mux_sel is used in case of SAV/EAV synchronization.
42          */
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;
68                                 break;
69                         }
70
71                         mux = 0;
72                         data_enable = 0;
73                         arg &= AWG_MAX_ARG;
74                         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;
84                         arg &= AWG_MAX_ARG;
85                         break;
86                 case JUMP:
87                         mux = 0;
88                         data_enable = 0;
89                         arg |= 0x40; /* for jump instruction 7th bit is 1 */
90                         arg &= AWG_MAX_ARG;
91                         break;
92                 case STOP:
93                         arg = 0;
94                         break;
95                 case SET:
96                 case RPTSET:
97                 case RPLSET:
98                 case HOLD:
99                         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
118 static int awg_generate_line_signal(
119                 struct awg_code_generation_params *fwparams,
120                 struct awg_timing *timing)
121 {
122         long int val;
123         int ret = 0;
124
125         if (timing->trailing_pixels > 0) {
126                 /* skip trailing pixel */
127                 val = timing->blanking_level;
128                 ret |= awg_generate_instr(RPLSET, val, 0, 0, fwparams);
129
130                 val = timing->trailing_pixels - 1 + AWG_DELAY;
131                 ret |= awg_generate_instr(SKIP, val, 0, 0, fwparams);
132         }
133
134         /* set DE signal high */
135         val = timing->blanking_level;
136         ret |= awg_generate_instr((timing->trailing_pixels > 0) ? SET : RPLSET,
137                         val, 0, 1, fwparams);
138
139         if (timing->blanking_pixels > 0) {
140                 /* skip the number of active pixel */
141                 val = timing->active_pixels - 1;
142                 ret |= awg_generate_instr(SKIP, val, 0, 1, fwparams);
143
144                 /* set DE signal low */
145                 val = timing->blanking_level;
146                 ret |= awg_generate_instr(SET, val, 0, 0, fwparams);
147         }
148
149         return ret;
150 }
151
152 int 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         }
179
180         if (timing->blanking_lines > 0) {
181                 /* skip blanking lines */
182                 val = timing->blanking_level;
183                 ret |= awg_generate_instr(RPLSET, val, 0, 0, fwparams);
184
185                 val = timing->blanking_lines - 1;
186                 ret |= awg_generate_instr(REPLAY, val, 0, 0, fwparams);
187         }
188
189         return ret;
190 }