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
42struct 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
53static 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
62static 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 */
74static 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
175err:
176	return false;
177}
178
179/**
180 * Stop hdmi phy macro cell tx3g4c28
181 *
182 * @hdmi: pointer on the hdmi internal structure
183 */
184static 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
208struct hdmi_phy_ops tx3g4c28phy_ops = {
209	.start = sti_hdmi_tx3g4c28phy_start,
210	.stop = sti_hdmi_tx3g4c28phy_stop,
211};
212