// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( C ) STMicroelectronics SA 2014
* Author : Vincent Abriou < vincent . abriou @ st . com > for STMicroelectronics .
*/
#include <drm/drm_print.h>
#include "sti_hdmi_tx3g4c28phy.h"
#define HDMI_SRZ_CFG 0 x504
#define HDMI_SRZ_PLL_CFG 0 x510
#define HDMI_SRZ_ICNTL 0 x518
#define HDMI_SRZ_CALCODE_EXT 0 x520
#define HDMI_SRZ_CFG_EN BIT(0 )
#define HDMI_SRZ_CFG_DISABLE_BYPASS_SINK_CURRENT BIT(1 )
#define HDMI_SRZ_CFG_EXTERNAL_DATA BIT(16 )
#define HDMI_SRZ_CFG_RBIAS_EXT BIT(17 )
#define HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION BIT(18 )
#define HDMI_SRZ_CFG_EN_BIASRES_DETECTION BIT(19 )
#define HDMI_SRZ_CFG_EN_SRC_TERMINATION BIT(24 )
#define HDMI_SRZ_CFG_INTERNAL_MASK (HDMI_SRZ_CFG_EN | \
HDMI_SRZ_CFG_DISABLE_BYPASS_SINK_CURRENT | \
HDMI_SRZ_CFG_EXTERNAL_DATA | \
HDMI_SRZ_CFG_RBIAS_EXT | \
HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION | \
HDMI_SRZ_CFG_EN_BIASRES_DETECTION | \
HDMI_SRZ_CFG_EN_SRC_TERMINATION)
#define PLL_CFG_EN BIT(0 )
#define PLL_CFG_NDIV_SHIFT (8 )
#define PLL_CFG_IDF_SHIFT (16 )
#define PLL_CFG_ODF_SHIFT (24 )
#define ODF_DIV_1 (0 )
#define ODF_DIV_2 (1 )
#define ODF_DIV_4 (2 )
#define ODF_DIV_8 (3 )
#define HDMI_TIMEOUT_PLL_LOCK 50 /*milliseconds */
struct plldividers_s {
uint32_t min;
uint32_t max;
uint32_t idf;
uint32_t odf;
};
/*
* Functional specification recommended values
*/
#define NB_PLL_MODE 5
static struct plldividers_s plldividers[NB_PLL_MODE] = {
{0 , 20000000 , 1 , ODF_DIV_8},
{20000000 , 42500000 , 2 , ODF_DIV_8},
{42500000 , 85000000 , 4 , ODF_DIV_4},
{85000000 , 170000000 , 8 , ODF_DIV_2},
{170000000 , 340000000 , 16 , ODF_DIV_1}
};
#define NB_HDMI_PHY_CONFIG 2
static struct hdmi_phy_config hdmiphy_config[NB_HDMI_PHY_CONFIG] = {
{0 , 250000000 , {0 x0, 0 x0, 0 x0, 0 x0} },
{250000000 , 300000000 , {0 x1110, 0 x0, 0 x0, 0 x0} },
};
/**
* sti_hdmi_tx3g4c28phy_start - Start hdmi phy macro cell tx3g4c28
*
* @ hdmi : pointer on the hdmi internal structure
*
* Return false if an error occur
*/
static bool sti_hdmi_tx3g4c28phy_start(struct sti_hdmi *hdmi)
{
u32 ckpxpll = hdmi->mode.clock * 1000 ;
u32 val, tmdsck, idf, odf, pllctrl = 0 ;
bool foundplldivides = false ;
int i;
DRM_DEBUG_DRIVER("ckpxpll = %dHz\n" , ckpxpll);
for (i = 0 ; i < NB_PLL_MODE; i++) {
if (ckpxpll >= plldividers[i].min &&
ckpxpll < plldividers[i].max) {
idf = plldividers[i].idf;
odf = plldividers[i].odf;
foundplldivides = true ;
break ;
}
}
if (!foundplldivides) {
DRM_ERROR("input TMDS clock speed (%d) not supported\n" ,
ckpxpll);
goto err;
}
/* Assuming no pixel repetition and 24bits color */
tmdsck = ckpxpll;
pllctrl |= 40 << PLL_CFG_NDIV_SHIFT;
if (tmdsck > 340000000 ) {
DRM_ERROR("output TMDS clock (%d) out of range\n" , tmdsck);
goto err;
}
pllctrl |= idf << PLL_CFG_IDF_SHIFT;
pllctrl |= odf << PLL_CFG_ODF_SHIFT;
/*
* Configure and power up the PHY PLL
*/
hdmi->event_received = false ;
DRM_DEBUG_DRIVER("pllctrl = 0x%x\n" , pllctrl);
hdmi_write(hdmi, (pllctrl | PLL_CFG_EN), HDMI_SRZ_PLL_CFG);
/* wait PLL interrupt */
wait_event_interruptible_timeout(hdmi->wait_event,
hdmi->event_received == true ,
msecs_to_jiffies
(HDMI_TIMEOUT_PLL_LOCK));
if ((hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK) == 0 ) {
DRM_ERROR("hdmi phy pll not locked\n" );
goto err;
}
DRM_DEBUG_DRIVER("got PHY PLL Lock\n" );
val = (HDMI_SRZ_CFG_EN |
HDMI_SRZ_CFG_EXTERNAL_DATA |
HDMI_SRZ_CFG_EN_BIASRES_DETECTION |
HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION);
if (tmdsck > 165000000 )
val |= HDMI_SRZ_CFG_EN_SRC_TERMINATION;
/*
* To configure the source termination and pre - emphasis appropriately
* for different high speed TMDS clock frequencies a phy configuration
* table must be provided , tailored to the SoC and board combination .
*/
for (i = 0 ; i < NB_HDMI_PHY_CONFIG; i++) {
if ((hdmiphy_config[i].min_tmds_freq <= tmdsck) &&
(hdmiphy_config[i].max_tmds_freq >= tmdsck)) {
val |= (hdmiphy_config[i].config[0 ]
& ~HDMI_SRZ_CFG_INTERNAL_MASK);
hdmi_write(hdmi, val, HDMI_SRZ_CFG);
val = hdmiphy_config[i].config[1 ];
hdmi_write(hdmi, val, HDMI_SRZ_ICNTL);
val = hdmiphy_config[i].config[2 ];
hdmi_write(hdmi, val, HDMI_SRZ_CALCODE_EXT);
DRM_DEBUG_DRIVER("serializer cfg 0x%x 0x%x 0x%x\n" ,
hdmiphy_config[i].config[0 ],
hdmiphy_config[i].config[1 ],
hdmiphy_config[i].config[2 ]);
return true ;
}
}
/*
* Default , power up the serializer with no pre - emphasis or
* output swing correction
*/
hdmi_write(hdmi, val, HDMI_SRZ_CFG);
hdmi_write(hdmi, 0 x0, HDMI_SRZ_ICNTL);
hdmi_write(hdmi, 0 x0, HDMI_SRZ_CALCODE_EXT);
return true ;
err:
return false ;
}
/**
* sti_hdmi_tx3g4c28phy_stop - Stop hdmi phy macro cell tx3g4c28
*
* @ hdmi : pointer on the hdmi internal structure
*/
static void sti_hdmi_tx3g4c28phy_stop(struct sti_hdmi *hdmi)
{
int val = 0 ;
DRM_DEBUG_DRIVER("\n" );
hdmi->event_received = false ;
val = HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION;
val |= HDMI_SRZ_CFG_EN_BIASRES_DETECTION;
hdmi_write(hdmi, val, HDMI_SRZ_CFG);
hdmi_write(hdmi, 0 , HDMI_SRZ_PLL_CFG);
/* wait PLL interrupt */
wait_event_interruptible_timeout(hdmi->wait_event,
hdmi->event_received == true ,
msecs_to_jiffies
(HDMI_TIMEOUT_PLL_LOCK));
if (hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK)
DRM_ERROR("hdmi phy pll not well disabled\n" );
}
struct hdmi_phy_ops tx3g4c28phy_ops = {
.start = sti_hdmi_tx3g4c28phy_start,
.stop = sti_hdmi_tx3g4c28phy_stop,
};
Messung V0.5 in Prozent C=95 H=75 G=85
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland