/**
  ******************************************************************************
  * @file    adv7533.c
  * @author  MCD Application Team
  * @brief   This file provides the ADV7533 DSI to HDMI bridge driver 
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT(c) 2016 STMicroelectronics</center></h2>
  *
  * 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.
  *   3. Neither the name of STMicroelectronics nor the names of its contributors
  *      may be used to endorse or promote products derived from this software
  *      without specific prior written permission.
  *
  * 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 HOLDER 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.
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "adv7533.h"

/** @addtogroup BSP
  * @{
  */
  
/** @addtogroup Components
  * @{
  */ 

/** @defgroup ADV7533 ADV7533
  * @brief     This file provides a set of functions needed to drive the 
  *            adv7533 DSI-HDMI bridge.
  * @{
  */
    
/* Private types -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private constants ---------------------------------------------------------*/
/** @defgroup ADV7533_Private_Constants ADV7533 Private Constants
  * @{
  */

/**
  * @}
  */

/* Private macros ------------------------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
/** @defgroup ADV7533_Exported_Variables
  * @{
  */

AUDIO_DrvTypeDef adv7533_drv = 
{
  adv7533_AudioInit,
  adv7533_DeInit,
  adv7533_ReadID,
  adv7533_Play,
  adv7533_Pause,
  adv7533_Resume,
  adv7533_Stop,  
  adv7533_SetFrequency,
  adv7533_SetVolume, /* Not supported, added for compatibility */
  adv7533_SetMute,  
  adv7533_SetOutputMode, /* Not supported, added for compatibility */ 
  adv7533_Reset /* Not supported, added for compatibility */
};

/**
  * @}
  */
   
/* Exported functions --------------------------------------------------------*/
/** @defgroup ADV7533_Exported_Functions ADV7533 Exported Functions
  * @{
  */

/**
  * @brief  Initializes the ADV7533 bridge.
  * @param  None
  * @retval Status
  */
uint8_t ADV7533_Init(void)
{
  HDMI_IO_Init();

  /* Configure the IC2 address for CEC_DSI interface */
  HDMI_IO_Write(ADV7533_MAIN_I2C_ADDR, 0xE1, ADV7533_CEC_DSI_I2C_ADDR);

  return 0;
}

/**
  * @brief  Power on the ADV7533 bridge.
  * @param  None
  * @retval None
  */
void ADV7533_PowerOn(void)
{
  uint8_t tmp;
  
  /* Power on */
  tmp = HDMI_IO_Read(ADV7533_MAIN_I2C_ADDR, 0x41);
  tmp &= ~0x40;
  HDMI_IO_Write(ADV7533_MAIN_I2C_ADDR, 0x41, tmp);
}

/**
  * @brief  Power off the ADV7533 bridge.
  * @param  None
  * @retval None
  */
void ADV7533_PowerDown(void)
{
   uint8_t tmp;
   
   /* Power down */
   tmp = HDMI_IO_Read(ADV7533_MAIN_I2C_ADDR, 0x41);
   tmp |= 0x40;
   HDMI_IO_Write(ADV7533_MAIN_I2C_ADDR, 0x41, tmp);
}

/**
  * @brief  Configure the DSI-HDMI ADV7533 bridge for video.
  * @param config : pointer to adv7533ConfigTypeDef that contains the
  *                 video configuration parameters
  * @retval None
  */
