//
//
// Copyright (c) 2017,2021 Alexey Melnikov
//
// This program is GPL Licensed. See COPYING for the full license.
//
//
////////////////////////////////////////////////////////////////////////////////////////////////////////

`timescale 1ns / 1ps

//
// LINE_LENGTH: Length of display line in pixels when HBlank = 0;
// HALF_DEPTH:  If =1 then color dept is 4 bits per component
//
// altera message_off 10720 
// altera message_off 12161

module video_mixer
#(
	parameter LINE_LENGTH  = 768,
	parameter HALF_DEPTH   = 0,
	parameter GAMMA        = 0
)
(
	input            CLK_VIDEO, // should be multiple by (ce_pix*4)
	output reg       CE_PIXEL,  // output pixel clock enable

	input            ce_pix,    // input pixel clock or clock_enable

	input            scandoubler,
	input            hq2x, 	    // high quality 2x scaling

	inout     [21:0] gamma_bus,

	// color
	input [DWIDTH:0] R,
	input [DWIDTH:0] G,
	input [DWIDTH:0] B,

	// Positive pulses.
	input            HSync,
	input            VSync,
	input            HBlank,
	input            VBlank,

	// Freeze engine
	// HDMI: displays last frame 
	// VGA:  black screen with HSync and VSync
	input            HDMI_FREEZE,
	output           freeze_sync,

	// video output signals
	output reg [7:0] VGA_R,
	output reg [7:0] VGA_G,
	output reg [7:0] VGA_B,
	output reg       VGA_VS,
	output reg       VGA_HS,
	output reg       VGA_DE
);

localparam DWIDTH = HALF_DEPTH ? 3 : 7;
localparam DWIDTH_SD = GAMMA ? 7 : DWIDTH;
localparam HALF_DEPTH_SD = GAMMA ? 0 : HALF_DEPTH;

wire frz_hs, frz_vs;
wire frz_hbl, frz_vbl;
video_freezer freezer
(
	.clk(CLK_VIDEO),
	.freeze(HDMI_FREEZE),
	.hs_in(HSync),
	.vs_in(VSync),
	.hbl_in(HBlank),
	.vbl_in(VBlank),
	.sync(freeze_sync),
	.hs_out(frz_hs),
	.vs_out(frz_vs),
	.hbl_out(frz_hbl),
	.vbl_out(frz_vbl)
);

reg frz;
always @(posedge CLK_VIDEO) begin
	reg frz1;
	
	frz1 <= HDMI_FREEZE;
	frz  <= frz1;
end

generate
	if(GAMMA && HALF_DEPTH) begin
		wire [7:0] R_in  = frz ? 8'd0 : {R,R};
		wire [7:0] G_in  = frz ? 8'd0 : {G,G};
		wire [7:0] B_in  = frz ? 8'd0 : {B,B};
	end else begin
		wire [DWIDTH:0] R_in = frz ? 1'd0 : R;
		wire [DWIDTH:0] G_in = frz ? 1'd0 : G;
		wire [DWIDTH:0] B_in = frz ? 1'd0 : B;
	end
endgenerate

wire hs_g, vs_g;
wire hb_g, vb_g;
wire [DWIDTH_SD:0] R_gamma, G_gamma, B_gamma;

generate
	if(GAMMA) begin
		assign gamma_bus[21] = 1;
		gamma_corr gamma(
			.clk_sys(gamma_bus[20]),
			.clk_vid(CLK_VIDEO),
			.ce_pix(ce_pix),

			.gamma_en(gamma_bus[19]),
			.gamma_wr(gamma_bus[18]),
			.gamma_wr_addr(gamma_bus[17:8]),
			.gamma_value(gamma_bus[7:0]),

			.HSync(frz_hs),
			.VSync(frz_vs),
			.HBlank(frz_hbl),
			.VBlank(frz_vbl),
			.RGB_in({R_in,G_in,B_in}),

			.HSync_out(hs_g),
			.VSync_out(vs_g),
			.HBlank_out(hb_g),
			.VBlank_out(vb_g),
			.RGB_out({R_gamma,G_gamma,B_gamma})
		);
	end else begin
		assign gamma_bus[21] = 0;
		assign {R_gamma,G_gamma,B_gamma} = {R_in,G_in,B_in};
		assign {hs_g, vs_g, hb_g, vb_g} = {frz_hs, frz_vs, frz_hbl, frz_vbl};
	end
endgenerate

wire [DWIDTH_SD:0] R_sd;
wire [DWIDTH_SD:0] G_sd;
wire [DWIDTH_SD:0] B_sd;
wire hs_sd, vs_sd, hb_sd, vb_sd, ce_pix_sd;

scandoubler #(.LENGTH(LINE_LENGTH), .HALF_DEPTH(HALF_DEPTH_SD)) sd
(
	.clk_vid(CLK_VIDEO),
	.hq2x(hq2x),

	.ce_pix(ce_pix),
	.hs_in(hs_g),
	.vs_in(vs_g),
	.hb_in(hb_g),
	.vb_in(vb_g),
	.r_in(R_gamma),
	.g_in(G_gamma),
	.b_in(B_gamma),

	.ce_pix_out(ce_pix_sd),
	.hs_out(hs_sd),
	.vs_out(vs_sd),
	.hb_out(hb_sd),
	.vb_out(vb_sd),
	.r_out(R_sd),
	.g_out(G_sd),
	.b_out(B_sd)
);

wire [DWIDTH_SD:0] rt = (scandoubler ? R_sd : R_gamma);
wire [DWIDTH_SD:0] gt = (scandoubler ? G_sd : G_gamma);
wire [DWIDTH_SD:0] bt = (scandoubler ? B_sd : B_gamma);

always @(posedge CLK_VIDEO) begin
	reg [7:0] r,g,b;
	reg hde,vde,hs,vs, old_vs;
	reg old_hde;
	reg old_ce;
	reg ce_osc, fs_osc;
	
	old_ce <= ce_pix;
	ce_osc <= ce_osc | (old_ce ^ ce_pix);

	old_vs <= vs;
	if(~old_vs & vs) begin
		fs_osc <= ce_osc;
		ce_osc <= 0;
	end

	CE_PIXEL <= scandoubler ? ce_pix_sd : fs_osc ? (~old_ce & ce_pix) : ce_pix;

	if(!GAMMA && HALF_DEPTH) begin
		r <= {rt,rt};
		g <= {gt,gt};
		b <= {bt,bt};
	end
	else begin
		r <= rt;
		g <= gt;
		b <= bt;
	end

	hde <= scandoubler ? ~hb_sd : ~hb_g;
	vde <= scandoubler ? ~vb_sd : ~vb_g;
	vs  <= scandoubler ?  vs_sd :  vs_g;
	hs  <= scandoubler ?  hs_sd :  hs_g;

	if(CE_PIXEL) begin
		VGA_R <= r;
		VGA_G <= g;
		VGA_B <= b;

		VGA_VS <= vs;
		VGA_HS <= hs;

		old_hde <= hde;
		if(old_hde ^ hde) VGA_DE <= vde & hde;
	end
end

endmodule
