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