Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / gpu / drm / sti / sti_hdmi_tx3g4c28phy.c
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 };