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