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