193 lines
4.2 KiB
C
193 lines
4.2 KiB
C
|
// SPDX-License-Identifier: GPL-2.0
|
||
|
/*
|
||
|
* Copyright (C) STMicroelectronics SA 2014
|
||
|
* Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
|
||
|
*/
|
||
|
|
||
|
#include <drm/drm_print.h>
|
||
|
|
||
|
#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;
|
||
|
}
|