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