2 * Copyright (C) STMicroelectronics SA 2014
3 * Author: Vincent Abriou <vincent.abriou@st.com> for STMicroelectronics.
4 * License terms: GNU General Public License (GPL), version 2
7 #include "sti_hdmi_tx3g4c28phy.h"
9 #define HDMI_SRZ_CFG 0x504
10 #define HDMI_SRZ_PLL_CFG 0x510
11 #define HDMI_SRZ_ICNTL 0x518
12 #define HDMI_SRZ_CALCODE_EXT 0x520
14 #define HDMI_SRZ_CFG_EN BIT(0)
15 #define HDMI_SRZ_CFG_DISABLE_BYPASS_SINK_CURRENT BIT(1)
16 #define HDMI_SRZ_CFG_EXTERNAL_DATA BIT(16)
17 #define HDMI_SRZ_CFG_RBIAS_EXT BIT(17)
18 #define HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION BIT(18)
19 #define HDMI_SRZ_CFG_EN_BIASRES_DETECTION BIT(19)
20 #define HDMI_SRZ_CFG_EN_SRC_TERMINATION BIT(24)
22 #define HDMI_SRZ_CFG_INTERNAL_MASK (HDMI_SRZ_CFG_EN | \
23 HDMI_SRZ_CFG_DISABLE_BYPASS_SINK_CURRENT | \
24 HDMI_SRZ_CFG_EXTERNAL_DATA | \
25 HDMI_SRZ_CFG_RBIAS_EXT | \
26 HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION | \
27 HDMI_SRZ_CFG_EN_BIASRES_DETECTION | \
28 HDMI_SRZ_CFG_EN_SRC_TERMINATION)
30 #define PLL_CFG_EN BIT(0)
31 #define PLL_CFG_NDIV_SHIFT (8)
32 #define PLL_CFG_IDF_SHIFT (16)
33 #define PLL_CFG_ODF_SHIFT (24)
40 #define HDMI_TIMEOUT_PLL_LOCK 50 /*milliseconds */
42 struct plldividers_s
{
50 * Functional specification recommended values
53 static struct plldividers_s plldividers
[NB_PLL_MODE
] = {
54 {0, 20000000, 1, ODF_DIV_8
},
55 {20000000, 42500000, 2, ODF_DIV_8
},
56 {42500000, 85000000, 4, ODF_DIV_4
},
57 {85000000, 170000000, 8, ODF_DIV_2
},
58 {170000000, 340000000, 16, ODF_DIV_1
}
61 #define NB_HDMI_PHY_CONFIG 2
62 static struct hdmi_phy_config hdmiphy_config
[NB_HDMI_PHY_CONFIG
] = {
63 {0, 250000000, {0x0, 0x0, 0x0, 0x0} },
64 {250000000, 300000000, {0x1110, 0x0, 0x0, 0x0} },
68 * Start hdmi phy macro cell tx3g4c28
70 * @hdmi: pointer on the hdmi internal structure
72 * Return false if an error occur
74 static bool sti_hdmi_tx3g4c28phy_start(struct sti_hdmi
*hdmi
)
76 u32 ckpxpll
= hdmi
->mode
.clock
* 1000;
77 u32 val
, tmdsck
, idf
, odf
, pllctrl
= 0;
78 bool foundplldivides
= false;
81 DRM_DEBUG_DRIVER("ckpxpll = %dHz\n", ckpxpll
);
83 for (i
= 0; i
< NB_PLL_MODE
; i
++) {
84 if (ckpxpll
>= plldividers
[i
].min
&&
85 ckpxpll
< plldividers
[i
].max
) {
86 idf
= plldividers
[i
].idf
;
87 odf
= plldividers
[i
].odf
;
88 foundplldivides
= true;
93 if (!foundplldivides
) {
94 DRM_ERROR("input TMDS clock speed (%d) not supported\n",
99 /* Assuming no pixel repetition and 24bits color */
101 pllctrl
|= 40 << PLL_CFG_NDIV_SHIFT
;
103 if (tmdsck
> 340000000) {
104 DRM_ERROR("output TMDS clock (%d) out of range\n", tmdsck
);
108 pllctrl
|= idf
<< PLL_CFG_IDF_SHIFT
;
109 pllctrl
|= odf
<< PLL_CFG_ODF_SHIFT
;
112 * Configure and power up the PHY PLL
114 hdmi
->event_received
= false;
115 DRM_DEBUG_DRIVER("pllctrl = 0x%x\n", pllctrl
);
116 hdmi_write(hdmi
, (pllctrl
| PLL_CFG_EN
), HDMI_SRZ_PLL_CFG
);
118 /* wait PLL interrupt */
119 wait_event_interruptible_timeout(hdmi
->wait_event
,
120 hdmi
->event_received
== true,
122 (HDMI_TIMEOUT_PLL_LOCK
));
124 if ((hdmi_read(hdmi
, HDMI_STA
) & HDMI_STA_DLL_LCK
) == 0) {
125 DRM_ERROR("hdmi phy pll not locked\n");
129 DRM_DEBUG_DRIVER("got PHY PLL Lock\n");
131 val
= (HDMI_SRZ_CFG_EN
|
132 HDMI_SRZ_CFG_EXTERNAL_DATA
|
133 HDMI_SRZ_CFG_EN_BIASRES_DETECTION
|
134 HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION
);
136 if (tmdsck
> 165000000)
137 val
|= HDMI_SRZ_CFG_EN_SRC_TERMINATION
;
140 * To configure the source termination and pre-emphasis appropriately
141 * for different high speed TMDS clock frequencies a phy configuration
142 * table must be provided, tailored to the SoC and board combination.
144 for (i
= 0; i
< NB_HDMI_PHY_CONFIG
; i
++) {
145 if ((hdmiphy_config
[i
].min_tmds_freq
<= tmdsck
) &&
146 (hdmiphy_config
[i
].max_tmds_freq
>= tmdsck
)) {
147 val
|= (hdmiphy_config
[i
].config
[0]
148 & ~HDMI_SRZ_CFG_INTERNAL_MASK
);
149 hdmi_write(hdmi
, val
, HDMI_SRZ_CFG
);
151 val
= hdmiphy_config
[i
].config
[1];
152 hdmi_write(hdmi
, val
, HDMI_SRZ_ICNTL
);
154 val
= hdmiphy_config
[i
].config
[2];
155 hdmi_write(hdmi
, val
, HDMI_SRZ_CALCODE_EXT
);
157 DRM_DEBUG_DRIVER("serializer cfg 0x%x 0x%x 0x%x\n",
158 hdmiphy_config
[i
].config
[0],
159 hdmiphy_config
[i
].config
[1],
160 hdmiphy_config
[i
].config
[2]);
166 * Default, power up the serializer with no pre-emphasis or
167 * output swing correction
169 hdmi_write(hdmi
, val
, HDMI_SRZ_CFG
);
170 hdmi_write(hdmi
, 0x0, HDMI_SRZ_ICNTL
);
171 hdmi_write(hdmi
, 0x0, HDMI_SRZ_CALCODE_EXT
);
180 * Stop hdmi phy macro cell tx3g4c28
182 * @hdmi: pointer on the hdmi internal structure
184 static void sti_hdmi_tx3g4c28phy_stop(struct sti_hdmi
*hdmi
)
188 DRM_DEBUG_DRIVER("\n");
190 hdmi
->event_received
= false;
192 val
= HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION
;
193 val
|= HDMI_SRZ_CFG_EN_BIASRES_DETECTION
;
195 hdmi_write(hdmi
, val
, HDMI_SRZ_CFG
);
196 hdmi_write(hdmi
, 0, HDMI_SRZ_PLL_CFG
);
198 /* wait PLL interrupt */
199 wait_event_interruptible_timeout(hdmi
->wait_event
,
200 hdmi
->event_received
== true,
202 (HDMI_TIMEOUT_PLL_LOCK
));
204 if (hdmi_read(hdmi
, HDMI_STA
) & HDMI_STA_DLL_LCK
)
205 DRM_ERROR("hdmi phy pll not well disabled\n");
208 struct hdmi_phy_ops tx3g4c28phy_ops
= {
209 .start
= sti_hdmi_tx3g4c28phy_start
,
210 .stop
= sti_hdmi_tx3g4c28phy_stop
,