--I2C Control for TVP5145 By Colin O'Flynn
--www.newae.com or coflynn@newae.com
--Revision 20040115
--Uses a simple I2C Core from opencores.org as the core communications
--This module will bring the TVP5145 online, then get information from it
--On the condition of the video signal (video OK / not OK)

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

package i2c_control is
component controlchip is
    Port (
    		RESET_in	 : in  std_logic; -- Reset (active high)
		   CLK_in	 : in  std_logic; -- Clock

         Lock_out   : out std_logic; --Video signal is locked
         Macro_out  : out std_logic; --Video is Macrovision protected
		
    		SCL_inout  : inout std_logic; -- I2C Clock
    		SDA_inout  : inout std_logic; -- I2C Data
   		SAS_out    : out std_logic --I2C Address Select
		);
end component controlchip;
end package i2c_control;



library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use work.i2c.all;

entity controlchip is
	Port (
    		RESET_in	  : in  std_logic; -- Reset (active high)
		   CLK_in	  : in  std_logic; -- Clock

         Lock_out   : out std_logic; --Video signal is locked
         Macro_out  : out std_logic; --Video is Macrovision protected
		
    		SCL_inout  : inout std_logic; -- I2C Clock
    		SDA_inout  : inout std_logic; -- I2C Data
   		SAS_out    : out std_logic --I2C Address Select
		);
end controlchip;


architecture Behavioral of controlchip is

   type state_status_var is (active, delayed, stopped);


   --Reset signals (Reset = active high, nReset = active low)
   signal Reset, nReset    : std_logic;    

   --I2C Address of device
	constant SLAVE_ADDR     : std_logic_vector(6 downto 0) := "1011100";

   --Clock prescaller, for 50 MHz and 100 Khz I2C clock 9 is a good value....
	constant CLK_CNT        : unsigned(7 downto 0) := conv_unsigned(9, 8);

   --Various I2C Signals we use
   signal I2C_cmd_ack      : std_logic;
	signal I2C_D            : std_logic_vector(7 downto 0);
	signal I2C_lack         : std_logic;
	signal I2C_start, I2C_read, I2C_write, I2C_ack, I2C_stop : std_logic;
   signal I2C_Dout         : std_logic_vector(7 downto 0);

   signal SCL_buf, SDA_buf : std_logic;

   --used to copy data to figure out if video signal is OK
   signal store_macro, store_lock :std_logic;

   signal video_lock_sig, video_macro_sig : std_logic;
              
   --signal state_active     : std_logic := '1';   
   --signal state_status        : state_status_var := active;
   
   constant DELAY_COUNTER_MAX : integer := 1;   