void ADV7533_Configure(adv7533ConfigTypeDef * config)
{
  uint8_t tmp;
  
  /* Sequence from Section 3 - Quick Start Guide */
  
  /* ADV7533 Power Settings */
  /* Power down */
  tmp = HDMI_IO_Read(ADV7533_MAIN_I2C_ADDR, 0x41);
  tmp &= ~0x40;
  HDMI_IO_Write(ADV7533_MAIN_I2C_ADDR, 0x41, tmp);
  /* HPD Override */
  tmp = HDMI_IO_Read(ADV7533_MAIN_I2C_ADDR, 0xD6);
  tmp |= 0x40;
  HDMI_IO_Write(ADV7533_MAIN_I2C_ADDR, 0xD6, tmp);
  /* Gate DSI LP Oscillator and DSI Bias Clock Powerdown */
  tmp = HDMI_IO_Read(ADV7533_CEC_DSI_I2C_ADDR, 0x03);
  tmp &= ~0x02;
  HDMI_IO_Write(ADV7533_CEC_DSI_I2C_ADDR, 0x03, tmp);
  
  /* Fixed registers that must be set on power-up */
  tmp = HDMI_IO_Read(ADV7533_MAIN_I2C_ADDR, 0x16);
  tmp &= ~0x3E;
  tmp |= 0x20; 
  HDMI_IO_Write(ADV7533_MAIN_I2C_ADDR, 0x16, tmp);
  HDMI_IO_Write(ADV7533_MAIN_I2C_ADDR, 0x9A, 0xE0);
  tmp = HDMI_IO_Read(ADV7533_MAIN_I2C_ADDR, 0xBA);
  tmp &= ~0xF8;
  tmp |= 0x70; 
  HDMI_IO_Write(ADV7533_MAIN_I2C_ADDR, 0xBA, tmp);
  HDMI_IO_Write(ADV7533_MAIN_I2C_ADDR, 0xDE, 0x82);
  
  tmp = HDMI_IO_Read(ADV7533_MAIN_I2C_ADDR, 0xE4); 
  tmp |= 0x40;
  HDMI_IO_Write(ADV7533_MAIN_I2C_ADDR, 0xE4, tmp);
  HDMI_IO_Write(ADV7533_MAIN_I2C_ADDR, 0xE5, 0x80);
  
  tmp = HDMI_IO_Read(ADV7533_CEC_DSI_I2C_ADDR, 0x15);
  tmp &= ~0x30;
  tmp |= 0x10; 
  tmp = HDMI_IO_Read(ADV7533_CEC_DSI_I2C_ADDR, 0x17);
  tmp &= ~0xF0;
  tmp |= 0xD0; 
  HDMI_IO_Write(ADV7533_CEC_DSI_I2C_ADDR, 0x17, tmp);
  tmp = HDMI_IO_Read(ADV7533_CEC_DSI_I2C_ADDR, 0x24);
  tmp &= ~0x10;
  HDMI_IO_Write(ADV7533_CEC_DSI_I2C_ADDR, 0x24, tmp);
  tmp = HDMI_IO_Read(ADV7533_CEC_DSI_I2C_ADDR, 0x57);
  tmp |= 0x01;
  tmp |= 0x10;
  HDMI_IO_Write(ADV7533_CEC_DSI_I2C_ADDR, 0x57, tmp);
  
  /* Configure the number of DSI lanes */
  HDMI_IO_Write(ADV7533_CEC_DSI_I2C_ADDR, 0x1C, (config->DSI_LANES << 4));
  
  /* Setup video output mode */
  /* Select HDMI mode */
  tmp = HDMI_IO_Read(ADV7533_MAIN_I2C_ADDR, 0xAF);
  tmp |= 0x02;
  HDMI_IO_Write(ADV7533_MAIN_I2C_ADDR, 0xAF, tmp); 
  /* HDMI Output Enable */
  tmp = HDMI_IO_Read(ADV7533_CEC_DSI_I2C_ADDR, 0x03);
  tmp |= 0x80;
  HDMI_IO_Write(ADV7533_CEC_DSI_I2C_ADDR, 0x03, tmp);

  /* GC packet enable */
  tmp = HDMI_IO_Read(ADV7533_MAIN_I2C_ADDR, 0x40);
  tmp |= 0x80;
  HDMI_IO_Write(ADV7533_MAIN_I2C_ADDR, 0x40, tmp);
  /* Input color depth 24-bit per pixel */
  tmp = HDMI_IO_Read(ADV7533_MAIN_I2C_ADDR, 0x4C);
  tmp &= ~0x0F;
  tmp |= 0x03;
  HDMI_IO_Write(ADV7533_MAIN_I2C_ADDR, 0x4C, tmp);
  /* Down dither output color depth */
  HDMI_IO_Write(ADV7533_MAIN_I2C_ADDR, 0x49, 0xfc);
  
  /* Internal timing disabled */
  tmp = HDMI_IO_Read(ADV7533_CEC_DSI_I2C_ADDR, 0x27);
  tmp &= ~0x80;
  HDMI_IO_Write(ADV7533_CEC_DSI_I2C_ADDR, 0x27, tmp);
}

/**
  * @brief  Enable video pattern generation.
  * @param  None
  * @retval None
  */
void ADV7533_PatternEnable(void)
{
  /* Timing generator enable */
  HDMI_IO_Write(ADV7533_CEC_DSI_I2C_ADDR, 0x55, 0x80); /* Color bar */
  HDMI_IO_Write(ADV7533_CEC_DSI_I2C_ADDR, 0x55, 0xA0); /* Color ramp */
  
  HDMI_IO_Write(ADV7533_CEC_DSI_I2C_ADDR, 0x03, 0x89);
  HDMI_IO_Write(ADV7533_CEC_DSI_I2C_ADDR, 0xAF, 0x16);
}

