Video and Image Processing Suite User Guide

ID 683416
Date 4/04/2022
Public
Document Table of Contents

15.9.2. Approach B

Approach B is for motion adaptive configurations where 4K HDR and SDR passthrough is not natively supported.

For these configurations, you can use a pair of switches together with appropriate software control to ensure that any 4K content bypasses the deinterlacer.

Figure 63. Using Pair of Switches with Appropriate Software ControlThe Color Plane Sequencer II, Dual-clock FIFO, and Color Space Converter II instances are necessary to ensure the deinterlacer reaches the fMAX target.

The software control should operate in the following sequence:

  1. Upon start up, or on Clocked Video Input loss of lock, the software places the mixer and the first switch into consume mode.
    Figure 64. Mixer and First Switch in Consume Mode

    In this mode, the frame buffer continues to operate, absorbing any residual frames from the deinterlacer subsystem, and repeating the last valid output frame consumed by the mixer. The mixer produces its background layer. The first switch consumes any frames or fields, or parts of frames or fields from the Clocked Video Input II instance. Therefore, the deinterlacer does not observe any new frames or fields.

  2. The software continues to monitor the status of the Clocked Video Input II instance. If the Clocked Video Input instance continues to detect and produce frames with consistent resolution, the software removes the IP cores from consume mode. The software then sets the two switches according to the resolution reported by the Clocked Video Input instance or for 4K progressive video.
    Figure 65. Without Bypassing the Deinterlacer
    Figure 66. Bypassing the Deinterlacer for 4K Progressive Video

    All Deinterlacer II variants support 1080p passthrough, so progressive resolutions of 1080p or less can either bypass the deinterlacer or not.

Approach B depends on the software control making the necessary switch CSR writes before any video packets reach the switch. The Clocked Video Input instance updates the resolution status registers at the end of the previous frame, so the software loop has a good portion of the vertical blanking interval to configure the video pipe. If little or no blanking is present, the software could insert a 1-line FIFO at the start of the video pipe to allow for the necessary time required. Intel recommends that you place an Avalon-ST Video Stream Cleaner instance after the Clocked Video Input instance.

You can use a software control code similar to the example below for Approach B. In this example, a down-scaler follows the second switch; in lines 66-67 and 91-92 the scaler's status bit detects when all video has been flushed from the pipe. If a scaler is not present, the software may use the status bit for whichever component that follows the second switch for this purpose.

In this code, the following const declarations are assumed:

  • VIP_HDMI_CVI_BASE - Address of the Clocked Video Input II
  • VIP_CLEANER_BASE - Address of the Avalon-ST Video Stream Cleaner
  • VIP_SWI_0_BASE - Address of the first (upstream) Switch II
  • VIP_DIL_BASE - Address of the Deinterlacer II
  • VIP_SWI_1_BASE - Address of the downstream Switch II
  • VIP_SCALE_DOWN_BASE - Address of the down Scaler II
  • VIP_VFB_BASE - Address of the triple-buffering Frame Buffer II
  • VIP_MIXER_BASE - Address of the Mixer II

Software Control Code

void start_cvi (unsigned int base) {
  unsigned int CVI_STAT=0;
  
  IOWR(base, 0, 1); // Starts the IP Core
  CVI_STAT=IORD(base, 1); // Read current status
  if ((CVI_STAT & 0x00000200)!=0) { // check the overflow
    IOWR(base, 1 , 0x00000200); // Reset the Overflow
  }
}

