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