module oled_control
		#(
			parameter	WB_ADR_WIDTH  = 16,
			parameter	WB_DAT_WIDTH  = 32,
			parameter	WB_SEL_WIDTH  = (WB_DAT_WIDTH / 8),
			parameter	TUSER_WIDTH   = 1,
			parameter	TDATA_WIDTH   = 8
		)
		(
			input	wire						reset,
			input	wire						clk,
			input	wire						clk_x7,
			
			// wishbone
			input	wire						s_wb_rst_i,
			input	wire						s_wb_clk_i,
			input	wire	[WB_ADR_WIDTH-1:0]	s_wb_adr_i,
			output	wire	[WB_DAT_WIDTH-1:0]	s_wb_dat_o,
			input	wire	[WB_DAT_WIDTH-1:0]	s_wb_dat_i,
			input	wire						s_wb_we_i,
			input	wire	[WB_SEL_WIDTH-1:0]	s_wb_sel_i,
			input	wire						s_wb_stb_i,
			output	wire						s_wb_ack_o,
			
			// video input
			input	wire	[TUSER_WIDTH-1:0]	s_axi4s_tuser,
			input	wire						s_axi4s_tlast,
			input	wire	[TDATA_WIDTH-1:0]	s_axi4s_tdata,
			input	wire						s_axi4s_tvalid,
			output	wire						s_axi4s_tready,
			
			input	wire	[4:0]				gpo,
			
			output	wire	[4:1]				pmod_p,
			output	wire	[4:1]				pmod_n
		);
	
	reg					reg_oled_res_n;
	reg		[2:1]		reg_oled_bs;
	reg					reg_oled_pwr_en;
	reg					reg_oled_vin_en;
	
	always @(posedge s_wb_clk_i) begin
		if ( s_wb_rst_i ) begin
			reg_oled_res_n  <= 1'b0;
			reg_oled_bs     <= 2'b11;
			reg_oled_pwr_en <= 1'b0;
			reg_oled_vin_en <= 1'b0;
		end
		else begin
			if ( s_wb_stb_i && s_wb_we_i ) begin
				case ( s_wb_adr_i )
				0:	reg_oled_res_n  <= s_wb_dat_i;
				1:	reg_oled_bs     <= s_wb_dat_i;
				2:	reg_oled_pwr_en <= s_wb_dat_i;
				3:	reg_oled_vin_en <= s_wb_dat_i;
				endcase
			end
		end
	end
	
	wire				async_wb_ready;
	assign s_wb_dat_o = 0;
	assign s_wb_ack_o = (s_wb_stb_i & async_wb_ready);
	
	
	// async
	wire				async_cmd;
	wire	[7:0]		async_data;
	wire				async_valid;
	wire				async_ready;
	
	jelly_data_async
			#(
				.ASYNC			(1),
				.DATA_WIDTH		(1+8)
			)
		i_data_async
			(
				.s_reset		(s_wb_rst_i),
				.s_clk			(s_wb_clk_i),
				.s_data			(s_wb_dat_i[8:0]),
				.s_valid		(s_wb_stb_i && s_wb_we_i && (s_wb_adr_i == 4)),
				.s_ready		(async_wb_ready),
				
				.m_reset		(reset),
				.m_clk			(clk),
				.m_data			({async_cmd, async_data}),
				.m_valid		(async_valid),
				.m_ready		(async_ready)
			);
	
	
	// video input
	(* ASYNC_REG = "true" *)	reg		reg_vin_en_ff, reg_vin_en;
	always @(posedge clk) begin
		if ( reset ) begin
			reg_vin_en_ff <= 1'b0;
			reg_vin_en    <= 0;
		end
		else begin
			reg_vin_en_ff <= reg_oled_vin_en;
			reg_vin_en    <= reg_vin_en_ff;
		end
	end
	
	
	wire	[TUSER_WIDTH-1:0]	axi4s_vin_tuser;
	wire						axi4s_vin_tlast;
	wire	[TDATA_WIDTH-1:0]	axi4s_vin_tdata;
	wire						axi4s_vin_tvalid;
	wire						axi4s_vin_tready;
	
	jelly_video_gate_core
			#(
				.TUSER_WIDTH		(TUSER_WIDTH),
				.TDATA_WIDTH		(TDATA_WIDTH)
			)
		i_video_gate_core
			(
				.aresetn			(~reset),
				.aclk				(clk),
				.aclken				(1'b1),
				
				.enable				(reg_vin_en),
				.busy				(),
				
				.param_skip			(1'b0),
				
				.s_axi4s_tuser		(s_axi4s_tuser),
				.s_axi4s_tlast		(s_axi4s_tlast),
				.s_axi4s_tdata		(s_axi4s_tdata),
				.s_axi4s_tvalid		(s_axi4s_tvalid),
				.s_axi4s_tready		(s_axi4s_tready),
				
				.m_axi4s_tuser		(axi4s_vin_tuser),
				.m_axi4s_tlast		(axi4s_vin_tlast),
				.m_axi4s_tdata		(axi4s_vin_tdata),
				.m_axi4s_tvalid		(axi4s_vin_tvalid),
				.m_axi4s_tready		(axi4s_vin_tready)
			);
	
	
	
	// write control
	reg				reg_busy;
	reg		[3:0]	reg_count;
	reg				reg_cs_n;
	reg				reg_dc_n;
	reg				reg_wr_n;
	reg		[7:0]	reg_data;
	
	always @(posedge clk) begin
		if ( reset ) begin
			reg_busy  <= 1'b0;
			reg_count <= 0;
			reg_cs_n  <= 1'b1;
			reg_wr_n  <= 1'b1;
			reg_dc_n  <= 1'b1;
			reg_data  <= 8'h00;
		end
		else begin
			if ( !reg_busy ) begin
				reg_count <= 0;
				reg_cs_n  <= 1'b1;
				reg_wr_n  <= 1'b1;
				reg_dc_n  <= 1'b1;
				reg_data  <= 8'h00;
				if ( async_valid ) begin
					reg_busy  <= 1;
					reg_cs_n  <= 1'b0;
					reg_wr_n  <= 1'b0;
					reg_dc_n  <= ~async_cmd;
					reg_data  <= async_data;
				end
				else if ( axi4s_vin_tvalid ) begin
					reg_busy  <= 1;
					reg_cs_n  <= 1'b0;
					reg_wr_n  <= 1'b0;
					reg_dc_n  <= 1'b1;
					reg_data  <= axi4s_vin_tdata;
				end
			end
			else begin
				reg_count <= reg_count + 1;
				if ( reg_count == 7 ) begin
					reg_busy <= 1'b0;
				end
				
				case ( reg_count )
				0:			begin	reg_cs_n  = 1'b0;	reg_wr_n  = 1'b0;	end
				1:			begin	reg_cs_n  = 1'b0;	reg_wr_n  = 1'b0;	end
				2:			begin	reg_cs_n  = 1'b0;	reg_wr_n  = 1'b0;	end
				3:			begin	reg_cs_n  = 1'b0;	reg_wr_n  = 1'b1;	end
				4:			begin	reg_cs_n  = 1'b0;	reg_wr_n  = 1'b1;	end
				5:			begin	reg_cs_n  = 1'b0;	reg_wr_n  = 1'b1;	end
				6:			begin	reg_cs_n  = 1'b1;	reg_wr_n  = 1'b1;	end
				7:			begin	reg_cs_n  = 1'b1;	reg_wr_n  = 1'b1;	end
				8:			begin	reg_cs_n  = 1'b1;	reg_wr_n  = 1'b1;	end
				9:			begin	reg_cs_n  = 1'b1;	reg_wr_n  = 1'b1;	end
				10:			begin	reg_cs_n  = 1'b1;	reg_wr_n  = 1'b1;	end
				11:			begin	reg_cs_n  = 1'b1;	reg_wr_n  = 1'b1;	end
				12:			begin	reg_cs_n  = 1'b1;	reg_wr_n  = 1'b1;	end
				13:			begin	reg_cs_n  = 1'b1;	reg_wr_n  = 1'b1;	end
				14:			begin	reg_cs_n  = 1'b1;	reg_wr_n  = 1'b1;	end
				15:			begin	reg_cs_n  = 1'b1;	reg_wr_n  = 1'b1;	end
				default:	begin	reg_cs_n  = 1'b1;	reg_wr_n  = 1'b1;	end
				endcase
			end
		end
	end
	
	assign async_ready      = !reg_busy;
	assign axi4s_vin_tready = (!reg_busy && !async_valid);
	
	
	oled_control_if
		i_oled_control_if
			(
				.reset			(reset),
				.clk			(clk),
				.clk_x7			(clk_x7),
				
				.olde_bs		(reg_oled_bs),
				.olde_cs_n		(reg_cs_n),
				.olde_res_n		(reg_oled_res_n),
				.olde_dc_n		(reg_dc_n),
				.olde_wr_n		(reg_wr_n),
				.olde_e			(1'b1),
				.olde_d			(reg_data),
				
				.pwr_en			(reg_oled_pwr_en),
				.gpo			(gpo),
				
				.pmod_p			(pmod_p),
				.pmod_n			(pmod_n)
			);
	
endmodule