begin

	Reset <= RESET_in;
	nReset <= not RESET_in;


  --Connect the I2C controller
	u1: simple_i2c port map (clk => CLK_in, ena => '1', clk_cnt => clk_cnt, nReset => nReset,
			read => I2C_read, write => I2C_write, start => I2C_start, stop => I2C_stop, 
         ack_in => I2C_ack, cmd_ack => I2C_cmd_ack, Dout => I2C_Dout, 
			Din => I2C_D, ack_out => I2C_lack, SCL => SCL_inout, SDA => SDA_inout);

   --set the I2C address bit on TVP5145
   SAS_out <= SLAVE_ADDR(1);


	init_statemachine : block
		type states is (i1, i2, i3, t1, t2, t3, t4, t5, t6, t7, t8, d1);
		signal state : states;
	begin

		nxt_state_decoder: process(CLK_in, nReset, state, I2C_start, I2C_read, I2C_write, I2C_ack, I2C_stop, I2C_D, store_lock, store_macro, I2C_cmd_ack, I2C_lack)
         
         variable tstate_status: state_status_var := active;

         variable delay_counter : integer range 0 to DELAY_COUNTER_MAX;

			variable nxt_state : states := i1;
			variable tD : std_logic_vector(7 downto 0);
			variable terr : std_logic;
			variable tstart, tread, twrite, tack, tstop, tstore_lock, tstore_macro : std_logic;
		begin
		
       	nxt_state := state;
			terr := '0';
			tstart := I2C_start;
			tread := I2C_read;
			twrite := I2C_write;
			tack := I2C_ack;
			tstop := I2C_stop;
			tD := I2C_D;
         tstore_lock := store_lock;
         tstore_macro := store_macro;
         tstate_status := active;
			
         case (state) is

		when i1 =>	-- send start condition, sent slave address + write

            --state_active <= '1';
            tstate_status := active;

				nxt_state := i2;
					tstart := '1';
					tread := '0';
					twrite := '1';
					tack := '0';
					tstop := '0';
					tD := (slave_addr & '0'); -- write to slave (R/W = '0')

		when i2 =>	-- send "access config" command

				-- check aknowledge bit
            if (I2C_cmd_ack = '1') then
				   if (I2C_lack = '1') then
					   terr := '1'; -- expected ACK
				   end if;

					tstart := '0';
					tread := '0';
					twrite := '1';
					tack := '0';
					tstop := '0';
					tD := x"7F"; --have to write to register 7F

               nxt_state := i3;
            
               --state_active <= '1'; 
               tstate_status := active;
               
            else
            
               --state_active <= '0';
               tstate_status := stopped;              

            end if;				

		when i3 =>	-- send data, sent stop condition

               if (I2C_cmd_ack = '1') then
						-- check aknowledge bit  
 						if (I2C_lack = '1') then
							terr := '1'; -- expected ACK
						end if;

						tstart := '0';
						tread := '0';
						twrite := '1';
						tack := '0';
						tstop := '1';
						tD := x"00"; --write any random data

                nxt_state := t1;

                  --state_active <= '1';
                  tstate_status := active;
               else
                  --state_active <= '0';
                  tstate_status := stopped;
					end if;


	         when t1 =>	-- send start condition, sent slave address + write
					if (I2C_cmd_ack = '1') then
						nxt_state := t2;
                  --state_active <= '1';
                  tstate_status := active;
						-- check aknowledge bit
						if (I2C_lack = '1') then
							terr := '1'; -- expected ACK
						end if;

						tstart := '1';
						tread := '0';
						twrite := '1';
						tack := '0';
						tstop := '0';
						tD := (slave_addr & '0'); -- write to slave (R/W = '0')

                  tstore_lock  := '0'; 
						tstore_macro := '0';
               else
                  --state_active <= '0';
                  tstate_status := stopped;
					end if;

				when t2 =>	-- Send Status Register #1 Address, stop
					if (I2C_cmd_ack = '1') then
						nxt_state := t3;
                  --state_active <= '1';
                  tstate_status := active;

						-- check aknowledge bit
						if (I2C_lack = '1') then
							terr := '1'; --expected ACK
						end if;

						tstart := '0';
						tread := '0';
						twrite := '1';
						tack := '0';
						tstop := '1';
						tD := x"81"; --Status Register #1

                  tstore_lock  := '0'; 
						tstore_macro := '0';
               else
                  --state_active <= '0';
                  tstate_status := stopped;
					end if;

				when t3 =>	-- send start, send slave address, read
					if (I2C_cmd_ack = '1') then
						nxt_state := t4;
                  --state_active <= '1';
                  tstate_status := active;
						-- check aknowledge bit
						if (I2C_lack = '1') then
							terr := '1'; -- expected ACK
						end if;

						tstart := '1';
						tread := '0';
						twrite := '1';
						tack := '0';
						tstop := '0';
						tD := (slave_addr & '1'); -- read from slave (R/W = '1')

                  tstore_lock  := '0'; 
						tstore_macro := '0';
               else
                  --state_active <= '0';
                  tstate_status := stopped;
					end if;

				when t4 =>	-- read Status Register #1
					if (I2C_cmd_ack = '1') then
						nxt_state := t8;
                  --state_active <= '1';
                  tstate_status := active;
						-- check aknowledge bit
						if (I2C_lack = '1') then
							terr := '1'; --expected ACK
						end if;

						tstart := '0';
						tread := '1';
						twrite := '0';
						tack := '1';
						tstop := '1';
                  tstore_lock  := '1'; --update video lock status
						tstore_macro := '0';
               else
                 -- state_active <= '0';
                  tstate_status := stopped;
					end if;

            when t5 =>	-- send start condition, send slave address + write
					if (I2C_cmd_ack = '1') then
						nxt_state := t6;
                  --state_active <= '1';
                  tstate_status := active;
						-- check aknowledge bit
						if (I2C_lack = '1') then
							terr := '1'; -- expected ACK
						end if;

						tstart := '1';
						tread := '0';
						twrite := '1';
						tack := '0';
						tstop := '0';
						tD := (slave_addr & '0'); -- write to slave (R/W = '0')

                  tstore_lock  := '0'; 
						tstore_macro := '0';
               else
                  --state_active <= '0';
                  tstate_status := stopped;
					end if;

				when t6 =>	-- Send Status Register #2 Address
					if (I2C_cmd_ack = '1') then
						nxt_state := t7;
                  --state_active <= '1';
                  tstate_status := active;

						-- check aknowledge bit
						if (I2C_lack = '1') then
							terr := '1'; --expected ACK
						end if;

						tstart := '0';
						tread := '0';
						twrite := '1';
						tack := '0';
						tstop := '1';
						tD := x"82"; --Status Register #2

                  tstore_lock  := '0'; 
						tstore_macro := '0';
               else
                  --state_active <= '0';
                  tstate_status := stopped;
					end if;

				when t7 =>	-- send start, send slave address, read
					if (I2C_cmd_ack = '1') then
						nxt_state := t8;
                  --state_active <= '1';
                  tstate_status := active;
						-- check aknowledge bit
						if (I2C_lack = '1') then
							terr := '1'; -- expected ACK
						end if;

						tstart := '1';
						tread := '0';
						twrite := '1';
						tack := '0';
						tstop := '0';
						tD := (slave_addr & '1'); -- read from slave (R/W = '1')

                  tstore_lock  := '0'; 
						tstore_macro := '0';
               else
                  --state_active <= '0';
                  tstate_status := stopped;
					end if;      

				when t8 =>	-- read Status Register #2
					if (I2C_cmd_ack = '1') then
						nxt_state := t1;
                  --state_active <= '1';
                  tstate_status := active;

						tstart := '0';
						tread := '1';
						twrite := '0';
						tack := '0';
						tstop := '1';

                  tstore_lock  := '0';
						tstore_macro := '1'; --update video macrovision status
               else
                 -- state_active <= '0';
                  tstate_status := stopped;
					end if;

            when d1 => --delay for a bit
           --    tstate_status := stopped;

           --    if (delay_counter = DELAY_COUNTER_MAX) then
           --       tstate_status := active; 
           --       nxt_state := t1;
           --       delay_counter := 0;
           --    else
           --       delay_counter := delay_counter + 1;
           --    end if;
               
			end case;
      --  end if;          

      	-- copy data to proper locations on clock
			if (nReset = '0') then
				state <= i1;
            
            store_lock <= '0';
            store_macro <= '0'; 

      		I2C_start <= '0';
				I2C_read <= '0';
				I2C_write <= '0';
				I2C_ack <= '0';
				I2C_stop <= '0';
				I2C_D <= (others => '0'); 
            
            delay_counter := 0;
              
         --if rising edge of clk, and the last clock cycle finished, meaning we
         --want to update all the I2C data then
			elsif Rising_Edge(CLK_in) then
               
            if (tstate_status = active) then
               store_lock <= tstore_lock;
               store_macro <= tstore_macro; 
                  
               state <= nxt_state;  
				   I2C_start <= tstart;
				   I2C_read <= tread;
				   I2C_write <= twrite;
				   I2C_ack <= tack;
				   I2C_stop <= tstop;
				   I2C_D <= tD;
            end if;
			end if;

         

		end process nxt_state_decoder;
	end block init_statemachine;
   

    process(store_lock, store_macro, I2C_Dout, CLK_in)
    begin
    --     if (Reset = '1') then
    --        video_lock_sig <= '0';
    --     elsif rising_edge(store_lock) then
    --        video_lock_sig <= I2C_Dout(2) AND I2C_Dout(1);
    --     end if;
    --
    --     if (Reset = '1') then
    --        video_macro_sig <= '0';
    --     elsif rising_edge(store_macro) then
    --           video_macro_sig <= I2C_Dout(1) OR I2C_Dout(0);
    --     end if;

    if Rising_edge(CLK_in) then
      if (Reset = '1') then 
         video_lock_sig <= '0';
      elsif store_lock = '1' then 
         video_lock_sig <= I2C_Dout(2) AND I2C_Dout(1);
      end if;

      if (Reset = '1') then 
         video_macro_sig <= '0';
      elsif store_macro = '1' then 
         video_macro_sig <= I2C_Dout(1) OR I2C_Dout(0);
      end if;
     end if;
    end process;

   Lock_out <= video_lock_sig;
   Macro_out <= video_macro_sig;

end Behavioral;
