162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) STMicroelectronics SA 2014 462306a36Sopenharmony_ci * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <drm/drm_print.h> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "sti_hdmi_tx3g4c28phy.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define HDMI_SRZ_CFG 0x504 1262306a36Sopenharmony_ci#define HDMI_SRZ_PLL_CFG 0x510 1362306a36Sopenharmony_ci#define HDMI_SRZ_ICNTL 0x518 1462306a36Sopenharmony_ci#define HDMI_SRZ_CALCODE_EXT 0x520 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define HDMI_SRZ_CFG_EN BIT(0) 1762306a36Sopenharmony_ci#define HDMI_SRZ_CFG_DISABLE_BYPASS_SINK_CURRENT BIT(1) 1862306a36Sopenharmony_ci#define HDMI_SRZ_CFG_EXTERNAL_DATA BIT(16) 1962306a36Sopenharmony_ci#define HDMI_SRZ_CFG_RBIAS_EXT BIT(17) 2062306a36Sopenharmony_ci#define HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION BIT(18) 2162306a36Sopenharmony_ci#define HDMI_SRZ_CFG_EN_BIASRES_DETECTION BIT(19) 2262306a36Sopenharmony_ci#define HDMI_SRZ_CFG_EN_SRC_TERMINATION BIT(24) 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define HDMI_SRZ_CFG_INTERNAL_MASK (HDMI_SRZ_CFG_EN | \ 2562306a36Sopenharmony_ci HDMI_SRZ_CFG_DISABLE_BYPASS_SINK_CURRENT | \ 2662306a36Sopenharmony_ci HDMI_SRZ_CFG_EXTERNAL_DATA | \ 2762306a36Sopenharmony_ci HDMI_SRZ_CFG_RBIAS_EXT | \ 2862306a36Sopenharmony_ci HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION | \ 2962306a36Sopenharmony_ci HDMI_SRZ_CFG_EN_BIASRES_DETECTION | \ 3062306a36Sopenharmony_ci HDMI_SRZ_CFG_EN_SRC_TERMINATION) 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define PLL_CFG_EN BIT(0) 3362306a36Sopenharmony_ci#define PLL_CFG_NDIV_SHIFT (8) 3462306a36Sopenharmony_ci#define PLL_CFG_IDF_SHIFT (16) 3562306a36Sopenharmony_ci#define PLL_CFG_ODF_SHIFT (24) 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define ODF_DIV_1 (0) 3862306a36Sopenharmony_ci#define ODF_DIV_2 (1) 3962306a36Sopenharmony_ci#define ODF_DIV_4 (2) 4062306a36Sopenharmony_ci#define ODF_DIV_8 (3) 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define HDMI_TIMEOUT_PLL_LOCK 50 /*milliseconds */ 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistruct plldividers_s { 4562306a36Sopenharmony_ci uint32_t min; 4662306a36Sopenharmony_ci uint32_t max; 4762306a36Sopenharmony_ci uint32_t idf; 4862306a36Sopenharmony_ci uint32_t odf; 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* 5262306a36Sopenharmony_ci * Functional specification recommended values 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ci#define NB_PLL_MODE 5 5562306a36Sopenharmony_cistatic struct plldividers_s plldividers[NB_PLL_MODE] = { 5662306a36Sopenharmony_ci {0, 20000000, 1, ODF_DIV_8}, 5762306a36Sopenharmony_ci {20000000, 42500000, 2, ODF_DIV_8}, 5862306a36Sopenharmony_ci {42500000, 85000000, 4, ODF_DIV_4}, 5962306a36Sopenharmony_ci {85000000, 170000000, 8, ODF_DIV_2}, 6062306a36Sopenharmony_ci {170000000, 340000000, 16, ODF_DIV_1} 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci#define NB_HDMI_PHY_CONFIG 2 6462306a36Sopenharmony_cistatic struct hdmi_phy_config hdmiphy_config[NB_HDMI_PHY_CONFIG] = { 6562306a36Sopenharmony_ci {0, 250000000, {0x0, 0x0, 0x0, 0x0} }, 6662306a36Sopenharmony_ci {250000000, 300000000, {0x1110, 0x0, 0x0, 0x0} }, 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/** 7062306a36Sopenharmony_ci * sti_hdmi_tx3g4c28phy_start - Start hdmi phy macro cell tx3g4c28 7162306a36Sopenharmony_ci * 7262306a36Sopenharmony_ci * @hdmi: pointer on the hdmi internal structure 7362306a36Sopenharmony_ci * 7462306a36Sopenharmony_ci * Return false if an error occur 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_cistatic bool sti_hdmi_tx3g4c28phy_start(struct sti_hdmi *hdmi) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci u32 ckpxpll = hdmi->mode.clock * 1000; 7962306a36Sopenharmony_ci u32 val, tmdsck, idf, odf, pllctrl = 0; 8062306a36Sopenharmony_ci bool foundplldivides = false; 8162306a36Sopenharmony_ci int i; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci DRM_DEBUG_DRIVER("ckpxpll = %dHz\n", ckpxpll); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci for (i = 0; i < NB_PLL_MODE; i++) { 8662306a36Sopenharmony_ci if (ckpxpll >= plldividers[i].min && 8762306a36Sopenharmony_ci ckpxpll < plldividers[i].max) { 8862306a36Sopenharmony_ci idf = plldividers[i].idf; 8962306a36Sopenharmony_ci odf = plldividers[i].odf; 9062306a36Sopenharmony_ci foundplldivides = true; 9162306a36Sopenharmony_ci break; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (!foundplldivides) { 9662306a36Sopenharmony_ci DRM_ERROR("input TMDS clock speed (%d) not supported\n", 9762306a36Sopenharmony_ci ckpxpll); 9862306a36Sopenharmony_ci goto err; 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* Assuming no pixel repetition and 24bits color */ 10262306a36Sopenharmony_ci tmdsck = ckpxpll; 10362306a36Sopenharmony_ci pllctrl |= 40 << PLL_CFG_NDIV_SHIFT; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (tmdsck > 340000000) { 10662306a36Sopenharmony_ci DRM_ERROR("output TMDS clock (%d) out of range\n", tmdsck); 10762306a36Sopenharmony_ci goto err; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci pllctrl |= idf << PLL_CFG_IDF_SHIFT; 11162306a36Sopenharmony_ci pllctrl |= odf << PLL_CFG_ODF_SHIFT; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* 11462306a36Sopenharmony_ci * Configure and power up the PHY PLL 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_ci hdmi->event_received = false; 11762306a36Sopenharmony_ci DRM_DEBUG_DRIVER("pllctrl = 0x%x\n", pllctrl); 11862306a36Sopenharmony_ci hdmi_write(hdmi, (pllctrl | PLL_CFG_EN), HDMI_SRZ_PLL_CFG); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* wait PLL interrupt */ 12162306a36Sopenharmony_ci wait_event_interruptible_timeout(hdmi->wait_event, 12262306a36Sopenharmony_ci hdmi->event_received == true, 12362306a36Sopenharmony_ci msecs_to_jiffies 12462306a36Sopenharmony_ci (HDMI_TIMEOUT_PLL_LOCK)); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci if ((hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK) == 0) { 12762306a36Sopenharmony_ci DRM_ERROR("hdmi phy pll not locked\n"); 12862306a36Sopenharmony_ci goto err; 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci DRM_DEBUG_DRIVER("got PHY PLL Lock\n"); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci val = (HDMI_SRZ_CFG_EN | 13462306a36Sopenharmony_ci HDMI_SRZ_CFG_EXTERNAL_DATA | 13562306a36Sopenharmony_ci HDMI_SRZ_CFG_EN_BIASRES_DETECTION | 13662306a36Sopenharmony_ci HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (tmdsck > 165000000) 13962306a36Sopenharmony_ci val |= HDMI_SRZ_CFG_EN_SRC_TERMINATION; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* 14262306a36Sopenharmony_ci * To configure the source termination and pre-emphasis appropriately 14362306a36Sopenharmony_ci * for different high speed TMDS clock frequencies a phy configuration 14462306a36Sopenharmony_ci * table must be provided, tailored to the SoC and board combination. 14562306a36Sopenharmony_ci */ 14662306a36Sopenharmony_ci for (i = 0; i < NB_HDMI_PHY_CONFIG; i++) { 14762306a36Sopenharmony_ci if ((hdmiphy_config[i].min_tmds_freq <= tmdsck) && 14862306a36Sopenharmony_ci (hdmiphy_config[i].max_tmds_freq >= tmdsck)) { 14962306a36Sopenharmony_ci val |= (hdmiphy_config[i].config[0] 15062306a36Sopenharmony_ci & ~HDMI_SRZ_CFG_INTERNAL_MASK); 15162306a36Sopenharmony_ci hdmi_write(hdmi, val, HDMI_SRZ_CFG); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci val = hdmiphy_config[i].config[1]; 15462306a36Sopenharmony_ci hdmi_write(hdmi, val, HDMI_SRZ_ICNTL); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci val = hdmiphy_config[i].config[2]; 15762306a36Sopenharmony_ci hdmi_write(hdmi, val, HDMI_SRZ_CALCODE_EXT); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci DRM_DEBUG_DRIVER("serializer cfg 0x%x 0x%x 0x%x\n", 16062306a36Sopenharmony_ci hdmiphy_config[i].config[0], 16162306a36Sopenharmony_ci hdmiphy_config[i].config[1], 16262306a36Sopenharmony_ci hdmiphy_config[i].config[2]); 16362306a36Sopenharmony_ci return true; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci /* 16862306a36Sopenharmony_ci * Default, power up the serializer with no pre-emphasis or 16962306a36Sopenharmony_ci * output swing correction 17062306a36Sopenharmony_ci */ 17162306a36Sopenharmony_ci hdmi_write(hdmi, val, HDMI_SRZ_CFG); 17262306a36Sopenharmony_ci hdmi_write(hdmi, 0x0, HDMI_SRZ_ICNTL); 17362306a36Sopenharmony_ci hdmi_write(hdmi, 0x0, HDMI_SRZ_CALCODE_EXT); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return true; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cierr: 17862306a36Sopenharmony_ci return false; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci/** 18262306a36Sopenharmony_ci * sti_hdmi_tx3g4c28phy_stop - Stop hdmi phy macro cell tx3g4c28 18362306a36Sopenharmony_ci * 18462306a36Sopenharmony_ci * @hdmi: pointer on the hdmi internal structure 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_cistatic void sti_hdmi_tx3g4c28phy_stop(struct sti_hdmi *hdmi) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci int val = 0; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci DRM_DEBUG_DRIVER("\n"); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci hdmi->event_received = false; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci val = HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION; 19562306a36Sopenharmony_ci val |= HDMI_SRZ_CFG_EN_BIASRES_DETECTION; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci hdmi_write(hdmi, val, HDMI_SRZ_CFG); 19862306a36Sopenharmony_ci hdmi_write(hdmi, 0, HDMI_SRZ_PLL_CFG); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* wait PLL interrupt */ 20162306a36Sopenharmony_ci wait_event_interruptible_timeout(hdmi->wait_event, 20262306a36Sopenharmony_ci hdmi->event_received == true, 20362306a36Sopenharmony_ci msecs_to_jiffies 20462306a36Sopenharmony_ci (HDMI_TIMEOUT_PLL_LOCK)); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK) 20762306a36Sopenharmony_ci DRM_ERROR("hdmi phy pll not well disabled\n"); 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistruct hdmi_phy_ops tx3g4c28phy_ops = { 21162306a36Sopenharmony_ci .start = sti_hdmi_tx3g4c28phy_start, 21262306a36Sopenharmony_ci .stop = sti_hdmi_tx3g4c28phy_stop, 21362306a36Sopenharmony_ci}; 214