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 
44 struct 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
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}
61 };
62 
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} },
67 };
68 
69 /**
70  * Start hdmi phy macro cell tx3g4c28
71  *
72  * @hdmi: pointer on the hdmi internal structure
73  *
74  * Return false if an error occur
75  */
76 static 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 
177 err:
178 	return false;
179 }
180 
181 /**
182  * Stop hdmi phy macro cell tx3g4c28
183  *
184  * @hdmi: pointer on the hdmi internal structure
185  */
186 static 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 
210 struct hdmi_phy_ops tx3g4c28phy_ops = {
211 	.start = sti_hdmi_tx3g4c28phy_start,
212 	.stop = sti_hdmi_tx3g4c28phy_stop,
213 };
214