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