193 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) STMicroelectronics SA 20144 * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.5 */6 7#include <drm/drm_print.h>8 9#include "sti_awg_utils.h"10 11#define AWG_DELAY (-5)12 13#define AWG_OPCODE_OFFSET 1014#define AWG_MAX_ARG 0x3ff15 16enum opcode {17 SET,18 RPTSET,19 RPLSET,20 SKIP,21 STOP,22 REPEAT,23 REPLAY,24 JUMP,25 HOLD,26};27 28static int awg_generate_instr(enum opcode opcode,29 long int arg,30 long int mux_sel,31 long int data_en,32 struct awg_code_generation_params *fwparams)33{34 u32 instruction = 0;35 u32 mux = (mux_sel << 8) & 0x1ff;36 u32 data_enable = (data_en << 9) & 0x2ff;37 long int arg_tmp = arg;38 39 /* skip, repeat and replay arg should not exceed 1023.40 * If user wants to exceed this value, the instruction should be41 * duplicate and arg should be adjust for each duplicated instruction.42 *43 * mux_sel is used in case of SAV/EAV synchronization.44 */45 46 while (arg_tmp > 0) {47 arg = arg_tmp;48 if (fwparams->instruction_offset >= AWG_MAX_INST) {49 DRM_ERROR("too many number of instructions\n");50 return -EINVAL;51 }52 53 switch (opcode) {54 case SKIP:55 /* leave 'arg' + 1 pixel elapsing without changing56 * output bus */57 arg--; /* pixel adjustment */58 arg_tmp--;59 60 if (arg < 0) {61 /* SKIP instruction not needed */62 return 0;63 }64 65 if (arg == 0) {66 /* SKIP 0 not permitted but we want to skip 167 * pixel. So we transform SKIP into SET68 * instruction */69 opcode = SET;70 break;71 }72 73 mux = 0;74 data_enable = 0;75 arg &= AWG_MAX_ARG;76 break;77 case REPEAT:78 case REPLAY:79 if (arg == 0) {80 /* REPEAT or REPLAY instruction not needed */81 return 0;82 }83 84 mux = 0;85 data_enable = 0;86 arg &= AWG_MAX_ARG;87 break;88 case JUMP:89 mux = 0;90 data_enable = 0;91 arg |= 0x40; /* for jump instruction 7th bit is 1 */92 arg &= AWG_MAX_ARG;93 break;94 case STOP:95 arg = 0;96 break;97 case SET:98 case RPTSET:99 case RPLSET:100 case HOLD:101 arg &= (0x0ff);102 break;103 default:104 DRM_ERROR("instruction %d does not exist\n", opcode);105 return -EINVAL;106 }107 108 arg_tmp = arg_tmp - arg;109 110 arg = ((arg + mux) + data_enable);111 112 instruction = ((opcode) << AWG_OPCODE_OFFSET) | arg;113 fwparams->ram_code[fwparams->instruction_offset] =114 instruction & (0x3fff);115 fwparams->instruction_offset++;116 }117 return 0;118}119 120static int awg_generate_line_signal(121 struct awg_code_generation_params *fwparams,122 struct awg_timing *timing)123{124 long int val;125 int ret = 0;126 127 if (timing->trailing_pixels > 0) {128 /* skip trailing pixel */129 val = timing->blanking_level;130 ret |= awg_generate_instr(RPLSET, val, 0, 0, fwparams);131 132 val = timing->trailing_pixels - 1 + AWG_DELAY;133 ret |= awg_generate_instr(SKIP, val, 0, 0, fwparams);134 }135 136 /* set DE signal high */137 val = timing->blanking_level;138 ret |= awg_generate_instr((timing->trailing_pixels > 0) ? SET : RPLSET,139 val, 0, 1, fwparams);140 141 if (timing->blanking_pixels > 0) {142 /* skip the number of active pixel */143 val = timing->active_pixels - 1;144 ret |= awg_generate_instr(SKIP, val, 0, 1, fwparams);145 146 /* set DE signal low */147 val = timing->blanking_level;148 ret |= awg_generate_instr(SET, val, 0, 0, fwparams);149 }150 151 return ret;152}153 154int sti_awg_generate_code_data_enable_mode(155 struct awg_code_generation_params *fwparams,156 struct awg_timing *timing)157{158 long int val, tmp_val;159 int ret = 0;160 161 if (timing->trailing_lines > 0) {162 /* skip trailing lines */163 val = timing->blanking_level;164 ret |= awg_generate_instr(RPLSET, val, 0, 0, fwparams);165 166 val = timing->trailing_lines - 1;167 ret |= awg_generate_instr(REPLAY, val, 0, 0, fwparams);168 }169 170 tmp_val = timing->active_lines - 1;171 172 while (tmp_val > 0) {173 /* generate DE signal for each line */174 ret |= awg_generate_line_signal(fwparams, timing);175 /* replay the sequence as many active lines defined */176 ret |= awg_generate_instr(REPLAY,177 min_t(int, AWG_MAX_ARG, tmp_val),178 0, 0, fwparams);179 tmp_val -= AWG_MAX_ARG;180 }181 182 if (timing->blanking_lines > 0) {183 /* skip blanking lines */184 val = timing->blanking_level;185 ret |= awg_generate_instr(RPLSET, val, 0, 0, fwparams);186 187 val = timing->blanking_lines - 1;188 ret |= awg_generate_instr(REPLAY, val, 0, 0, fwparams);189 }190 191 return ret;192}193