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