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