sti_hdmi_tx3g4c28phy.c (5945B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (C) STMicroelectronics SA 2014 4 * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics. 5 */ 6 7#include <drm/drm_print.h> 8 9#include "sti_hdmi_tx3g4c28phy.h" 10 11#define HDMI_SRZ_CFG 0x504 12#define HDMI_SRZ_PLL_CFG 0x510 13#define HDMI_SRZ_ICNTL 0x518 14#define HDMI_SRZ_CALCODE_EXT 0x520 15 16#define HDMI_SRZ_CFG_EN BIT(0) 17#define HDMI_SRZ_CFG_DISABLE_BYPASS_SINK_CURRENT BIT(1) 18#define HDMI_SRZ_CFG_EXTERNAL_DATA BIT(16) 19#define HDMI_SRZ_CFG_RBIAS_EXT BIT(17) 20#define HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION BIT(18) 21#define HDMI_SRZ_CFG_EN_BIASRES_DETECTION BIT(19) 22#define HDMI_SRZ_CFG_EN_SRC_TERMINATION BIT(24) 23 24#define HDMI_SRZ_CFG_INTERNAL_MASK (HDMI_SRZ_CFG_EN | \ 25 HDMI_SRZ_CFG_DISABLE_BYPASS_SINK_CURRENT | \ 26 HDMI_SRZ_CFG_EXTERNAL_DATA | \ 27 HDMI_SRZ_CFG_RBIAS_EXT | \ 28 HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION | \ 29 HDMI_SRZ_CFG_EN_BIASRES_DETECTION | \ 30 HDMI_SRZ_CFG_EN_SRC_TERMINATION) 31 32#define PLL_CFG_EN BIT(0) 33#define PLL_CFG_NDIV_SHIFT (8) 34#define PLL_CFG_IDF_SHIFT (16) 35#define PLL_CFG_ODF_SHIFT (24) 36 37#define ODF_DIV_1 (0) 38#define ODF_DIV_2 (1) 39#define ODF_DIV_4 (2) 40#define ODF_DIV_8 (3) 41 42#define HDMI_TIMEOUT_PLL_LOCK 50 /*milliseconds */ 43 44struct plldividers_s { 45 uint32_t min; 46 uint32_t max; 47 uint32_t idf; 48 uint32_t odf; 49}; 50 51/* 52 * Functional specification recommended values 53 */ 54#define NB_PLL_MODE 5 55static struct plldividers_s plldividers[NB_PLL_MODE] = { 56 {0, 20000000, 1, ODF_DIV_8}, 57 {20000000, 42500000, 2, ODF_DIV_8}, 58 {42500000, 85000000, 4, ODF_DIV_4}, 59 {85000000, 170000000, 8, ODF_DIV_2}, 60 {170000000, 340000000, 16, ODF_DIV_1} 61}; 62 63#define NB_HDMI_PHY_CONFIG 2 64static struct hdmi_phy_config hdmiphy_config[NB_HDMI_PHY_CONFIG] = { 65 {0, 250000000, {0x0, 0x0, 0x0, 0x0} }, 66 {250000000, 300000000, {0x1110, 0x0, 0x0, 0x0} }, 67}; 68 69/** 70 * sti_hdmi_tx3g4c28phy_start - Start hdmi phy macro cell tx3g4c28 71 * 72 * @hdmi: pointer on the hdmi internal structure 73 * 74 * Return false if an error occur 75 */ 76static bool sti_hdmi_tx3g4c28phy_start(struct sti_hdmi *hdmi) 77{ 78 u32 ckpxpll = hdmi->mode.clock * 1000; 79 u32 val, tmdsck, idf, odf, pllctrl = 0; 80 bool foundplldivides = false; 81 int i; 82 83 DRM_DEBUG_DRIVER("ckpxpll = %dHz\n", ckpxpll); 84 85 for (i = 0; i < NB_PLL_MODE; i++) { 86 if (ckpxpll >= plldividers[i].min && 87 ckpxpll < plldividers[i].max) { 88 idf = plldividers[i].idf; 89 odf = plldividers[i].odf; 90 foundplldivides = true; 91 break; 92 } 93 } 94 95 if (!foundplldivides) { 96 DRM_ERROR("input TMDS clock speed (%d) not supported\n", 97 ckpxpll); 98 goto err; 99 } 100 101 /* Assuming no pixel repetition and 24bits color */ 102 tmdsck = ckpxpll; 103 pllctrl |= 40 << PLL_CFG_NDIV_SHIFT; 104 105 if (tmdsck > 340000000) { 106 DRM_ERROR("output TMDS clock (%d) out of range\n", tmdsck); 107 goto err; 108 } 109 110 pllctrl |= idf << PLL_CFG_IDF_SHIFT; 111 pllctrl |= odf << PLL_CFG_ODF_SHIFT; 112 113 /* 114 * Configure and power up the PHY PLL 115 */ 116 hdmi->event_received = false; 117 DRM_DEBUG_DRIVER("pllctrl = 0x%x\n", pllctrl); 118 hdmi_write(hdmi, (pllctrl | PLL_CFG_EN), HDMI_SRZ_PLL_CFG); 119 120 /* wait PLL interrupt */ 121 wait_event_interruptible_timeout(hdmi->wait_event, 122 hdmi->event_received == true, 123 msecs_to_jiffies 124 (HDMI_TIMEOUT_PLL_LOCK)); 125 126 if ((hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK) == 0) { 127 DRM_ERROR("hdmi phy pll not locked\n"); 128 goto err; 129 } 130 131 DRM_DEBUG_DRIVER("got PHY PLL Lock\n"); 132 133 val = (HDMI_SRZ_CFG_EN | 134 HDMI_SRZ_CFG_EXTERNAL_DATA | 135 HDMI_SRZ_CFG_EN_BIASRES_DETECTION | 136 HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION); 137 138 if (tmdsck > 165000000) 139 val |= HDMI_SRZ_CFG_EN_SRC_TERMINATION; 140 141 /* 142 * To configure the source termination and pre-emphasis appropriately 143 * for different high speed TMDS clock frequencies a phy configuration 144 * table must be provided, tailored to the SoC and board combination. 145 */ 146 for (i = 0; i < NB_HDMI_PHY_CONFIG; i++) { 147 if ((hdmiphy_config[i].min_tmds_freq <= tmdsck) && 148 (hdmiphy_config[i].max_tmds_freq >= tmdsck)) { 149 val |= (hdmiphy_config[i].config[0] 150 & ~HDMI_SRZ_CFG_INTERNAL_MASK); 151 hdmi_write(hdmi, val, HDMI_SRZ_CFG); 152 153 val = hdmiphy_config[i].config[1]; 154 hdmi_write(hdmi, val, HDMI_SRZ_ICNTL); 155 156 val = hdmiphy_config[i].config[2]; 157 hdmi_write(hdmi, val, HDMI_SRZ_CALCODE_EXT); 158 159 DRM_DEBUG_DRIVER("serializer cfg 0x%x 0x%x 0x%x\n", 160 hdmiphy_config[i].config[0], 161 hdmiphy_config[i].config[1], 162 hdmiphy_config[i].config[2]); 163 return true; 164 } 165 } 166 167 /* 168 * Default, power up the serializer with no pre-emphasis or 169 * output swing correction 170 */ 171 hdmi_write(hdmi, val, HDMI_SRZ_CFG); 172 hdmi_write(hdmi, 0x0, HDMI_SRZ_ICNTL); 173 hdmi_write(hdmi, 0x0, HDMI_SRZ_CALCODE_EXT); 174 175 return true; 176 177err: 178 return false; 179} 180 181/** 182 * sti_hdmi_tx3g4c28phy_stop - Stop hdmi phy macro cell tx3g4c28 183 * 184 * @hdmi: pointer on the hdmi internal structure 185 */ 186static void sti_hdmi_tx3g4c28phy_stop(struct sti_hdmi *hdmi) 187{ 188 int val = 0; 189 190 DRM_DEBUG_DRIVER("\n"); 191 192 hdmi->event_received = false; 193 194 val = HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION; 195 val |= HDMI_SRZ_CFG_EN_BIASRES_DETECTION; 196 197 hdmi_write(hdmi, val, HDMI_SRZ_CFG); 198 hdmi_write(hdmi, 0, HDMI_SRZ_PLL_CFG); 199 200 /* wait PLL interrupt */ 201 wait_event_interruptible_timeout(hdmi->wait_event, 202 hdmi->event_received == true, 203 msecs_to_jiffies 204 (HDMI_TIMEOUT_PLL_LOCK)); 205 206 if (hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK) 207 DRM_ERROR("hdmi phy pll not well disabled\n"); 208} 209 210struct hdmi_phy_ops tx3g4c28phy_ops = { 211 .start = sti_hdmi_tx3g4c28phy_start, 212 .stop = sti_hdmi_tx3g4c28phy_stop, 213};