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