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 <drm/drm_print.h>
   8
   9#include "sti_awg_utils.h"
  10
  11#define AWG_DELAY (-5)
  12
  13#define AWG_OPCODE_OFFSET 10
  14#define AWG_MAX_ARG       0x3ff
  15
  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 be
  41         * 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 changing
  56                         * 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 1
  67                                 * pixel. So we transform SKIP into SET
  68                                 * 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