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 11enum opcode { 12 SET, 13 RPTSET, 14 RPLSET, 15 SKIP, 16 STOP, 17 REPEAT, 18 REPLAY, 19 JUMP, 20 HOLD, 21}; 22 23static 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 &= (0x3ff); 69 break; 70 case REPEAT: 71 case REPLAY: 72 if (arg == 0) { 73 /* REPEAT or REPLAY instruction not needed */ 74 return 0; 75 } 76 77 mux = 0; 78 data_enable = 0; 79 arg &= (0x3ff); 80 break; 81 case JUMP: 82 mux = 0; 83 data_enable = 0; 84 arg |= 0x40; /* for jump instruction 7th bit is 1 */ 85 arg &= 0x3ff; 86 break; 87 case STOP: 88 arg = 0; 89 break; 90 case SET: 91 case RPTSET: 92 case RPLSET: 93 case HOLD: 94 arg &= (0x0ff); 95 break; 96 default: 97 DRM_ERROR("instruction %d does not exist\n", opcode); 98 return -EINVAL; 99 } 100 101 arg_tmp = arg_tmp - arg; 102 103 arg = ((arg + mux) + data_enable); 104 105 instruction = ((opcode) << AWG_OPCODE_OFFSET) | arg; 106 fwparams->ram_code[fwparams->instruction_offset] = 107 instruction & (0x3fff); 108 fwparams->instruction_offset++; 109 } 110 return 0; 111} 112 113int sti_awg_generate_code_data_enable_mode( 114 struct awg_code_generation_params *fwparams, 115 struct awg_timing *timing) 116{ 117 long int val; 118 long int data_en; 119 int ret = 0; 120 121 if (timing->trailing_lines > 0) { 122 /* skip trailing lines */ 123 val = timing->blanking_level; 124 data_en = 0; 125 ret |= awg_generate_instr(RPLSET, val, 0, data_en, fwparams); 126 127 val = timing->trailing_lines - 1; 128 data_en = 0; 129 ret |= awg_generate_instr(REPLAY, val, 0, data_en, fwparams); 130 } 131 132 if (timing->trailing_pixels > 0) { 133 /* skip trailing pixel */ 134 val = timing->blanking_level; 135 data_en = 0; 136 ret |= awg_generate_instr(RPLSET, val, 0, data_en, fwparams); 137 138 val = timing->trailing_pixels - 1; 139 data_en = 0; 140 ret |= awg_generate_instr(SKIP, val, 0, data_en, fwparams); 141 } 142 143 /* set DE signal high */ 144 val = timing->blanking_level; 145 data_en = 1; 146 ret |= awg_generate_instr((timing->trailing_pixels > 0) ? SET : RPLSET, 147 val, 0, data_en, fwparams); 148 149 if (timing->blanking_pixels > 0) { 150 /* skip the number of active pixel */ 151 val = timing->active_pixels - 1; 152 data_en = 1; 153 ret |= awg_generate_instr(SKIP, val, 0, data_en, fwparams); 154 155 /* set DE signal low */ 156 val = timing->blanking_level; 157 data_en = 0; 158 ret |= awg_generate_instr(SET, val, 0, data_en, fwparams); 159 } 160 161 /* replay the sequence as many active lines defined */ 162 val = timing->active_lines - 1; 163 data_en = 0; 164 ret |= awg_generate_instr(REPLAY, val, 0, data_en, fwparams); 165 166 if (timing->blanking_lines > 0) { 167 /* skip blanking lines */ 168 val = timing->blanking_level; 169 data_en = 0; 170 ret |= awg_generate_instr(RPLSET, val, 0, data_en, fwparams); 171 172 val = timing->blanking_lines - 1; 173 data_en = 0; 174 ret |= awg_generate_instr(REPLAY, val, 0, data_en, fwparams); 175 } 176 177 return ret; 178} 179