All you guys need is NXP source deliverable.
Here it is: GitHub - joelagnel/beagle-nxp-hdmi: TDA19988 driver for Linux Kernel
This deliverable is for Linux, but exists for MCU's as vel. Both share same driver.
Read beagle-nxp-hdmi/hdmi/comps/tmdlHdmiTx/docs/14_user_doc/TRANSMITTER_TDA998X_SW_UM_Devlib.pdf at maste... on how to set up driver or port to other OS. Driver supports Linux/Android/RTOS/Bare Metal.
In my implementation for BT656 source and STM32 I included:
tmdlHdmiTx.c
tmdlHdmiTx_local.c
tmbslTDA9989_edid.c
tmbslTDA9989_HDCP.c
tmbslTDA9989_InOut.c
tmbslTDA9989_local.c
tmbslTDA9989_misc.c
tmbslTDA9989_state.c
I modified supplied templates tmdlHdmiTx_cfg.c and tmdlHdmiTx_IW.c according to that pdf (above).
And my initialization code, which uses NXP driver:
int hdmi_enable(void)
{
tda_instance *this = &our_instance;
int err = 0;
LOG("called\n");
#ifndef STM32F446xx
down(&this->driver.sem);
#endif
this->driver.omap_dss_hdmi_panel = true;
this->tda.power = tmPowerOn;
TRY(tmdlHdmiTxSetPowerState(this->tda.instance, this->tda.power));
if (err == TM_ERR_NO_RESOURCES)
{
LOG("Busy...\n");
TRY(tmdlHdmiTxHandleInterrupt(this->tda.instance));
TRY(tmdlHdmiTxHandleInterrupt(this->tda.instance));
TRY(tmdlHdmiTxHandleInterrupt(this->tda.instance));
}
tmdlHdmiTxGetHPDStatus(this->tda.instance, &this->tda.hot_plug_detect);
show_video(this);
TRY_DONE:
#ifndef STM32F446xx
up(&this->driver.sem);
#endif
return err;
}
static int hdmi_tx_init(tda_instance *this)
{
int err = 0;
LOG("called\n");
/*
general device context
*/
memset(this, 0, sizeof(tda_instance));
this->param.verbose = param_verbose;
this->param.major = param_major;
this->param.minor = param_minor;
/*Initialize HDMI Transmiter*/
TRY(tmdlHdmiTxOpen(&this->tda.instance));
/* Register the HDMI TX events callbacks */
TRY(tmdlHdmiTxRegisterCallbacks(this->tda.instance, (ptmdlHdmiTxCallback_t)eventCallbackTx));
/* EnableEvent, all by default */
TRY(tmdlHdmiTxEnableEvent(this->tda.instance, TMDL_HDMITX_HDCP_ACTIVE));
TRY(tmdlHdmiTxEnableEvent(this->tda.instance, TMDL_HDMITX_HDCP_INACTIVE));
TRY(tmdlHdmiTxEnableEvent(this->tda.instance, TMDL_HDMITX_HPD_ACTIVE));
TRY(tmdlHdmiTxEnableEvent(this->tda.instance, TMDL_HDMITX_HPD_INACTIVE));
TRY(tmdlHdmiTxEnableEvent(this->tda.instance, TMDL_HDMITX_RX_KEYS_RECEIVED));
TRY(tmdlHdmiTxEnableEvent(this->tda.instance, TMDL_HDMITX_RX_DEVICE_ACTIVE));
TRY(tmdlHdmiTxEnableEvent(this->tda.instance, TMDL_HDMITX_RX_DEVICE_INACTIVE));
TRY(tmdlHdmiTxEnableEvent(this->tda.instance, TMDL_HDMITX_EDID_RECEIVED));
/* Size of the application EDID buffer */
this->tda.setup.edidBufferSize = EDID_BLOCK_COUNT * EDID_BLOCK_SIZE;
/* Buffer to store the application EDID data */
this->tda.setup.pEdidBuffer = this->tda.raw_edid;
/* To Enable/disable repeater feature, nor relevant here */
this->tda.setup.repeaterEnable = false;
/* To enable/disable simplayHD feature: blue screen when not authenticated */
#ifdef SIMPLAYHD
this->tda.setup.simplayHd = (this->tda.hdcp_enable ? true : false);
#else
this->tda.setup.simplayHd = false;
#endif
/* Provides HDMI TX instance configuration */
TRY(tmdlHdmiTxInstanceSetup(this->tda.instance, &this->tda.setup));
/* Get IC version */
TRY(tmdlHdmiTxGetCapabilities(&this->tda.capabilities));
/* Main settings */
this->tda.setio.video_out.mode = TMDL_HDMITX_VOUTMODE_RGB444;
this->tda.setio.video_out.colorDepth = TMDL_HDMITX_COLORDEPTH_24;
#ifdef TMFL_TDA19989
this->tda.setio.video_out.dviVqr = TMDL_HDMITX_VQR_DEFAULT; /* Use HDMI rules for DVI output */
#endif
this->tda.setio.video_out.format = TMDL_HDMITX_VFMT_19_1280x720p_50Hz;
this->tda.setio.video_in.mode = TMDL_HDMITX_VINMODE_CCIR656;
this->tda.setio.video_in.format = this->tda.setio.video_out.format;
this->tda.setio.video_in.pixelRate = TMDL_HDMITX_PIXRATE_SINGLE;
this->tda.setio.video_in.syncSource = TMDL_HDMITX_SYNCSRC_EMBEDDED; /* we use HS,VS as synchronisation source */
this->tda.setio.audio_in.format = TMDL_HDMITX_AFMT_I2S;
this->tda.setio.audio_in.rate = TMDL_HDMITX_AFS_44K;
this->tda.setio.audio_in.i2sFormat = TMDL_HDMITX_I2SFOR_PHILIPS_L;
this->tda.setio.audio_in.i2sQualifier = TMDL_HDMITX_I2SQ_16BITS;
this->tda.setio.audio_in.dstRate = TMDL_HDMITX_DSTRATE_SINGLE; /* not relevant here */
this->tda.setio.audio_in.channelAllocation = 0; /* audio channel allocation (Ref to CEA-861D p85) */
/* audio channel allocation (Ref to CEA-861D p85) */
this->tda.setio.audio_in.channelStatus.PcmIdentification = TMDL_HDMITX_AUDIO_DATA_PCM;
this->tda.setio.audio_in.channelStatus.CopyrightInfo = TMDL_HDMITX_CSCOPYRIGHT_UNPROTECTED;
this->tda.setio.audio_in.channelStatus.FormatInfo = TMDL_HDMITX_CSFI_PCM_2CHAN_NO_PRE;
this->tda.setio.audio_in.channelStatus.categoryCode = 0;
this->tda.setio.audio_in.channelStatus.clockAccuracy = TMDL_HDMITX_CSCLK_LEVEL_II;
this->tda.setio.audio_in.channelStatus.maxWordLength = TMDL_HDMITX_CSMAX_LENGTH_24;
this->tda.setio.audio_in.channelStatus.wordLength = TMDL_HDMITX_CSWORD_DEFAULT;
this->tda.setio.audio_in.channelStatus.origSampleFreq = TMDL_HDMITX_CSOFREQ_44_1k;
this->tda.setio.sink = TMDL_HDMITX_SINK_HDMI; /* skip edid reading */
/* this->tda.src_address = 0x1000; /\* debug *\/ */
this->tda.src_address = NO_PHY_ADDR; /* it's unref */
TRY_DONE:
return err;
}
void hdcp_on(tda_instance *this)
{
int err = 0;
if (this->tda.hdcp_status != HDCP_IS_NOT_INSTALLED)
{ /* check HDCP is installed ... */
if (this->tda.hdcp_enable)
{ /* ... but requested ! */
TRY(tmdlHdmiTxSetHdcp(this->tda.instance, True)); /* switch if on */
#if defined(TMFL_TDA19989) || defined(TMFL_TDA9984)
/* hide video content until HDCP authentification is finished */
if (!this->tda.setup.simplayHd)
{
TRY(tmdlHdmiTxSetBScreen(this->tda.instance, TMDL_HDMITX_PATTERN_BLUE));
}
#endif
}
}
TRY_DONE:
(void)0;
}
/*
* Off HDCP
*/
void hdcp_off(tda_instance *this)
{
int err = 0;
if (this->tda.hdcp_status != HDCP_IS_NOT_INSTALLED)
{ /* check HDCP is installed ... */
if (this->tda.hdcp_enable)
{ /* but no more requested */
TRY(tmdlHdmiTxSetHdcp(this->tda.instance, False)); /* switch if off */
}
}
TRY_DONE:
(void)0;
}
void show_video(tda_instance *this)
{
int err = 0;
if (this->tda.rx_device_active)
{ /* check RxSens */
if (this->tda.hot_plug_detect == TMDL_HDMITX_HOTPLUG_ACTIVE)
{ /* should be useless, but legacy... */
if (this->tda.power == tmPowerOn)
{ /* check CEC or DSS didn't switch it off */
if (this->tda.src_address != 0xFFFF)
{ /* check EDID has been received */
hdcp_off(this);
TRY(tmdlHdmiTxSetInputOutput(this->tda.instance,
this->tda.setio.video_in,
this->tda.setio.video_out,
this->tda.setio.audio_in,
this->tda.setio.sink));
hdcp_on(this);
/*
Mind that SetInputOutput disable the blue color matrix settings of tmdlHdmiTxSetBScreen ...
so put tmdlHdmiTxSetBScreen (or hdcp_on) always after
*/
}
}
}
}
TRY_DONE:
(void)0;
}
int hdmi_disable(int event_tracking)
{
tda_instance *this = &our_instance;
int err = 0;
LOG("called\n");
#ifndef STM32F446xx
down(&this->driver.sem);
#endif
this->tda.power = (event_tracking ? tmPowerSuspend : tmPowerStandby);
TRY(tmdlHdmiTxSetPowerState(this->tda.instance, this->tda.power));
TRY_DONE:
this->driver.omap_dss_hdmi_panel = false;
#ifndef STM32F446xx
up(&this->driver.sem);
#endif
return err;
}
int hdmi_enable(void)
{
tda_instance *this = &our_instance;
int err = 0;
LOG("called\n");
#ifndef STM32F446xx
down(&this->driver.sem);
#endif
this->driver.omap_dss_hdmi_panel = true;
this->tda.power = tmPowerOn;
TRY(tmdlHdmiTxSetPowerState(this->tda.instance, this->tda.power));
if (err == TM_ERR_NO_RESOURCES)
{
LOG("Busy...\n");
TRY(tmdlHdmiTxHandleInterrupt(this->tda.instance));
TRY(tmdlHdmiTxHandleInterrupt(this->tda.instance));
TRY(tmdlHdmiTxHandleInterrupt(this->tda.instance));
}
tmdlHdmiTxGetHPDStatus(this->tda.instance, &this->tda.hot_plug_detect);
show_video(this);
TRY_DONE:
#ifndef STM32F446xx
up(&this->driver.sem);
#endif
return err;
}
static int tx_init(void)
{
tda_instance *this = &our_instance;
// dev_t dev=0;
int err = 0;
/* Hello word */
I2C_PRINTF("%s(%s) %d.%d.%d compiled: %s %s %s\n", HDMITX_NAME, "TDA19988",
0,
0,
0,
__DATE__, __TIME__, 0);
if (this->param.verbose)
LOG(".verbose mode\n");
/*
/!\ WARNING /! \
the startup power sequence SHALL BE standby AND THEN suspend (core driver legacy...)
this is the only way to get the TDA idle but with active HDP and RxSens interrupt listening
*/
hdmi_disable(1); /* power start sequence phase 2 */
/*
/!\ WARNING /! \
if HDMI is plugged, the core driver will send HDP nor RXSENS event when beeing powered on !
So the Android HDMI service shall start by asking the HDP status using the IOCTL GET_HPD_STATUS
*/
tmdlHdmiTxGetHPDStatus(this->tda.instance,
&this->tda.hot_plug_detect); /* power start sequence phase 3 */
/* sysfs_attrs */
#ifndef STM32F446xx
comm_init(); /* create display sysfs links (not used in STM32F4XX) */
#endif
hdmi_enable();
return 0;
}
uint8_t hdmiInit(void)
{
tda_instance *this = &our_instance;
// dev_t dev=0;
uint8_t err = 0;
tmErrorCode_t status = TM_OK;
/* High level */
err = hdmi_tx_init(this);
if (err)
goto i2c_out;
this->tda.hdcp_enable = 0;
/* Standby the HDMI TX instance : this is mandatory for TDA boot up sequence, do not change it */
this->tda.power = tmPowerStandby; /* power start sequence phase 1, see phase 2 */
tmdlHdmiTxSetPowerState(this->tda.instance, this->tda.power);
/* update HPD */
tmdlHdmiTxGetHPDStatus(this->tda.instance, &this->tda.hot_plug_detect);
tmdlHdmiTxGetSWVersion(&this->tda.version);
I2C_PRINTF("HDMI TX SW Version:%lu.%lu compatibility:%lu\n",
this->tda.version.majorVersionNr,
this->tda.version.minorVersionNr,
this->tda.version.compatibilityNr);
tx_init();
return 0;
i2c_tx_out:
LOG("tmdlHdmiTx closed\n");
/* close DevLib */
err = tmdlHdmiTxClose(this->tda.instance);
i2c_out:
LOG("this->driver.i2c_client removed\n");
this->driver.i2c_client = NULL;
return err;
}
Call hdmiInit in your initialization sequence after I2C was initialized. And in while loop check for interrupt (since most of the driver can't be run from ISR):
if (hdmiIntDetected > 0)
{
tmdlHdmiTxHandleInterrupt(hdmiInstance);
hdmiIntDetected = 0;
}