/**
  * @brief  Disable video pattern generation.
  * @param  none
  * @retval none
  */
void ADV7533_PatternDisable(void)
{
  /* Timing generator enable */
  HDMI_IO_Write(ADV7533_CEC_DSI_I2C_ADDR, 0x55, 0x00);
}

/**
  * @brief Initializes the ADV7533 audio  interface.
  * @param DeviceAddr: Device address on communication Bus.   
  * @param OutputDevice: Not used (for compatiblity only).   
  * @param Volume: Not used (for compatiblity only).   
  * @param AudioFreq: Audio Frequency 
  * @retval 0 if correct communication, else wrong communication
  */
uint32_t adv7533_AudioInit(uint16_t DeviceAddr, uint16_t OutputDevice, uint8_t Volume,uint32_t AudioFreq)
{
  uint32_t val = 4096;
  uint8_t  tmp = 0;

  /* Audio data enable*/
  tmp = HDMI_IO_Read(ADV7533_CEC_DSI_I2C_ADDR, 0x05);
  tmp &= ~0x20;
  HDMI_IO_Write(ADV7533_CEC_DSI_I2C_ADDR, 0x05, tmp);

  /* HDMI statup */
  tmp= (uint8_t)((val & 0xF0000)>>16);
  HDMI_IO_Write(DeviceAddr, 0x01, tmp);

  tmp= (uint8_t)((val & 0xFF00)>>8);
  HDMI_IO_Write(DeviceAddr, 0x02, tmp);

  tmp= (uint8_t)((val & 0xFF));
  HDMI_IO_Write(DeviceAddr, 0x03, tmp);

  /* Enable spdif */
  tmp = HDMI_IO_Read(DeviceAddr, 0x0B);
  tmp |= 0x80;
  HDMI_IO_Write(DeviceAddr, 0x0B, tmp);

  /* Enable I2S */
  tmp = HDMI_IO_Read(DeviceAddr, 0x0C);
  tmp |=0x04;
  HDMI_IO_Write(DeviceAddr, 0x0C, tmp);

  /* Set audio sampling frequency */
  adv7533_SetFrequency(DeviceAddr, AudioFreq);

  /* Select SPDIF is 0x10 , I2S=0x00  */
  tmp = HDMI_IO_Read(ADV7533_MAIN_I2C_ADDR, 0x0A);
  tmp &=~ 0x10;
  HDMI_IO_Write(DeviceAddr, 0x0A, tmp);

  /* Set v1P2 enable */
  tmp = HDMI_IO_Read(DeviceAddr, 0xE4);
  tmp |= 0x80;
  HDMI_IO_Write(DeviceAddr, 0xE4, tmp);
 
  return 0;
}

/**
  * @brief  Deinitializes the adv7533
  * @param  None
  * @retval  None
  */
void adv7533_DeInit(void)
{
  /* Deinitialize Audio adv7533 interface */
  AUDIO_IO_DeInit();
}

/**
  * @brief  Get the adv7533 ID.
  * @param DeviceAddr: Device address on communication Bus.
  * @retval The adv7533 ID 
  */
uint32_t adv7533_ReadID(uint16_t DeviceAddr)
{
  uint32_t  tmp = 0;
  
  tmp = HDMI_IO_Read(DeviceAddr, ADV7533_CHIPID_ADDR0);
  tmp = (tmp<<8);
  tmp |= HDMI_IO_Read(DeviceAddr, ADV7533_CHIPID_ADDR1);
  
  return(tmp);
}

/**
  * @brief Pauses playing on the audio hdmi
  * @param DeviceAddr: Device address on communication Bus. 
  * @retval 0 if correct communication, else wrong communication
  */
uint32_t adv7533_Pause(uint16_t DeviceAddr)
{ 
  return(adv7533_SetMute(DeviceAddr,AUDIO_MUTE_ON));
}       
            
/**
  * @brief Resumes playing on the audio hdmi.
  * @param DeviceAddr: Device address on communication Bus. 
  * @retval 0 if correct communication, else wrong communication
  */   
uint32_t adv7533_Resume(uint16_t DeviceAddr)
{ 
  return(adv7533_SetMute(DeviceAddr,AUDIO_MUTE_OFF));
} 

/**
  * @brief Start the audio hdmi play feature.
  * @note  For this codec no Play options are required.
  * @param DeviceAddr: Device address on communication Bus.   
  * @retval 0 if correct communication, else wrong communication
  */
uint32_t adv7533_Play(uint16_t DeviceAddr ,uint16_t* pBuffer  ,uint16_t Size)
{
  return(adv7533_SetMute(DeviceAddr,AUDIO_MUTE_OFF));
}
            
