Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (C) STMicroelectronics SA 2014
0004  * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
0005  */
0006 
0007 #include <drm/drm_print.h>
0008 
0009 #include "sti_awg_utils.h"
0010 
0011 #define AWG_DELAY (-5)
0012 
0013 #define AWG_OPCODE_OFFSET 10
0014 #define AWG_MAX_ARG       0x3ff
0015 
0016 enum opcode {
0017     SET,
0018     RPTSET,
0019     RPLSET,
0020     SKIP,
0021     STOP,
0022     REPEAT,
0023     REPLAY,
0024     JUMP,
0025     HOLD,
0026 };
0027 
0028 static int awg_generate_instr(enum opcode opcode,
0029                   long int arg,
0030                   long int mux_sel,
0031                   long int data_en,
0032                   struct awg_code_generation_params *fwparams)
0033 {
0034     u32 instruction = 0;
0035     u32 mux = (mux_sel << 8) & 0x1ff;
0036     u32 data_enable = (data_en << 9) & 0x2ff;
0037     long int arg_tmp = arg;
0038 
0039     /* skip, repeat and replay arg should not exceed 1023.
0040      * If user wants to exceed this value, the instruction should be
0041      * duplicate and arg should be adjust for each duplicated instruction.
0042      *
0043      * mux_sel is used in case of SAV/EAV synchronization.
0044      */
0045 
0046     while (arg_tmp > 0) {
0047         arg = arg_tmp;
0048         if (fwparams->instruction_offset >= AWG_MAX_INST) {
0049             DRM_ERROR("too many number of instructions\n");
0050             return -EINVAL;
0051         }
0052 
0053         switch (opcode) {
0054         case SKIP:
0055             /* leave 'arg' + 1 pixel elapsing without changing
0056              * output bus */
0057             arg--; /* pixel adjustment */
0058             arg_tmp--;
0059 
0060             if (arg < 0) {
0061                 /* SKIP instruction not needed */
0062                 return 0;
0063             }
0064 
0065             if (arg == 0) {
0066                 /* SKIP 0 not permitted but we want to skip 1
0067                  * pixel. So we transform SKIP into SET
0068                  * instruction */
0069                 opcode = SET;
0070                 break;
0071             }
0072 
0073             mux = 0;
0074             data_enable = 0;
0075             arg &= AWG_MAX_ARG;
0076             break;
0077         case REPEAT:
0078         case REPLAY:
0079             if (arg == 0) {
0080                 /* REPEAT or REPLAY instruction not needed */
0081                 return 0;
0082             }
0083 
0084             mux = 0;
0085             data_enable = 0;
0086             arg &= AWG_MAX_ARG;
0087             break;
0088         case JUMP:
0089             mux = 0;
0090             data_enable = 0;
0091             arg |= 0x40; /* for jump instruction 7th bit is 1 */
0092             arg &= AWG_MAX_ARG;
0093             break;
0094         case STOP:
0095             arg = 0;
0096             break;
0097         case SET:
0098         case RPTSET:
0099         case RPLSET:
0100         case HOLD:
0101             arg &= (0x0ff);
0102             break;
0103         default:
0104             DRM_ERROR("instruction %d does not exist\n", opcode);
0105             return -EINVAL;
0106         }
0107 
0108         arg_tmp = arg_tmp - arg;
0109 
0110         arg = ((arg + mux) + data_enable);
0111 
0112         instruction = ((opcode) << AWG_OPCODE_OFFSET) | arg;
0113         fwparams->ram_code[fwparams->instruction_offset] =
0114             instruction & (0x3fff);
0115         fwparams->instruction_offset++;
0116     }
0117     return 0;
0118 }
0119 
0120 static int awg_generate_line_signal(
0121         struct awg_code_generation_params *fwparams,
0122         struct awg_timing *timing)
0123 {
0124     long int val;
0125     int ret = 0;
0126 
0127     if (timing->trailing_pixels > 0) {
0128         /* skip trailing pixel */
0129         val = timing->blanking_level;
0130         ret |= awg_generate_instr(RPLSET, val, 0, 0, fwparams);
0131 
0132         val = timing->trailing_pixels - 1 + AWG_DELAY;
0133         ret |= awg_generate_instr(SKIP, val, 0, 0, fwparams);
0134     }
0135 
0136     /* set DE signal high */
0137     val = timing->blanking_level;
0138     ret |= awg_generate_instr((timing->trailing_pixels > 0) ? SET : RPLSET,
0139             val, 0, 1, fwparams);
0140 
0141     if (timing->blanking_pixels > 0) {
0142         /* skip the number of active pixel */
0143         val = timing->active_pixels - 1;
0144         ret |= awg_generate_instr(SKIP, val, 0, 1, fwparams);
0145 
0146         /* set DE signal low */
0147         val = timing->blanking_level;
0148         ret |= awg_generate_instr(SET, val, 0, 0, fwparams);
0149     }
0150 
0151     return ret;
0152 }
0153 
0154 int sti_awg_generate_code_data_enable_mode(
0155         struct awg_code_generation_params *fwparams,
0156         struct awg_timing *timing)
0157 {
0158     long int val, tmp_val;
0159     int ret = 0;
0160 
0161     if (timing->trailing_lines > 0) {
0162         /* skip trailing lines */
0163         val = timing->blanking_level;
0164         ret |= awg_generate_instr(RPLSET, val, 0, 0, fwparams);
0165 
0166         val = timing->trailing_lines - 1;
0167         ret |= awg_generate_instr(REPLAY, val, 0, 0, fwparams);
0168     }
0169 
0170     tmp_val = timing->active_lines - 1;
0171 
0172     while (tmp_val > 0) {
0173         /* generate DE signal for each line */
0174         ret |= awg_generate_line_signal(fwparams, timing);
0175         /* replay the sequence as many active lines defined */
0176         ret |= awg_generate_instr(REPLAY,
0177                       min_t(int, AWG_MAX_ARG, tmp_val),
0178                       0, 0, fwparams);
0179         tmp_val -= AWG_MAX_ARG;
0180     }
0181 
0182     if (timing->blanking_lines > 0) {
0183         /* skip blanking lines */
0184         val = timing->blanking_level;
0185         ret |= awg_generate_instr(RPLSET, val, 0, 0, fwparams);
0186 
0187         val = timing->blanking_lines - 1;
0188         ret |= awg_generate_instr(REPLAY, val, 0, 0, fwparams);
0189     }
0190 
0191     return ret;
0192 }