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 /** 705402626cSBenjamin Gaignard * 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 */ 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 /** 1825402626cSBenjamin Gaignard * Stop hdmi phy macro cell tx3g4c28 1835402626cSBenjamin Gaignard * 1845402626cSBenjamin Gaignard * @hdmi: pointer on the hdmi internal structure 1855402626cSBenjamin Gaignard */ 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