|  | /* | 
|  | * Copyright (C) STMicroelectronics SA 2014 | 
|  | * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics. | 
|  | * License terms:  GNU General Public License (GPL), version 2 | 
|  | */ | 
|  |  | 
|  | #include "sti_awg_utils.h" | 
|  |  | 
|  | #define AWG_OPCODE_OFFSET 10 | 
|  |  | 
|  | enum opcode { | 
|  | SET, | 
|  | RPTSET, | 
|  | RPLSET, | 
|  | SKIP, | 
|  | STOP, | 
|  | REPEAT, | 
|  | REPLAY, | 
|  | JUMP, | 
|  | HOLD, | 
|  | }; | 
|  |  | 
|  | static int awg_generate_instr(enum opcode opcode, | 
|  | long int arg, | 
|  | long int mux_sel, | 
|  | long int data_en, | 
|  | struct awg_code_generation_params *fwparams) | 
|  | { | 
|  | u32 instruction = 0; | 
|  | u32 mux = (mux_sel << 8) & 0x1ff; | 
|  | u32 data_enable = (data_en << 9) & 0x2ff; | 
|  | long int arg_tmp = arg; | 
|  |  | 
|  | /* skip, repeat and replay arg should not exceed 1023. | 
|  | * If user wants to exceed this value, the instruction should be | 
|  | * duplicate and arg should be adjust for each duplicated instruction. | 
|  | */ | 
|  |  | 
|  | while (arg_tmp > 0) { | 
|  | arg = arg_tmp; | 
|  | if (fwparams->instruction_offset >= AWG_MAX_INST) { | 
|  | DRM_ERROR("too many number of instructions\n"); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | switch (opcode) { | 
|  | case SKIP: | 
|  | /* leave 'arg' + 1 pixel elapsing without changing | 
|  | * output bus */ | 
|  | arg--; /* pixel adjustment */ | 
|  | arg_tmp--; | 
|  |  | 
|  | if (arg < 0) { | 
|  | /* SKIP instruction not needed */ | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | if (arg == 0) { | 
|  | /* SKIP 0 not permitted but we want to skip 1 | 
|  | * pixel. So we transform SKIP into SET | 
|  | * instruction */ | 
|  | opcode = SET; | 
|  | break; | 
|  | } | 
|  |  | 
|  | mux = 0; | 
|  | data_enable = 0; | 
|  | arg = (arg << 22) >> 22; | 
|  | arg &= (0x3ff); | 
|  | break; | 
|  | case REPEAT: | 
|  | case REPLAY: | 
|  | if (arg == 0) { | 
|  | /* REPEAT or REPLAY instruction not needed */ | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | mux = 0; | 
|  | data_enable = 0; | 
|  | arg = (arg << 22) >> 22; | 
|  | arg &= (0x3ff); | 
|  | break; | 
|  | case JUMP: | 
|  | mux = 0; | 
|  | data_enable = 0; | 
|  | arg |= 0x40; /* for jump instruction 7th bit is 1 */ | 
|  | arg = (arg << 22) >> 22; | 
|  | arg &= 0x3ff; | 
|  | break; | 
|  | case STOP: | 
|  | arg = 0; | 
|  | break; | 
|  | case SET: | 
|  | case RPTSET: | 
|  | case RPLSET: | 
|  | case HOLD: | 
|  | arg = (arg << 24) >> 24; | 
|  | arg &= (0x0ff); | 
|  | break; | 
|  | default: | 
|  | DRM_ERROR("instruction %d does not exist\n", opcode); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | arg_tmp = arg_tmp - arg; | 
|  |  | 
|  | arg = ((arg + mux) + data_enable); | 
|  |  | 
|  | instruction = ((opcode) << AWG_OPCODE_OFFSET) | arg; | 
|  | fwparams->ram_code[fwparams->instruction_offset] = | 
|  | instruction & (0x3fff); | 
|  | fwparams->instruction_offset++; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int sti_awg_generate_code_data_enable_mode( | 
|  | struct awg_code_generation_params *fwparams, | 
|  | struct awg_timing *timing) | 
|  | { | 
|  | long int val; | 
|  | long int data_en; | 
|  | int ret = 0; | 
|  |  | 
|  | if (timing->trailing_lines > 0) { | 
|  | /* skip trailing lines */ | 
|  | val = timing->blanking_level; | 
|  | data_en = 0; | 
|  | ret |= awg_generate_instr(RPLSET, val, 0, data_en, fwparams); | 
|  |  | 
|  | val = timing->trailing_lines - 1; | 
|  | data_en = 0; | 
|  | ret |= awg_generate_instr(REPLAY, val, 0, data_en, fwparams); | 
|  | } | 
|  |  | 
|  | if (timing->trailing_pixels > 0) { | 
|  | /* skip trailing pixel */ | 
|  | val = timing->blanking_level; | 
|  | data_en = 0; | 
|  | ret |= awg_generate_instr(RPLSET, val, 0, data_en, fwparams); | 
|  |  | 
|  | val = timing->trailing_pixels - 1; | 
|  | data_en = 0; | 
|  | ret |= awg_generate_instr(SKIP, val, 0, data_en, fwparams); | 
|  | } | 
|  |  | 
|  | /* set DE signal high */ | 
|  | val = timing->blanking_level; | 
|  | data_en = 1; | 
|  | ret |= awg_generate_instr((timing->trailing_pixels > 0) ? SET : RPLSET, | 
|  | val, 0, data_en, fwparams); | 
|  |  | 
|  | if (timing->blanking_pixels > 0) { | 
|  | /* skip the number of active pixel */ | 
|  | val = timing->active_pixels - 1; | 
|  | data_en = 1; | 
|  | ret |= awg_generate_instr(SKIP, val, 0, data_en, fwparams); | 
|  |  | 
|  | /* set DE signal low */ | 
|  | val = timing->blanking_level; | 
|  | data_en = 0; | 
|  | ret |= awg_generate_instr(SET, val, 0, data_en, fwparams); | 
|  | } | 
|  |  | 
|  | /* replay the sequence as many active lines defined */ | 
|  | val = timing->active_lines - 1; | 
|  | data_en = 0; | 
|  | ret |= awg_generate_instr(REPLAY, val, 0, data_en, fwparams); | 
|  |  | 
|  | if (timing->blanking_lines > 0) { | 
|  | /* skip blanking lines */ | 
|  | val = timing->blanking_level; | 
|  | data_en = 0; | 
|  | ret |= awg_generate_instr(RPLSET, val, 0, data_en, fwparams); | 
|  |  | 
|  | val = timing->blanking_lines - 1; | 
|  | data_en = 0; | 
|  | ret |= awg_generate_instr(REPLAY, val, 0, data_en, fwparams); | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } |