Home記事一覧フォーラム

AMD (Xilinx) Kria KV260, KR260用 Live Audio & Live Video サンプル・プロジェクト

AMD (Xilinx) Kria KV260, KR260のLive Audio機能を使ってFPGA側から映像、音声信号を生成してHDMI出力するサンプル・プロジェクトです。

Vivado / Vitis Ver.2023.2以降のVitis Unified IDEが対象です。

ダウンロード: Live_Audio_KV260_KR260_Template.tar.gz

実行方法については、AMD (Xilinx) Kria KV260, KR260用 Vitis Unified IDE Stand alone (Bare metal) テンプレート・プロジェクトを参照してください。(ただし、このプロジェクトではLEDの接続は不要です。)

ソースコード

vivado.tcl : Vivadoプロジェクトを生成するスクリプト(ライセンス:パブリックドメイン)
set board_type kv260
set rtl_top_name rtl_top
set rtl_files {rtl_top.v cdc_fifo.v cdc_synchronizer.v dual_clk_ram.v shift_register.v shift_register_vector.v shift_register_factory.v vga_iface.v xlive_audio.v}
set pin_xdc_file {pins.xdc}
set timing_xdc_file {timings.xdc}
set project_name project_1
set project_dir project_1
set design_name design_1
set ps_ip xilinx.com:ip:zynq_ultra_ps_e
set ps_name zynq_ultra_ps_e_0
set init_rule xilinx.com:bd_rule:zynq_ultra_ps_e
set rtl_top_instance ${rtl_top_name}_0

if { $board_type eq "kr260" } {
    set board_parts [get_board_parts "*:kr260_som:*" -latest_file_version]
    set som_connection {som240_1_connector xilinx.com:kr260_carrier:som240_1_connector:1.0 som240_2_connector xilinx.com:kr260_carrier:som240_2_connector:1.0}
} else {
    set board_parts [get_board_parts "*:kv260_som:*" -latest_file_version]
    set som_connection {som240_1_connector xilinx.com:kv260_carrier:som240_1_connector:1.3}
}

# create project
create_project -name $project_name -force -dir $project_dir -part [get_property PART_NAME $board_parts]

set_property board_part $board_parts [current_project]

add_files -fileset constrs_1 -norecurse $pin_xdc_file

add_files -fileset constrs_1 -norecurse $timing_xdc_file
set_property used_in_synthesis false [get_files $timing_xdc_file]

add_files -fileset sources_1 -norecurse $rtl_files

set_property board_connections $som_connection [current_project]

create_bd_design $design_name

current_bd_design $design_name

set top_instance [get_bd_cells /]

current_bd_instance $top_instance

# config bd
create_bd_cell -type ip -vlnv $ps_ip $ps_name

apply_bd_automation -rule $init_rule -config {apply_board_preset "1"} [get_bd_cells $ps_name]

set_property -dict [list \
CONFIG.PSU__USE__M_AXI_GP0  {0} \
CONFIG.PSU__USE__M_AXI_GP1  {0} \
CONFIG.PSU__USE__IRQ0 {0} \
CONFIG.PSU__FPGA_PL1_ENABLE {0} \
CONFIG.PSU__USE__AUDIO {1} \
CONFIG.PSU__USE__VIDEO {1} \
] [get_bd_cells $ps_name]

create_bd_cell -type ip -vlnv xilinx.com:ip:clk_wiz:6.0 clk_wiz_0

set_property -dict [list \
CONFIG.PRIM_SOURCE {Global_buffer} \
CONFIG.RESET_TYPE {ACTIVE_LOW} \
] [get_bd_cells clk_wiz_0]

create_bd_cell -type ip -vlnv xilinx.com:ip:clk_wiz:6.0 clk_wiz_1

set_property -dict [list \
CONFIG.CLKOUT1_REQUESTED_OUT_FREQ {22.5792} \
CONFIG.PRIM_SOURCE {Global_buffer} \
CONFIG.RESET_TYPE {ACTIVE_LOW} \
CONFIG.USE_LOCKED {false} \
] [get_bd_cells clk_wiz_1]

create_bd_cell -type module -reference $rtl_top_name $rtl_top_instance


# port connection
connect_bd_net [get_bd_pins ${ps_name}/pl_clk0] [get_bd_pins clk_wiz_0/clk_in1] [get_bd_pins clk_wiz_1/clk_in1]

connect_bd_net [get_bd_pins ${ps_name}/pl_resetn0] [get_bd_pins clk_wiz_0/resetn] [get_bd_pins clk_wiz_1/resetn]

connect_bd_net [get_bd_pins ${rtl_top_instance}/clk] [get_bd_pins clk_wiz_0/clk_out1]

connect_bd_net [get_bd_pins ${rtl_top_instance}/resetn] [get_bd_pins clk_wiz_0/locked]

connect_bd_net [get_bd_pins ${rtl_top_instance}/clkv] [get_bd_pins ${ps_name}/dp_video_in_clk] [get_bd_pins ${ps_name}/dp_video_ref_clk]

connect_bd_net [get_bd_pins ${rtl_top_instance}/audio_data] [get_bd_pins ${ps_name}/dp_s_axis_audio_tdata]

connect_bd_net [get_bd_pins ${rtl_top_instance}/audio_id] [get_bd_pins ${ps_name}/dp_s_axis_audio_tid]

connect_bd_net [get_bd_pins ${rtl_top_instance}/audio_valid] [get_bd_pins ${ps_name}/dp_s_axis_audio_tvalid]

connect_bd_net [get_bd_pins ${rtl_top_instance}/video_color] [get_bd_pins ${ps_name}/dp_live_video_in_pixel1]

connect_bd_net [get_bd_pins ${rtl_top_instance}/video_de] [get_bd_pins ${ps_name}/dp_live_video_in_de]

connect_bd_net [get_bd_pins ${rtl_top_instance}/video_hsyncn] [get_bd_pins ${ps_name}/dp_live_video_in_hsync]

connect_bd_net [get_bd_pins ${rtl_top_instance}/video_vsyncn] [get_bd_pins ${ps_name}/dp_live_video_in_vsync]

connect_bd_net [get_bd_pins ${rtl_top_instance}/audio_ready] [get_bd_pins ${ps_name}/dp_s_axis_audio_tready]

connect_bd_net [get_bd_pins clk_wiz_1/clk_out1] [get_bd_pins ${rtl_top_instance}/clka] [get_bd_pins ${ps_name}/dp_s_axis_audio_clk]


# make wrapper
current_bd_instance $top_instance

make_wrapper -files [get_files $project_dir/${project_name}.srcs/sources_1/bd/$design_name/${design_name}.bd] -top

add_files -norecurse $project_dir/${project_name}.gen/sources_1/bd/$design_name/hdl/${design_name}_wrapper.v

set_property top ${design_name}_wrapper [current_fileset]

update_compile_order -fileset sources_1

validate_bd_design

regenerate_bd_layout

save_bd_design

rtl_top.v : RTLトップモジュール。ノコギリ波の音声信号とカラーバーの映像信号の生成など。
/*
  Copyright (c) 2023, miya
  All rights reserved.

  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/


module rtl_top
  #(
    parameter SAMPLING_RATE = 44100
    )
  (
   input wire         clk,
   input wire         clkv,
   input wire         clka,
   output wire        video_de,
   output wire        video_hsyncn,
   output wire        video_vsyncn,
   output wire [35:0] video_color,
   output wire [31:0] audio_data,
   output wire        audio_id,
   output wire        audio_valid,
   input wire         audio_ready,
   input wire         resetn
   );

  localparam ZERO = 1'd0;
  localparam ONE = 1'd1;
  localparam TRUE = 1'b1;
  localparam FALSE = 1'b0;
  localparam AUDIO_WIDTH = 16;
  localparam BUFFER_DEPTH = 4;

  // reset
  wire reset;
  wire resetv;
  wire reseta;
  wire resetp;

  assign resetp = ~resetn;

  // synchronize reset signal
  shift_register
    #(
      .DELAY (3)
      )
  shift_register_reset
    (
     .clk (clk),
     .din (resetp),
     .dout (reset)
     );

  shift_register
    #(
      .DELAY (3)
      )
  shift_register_reseta
    (
     .clk (clka),
     .din (resetp),
     .dout (reseta)
     );

  shift_register
    #(
      .DELAY (3)
      )
  shift_register_resetv
    (
     .clk (clkv),
     .din (resetp),
     .dout (resetv)
     );

  // graphics
  wire        video_hsync;
  wire        video_vsync;
  wire [23:0] vga_color_in;
  wire [23:0] vga_color_out;
  reg [7:0]   color_r;
  reg [7:0]   color_g;
  reg [7:0]   color_b;
  wire [10:0] count_h;
  wire [10:0] count_v;
  reg [10:0]  count_line;
  wire [10:0] count_line_v;
  wire        vsync;
  reg         prev_vsync;

  assign video_hsyncn = ~video_hsync;
  assign video_vsyncn = ~video_vsync;
  assign video_color = {vga_color_out[7:0], {4{vga_color_out[0]}}, vga_color_out[23:16], {4{vga_color_out[16]}}, vga_color_out[15:8], {4{vga_color_out[8]}}};

  always @(posedge clk)
    begin
      prev_vsync <= vsync;
    end

  // move white line
  always @(posedge clk)
    begin
      if (reset == TRUE)
        begin
          count_line <= ZERO;
        end
      else
        begin
          if ((vsync == TRUE) && (prev_vsync == FALSE))
            begin
              if (count_line < 1279)
                begin
                  count_line <= count_line + ONE;
                end
              else
                begin
                  count_line <= ZERO;
                end
            end
        end
    end

  cdc_synchronizer
    #(
      .DATA_WIDTH (11)
      )
  cdc_synchronizer_count_line_v
    (
     .data_in (count_line),
     .data_out (count_line_v),
     .clk (clkv),
     .reset (resetv)
     );

  // draw color bar
  always @(posedge clkv)
    begin
      if (count_line_v == count_h)
        begin
          // draw white line
          color_r = 255;
          color_g = 255;
          color_b = 255;
        end
      else if (count_h < 256)
        begin
          color_r <= count_h;
          color_g <= ZERO;
          color_b <= ZERO;
        end
      else if ((count_h > 511) && (count_h < 768))
        begin
          color_r <= ZERO;
          color_g <= count_h - 512;
          color_b <= ZERO;
        end
      else if (count_h > 1023)
        begin
          color_r <= ZERO;
          color_g <= ZERO;
          color_b <= count_h - 1024;
        end
      else
        begin
          color_r <= ZERO;
          color_g <= ZERO;
          color_b <= ZERO;
        end
    end

  assign vga_color_in = {color_r, color_g, color_b};

  vga_iface
    #(
      .PIXEL_DELAY(2),
      .BPP (24)
      )
  vga_iface_0
    (
     .clk (clk),
     .reset (reset),
     .vsync (vsync),
     .vcount (vcount),
     .ext_clkv (clkv),
     .ext_resetv (resetv),
     .ext_color_in (vga_color_in),
     .ext_vga_hs (video_hsync),
     .ext_vga_vs (video_vsync),
     .ext_vga_de (video_de),
     .ext_vga_color_out (vga_color_out),
     .ext_count_h (count_h),
     .ext_count_v (count_v)
     );

  // audio test signal
  reg signed [AUDIO_WIDTH-1:0] sample_l;
  reg signed [AUDIO_WIDTH-1:0] sample_r;
  wire [AUDIO_WIDTH*2-1:0]     sample_data;
  wire                         sample_full;
  reg                          sample_en;
  localparam DECL = (1 << AUDIO_WIDTH) * 440 / SAMPLING_RATE;
  localparam DECR = (1 << AUDIO_WIDTH) * 660 / SAMPLING_RATE;

  assign sample_data = {sample_l, sample_r};

  always @(posedge clk)
    begin
      if (reseta == TRUE)
        begin
          sample_en <= FALSE;
          sample_l <= ZERO;
          sample_r <= ZERO;
        end
      else
        begin
          if (sample_full == FALSE)
            begin
              sample_en <= TRUE;
              sample_l <= sample_l - DECL;
              sample_r <= sample_r - DECR;
            end
          else
            begin
              sample_en <= FALSE;
            end
        end
    end

  xlive_audio
    #(
      .AUDIO_WIDTH (AUDIO_WIDTH),
      .BUFFER_DEPTH (BUFFER_DEPTH)
      )
  xlive_audio_0
    (
     .clk_tx (clka),
     .clk_rx (clk),
     .reset_tx (reseta),
     .reset_rx (reset),
     .data_rx (sample_data),
     .en_rx (sample_en),
     .full_rx (sample_full),
     .data_tx (audio_data),
     .valid_tx (audio_valid),
     .id_tx (audio_id),
     .ready_tx (audio_ready)
     );

endmodule

xlive_audio.v : Live Audio機能の制御等
/*
  Copyright (c) 2023, miya
  All rights reserved.

  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/


module xlive_audio
  #(
    parameter AUDIO_WIDTH = 16,
    parameter BUFFER_DEPTH = 4
    )
  (
   input wire                     clk_tx,
   input wire                     clk_rx,
   input wire                     reset_tx,
   input wire                     reset_rx,
   input wire [AUDIO_WIDTH*2-1:0] data_rx,
   input wire                     en_rx,
   output wire                    full_rx,
   output reg [31:0]              data_tx,
   output reg                     valid_tx,
   output reg                     id_tx,
   input wire                     ready_tx
   );

  localparam ZERO = 1'd0;
  localparam ONE = 1'd1;
  localparam TRUE = 1'b1;
  localparam FALSE = 1'b0;

  wire        audio_valid;
  wire        audio_empty;
  reg [9:0]  frame_counter;
  wire [7:0] cs_addr;
  wire        cs_data;
  reg        start;
  reg        busy;
  wire        completed;
  wire [8:0] start_d;
  wire        fc_zero;
  wire        fc_odd;
  reg        audio_req;
  reg [26:0] audio_data1;
  reg [3:0]  preamble;
  reg        parity;
  wire signed [AUDIO_WIDTH*2-1:0] audio_data;
  reg [AUDIO_WIDTH*2-1:0]     audio_data0;

  assign completed = valid_tx & ready_tx;

  always @(posedge clk_tx)
    begin
      if (reset_tx == TRUE)
        begin
          start <= FALSE;
          busy <= FALSE;
        end
      else
        begin
          if (busy == FALSE)
            begin
              start <= TRUE;
              busy <= TRUE;
            end
          else
            begin
              start <= FALSE;
              if (completed == TRUE)
                begin
                  busy <= FALSE;
                end
            end
        end
    end

  always @(posedge clk_tx)
    begin
      if (reset_tx == TRUE)
        begin
          frame_counter <= 0;
        end
      else
        begin
          if (completed == TRUE)
            begin
              if (frame_counter == 383)
                begin
                  frame_counter <= 0;
                end
              else
                begin
                  frame_counter <= frame_counter + 1;
                end
            end
        end
    end

  assign fc_zero = (frame_counter == ZERO) ? TRUE : FALSE;

  assign fc_odd = frame_counter[0];

  always @(posedge clk_tx)
    begin
      if ((start == TRUE) && (fc_odd == FALSE))
        begin
          audio_req <= TRUE;
        end
      else
        begin
          audio_req <= FALSE;
        end
    end

  always @(posedge clk_tx)
    begin
      if (reset_tx == TRUE)
        begin
          audio_data0 <= ZERO;
        end
      else
        begin
          if (audio_valid == TRUE)
            begin
              audio_data0 <= audio_data;
            end
        end
    end

  always @(posedge clk_tx)
    begin
      if (fc_odd == FALSE)
        begin
          audio_data1 <= {cs_data, 2'b0, audio_data0[AUDIO_WIDTH*2-1:AUDIO_WIDTH], {(24-AUDIO_WIDTH){1'b0}}};
        end
      else
        begin
          audio_data1 <= {cs_data, 2'b0, audio_data0[AUDIO_WIDTH-1:0], {(24-AUDIO_WIDTH){1'b0}}};
        end
    end

  always @(posedge clk_tx)
    begin
      if (fc_zero)
        begin
          preamble <= 4'b0001;
        end
      else
        begin
          if (fc_odd == FALSE)
            begin
              preamble <= 4'b0010;
            end
          else
            begin
              preamble <= 4'b0011;
            end
        end
    end

  always @(posedge clk_tx)
    begin
      if (start_d[6] == TRUE)
        begin
          data_tx <= {parity, audio_data1, preamble};
          id_tx <= fc_odd;
        end
    end

  always @(posedge clk_tx)
    begin
      if (reset_tx == TRUE)
        begin
          valid_tx <= FALSE;
        end
      else
        begin
          if (start_d[8] == TRUE)
            begin
              valid_tx <= TRUE;
            end
          else
            begin
              if (completed == TRUE)
                begin
                  valid_tx <= FALSE;
                end
            end
        end
    end

  always @(posedge clk_tx)
    begin
      parity <= ^audio_data1;
    end

  assign cs_addr = frame_counter[9:1];

  cdc_fifo
    #(
      .DATA_WIDTH (AUDIO_WIDTH * 2),
      .ADDR_WIDTH (BUFFER_DEPTH),
      .MAX_ITEMS ((1 << BUFFER_DEPTH) - 8)
      )
  cdc_fifo_0
    (
     .clk_cr (clk_tx),
     .reset_cr (reset_tx),
     .data_cr (audio_data),
     .req_cr (audio_req),
     .empty_cr (audio_empty),
     .valid_cr (audio_valid),
     .clk_cw (clk_rx),
     .reset_cw (reset_rx),
     .data_cw (data_rx),
     .we_cw (en_rx),
     .almost_full_cw (full_rx)
     );

  channel_status_data channel_status_data_0
    (
     .clk (clk_tx),
     .addr (cs_addr),
     .data (cs_data)
     );

  shift_register_factory
    #(
      .DEPTH (9)
      )
  shift_register_factory_start_d
    (
     .clk (clk_tx),
     .din (start),
     .sreg (start_d)
     );

endmodule

module channel_status_data
  (
   input wire       clk,
   input wire [7:0] addr,
   output reg       data
   );

  reg ram [0:63];

  always @(posedge clk)
    begin
      if (addr[7:6] == 2'b00)
        begin
          data <= ram[addr];
        end
      else
        begin
          data <= 1'b0;
        end
    end

  initial
    begin
      ram[8'h00] = 1'b1; // AES:1 S/PDIF:0
      ram[8'h01] = 1'b0; // PCM:0 other:1
      ram[8'h02] = 1'b1; // NO emphasis 100
      ram[8'h03] = 1'b0;
      ram[8'h04] = 1'b0;
      ram[8'h05] = 1'b0; // Source sampling frequency locked:0 unlocked:1
      ram[8'h06] = 1'b1; // Sampling frequency auto:00 48k:01 44k:10 32k:11
      ram[8'h07] = 1'b0;

      ram[8'h08] = 1'b0; // Stereophonic mode 0100
      ram[8'h09] = 1'b1;
      ram[8'h0a] = 1'b0;
      ram[8'h0b] = 1'b0;
      ram[8'h0c] = 1'b0; // 0000: No user information 0001:192bit user bits
      ram[8'h0d] = 1'b0;
      ram[8'h0e] = 1'b0;
      ram[8'h0f] = 1'b0;

      ram[8'h10] = 1'b0; // Sample bits 16bits:000100 24bits:001101
      ram[8'h11] = 1'b0;
      ram[8'h12] = 1'b0;
      ram[8'h13] = 1'b1;
      ram[8'h14] = 1'b0;
      ram[8'h15] = 1'b0;
      ram[8'h16] = 1'b0; // Alignment level not indicated:00
      ram[8'h17] = 1'b0;

      ram[8'h18] = 1'b0;
      ram[8'h19] = 1'b0;
      ram[8'h1a] = 1'b0;
      ram[8'h1b] = 1'b0;
      ram[8'h1c] = 1'b0;
      ram[8'h1d] = 1'b0;
      ram[8'h1e] = 1'b0;
      ram[8'h1f] = 1'b0;

      ram[8'h20] = 1'b0;
      ram[8'h21] = 1'b0;
      ram[8'h22] = 1'b0;
      ram[8'h23] = 1'b0;
      ram[8'h24] = 1'b0;
      ram[8'h25] = 1'b0;
      ram[8'h26] = 1'b0;
      ram[8'h27] = 1'b0;

      ram[8'h28] = 1'b0;
      ram[8'h29] = 1'b0;
      ram[8'h2a] = 1'b0;
      ram[8'h2b] = 1'b0;
      ram[8'h2c] = 1'b0;
      ram[8'h2d] = 1'b0;
      ram[8'h2e] = 1'b0;
      ram[8'h2f] = 1'b0;

      ram[8'h30] = 1'b0;
      ram[8'h31] = 1'b0;
      ram[8'h32] = 1'b0;
      ram[8'h33] = 1'b0;
      ram[8'h34] = 1'b0;
      ram[8'h35] = 1'b0;
      ram[8'h36] = 1'b0;
      ram[8'h37] = 1'b0;

      ram[8'h38] = 1'b0;
      ram[8'h39] = 1'b0;
      ram[8'h3a] = 1'b0;
      ram[8'h3b] = 1'b0;
      ram[8'h3c] = 1'b0;
      ram[8'h3d] = 1'b0;
      ram[8'h3e] = 1'b0;
      ram[8'h3f] = 1'b0;
    end

endmodule

xdpdma_video_example.c : AMDのDPDMA ExampleプログラムをLive Audioを使用する形に修正したもの。(ライセンス:MIT License)
/*******************************************************************************
* Copyright (C) 2017 - 2022 Xilinx, Inc.  All rights reserved.
* Copyright (C) 2022 - 2023 Advanced Micro Devices, Inc.  All rights reserved.
* SPDX-License-Identifier: MIT
*******************************************************************************/


/*****************************************************************************/
/**
*
* @file xdpdma_video_example.c
*
*
* This file contains a design example using the DPDMA driver (XDpDma)
* This example demonstrates the use of DPDMA for displaying a Graphics Overlay
*
* @note
*
* None.
*
* <pre>
* MODIFICATION HISTORY:
*
* Ver   Who Date     Changes
* ----- --- -------- -----------------------------------------------
* 1.0  aad 10/19/17  Initial Release
* 1.1  aad 02/22/18  Fixed the header
*</pre>
*
******************************************************************************/


/* 2023/12/14 Modified by miya */

/***************************** Include Files *********************************/

#include "xil_exception.h"
#include "xil_printf.h"
#include "xil_cache.h"
#include "xdpdma_video_example.h"

/************************** Constant Definitions *****************************/
#ifndef SDT
#define DPPSU_DEVICE_ID    XPAR_PSU_DP_DEVICE_ID
#define AVBUF_DEVICE_ID    XPAR_PSU_DP_DEVICE_ID
#define DPDMA_DEVICE_ID    XPAR_XDPDMA_0_DEVICE_ID
#define INTC_DEVICE_ID    XPAR_SCUGIC_0_DEVICE_ID
#define DPPSU_INTR_ID    151
#define DPDMA_INTR_ID    154

#define DPPSU_BASEADDR    XPAR_PSU_DP_BASEADDR
#define AVBUF_BASEADDR    XPAR_PSU_DP_BASEADDR
#define DPDMA_BASEADDR    XPAR_PSU_DPDMA_BASEADDR
#else
#define DPPSU_BASEADDR    XPAR_XDPPSU_0_BASEADDR
#define AVBUF_BASEADDR    XPAR_XDPPSU_0_BASEADDR
#define DPDMA_BASEADDR    XPAR_XDPDMA_0_BASEADDR
#define INTC_BASEADDR    XPAR_XSCUGIC_0_BASEADDR
#endif

#define BUFFERSIZE      1280 * 720 * 4    /* HTotal * VTotal * BPP */
#define LINESIZE      1280 * 4      /* HTotal * BPP */
#define STRIDE        LINESIZE      /* The stride value should
                          be aligned to 256*/


/************************** Variable Declarations ***************************/
XDpPsu DpPsu;
XAVBuf AVBuf;
XScuGic Intr;
Run_Config RunCfg;

/**************************** Type Definitions *******************************/

/*****************************************************************************/
/**
*
* Main function to call the DPDMA Video example.
*
* @param  None
*
* @return  XST_SUCCESS if successful, otherwise XST_FAILURE.
*
* @note    None
*
******************************************************************************/

int main()
{
  int Status;

  Xil_DCacheDisable();
  Xil_ICacheDisable();

  xil_printf("DPDMA Generic Video Example Test \r\n");
  Status = DpdmaVideoExample(&RunCfg);
  if (Status != XST_SUCCESS) {
      xil_printf("DPDMA Video Example Test Failed\r\n");
      return XST_FAILURE;
  }

  xil_printf("Successfully ran DPDMA Video Example Test\r\n");

  return XST_SUCCESS;
}

/*****************************************************************************/
/**
*
* The purpose of this function is to illustrate how to use the XDpDma device
* driver in Graphics overlay mode.
*
* @param  RunCfgPtr is a pointer to the application configuration structure.
*
* @return  XST_SUCCESS if successful, else XST_FAILURE.
*
* @note    None.
*
*****************************************************************************/

int DpdmaVideoExample(Run_Config *RunCfgPtr)

{
  u32 Status;
  /* Initialize the application configuration */
  InitRunConfig(RunCfgPtr);
  Status = InitDpDmaSubsystem(RunCfgPtr);
  if (Status != XST_SUCCESS) {
        return XST_FAILURE;
  }

  SetupInterrupts(RunCfgPtr);

  for (int i = 0; i < 2; i++)
  {
    sleep(1);
    InitDpDmaSubsystem(RunCfgPtr);
  }

  return XST_SUCCESS;
}

/*****************************************************************************/
/**
*
* The purpose of this function is to initialize the application configuration.
*
* @param  RunCfgPtr is a pointer to the application configuration structure.
*
* @return  None.
*
* @note    None.
*
*****************************************************************************/

void InitRunConfig(Run_Config *RunCfgPtr)
{
  /* Initial configuration parameters. */
  RunCfgPtr->DpPsuPtr       = &DpPsu;
  RunCfgPtr->IntrPtr       = &Intr;
  RunCfgPtr->AVBufPtr      = &AVBuf;
  RunCfgPtr->VideoMode     = XVIDC_VM_1280x720_60_P;
  RunCfgPtr->Bpc         = XVIDC_BPC_8;
  RunCfgPtr->ColorEncode    = XDPPSU_CENC_RGB;
  RunCfgPtr->UseMaxCfgCaps  = 0;
  RunCfgPtr->LaneCount    = LANE_COUNT_1;
  RunCfgPtr->LinkRate      = LINK_RATE_270GBPS;
  RunCfgPtr->EnSynchClkMode  = 0;
  RunCfgPtr->UseMaxLaneCount  = 0;
  RunCfgPtr->UseMaxLinkRate  = 0;
}

/*****************************************************************************/
/**
*
* The purpose of this function is to initialize the DP Subsystem (XDpDma,
* XAVBuf, XDpPsu)
*
* @param  RunCfgPtr is a pointer to the application configuration structure.
*
* @return  None.
*
* @note    None.
*
*****************************************************************************/

int InitDpDmaSubsystem(Run_Config *RunCfgPtr)
{
  u32 Status;
  XDpPsu    *DpPsuPtr = RunCfgPtr->DpPsuPtr;
  XDpPsu_Config  *DpPsuCfgPtr;
  XAVBuf    *AVBufPtr = RunCfgPtr->AVBufPtr;

  /* Initialize DisplayPort driver. */
#ifndef SDT
  DpPsuCfgPtr = XDpPsu_LookupConfig(DPPSU_DEVICE_ID);
#else
  DpPsuCfgPtr = XDpPsu_LookupConfig(DPPSU_BASEADDR);
#endif
  XDpPsu_CfgInitialize(DpPsuPtr, DpPsuCfgPtr, DpPsuCfgPtr->BaseAddr);
  /* Initialize Video Pipeline driver */
#ifndef SDT
  XAVBuf_CfgInitialize(AVBufPtr, DpPsuPtr->Config.BaseAddr, AVBUF_DEVICE_ID);
#else
  XAVBuf_CfgInitialize(AVBufPtr, DpPsuPtr->Config.BaseAddr);
#endif

  /* Initialize the DisplayPort TX core. */
  Status = XDpPsu_InitializeTx(DpPsuPtr);
  if (Status != XST_SUCCESS) {
    return XST_FAILURE;
  }
  /* Set the format graphics frame for Video Pipeline*/
  Status = XAVBuf_SetInputLiveVideoFormat(AVBufPtr, RGB_12BPC);
  if (Status != XST_SUCCESS) {
      return XST_FAILURE;
  }
  /* Set the output Video Format */
  XAVBuf_SetOutputVideoFormat(AVBufPtr, RGB_8BPC);
  /* Select the Video/Audio input source */
  XAVBuf_InputVideoSelect(AVBufPtr, XAVBUF_VIDSTREAM1_LIVE,
        XAVBUF_VIDSTREAM2_NONE);
  XAVBuf_InputAudioSelect(AVBufPtr, XAVBUF_AUDSTREAM1_LIVE,
        XAVBUF_AUDSTREAM2_NO_AUDIO);

  /* Set the Pixel Clock */
  XDpPsu_MainStreamAttributes *MsaConfig = &DpPsuPtr->MsaConfig;
  RunCfgPtr->PixClkHz = MsaConfig->PixelClockHz;
  XAVBuf_SetPixelClock(RunCfgPtr->PixClkHz);

  /* Start setting the Audio Parameter */
  XDpPsu_WriteReg(DpPsuPtr->Config.BaseAddr, XDPPSU_TX_AUDIO_CONTROL, 0);
  usleep(10000);

  /* Disable Audio Buffers */
  XAVBuf_EnableAudio0Buffers(AVBufPtr, 0);
  XAVBuf_EnableAudio1Buffers(AVBufPtr, 0);

  /* Send the Audio Info Data */
  uint8_t db1, db2, db3, db4;
  int IF_type = 0x84;
  int IF_info_length = 27;
  int IF_version = 0x11;
  int IF_audio_channel_count = 1; // 0:refer to stream header 1:2ch 2:3ch 3:4ch 4:5ch 5:6ch 6:7ch 7:8ch
  int IF_audio_coding_type = 1; // 0:refer to stream header 1:L-PCM
  int IF_sampling_frequency = 2; // 0: refer to stream header 1:32kHz 2:44.1kHz 3:48kHz 4:88.2kHz 5:96kHz 6:176.4kHz 7:192kHz
  int IF_sample_size = 1; // 0:refer to stream header 1:16bit 2:20bit 3:24bit
  int IF_channel_allocation = 0; // 0:1ch-L 2ch-R
  db1 = 0x00;
  db2 = IF_type;
  db3 = IF_info_length & 0xFF;
  db4 = (IF_version << 2) | (IF_info_length >> 8);
  uint32_t IF00 = (db4 << 24) | (db3 << 16) | (db2 << 8) | db1;

  db1 = IF_audio_channel_count | (IF_audio_coding_type << 4);
  db2 = (IF_sampling_frequency << 2) | IF_sample_size;
  db3 = 0;
  db4 = IF_channel_allocation;
  uint32_t IF04 = (db4 << 24) | (db3 << 16) | (db2 << 8) | db1;

  XDpPsu_WriteReg(DpPsuPtr->Config.BaseAddr, XDPPSU_TX_AUDIO_INFO_DATA + 0, IF00);
  XDpPsu_WriteReg(DpPsuPtr->Config.BaseAddr, XDPPSU_TX_AUDIO_INFO_DATA + 4, IF04);
  XDpPsu_WriteReg(DpPsuPtr->Config.BaseAddr, XDPPSU_TX_AUDIO_INFO_DATA + 8, 0);
  XDpPsu_WriteReg(DpPsuPtr->Config.BaseAddr, XDPPSU_TX_AUDIO_INFO_DATA + 12, 0);
  XDpPsu_WriteReg(DpPsuPtr->Config.BaseAddr, XDPPSU_TX_AUDIO_INFO_DATA + 16, 0);
  XDpPsu_WriteReg(DpPsuPtr->Config.BaseAddr, XDPPSU_TX_AUDIO_INFO_DATA + 20, 0);
  XDpPsu_WriteReg(DpPsuPtr->Config.BaseAddr, XDPPSU_TX_AUDIO_INFO_DATA + 24, 0);
  XDpPsu_WriteReg(DpPsuPtr->Config.BaseAddr, XDPPSU_TX_AUDIO_INFO_DATA + 28, 0);

  /* Set the Audio Channel (2ch - 1) */
  XDpPsu_WriteReg(DpPsuPtr->Config.BaseAddr, XDPPSU_TX_AUDIO_CHANNELS, 1);

  XDpPsu_WriteReg(DpPsuPtr->Config.BaseAddr, XDPPSU_TX_AUDIO_CONTROL, 1);
  usleep(10000);

  /* Set the Audio Volume L, R, 0-255 */
  XAVBuf_AudioMixerVolumeControl(AVBufPtr, 255, 255);


  /* Configure Video pipeline for graphics channel */
  XAVBuf_ConfigureGraphicsPipeline(AVBufPtr);
  /* Configure the output video pipeline */
  XAVBuf_ConfigureOutputVideo(AVBufPtr);
  /* Disable the global alpha, since we are using the pixel based alpha */
  XAVBuf_SetBlenderAlpha(AVBufPtr, 0, 0);
  /* Set the clock mode */
  XDpPsu_CfgMsaEnSynchClkMode(DpPsuPtr, RunCfgPtr->EnSynchClkMode);
  /* Set the clock source depending on the use case.
   * Here for simplicity we are using PS clock as the source*/

  XAVBuf_SetAudioVideoClkSrc(AVBufPtr, XAVBUF_PS_CLK, XAVBUF_PL_CLK);
  /* Issue a soft reset after selecting the input clock sources */
  //XAVBuf_SoftReset(AVBufPtr);
  //XAVBuf_AudioSoftReset(AVBufPtr);

  return XST_SUCCESS;
}

/*****************************************************************************/
/**
*
* The purpose of this function is to setup call back functions for the DP
* controller interrupts.
*
* @param  RunCfgPtr is a pointer to the application configuration structure.
*
* @return  None.
*
* @note    None.
*
*****************************************************************************/

void SetupInterrupts(Run_Config *RunCfgPtr)
{
  XDpPsu *DpPsuPtr = RunCfgPtr->DpPsuPtr;
  XScuGic    *IntrPtr = RunCfgPtr->IntrPtr;
  XScuGic_Config  *IntrCfgPtr;
  u32  IntrMask = XDPPSU_INTR_HPD_IRQ_MASK | XDPPSU_INTR_HPD_EVENT_MASK;

  XDpPsu_WriteReg(DpPsuPtr->Config.BaseAddr, XDPPSU_INTR_DIS, 0xFFFFFFFF);
  XDpPsu_WriteReg(DpPsuPtr->Config.BaseAddr, XDPPSU_INTR_MASK, 0xFFFFFFFF);

  XDpPsu_SetHpdEventHandler(DpPsuPtr, DpPsu_IsrHpdEvent, RunCfgPtr);
  XDpPsu_SetHpdPulseHandler(DpPsuPtr, DpPsu_IsrHpdPulse, RunCfgPtr);

#ifndef SDT
  /* Initialize interrupt controller driver. */
  IntrCfgPtr = XScuGic_LookupConfig(INTC_DEVICE_ID);
  XScuGic_CfgInitialize(IntrPtr, IntrCfgPtr, IntrCfgPtr->CpuBaseAddress);

  /* Register ISRs. */
  XScuGic_Connect(IntrPtr, DPPSU_INTR_ID,
      (Xil_InterruptHandler)XDpPsu_HpdInterruptHandler, RunCfgPtr->DpPsuPtr);

  /* Trigger DP interrupts on rising edge. */
  XScuGic_SetPriorityTriggerType(IntrPtr, DPPSU_INTR_ID, 0x0, 0x03);

  /* Initialize exceptions. */
  Xil_ExceptionInit();
  Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,
      (Xil_ExceptionHandler)XScuGic_DeviceInterruptHandler,
      INTC_DEVICE_ID);

  /* Enable exceptions for interrupts. */
  Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
  Xil_ExceptionEnable();

  /* Enable DP interrupts. */
  XScuGic_Enable(IntrPtr, DPPSU_INTR_ID);
  XDpPsu_WriteReg(DpPsuPtr->Config.BaseAddr, XDPPSU_INTR_EN, IntrMask);
#else
  XSetupInterruptSystem(RunCfgPtr->DpPsuPtr, &XDpPsu_HpdInterruptHandler, RunCfgPtr->DpPsuPtr->Config.IntrId,
    RunCfgPtr->DpPsuPtr->Config.IntrParent, XINTERRUPT_DEFAULT_PRIORITY);
  XDpPsu_WriteReg(DpPsuPtr->Config.BaseAddr, XDPPSU_INTR_EN, IntrMask);
#endif
}