diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nv50_sor.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nv50_sor.c | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c new file mode 100644 index 00000000000..8c280463a66 --- /dev/null +++ b/drivers/gpu/drm/nouveau/nv50_sor.c | |||
@@ -0,0 +1,309 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2008 Maarten Maathuis. | ||
3 | * All Rights Reserved. | ||
4 | * | ||
5 | * Permission is hereby granted, free of charge, to any person obtaining | ||
6 | * a copy of this software and associated documentation files (the | ||
7 | * "Software"), to deal in the Software without restriction, including | ||
8 | * without limitation the rights to use, copy, modify, merge, publish, | ||
9 | * distribute, sublicense, and/or sell copies of the Software, and to | ||
10 | * permit persons to whom the Software is furnished to do so, subject to | ||
11 | * the following conditions: | ||
12 | * | ||
13 | * The above copyright notice and this permission notice (including the | ||
14 | * next paragraph) shall be included in all copies or substantial | ||
15 | * portions of the Software. | ||
16 | * | ||
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
18 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
20 | * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE | ||
21 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
22 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
23 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | #include "drmP.h" | ||
28 | #include "drm_crtc_helper.h" | ||
29 | |||
30 | #define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO) | ||
31 | #include "nouveau_reg.h" | ||
32 | #include "nouveau_drv.h" | ||
33 | #include "nouveau_dma.h" | ||
34 | #include "nouveau_encoder.h" | ||
35 | #include "nouveau_connector.h" | ||
36 | #include "nouveau_crtc.h" | ||
37 | #include "nv50_display.h" | ||
38 | |||
39 | static void | ||
40 | nv50_sor_disconnect(struct nouveau_encoder *nv_encoder) | ||
41 | { | ||
42 | struct drm_device *dev = to_drm_encoder(nv_encoder)->dev; | ||
43 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
44 | struct nouveau_channel *evo = dev_priv->evo; | ||
45 | int ret; | ||
46 | |||
47 | NV_DEBUG(dev, "Disconnecting SOR %d\n", nv_encoder->or); | ||
48 | |||
49 | ret = RING_SPACE(evo, 2); | ||
50 | if (ret) { | ||
51 | NV_ERROR(dev, "no space while disconnecting SOR\n"); | ||
52 | return; | ||
53 | } | ||
54 | BEGIN_RING(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1); | ||
55 | OUT_RING(evo, 0); | ||
56 | } | ||
57 | |||
58 | static void | ||
59 | nv50_sor_dp_link_train(struct drm_encoder *encoder) | ||
60 | { | ||
61 | struct drm_device *dev = encoder->dev; | ||
62 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
63 | struct bit_displayport_encoder_table *dpe; | ||
64 | int dpe_headerlen; | ||
65 | |||
66 | dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); | ||
67 | if (!dpe) { | ||
68 | NV_ERROR(dev, "SOR-%d: no DP encoder table!\n", nv_encoder->or); | ||
69 | return; | ||
70 | } | ||
71 | |||
72 | if (dpe->script0) { | ||
73 | NV_DEBUG(dev, "SOR-%d: running DP script 0\n", nv_encoder->or); | ||
74 | nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script0), | ||
75 | nv_encoder->dcb); | ||
76 | } | ||
77 | |||
78 | if (!nouveau_dp_link_train(encoder)) | ||
79 | NV_ERROR(dev, "SOR-%d: link training failed\n", nv_encoder->or); | ||
80 | |||
81 | if (dpe->script1) { | ||
82 | NV_DEBUG(dev, "SOR-%d: running DP script 1\n", nv_encoder->or); | ||
83 | nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script1), | ||
84 | nv_encoder->dcb); | ||
85 | } | ||
86 | } | ||
87 | |||
88 | static void | ||
89 | nv50_sor_dpms(struct drm_encoder *encoder, int mode) | ||
90 | { | ||
91 | struct drm_device *dev = encoder->dev; | ||
92 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
93 | uint32_t val; | ||
94 | int or = nv_encoder->or; | ||
95 | |||
96 | NV_DEBUG(dev, "or %d mode %d\n", or, mode); | ||
97 | |||
98 | /* wait for it to be done */ | ||
99 | if (!nv_wait(NV50_PDISPLAY_SOR_DPMS_CTRL(or), | ||
100 | NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING, 0)) { | ||
101 | NV_ERROR(dev, "timeout: SOR_DPMS_CTRL_PENDING(%d) == 0\n", or); | ||
102 | NV_ERROR(dev, "SOR_DPMS_CTRL(%d) = 0x%08x\n", or, | ||
103 | nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_CTRL(or))); | ||
104 | } | ||
105 | |||
106 | val = nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_CTRL(or)); | ||
107 | |||
108 | if (mode == DRM_MODE_DPMS_ON) | ||
109 | val |= NV50_PDISPLAY_SOR_DPMS_CTRL_ON; | ||
110 | else | ||
111 | val &= ~NV50_PDISPLAY_SOR_DPMS_CTRL_ON; | ||
112 | |||
113 | nv_wr32(dev, NV50_PDISPLAY_SOR_DPMS_CTRL(or), val | | ||
114 | NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING); | ||
115 | if (!nv_wait(NV50_PDISPLAY_SOR_DPMS_STATE(or), | ||
116 | NV50_PDISPLAY_SOR_DPMS_STATE_WAIT, 0)) { | ||
117 | NV_ERROR(dev, "timeout: SOR_DPMS_STATE_WAIT(%d) == 0\n", or); | ||
118 | NV_ERROR(dev, "SOR_DPMS_STATE(%d) = 0x%08x\n", or, | ||
119 | nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_STATE(or))); | ||
120 | } | ||
121 | |||
122 | if (nv_encoder->dcb->type == OUTPUT_DP && mode == DRM_MODE_DPMS_ON) | ||
123 | nv50_sor_dp_link_train(encoder); | ||
124 | } | ||
125 | |||
126 | static void | ||
127 | nv50_sor_save(struct drm_encoder *encoder) | ||
128 | { | ||
129 | NV_ERROR(encoder->dev, "!!\n"); | ||
130 | } | ||
131 | |||
132 | static void | ||
133 | nv50_sor_restore(struct drm_encoder *encoder) | ||
134 | { | ||
135 | NV_ERROR(encoder->dev, "!!\n"); | ||
136 | } | ||
137 | |||
138 | static bool | ||
139 | nv50_sor_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, | ||
140 | struct drm_display_mode *adjusted_mode) | ||
141 | { | ||
142 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
143 | struct nouveau_connector *connector; | ||
144 | |||
145 | NV_DEBUG(encoder->dev, "or %d\n", nv_encoder->or); | ||
146 | |||
147 | connector = nouveau_encoder_connector_get(nv_encoder); | ||
148 | if (!connector) { | ||
149 | NV_ERROR(encoder->dev, "Encoder has no connector\n"); | ||
150 | return false; | ||
151 | } | ||
152 | |||
153 | if (connector->scaling_mode != DRM_MODE_SCALE_NONE && | ||
154 | connector->native_mode) { | ||
155 | int id = adjusted_mode->base.id; | ||
156 | *adjusted_mode = *connector->native_mode; | ||
157 | adjusted_mode->base.id = id; | ||
158 | } | ||
159 | |||
160 | return true; | ||
161 | } | ||
162 | |||
163 | static void | ||
164 | nv50_sor_prepare(struct drm_encoder *encoder) | ||
165 | { | ||
166 | } | ||
167 | |||
168 | static void | ||
169 | nv50_sor_commit(struct drm_encoder *encoder) | ||
170 | { | ||
171 | } | ||
172 | |||
173 | static void | ||
174 | nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, | ||
175 | struct drm_display_mode *adjusted_mode) | ||
176 | { | ||
177 | struct drm_nouveau_private *dev_priv = encoder->dev->dev_private; | ||
178 | struct nouveau_channel *evo = dev_priv->evo; | ||
179 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
180 | struct drm_device *dev = encoder->dev; | ||
181 | struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc); | ||
182 | uint32_t mode_ctl = 0; | ||
183 | int ret; | ||
184 | |||
185 | NV_DEBUG(dev, "or %d\n", nv_encoder->or); | ||
186 | |||
187 | nv50_sor_dpms(encoder, DRM_MODE_DPMS_ON); | ||
188 | |||
189 | switch (nv_encoder->dcb->type) { | ||
190 | case OUTPUT_TMDS: | ||
191 | if (nv_encoder->dcb->sorconf.link & 1) { | ||
192 | if (adjusted_mode->clock < 165000) | ||
193 | mode_ctl = 0x0100; | ||
194 | else | ||
195 | mode_ctl = 0x0500; | ||
196 | } else | ||
197 | mode_ctl = 0x0200; | ||
198 | break; | ||
199 | case OUTPUT_DP: | ||
200 | mode_ctl |= 0x00050000; | ||
201 | if (nv_encoder->dcb->sorconf.link & 1) | ||
202 | mode_ctl |= 0x00000800; | ||
203 | else | ||
204 | mode_ctl |= 0x00000900; | ||
205 | break; | ||
206 | default: | ||
207 | break; | ||
208 | } | ||
209 | |||
210 | if (crtc->index == 1) | ||
211 | mode_ctl |= NV50_EVO_SOR_MODE_CTRL_CRTC1; | ||
212 | else | ||
213 | mode_ctl |= NV50_EVO_SOR_MODE_CTRL_CRTC0; | ||
214 | |||
215 | if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) | ||
216 | mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NHSYNC; | ||
217 | |||
218 | if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) | ||
219 | mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NVSYNC; | ||
220 | |||
221 | ret = RING_SPACE(evo, 2); | ||
222 | if (ret) { | ||
223 | NV_ERROR(dev, "no space while connecting SOR\n"); | ||
224 | return; | ||
225 | } | ||
226 | BEGIN_RING(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1); | ||
227 | OUT_RING(evo, mode_ctl); | ||
228 | } | ||
229 | |||
230 | static const struct drm_encoder_helper_funcs nv50_sor_helper_funcs = { | ||
231 | .dpms = nv50_sor_dpms, | ||
232 | .save = nv50_sor_save, | ||
233 | .restore = nv50_sor_restore, | ||
234 | .mode_fixup = nv50_sor_mode_fixup, | ||
235 | .prepare = nv50_sor_prepare, | ||
236 | .commit = nv50_sor_commit, | ||
237 | .mode_set = nv50_sor_mode_set, | ||
238 | .detect = NULL | ||
239 | }; | ||
240 | |||
241 | static void | ||
242 | nv50_sor_destroy(struct drm_encoder *encoder) | ||
243 | { | ||
244 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
245 | |||
246 | if (!encoder) | ||
247 | return; | ||
248 | |||
249 | NV_DEBUG(encoder->dev, "\n"); | ||
250 | |||
251 | drm_encoder_cleanup(encoder); | ||
252 | |||
253 | kfree(nv_encoder); | ||
254 | } | ||
255 | |||
256 | static const struct drm_encoder_funcs nv50_sor_encoder_funcs = { | ||
257 | .destroy = nv50_sor_destroy, | ||
258 | }; | ||
259 | |||
260 | int | ||
261 | nv50_sor_create(struct drm_device *dev, struct dcb_entry *entry) | ||
262 | { | ||
263 | struct nouveau_encoder *nv_encoder = NULL; | ||
264 | struct drm_encoder *encoder; | ||
265 | bool dum; | ||
266 | int type; | ||
267 | |||
268 | NV_DEBUG(dev, "\n"); | ||
269 | |||
270 | switch (entry->type) { | ||
271 | case OUTPUT_TMDS: | ||
272 | NV_INFO(dev, "Detected a TMDS output\n"); | ||
273 | type = DRM_MODE_ENCODER_TMDS; | ||
274 | break; | ||
275 | case OUTPUT_LVDS: | ||
276 | NV_INFO(dev, "Detected a LVDS output\n"); | ||
277 | type = DRM_MODE_ENCODER_LVDS; | ||
278 | |||
279 | if (nouveau_bios_parse_lvds_table(dev, 0, &dum, &dum)) { | ||
280 | NV_ERROR(dev, "Failed parsing LVDS table\n"); | ||
281 | return -EINVAL; | ||
282 | } | ||
283 | break; | ||
284 | case OUTPUT_DP: | ||
285 | NV_INFO(dev, "Detected a DP output\n"); | ||
286 | type = DRM_MODE_ENCODER_TMDS; | ||
287 | break; | ||
288 | default: | ||
289 | return -EINVAL; | ||
290 | } | ||
291 | |||
292 | nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); | ||
293 | if (!nv_encoder) | ||
294 | return -ENOMEM; | ||
295 | encoder = to_drm_encoder(nv_encoder); | ||
296 | |||
297 | nv_encoder->dcb = entry; | ||
298 | nv_encoder->or = ffs(entry->or) - 1; | ||
299 | |||
300 | nv_encoder->disconnect = nv50_sor_disconnect; | ||
301 | |||
302 | drm_encoder_init(dev, encoder, &nv50_sor_encoder_funcs, type); | ||
303 | drm_encoder_helper_add(encoder, &nv50_sor_helper_funcs); | ||
304 | |||
305 | encoder->possible_crtcs = entry->heads; | ||
306 | encoder->possible_clones = 0; | ||
307 | |||
308 | return 0; | ||
309 | } | ||