// read the current status of the video input
unsigned int current_cvi_status (unsigned int base) {
  unsigned int current_status;
  current_status = IORD(base, 1);
  return current_status;

  struct image_config {
   int x;
   int y;
   bool interlace;
   color_info image_color;
}

//-- Read the current image_config of the video input
image_config current_cvi_image_config (unsigned int cvi_base_addr) {

  image_config  current_image_config;
  unsigned int  current_status;
  unsigned int  interlaced_input;
  unsigned int  height_f1;
  int           color_pattern_reg;

  current_image_config.y = IORD(cvi_base_addr, 5); //-- Using f0 height only
  current_image_config.x = IORD(cvi_base_addr, 4);

  current_status   = current_cvi_status(cvi_base_addr);
  interlaced_input = ((current_status & 0x80)>>7);

  if (interlaced_input) {
    height_f1              = IORD(cvi_base_addr, 6);
    current_image_config.y = current_image_config.y + height_f1; // height is f0 + f1
  }

  current_image_config.interlace = interlaced_input;

  color_pattern_reg = IORD(cvi_base_addr, 14);
  current_image_config.image_color.space =  color_pattern_reg & 0x00FF;
  current_image_config.image_color.depth = (color_pattern_reg & 0xFF00)>>8;

  if ( current_image_config.image_color.space==Y420_COLOR_SPACE ) {
     current_image_config.x = current_image_config.x << 1; // width is halved for 4:2:0
  }

  return current_image_config;
}

void start_vip_core (unsigned int base) {
  IOWR(base,  0, 1);
}

void configure_dil_out_of_pipe() {
  unsigned int status;
  status = IORD(VIP_SWI_0_BASE, 1);
  status = IORD(VIP_SWI_1_BASE, 1);
  status = IORD(VIP_DIL_BASE, 1);

  IOWR(VIP_SWI_1_BASE,  0, 0); //Stop SWI0
  IOWR(VIP_SWI_0_BASE,  0, 0); //Stop SWI1

  //Wait until STATUS reflects STOPPED in the scaler:
  status = 1;
  while (status == 1) {
     status = IORD(VIP_SCALE_DOWN_BASE, 1);
  }

  IOWR(VIP_SWI_0_BASE,  4, 1); //SWI0 Output 0 control - ON
  IOWR(VIP_SWI_0_BASE,  5, 0); //SWI0 Output 1 control - OFF
  IOWR(VIP_SWI_1_BASE,  4, 1); //SWI1 Output 0 control - outputs from input 0 (passthru) 
  IOWR(VIP_SWI_1_BASE,  0, 1); //Start SWI1
  IOWR(VIP_SWI_0_BASE,  0, 1); //Start SWI0 
  IOWR(VIP_DIL_BASE,    0, 0); //Stop dil   

}

void configure_dil_into_pipe() {
  unsigned int status;  

  IOWR(VIP_SWI_1_BASE,  0, 0); //Stop SWI0
  IOWR(VIP_SWI_0_BASE,  0, 0); //Stop SWI1

  //Wait until STATUS reflects STOPPED:
  status = 1;
  while (status == 1) {
     status = IORD(VIP_SCALE_DOWN_BASE, 1);
  }

  IOWR(VIP_SWI_0_BASE,  4, 0); //SWI0 Output 0 control - OFF
  IOWR(VIP_SWI_0_BASE,  5, 1); //SWI0 Output 1 control - ON
  IOWR(VIP_SWI_1_BASE,  4, 2); //SWI1 Output 0 control - outputs from input 1 (dil) 
  IOWR(VIP_SWI_1_BASE,  0, 1); //Start SWI1
  IOWR(VIP_DIL_BASE,    0, 1); //Start dil      
  IOWR(VIP_SWI_0_BASE,  0, 1); //Start SWI0

}

int main() {
  
  const int NUM_VFB_FRAMES_TO_ABSORB = 10;  //Very safe,could be less
  int       vip_pipe_active          = 0;

  image_config prev_cvi_image_config;
  image_config cvi_image_config;
  image_config output_image_config;
  image_config requested_output_image_config;

  start_vip_core(VIP_HDMI_CVI_BASE);
  start_vip_core(VIP_CLEANER_BASE);  
  start_vip_core(VIP_DIL_BASE);
  start_vip_core(VIP_VFB_BASE);
  start_vip_core(VIP_SCALE_DOWN_BASE);

  configure_dil_out_of_pipe(); 

  int vfb_frame_counter_prev = 0;
  int vfb_frame_counter      = 0;
  int vfb_frame_counter_orig = 0;
  int dil_configured         = 0;
  int cleaned_frames_passed  = 0;

  while(1) {

  // Always keep a frame count:
  vfb_frame_counter_prev = vfb_frame_counter;
  vfb_frame_counter = IORD(VIP_VFB_BASE, 3);
    
  prev_cvi_image_config = cvi_image_config;
  cvi_status       = current_cvi_status(VIP_HDMI_CVI_BASE);
  cvi_image_config = current_cvi_image_config(VIP_HDMI_CVI_BASE);

  if (   ((cvi_status & 0x00000800)==0)
   || (cvi_image_config.x != prev_cvi_image_config.x)
   || (cvi_image_config.y != prev_cvi_image_config.y)
   || (cvi_image_config.x >  UHD_DIM.x)
   || (cvi_image_config.y >  UHD_DIM.y)
   || (cvi_image_config.image_color.space != prev_cvi_image_config.image_color.space)
   ) {
    // The CVI is unlocked or the incoming resolution has changed or resolution 
    // is invalid.  Put mixer's VIP pipe input in to consume so that output keeps
    // running and display is clean.
    if (vip_pipe_active) {
      IOWR(VIP_MIXER_BASE, 10,   2); // input 0 consumed

    // Need to put the first SWI into consume mode too, because we cannot allow any 
    // resolutions >1080p into the dil:
      IOWR(VIP_SWI_0_BASE, 16,   1);        
        
    vfb_frame_counter_orig = IORD(VIP_VFB_BASE, 3);
      }

    vip_pipe_active       = 0;
    cleaned_frames_passed = 0;
    dil_configured        = 0;

    } else if (((cvi_status & 0x00000C00) >> 10) == 3) {// CVI locked with valid resolution 

    if (vip_pipe_active == 0) {

    // Before pipe is started, configure switches:
    if (!dil_configured) {

    cvi_image_config = current_cvi_image_config(VIP_HDMI_CVI_BASE);
    if (cvi_image_config.interlace) {
        configure_dil_into_pipe();
        } else {
        configure_dil_out_of_pipe();
        }

     // It is safe to take the switch out of consume mode now:
        IOWR(VIP_SWI_0_BASE, 16,   0);        

        dil_configured = 1;
        }

    input_is_4k = (cvi_image_config.x==3840)?1:0;
    vip_pipe_active = 1;
          
      }

    if (cleaned_frames_passed == 0) {
    if (vfb_frame_counter != vfb_frame_counter_prev) {
    if ((vfb_frame_counter - vfb_frame_counter_orig) >= NUM_VFB_FRAMES_TO_ABSORB) {
    // Mixer path re-enabled after NUM_VFB_FRAMES_TO_ABSORB frames, as cleaned 
    // frames should now be flushed through
          IOWR(VIP_MIXER_BASE, 10,   1);                  
          cleaned_frames_passed = 1;
          } 
        }
      }

    }

    prev_cvi_image_config = cvi_image_config;

  } //-- while(1)

  return 0;
}