/**
  * @brief Stop playing on the audio hdmi
  * @param DeviceAddr: Device address on communication Bus. 
  * @retval 0 if correct communication, else wrong communication
  */
uint32_t adv7533_Stop(uint16_t DeviceAddr,uint32_t cmd)
{ 
  return(adv7533_SetMute(DeviceAddr,AUDIO_MUTE_ON));
}               
            
/**
  * @brief Enables or disables the mute feature on the audio hdmi.
  * @param DeviceAddr: Device address on communication Bus.   
  * @param Cmd: AUDIO_MUTE_ON to enable the mute or AUDIO_MUTE_OFF to disable the
  *             mute mode.
  * @retval 0 if correct communication, else wrong communication
  */
uint32_t adv7533_SetMute(uint16_t DeviceAddr, uint32_t Cmd)
{
  uint8_t tmp = 0;
  
  tmp = HDMI_IO_Read(DeviceAddr, 0x0D);
  if (Cmd == AUDIO_MUTE_ON)  
  {
    /* enable audio mute*/ 
    tmp |= 0x40;
    HDMI_IO_Write(DeviceAddr, 0x0D, tmp);
  }
  else
  {
    /*audio mute off disable the mute */
    tmp &= ~0x40;
    HDMI_IO_Write(DeviceAddr, 0x0D, tmp);
  }
  return 0;
}

/**
  * @brief Sets output mode.
  * @param DeviceAddr: Device address on communication Bus.
  * @param Output : hdmi output.
  * @retval 0 if correct communication, else wrong communication
  */
uint32_t adv7533_SetOutputMode(uint16_t DeviceAddr, uint8_t Output)
{
  return 0;
}    
            
/**
  * @brief Sets volumee.
  * @param DeviceAddr: Device address on communication Bus.
  * @param Volume : volume value.
  * @retval 0 if correct communication, else wrong communication
  */           
uint32_t adv7533_SetVolume(uint16_t DeviceAddr, uint8_t Volume)
{
 return 0;
}
            
/**
  * @brief Resets adv7533 registers.
  * @param DeviceAddr: Device address on communication Bus. 
  * @retval 0 if correct communication, else wrong communication
  */
uint32_t adv7533_Reset(uint16_t DeviceAddr)
{
  return 0;
}

/**
  * @brief Sets new frequency.
  * @param DeviceAddr: Device address on communication Bus.
  * @param AudioFreq: Audio frequency used to play the audio stream.
  * @retval 0 if correct communication, else wrong communication
  */
uint32_t adv7533_SetFrequency(uint16_t DeviceAddr, uint32_t AudioFreq)
{
  uint8_t tmp = 0;

  tmp = HDMI_IO_Read(DeviceAddr, 0x15);
  tmp &= (~0xF0);
  /*  Clock Configurations */
  switch (AudioFreq)
  {
  case  AUDIO_FREQUENCY_32K:
    /* Sampling Frequency =32 KHZ*/
    tmp |= 0x30;
    HDMI_IO_Write(DeviceAddr, 0x15, tmp);
    break;
  case  AUDIO_FREQUENCY_44K: 
    /* Sampling Frequency =44,1 KHZ*/
    tmp |= 0x00;
    HDMI_IO_Write(DeviceAddr, 0x15, tmp);
    break;
    
  case  AUDIO_FREQUENCY_48K: 
    /* Sampling Frequency =48KHZ*/
    tmp |= 0x20;
    HDMI_IO_Write(DeviceAddr, 0x15, tmp);
    break;
    
  case  AUDIO_FREQUENCY_96K: 
    /* Sampling Frequency =96 KHZ*/
    tmp |= 0xA0;
    HDMI_IO_Write(DeviceAddr, 0x15, tmp);
    break;
    
  case  AUDIO_FREQUENCY_88K: 
    /* Sampling Frequency =88,2 KHZ*/
    tmp |= 0x80;
    HDMI_IO_Write(DeviceAddr, 0x15, tmp);
    break;
    
  case  AUDIO_FREQUENCY_176K: 
    /* Sampling Frequency =176,4 KHZ*/
    tmp |= 0xC0;
    HDMI_IO_Write(DeviceAddr, 0x15, tmp);
    break;
    
  case  AUDIO_FREQUENCY_192K: 
    /* Sampling Frequency =192KHZ*/
    tmp |= 0xE0;
    HDMI_IO_Write(DeviceAddr, 0x15, tmp);
    break;
  }
  return 0;
}

/**
  * @}
  */

/**
  * @}
  */

/**
  * @}
  */

/**
  * @}
  */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
