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