1 /*
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
5  */
6 
7 #include "sti_hdmi_tx3g4c28phy.h"
8 
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
13 
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)
21 
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)
29 
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)
34 
35 #define ODF_DIV_1                                (0)
36 #define ODF_DIV_2                                (1)
37 #define ODF_DIV_4                                (2)
38 #define ODF_DIV_8                                (3)
39 
40 #define HDMI_TIMEOUT_PLL_LOCK  50  /*milliseconds */
41 
42 struct plldividers_s {
43 	uint32_t min;
44 	uint32_t max;
45 	uint32_t idf;
46 	uint32_t odf;
47 };
48 
49 /*
50  * Functional specification recommended values
51  */
52 #define NB_PLL_MODE 5
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}
59 };
60 
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} },
65 };
66 
67 /**
68  * Start hdmi phy macro cell tx3g4c28
69  *
70  * @hdmi: pointer on the hdmi internal structure
71  *
72  * Return false if an error occur
73  */
74 static bool sti_hdmi_tx3g4c28phy_start(struct sti_hdmi *hdmi)
75 {
76 	u32 ckpxpll = hdmi->mode.clock * 1000;
77 	u32 val, tmdsck, idf, odf, pllctrl = 0;
78 	bool foundplldivides = false;
79 	int i;
80 
81 	DRM_DEBUG_DRIVER("ckpxpll = %dHz\n", ckpxpll);
82 
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;
89 			break;
90 		}
91 	}
92 
93 	if (!foundplldivides) {
94 		DRM_ERROR("input TMDS clock speed (%d) not supported\n",
95 			  ckpxpll);
96 		goto err;
97 	}
98 
99 	/* Assuming no pixel repetition and 24bits color */
100 	tmdsck = ckpxpll;
101 	pllctrl |= 40 << PLL_CFG_NDIV_SHIFT;
102 
103 	if (tmdsck > 340000000) {
104 		DRM_ERROR("output TMDS clock (%d) out of range\n", tmdsck);
105 		goto err;
106 	}
107 
108 	pllctrl |= idf << PLL_CFG_IDF_SHIFT;
109 	pllctrl |= odf << PLL_CFG_ODF_SHIFT;
110 
111 	/*
112 	 * Configure and power up the PHY PLL
113 	 */
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);
117 
118 	/* wait PLL interrupt */
119 	wait_event_interruptible_timeout(hdmi->wait_event,
120 					 hdmi->event_received == true,
121 					 msecs_to_jiffies
122 					 (HDMI_TIMEOUT_PLL_LOCK));
123 
124 	if ((hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK) == 0) {
125 		DRM_ERROR("hdmi phy pll not locked\n");
126 		goto err;
127 	}
128 
129 	DRM_DEBUG_DRIVER("got PHY PLL Lock\n");
130 
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);
135 
136 	if (tmdsck > 165000000)
137 		val |= HDMI_SRZ_CFG_EN_SRC_TERMINATION;
138 
139 	/*
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.
143 	 */
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);
150 
151 			val = hdmiphy_config[i].config[1];
152 			hdmi_write(hdmi, val, HDMI_SRZ_ICNTL);
153 
154 			val = hdmiphy_config[i].config[2];
155 			hdmi_write(hdmi, val, HDMI_SRZ_CALCODE_EXT);
156 
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]);
161 			return true;
162 		}
163 	}
164 
165 	/*
166 	 * Default, power up the serializer with no pre-emphasis or
167 	 * output swing correction
168 	 */
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);
172 
173 	return true;
174 
175 err:
176 	return false;
177 }
178 
179 /**
180  * Stop hdmi phy macro cell tx3g4c28
181  *
182  * @hdmi: pointer on the hdmi internal structure
183  */
184 static void sti_hdmi_tx3g4c28phy_stop(struct sti_hdmi *hdmi)
185 {
186 	int val = 0;
187 
188 	DRM_DEBUG_DRIVER("\n");
189 
190 	hdmi->event_received = false;
191 
192 	val = HDMI_SRZ_CFG_EN_SINK_TERM_DETECTION;
193 	val |= HDMI_SRZ_CFG_EN_BIASRES_DETECTION;
194 
195 	hdmi_write(hdmi, val, HDMI_SRZ_CFG);
196 	hdmi_write(hdmi, 0, HDMI_SRZ_PLL_CFG);
197 
198 	/* wait PLL interrupt */
199 	wait_event_interruptible_timeout(hdmi->wait_event,
200 					 hdmi->event_received == true,
201 					 msecs_to_jiffies
202 					 (HDMI_TIMEOUT_PLL_LOCK));
203 
204 	if (hdmi_read(hdmi, HDMI_STA) & HDMI_STA_DLL_LCK)
205 		DRM_ERROR("hdmi phy pll not well disabled\n");
206 }
207 
208 struct hdmi_phy_ops tx3g4c28phy_ops = {
209 	.start = sti_hdmi_tx3g4c28phy_start,
210 	.stop = sti_hdmi_tx3g4c28phy_stop,
211 };
212