1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) STMicroelectronics SA 2014
4 * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
7 #include <drm/drm_print.h>
9 #include "sti_hdmi_tx3g4c28phy.h"
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
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)
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)
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)
42 #define HDMI_TIMEOUT_PLL_LOCK 50 /*milliseconds */
44 struct plldividers_s
{
52 * Functional specification recommended values
55 static 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
}
63 #define NB_HDMI_PHY_CONFIG 2
64 static struct hdmi_phy_config hdmiphy_config
[NB_HDMI_PHY_CONFIG
] = {
65 {0, 250000000, {0x0, 0x0, 0x0, 0x0} },
66 {250000000, 300000000, {0x1110, 0x0, 0x0, 0x0} },
70 * Start hdmi phy macro cell tx3g4c28
72 * @hdmi: pointer on the hdmi internal structure
74 * Return false if an error occur
76 static bool sti_hdmi_tx3g4c28phy_start(struct sti_hdmi
*hdmi
)
78 u32 ckpxpll
= hdmi
->mode
.clock
* 1000;
79 u32 val
, tmdsck
, idf
, odf
, pllctrl
= 0;
80 bool foundplldivides
= false;
83 DRM_DEBUG_DRIVER("ckpxpll = %dHz\n", ckpxpll
);
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;
95 if (!foundplldivides
) {
96 DRM_ERROR("input TMDS clock speed (%d) not supported\n",
101 /* Assuming no pixel repetition and 24bits color */
103 pllctrl
|= 40 << PLL_CFG_NDIV_SHIFT
;
105 if (tmdsck
> 340000000) {
106 DRM_ERROR("output TMDS clock (%d) out of range\n", tmdsck
);
110 pllctrl
|= idf
<< PLL_CFG_IDF_SHIFT
;
111 pllctrl
|= odf
<< PLL_CFG_ODF_SHIFT
;
114 * Configure and power up the PHY PLL
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
);
120 /* wait PLL interrupt */
121 wait_event_interruptible_timeout(hdmi
->wait_event
,
122 hdmi
->event_received
== true,
124 (HDMI_TIMEOUT_PLL_LOCK
));
126 if ((hdmi_read(hdmi
, HDMI_STA
) & HDMI_STA_DLL_LCK
) == 0) {
127 DRM_ERROR("hdmi phy pll not locked\n");
131 DRM_DEBUG_DRIVER("got PHY PLL Lock\n");
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
);
138 if (tmdsck
> 165000000)
139 val
|= HDMI_SRZ_CFG_EN_SRC_TERMINATION
;
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.
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
);
153 val
= hdmiphy_config
[i
].config
[1];
154 hdmi_write(hdmi
, val
, HDMI_SRZ_ICNTL
);
156 val
= hdmiphy_config
[i
].config
[2];
157 hdmi_write(hdmi
, val
, HDMI_SRZ_CALCODE_EXT
);
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]);
168 * Default, power up the serializer with no pre-emphasis or
169 * output swing correction
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
);
182 * Stop hdmi phy macro cell tx3g4c28
184 * @hdmi: pointer on the hdmi internal structure
186 static void sti_hdmi_tx3g4c28phy_stop(struct sti_hdmi
*hdmi
)
190 DRM_DEBUG_DRIVER("\n");
192 hdmi
->event_received
= false;
194 val
= HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION
;
195 val
|= HDMI_SRZ_CFG_EN_BIASRES_DETECTION
;
197 hdmi_write(hdmi
, val
, HDMI_SRZ_CFG
);
198 hdmi_write(hdmi
, 0, HDMI_SRZ_PLL_CFG
);
200 /* wait PLL interrupt */
201 wait_event_interruptible_timeout(hdmi
->wait_event
,
202 hdmi
->event_received
== true,
204 (HDMI_TIMEOUT_PLL_LOCK
));
206 if (hdmi_read(hdmi
, HDMI_STA
) & HDMI_STA_DLL_LCK
)
207 DRM_ERROR("hdmi phy pll not well disabled\n");
210 struct hdmi_phy_ops tx3g4c28phy_ops
= {
211 .start
= sti_hdmi_tx3g4c28phy_start
,
212 .stop
= sti_hdmi_tx3g4c28phy_stop
,