1e2842570SBenjamin Gaignard // SPDX-License-Identifier: GPL-2.0
25402626cSBenjamin Gaignard /*
35402626cSBenjamin Gaignard  * Copyright (C) STMicroelectronics SA 2014
45402626cSBenjamin Gaignard  * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
55402626cSBenjamin Gaignard  */
65402626cSBenjamin Gaignard 
75e2f97a9SSam Ravnborg #include <drm/drm_print.h>
85e2f97a9SSam Ravnborg 
95402626cSBenjamin Gaignard #include "sti_hdmi_tx3g4c28phy.h"
105402626cSBenjamin Gaignard 
115402626cSBenjamin Gaignard #define HDMI_SRZ_CFG                             0x504
125402626cSBenjamin Gaignard #define HDMI_SRZ_PLL_CFG                         0x510
135402626cSBenjamin Gaignard #define HDMI_SRZ_ICNTL                           0x518
145402626cSBenjamin Gaignard #define HDMI_SRZ_CALCODE_EXT                     0x520
155402626cSBenjamin Gaignard 
165402626cSBenjamin Gaignard #define HDMI_SRZ_CFG_EN                          BIT(0)
175402626cSBenjamin Gaignard #define HDMI_SRZ_CFG_DISABLE_BYPASS_SINK_CURRENT BIT(1)
185402626cSBenjamin Gaignard #define HDMI_SRZ_CFG_EXTERNAL_DATA               BIT(16)
195402626cSBenjamin Gaignard #define HDMI_SRZ_CFG_RBIAS_EXT                   BIT(17)
205402626cSBenjamin Gaignard #define HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION      BIT(18)
215402626cSBenjamin Gaignard #define HDMI_SRZ_CFG_EN_BIASRES_DETECTION        BIT(19)
225402626cSBenjamin Gaignard #define HDMI_SRZ_CFG_EN_SRC_TERMINATION          BIT(24)
235402626cSBenjamin Gaignard 
245402626cSBenjamin Gaignard #define HDMI_SRZ_CFG_INTERNAL_MASK  (HDMI_SRZ_CFG_EN     | \
255402626cSBenjamin Gaignard 		HDMI_SRZ_CFG_DISABLE_BYPASS_SINK_CURRENT | \
265402626cSBenjamin Gaignard 		HDMI_SRZ_CFG_EXTERNAL_DATA               | \
275402626cSBenjamin Gaignard 		HDMI_SRZ_CFG_RBIAS_EXT                   | \
285402626cSBenjamin Gaignard 		HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION      | \
295402626cSBenjamin Gaignard 		HDMI_SRZ_CFG_EN_BIASRES_DETECTION        | \
305402626cSBenjamin Gaignard 		HDMI_SRZ_CFG_EN_SRC_TERMINATION)
315402626cSBenjamin Gaignard 
325402626cSBenjamin Gaignard #define PLL_CFG_EN                               BIT(0)
335402626cSBenjamin Gaignard #define PLL_CFG_NDIV_SHIFT                       (8)
345402626cSBenjamin Gaignard #define PLL_CFG_IDF_SHIFT                        (16)
355402626cSBenjamin Gaignard #define PLL_CFG_ODF_SHIFT                        (24)
365402626cSBenjamin Gaignard 
375402626cSBenjamin Gaignard #define ODF_DIV_1                                (0)
385402626cSBenjamin Gaignard #define ODF_DIV_2                                (1)
395402626cSBenjamin Gaignard #define ODF_DIV_4                                (2)
405402626cSBenjamin Gaignard #define ODF_DIV_8                                (3)
415402626cSBenjamin Gaignard 
425402626cSBenjamin Gaignard #define HDMI_TIMEOUT_PLL_LOCK  50  /*milliseconds */
435402626cSBenjamin Gaignard 
445402626cSBenjamin Gaignard struct plldividers_s {
455402626cSBenjamin Gaignard 	uint32_t min;
465402626cSBenjamin Gaignard 	uint32_t max;
475402626cSBenjamin Gaignard 	uint32_t idf;
485402626cSBenjamin Gaignard 	uint32_t odf;
495402626cSBenjamin Gaignard };
505402626cSBenjamin Gaignard 
515402626cSBenjamin Gaignard /*
525402626cSBenjamin Gaignard  * Functional specification recommended values
535402626cSBenjamin Gaignard  */
545402626cSBenjamin Gaignard #define NB_PLL_MODE 5
555402626cSBenjamin Gaignard static struct plldividers_s plldividers[NB_PLL_MODE] = {
565402626cSBenjamin Gaignard 	{0, 20000000, 1, ODF_DIV_8},
575402626cSBenjamin Gaignard 	{20000000, 42500000, 2, ODF_DIV_8},
585402626cSBenjamin Gaignard 	{42500000, 85000000, 4, ODF_DIV_4},
595402626cSBenjamin Gaignard 	{85000000, 170000000, 8, ODF_DIV_2},
605402626cSBenjamin Gaignard 	{170000000, 340000000, 16, ODF_DIV_1}
615402626cSBenjamin Gaignard };
625402626cSBenjamin Gaignard 
635402626cSBenjamin Gaignard #define NB_HDMI_PHY_CONFIG 2
645402626cSBenjamin Gaignard static struct hdmi_phy_config hdmiphy_config[NB_HDMI_PHY_CONFIG] = {
655402626cSBenjamin Gaignard 	{0, 250000000, {0x0, 0x0, 0x0, 0x0} },
665402626cSBenjamin Gaignard 	{250000000, 300000000, {0x1110, 0x0, 0x0, 0x0} },
675402626cSBenjamin Gaignard };
685402626cSBenjamin Gaignard 
695402626cSBenjamin Gaignard /**
70*2f921c07SLee Jones  * sti_hdmi_tx3g4c28phy_start - Start hdmi phy macro cell tx3g4c28
715402626cSBenjamin Gaignard  *
725402626cSBenjamin Gaignard  * @hdmi: pointer on the hdmi internal structure
735402626cSBenjamin Gaignard  *
745402626cSBenjamin Gaignard  * Return false if an error occur
755402626cSBenjamin Gaignard  */
sti_hdmi_tx3g4c28phy_start(struct sti_hdmi * hdmi)765402626cSBenjamin Gaignard static bool sti_hdmi_tx3g4c28phy_start(struct sti_hdmi *hdmi)
775402626cSBenjamin Gaignard {
785402626cSBenjamin Gaignard 	u32 ckpxpll = hdmi->mode.clock * 1000;
795402626cSBenjamin Gaignard 	u32 val, tmdsck, idf, odf, pllctrl = 0;
805402626cSBenjamin Gaignard 	bool foundplldivides = false;
815402626cSBenjamin Gaignard 	int i;
825402626cSBenjamin Gaignard 
835402626cSBenjamin Gaignard 	DRM_DEBUG_DRIVER("ckpxpll = %dHz\n", ckpxpll);
845402626cSBenjamin Gaignard 
855402626cSBenjamin Gaignard 	for (i = 0; i < NB_PLL_MODE; i++) {
865402626cSBenjamin Gaignard 		if (ckpxpll >= plldividers[i].min &&
875402626cSBenjamin Gaignard 		    ckpxpll < plldividers[i].max) {
885402626cSBenjamin Gaignard 			idf = plldividers[i].idf;
895402626cSBenjamin Gaignard 			odf = plldividers[i].odf;
905402626cSBenjamin Gaignard 			foundplldivides = true;
915402626cSBenjamin Gaignard 			break;
925402626cSBenjamin Gaignard 		}
935402626cSBenjamin Gaignard 	}
945402626cSBenjamin Gaignard 
955402626cSBenjamin Gaignard 	if (!foundplldivides) {
965402626cSBenjamin Gaignard 		DRM_ERROR("input TMDS clock speed (%d) not supported\n",
975402626cSBenjamin Gaignard 			  ckpxpll);
985402626cSBenjamin Gaignard 		goto err;
995402626cSBenjamin Gaignard 	}
1005402626cSBenjamin Gaignard 
1015402626cSBenjamin Gaignard 	/* Assuming no pixel repetition and 24bits color */
1025402626cSBenjamin Gaignard 	tmdsck = ckpxpll;
1035402626cSBenjamin Gaignard 	pllctrl |= 40 << PLL_CFG_NDIV_SHIFT;
1045402626cSBenjamin Gaignard 
1055402626cSBenjamin Gaignard 	if (tmdsck > 340000000) {
1065402626cSBenjamin Gaignard 		DRM_ERROR("output TMDS clock (%d) out of range\n", tmdsck);
1075402626cSBenjamin Gaignard 		goto err;
1085402626cSBenjamin Gaignard 	}
1095402626cSBenjamin Gaignard 
1105402626cSBenjamin Gaignard 	pllctrl |= idf << PLL_CFG_IDF_SHIFT;
1115402626cSBenjamin Gaignard 	pllctrl |= odf << PLL_CFG_ODF_SHIFT;
1125402626cSBenjamin Gaignard 
1135402626cSBenjamin Gaignard 	/*
1145402626cSBenjamin Gaignard 	 * Configure and power up the PHY PLL
1155402626cSBenjamin Gaignard 	 */
1165402626cSBenjamin Gaignard 	hdmi->event_received = false;
1175402626cSBenjamin Gaignard 	DRM_DEBUG_DRIVER("pllctrl = 0x%x\n", pllctrl);
1185402626cSBenjamin Gaignard 	hdmi_write(hdmi, (pllctrl | PLL_CFG_EN), HDMI_SRZ_PLL_CFG);
1195402626cSBenjamin Gaignard 
1205402626cSBenjamin Gaignard 	/* wait PLL interrupt */
1215402626cSBenjamin Gaignard 	wait_event_interruptible_timeout(hdmi->wait_event,
1225402626cSBenjamin Gaignard 					 hdmi->event_received == true,
1235402626cSBenjamin Gaignard 					 msecs_to_jiffies
1245402626cSBenjamin Gaignard 					 (HDMI_TIMEOUT_PLL_LOCK));
1255402626cSBenjamin Gaignard 
1265402626cSBenjamin Gaignard 	if ((hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK) == 0) {
1275402626cSBenjamin Gaignard 		DRM_ERROR("hdmi phy pll not locked\n");
1285402626cSBenjamin Gaignard 		goto err;
1295402626cSBenjamin Gaignard 	}
1305402626cSBenjamin Gaignard 
1315402626cSBenjamin Gaignard 	DRM_DEBUG_DRIVER("got PHY PLL Lock\n");
1325402626cSBenjamin Gaignard 
1335402626cSBenjamin Gaignard 	val = (HDMI_SRZ_CFG_EN |
1345402626cSBenjamin Gaignard 	       HDMI_SRZ_CFG_EXTERNAL_DATA |
1355402626cSBenjamin Gaignard 	       HDMI_SRZ_CFG_EN_BIASRES_DETECTION |
1365402626cSBenjamin Gaignard 	       HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION);
1375402626cSBenjamin Gaignard 
1385402626cSBenjamin Gaignard 	if (tmdsck > 165000000)
1395402626cSBenjamin Gaignard 		val |= HDMI_SRZ_CFG_EN_SRC_TERMINATION;
1405402626cSBenjamin Gaignard 
1415402626cSBenjamin Gaignard 	/*
1425402626cSBenjamin Gaignard 	 * To configure the source termination and pre-emphasis appropriately
1435402626cSBenjamin Gaignard 	 * for different high speed TMDS clock frequencies a phy configuration
1445402626cSBenjamin Gaignard 	 * table must be provided, tailored to the SoC and board combination.
1455402626cSBenjamin Gaignard 	 */
1465402626cSBenjamin Gaignard 	for (i = 0; i < NB_HDMI_PHY_CONFIG; i++) {
1475402626cSBenjamin Gaignard 		if ((hdmiphy_config[i].min_tmds_freq <= tmdsck) &&
1485402626cSBenjamin Gaignard 		    (hdmiphy_config[i].max_tmds_freq >= tmdsck)) {
1495402626cSBenjamin Gaignard 			val |= (hdmiphy_config[i].config[0]
1505402626cSBenjamin Gaignard 				& ~HDMI_SRZ_CFG_INTERNAL_MASK);
1515402626cSBenjamin Gaignard 			hdmi_write(hdmi, val, HDMI_SRZ_CFG);
1525402626cSBenjamin Gaignard 
1535402626cSBenjamin Gaignard 			val = hdmiphy_config[i].config[1];
1545402626cSBenjamin Gaignard 			hdmi_write(hdmi, val, HDMI_SRZ_ICNTL);
1555402626cSBenjamin Gaignard 
1565402626cSBenjamin Gaignard 			val = hdmiphy_config[i].config[2];
1575402626cSBenjamin Gaignard 			hdmi_write(hdmi, val, HDMI_SRZ_CALCODE_EXT);
1585402626cSBenjamin Gaignard 
1595402626cSBenjamin Gaignard 			DRM_DEBUG_DRIVER("serializer cfg 0x%x 0x%x 0x%x\n",
1605402626cSBenjamin Gaignard 					 hdmiphy_config[i].config[0],
1615402626cSBenjamin Gaignard 					 hdmiphy_config[i].config[1],
1625402626cSBenjamin Gaignard 					 hdmiphy_config[i].config[2]);
1635402626cSBenjamin Gaignard 			return true;
1645402626cSBenjamin Gaignard 		}
1655402626cSBenjamin Gaignard 	}
1665402626cSBenjamin Gaignard 
1675402626cSBenjamin Gaignard 	/*
1685402626cSBenjamin Gaignard 	 * Default, power up the serializer with no pre-emphasis or
1695402626cSBenjamin Gaignard 	 * output swing correction
1705402626cSBenjamin Gaignard 	 */
1715402626cSBenjamin Gaignard 	hdmi_write(hdmi, val,  HDMI_SRZ_CFG);
1725402626cSBenjamin Gaignard 	hdmi_write(hdmi, 0x0, HDMI_SRZ_ICNTL);
1735402626cSBenjamin Gaignard 	hdmi_write(hdmi, 0x0, HDMI_SRZ_CALCODE_EXT);
1745402626cSBenjamin Gaignard 
1755402626cSBenjamin Gaignard 	return true;
1765402626cSBenjamin Gaignard 
1775402626cSBenjamin Gaignard err:
1785402626cSBenjamin Gaignard 	return false;
1795402626cSBenjamin Gaignard }
1805402626cSBenjamin Gaignard 
1815402626cSBenjamin Gaignard /**
182*2f921c07SLee Jones  * sti_hdmi_tx3g4c28phy_stop - Stop hdmi phy macro cell tx3g4c28
1835402626cSBenjamin Gaignard  *
1845402626cSBenjamin Gaignard  * @hdmi: pointer on the hdmi internal structure
1855402626cSBenjamin Gaignard  */
sti_hdmi_tx3g4c28phy_stop(struct sti_hdmi * hdmi)1865402626cSBenjamin Gaignard static void sti_hdmi_tx3g4c28phy_stop(struct sti_hdmi *hdmi)
1875402626cSBenjamin Gaignard {
1885402626cSBenjamin Gaignard 	int val = 0;
1895402626cSBenjamin Gaignard 
1905402626cSBenjamin Gaignard 	DRM_DEBUG_DRIVER("\n");
1915402626cSBenjamin Gaignard 
1925402626cSBenjamin Gaignard 	hdmi->event_received = false;
1935402626cSBenjamin Gaignard 
1945402626cSBenjamin Gaignard 	val = HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION;
1955402626cSBenjamin Gaignard 	val |= HDMI_SRZ_CFG_EN_BIASRES_DETECTION;
1965402626cSBenjamin Gaignard 
1975402626cSBenjamin Gaignard 	hdmi_write(hdmi, val, HDMI_SRZ_CFG);
1985402626cSBenjamin Gaignard 	hdmi_write(hdmi, 0, HDMI_SRZ_PLL_CFG);
1995402626cSBenjamin Gaignard 
2005402626cSBenjamin Gaignard 	/* wait PLL interrupt */
2015402626cSBenjamin Gaignard 	wait_event_interruptible_timeout(hdmi->wait_event,
2025402626cSBenjamin Gaignard 					 hdmi->event_received == true,
2035402626cSBenjamin Gaignard 					 msecs_to_jiffies
2045402626cSBenjamin Gaignard 					 (HDMI_TIMEOUT_PLL_LOCK));
2055402626cSBenjamin Gaignard 
2065402626cSBenjamin Gaignard 	if (hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK)
2075402626cSBenjamin Gaignard 		DRM_ERROR("hdmi phy pll not well disabled\n");
2085402626cSBenjamin Gaignard }
2095402626cSBenjamin Gaignard 
2105402626cSBenjamin Gaignard struct hdmi_phy_ops tx3g4c28phy_ops = {
2115402626cSBenjamin Gaignard 	.start = sti_hdmi_tx3g4c28phy_start,
2125402626cSBenjamin Gaignard 	.stop = sti_hdmi_tx3g4c28phy_stop,
2135402626cSBenjamin Gaignard };
214