/* * Copyright (C) STMicroelectronics SA 2014 * Author: Vincent Abriou 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; }