| /* | |
| * 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_DELAY (-5) | |
| #define AWG_OPCODE_OFFSET 10 | |
| #define AWG_MAX_ARG 0x3ff | |
| 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. | |
| * | |
| * mux_sel is used in case of SAV/EAV synchronization. | |
| */ | |
| 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 &= AWG_MAX_ARG; | |
| break; | |
| case REPEAT: | |
| case REPLAY: | |
| if (arg == 0) { | |
| /* REPEAT or REPLAY instruction not needed */ | |
| return 0; | |
| } | |
| mux = 0; | |
| data_enable = 0; | |
| arg &= AWG_MAX_ARG; | |
| break; | |
| case JUMP: | |
| mux = 0; | |
| data_enable = 0; | |
| arg |= 0x40; /* for jump instruction 7th bit is 1 */ | |
| arg &= AWG_MAX_ARG; | |
| break; | |
| case STOP: | |
| arg = 0; | |
| break; | |
| case SET: | |
| case RPTSET: | |
| case RPLSET: | |
| case HOLD: | |
| 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; | |
| } | |
| static int awg_generate_line_signal( | |
| struct awg_code_generation_params *fwparams, | |
| struct awg_timing *timing) | |
| { | |
| long int val; | |
| int ret = 0; | |
| if (timing->trailing_pixels > 0) { | |
| /* skip trailing pixel */ | |
| val = timing->blanking_level; | |
| ret |= awg_generate_instr(RPLSET, val, 0, 0, fwparams); | |
| val = timing->trailing_pixels - 1 + AWG_DELAY; | |
| ret |= awg_generate_instr(SKIP, val, 0, 0, fwparams); | |
| } | |
| /* set DE signal high */ | |
| val = timing->blanking_level; | |
| ret |= awg_generate_instr((timing->trailing_pixels > 0) ? SET : RPLSET, | |
| val, 0, 1, fwparams); | |
| if (timing->blanking_pixels > 0) { | |
| /* skip the number of active pixel */ | |
| val = timing->active_pixels - 1; | |
| ret |= awg_generate_instr(SKIP, val, 0, 1, fwparams); | |
| /* set DE signal low */ | |
| val = timing->blanking_level; | |
| ret |= awg_generate_instr(SET, val, 0, 0, fwparams); | |
| } | |
| return ret; | |
| } | |
| int sti_awg_generate_code_data_enable_mode( | |
| struct awg_code_generation_params *fwparams, | |
| struct awg_timing *timing) | |
| { | |
| long int val, tmp_val; | |
| int ret = 0; | |
| if (timing->trailing_lines > 0) { | |
| /* skip trailing lines */ | |
| val = timing->blanking_level; | |
| ret |= awg_generate_instr(RPLSET, val, 0, 0, fwparams); | |
| val = timing->trailing_lines - 1; | |
| ret |= awg_generate_instr(REPLAY, val, 0, 0, fwparams); | |
| } | |
| tmp_val = timing->active_lines - 1; | |
| while (tmp_val > 0) { | |
| /* generate DE signal for each line */ | |
| ret |= awg_generate_line_signal(fwparams, timing); | |
| /* replay the sequence as many active lines defined */ | |
| ret |= awg_generate_instr(REPLAY, | |
| min_t(int, AWG_MAX_ARG, tmp_val), | |
| 0, 0, fwparams); | |
| tmp_val -= AWG_MAX_ARG; | |
| } | |
| if (timing->blanking_lines > 0) { | |
| /* skip blanking lines */ | |
| val = timing->blanking_level; | |
| ret |= awg_generate_instr(RPLSET, val, 0, 0, fwparams); | |
| val = timing->blanking_lines - 1; | |
| ret |= awg_generate_instr(REPLAY, val, 0, 0, fwparams); | |
| } | |
| return ret; | |
| } |