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 } 191