diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nvd0_display.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nvd0_display.c | 203 |
1 files changed, 202 insertions, 1 deletions
diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c index 6339a3d00363..3a2a4bb1276d 100644 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ b/drivers/gpu/drm/nouveau/nvd0_display.c | |||
@@ -23,7 +23,9 @@ | |||
23 | */ | 23 | */ |
24 | 24 | ||
25 | #include <linux/dma-mapping.h> | 25 | #include <linux/dma-mapping.h> |
26 | |||
26 | #include "drmP.h" | 27 | #include "drmP.h" |
28 | #include "drm_crtc_helper.h" | ||
27 | 29 | ||
28 | #include "nouveau_drv.h" | 30 | #include "nouveau_drv.h" |
29 | #include "nouveau_connector.h" | 31 | #include "nouveau_connector.h" |
@@ -92,6 +94,12 @@ evo_kick(u32 *push, struct drm_device *dev, int id) | |||
92 | #define evo_mthd(p,m,s) *((p)++) = (((s) << 18) | (m)) | 94 | #define evo_mthd(p,m,s) *((p)++) = (((s) << 18) | (m)) |
93 | #define evo_data(p,d) *((p)++) = (d) | 95 | #define evo_data(p,d) *((p)++) = (d) |
94 | 96 | ||
97 | static struct drm_crtc * | ||
98 | nvd0_display_crtc_get(struct drm_encoder *encoder) | ||
99 | { | ||
100 | return nouveau_encoder(encoder)->crtc; | ||
101 | } | ||
102 | |||
95 | /****************************************************************************** | 103 | /****************************************************************************** |
96 | * DAC | 104 | * DAC |
97 | *****************************************************************************/ | 105 | *****************************************************************************/ |
@@ -99,6 +107,163 @@ evo_kick(u32 *push, struct drm_device *dev, int id) | |||
99 | /****************************************************************************** | 107 | /****************************************************************************** |
100 | * SOR | 108 | * SOR |
101 | *****************************************************************************/ | 109 | *****************************************************************************/ |
110 | static void | ||
111 | nvd0_sor_dpms(struct drm_encoder *encoder, int mode) | ||
112 | { | ||
113 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
114 | struct drm_device *dev = encoder->dev; | ||
115 | struct drm_encoder *partner; | ||
116 | int or = nv_encoder->or; | ||
117 | u32 dpms_ctrl; | ||
118 | |||
119 | nv_encoder->last_dpms = mode; | ||
120 | |||
121 | list_for_each_entry(partner, &dev->mode_config.encoder_list, head) { | ||
122 | struct nouveau_encoder *nv_partner = nouveau_encoder(partner); | ||
123 | |||
124 | if (partner->encoder_type != DRM_MODE_ENCODER_TMDS) | ||
125 | continue; | ||
126 | |||
127 | if (nv_partner != nv_encoder && | ||
128 | nv_partner->dcb->or == nv_encoder->or) { | ||
129 | if (nv_partner->last_dpms == DRM_MODE_DPMS_ON) | ||
130 | return; | ||
131 | break; | ||
132 | } | ||
133 | } | ||
134 | |||
135 | dpms_ctrl = (mode == DRM_MODE_DPMS_ON); | ||
136 | dpms_ctrl |= 0x80000000; | ||
137 | |||
138 | nv_wait(dev, 0x61c004 + (or * 0x0800), 0x80000000, 0x00000000); | ||
139 | nv_mask(dev, 0x61c004 + (or * 0x0800), 0x80000001, dpms_ctrl); | ||
140 | nv_wait(dev, 0x61c004 + (or * 0x0800), 0x80000000, 0x00000000); | ||
141 | nv_wait(dev, 0x61c030 + (or * 0x0800), 0x10000000, 0x00000000); | ||
142 | } | ||
143 | |||
144 | static bool | ||
145 | nvd0_sor_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode, | ||
146 | struct drm_display_mode *adjusted_mode) | ||
147 | { | ||
148 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
149 | struct nouveau_connector *nv_connector; | ||
150 | |||
151 | nv_connector = nouveau_encoder_connector_get(nv_encoder); | ||
152 | if (nv_connector && nv_connector->native_mode) { | ||
153 | if (nv_connector->scaling_mode != DRM_MODE_SCALE_NONE) { | ||
154 | int id = adjusted_mode->base.id; | ||
155 | *adjusted_mode = *nv_connector->native_mode; | ||
156 | adjusted_mode->base.id = id; | ||
157 | } | ||
158 | } | ||
159 | |||
160 | return true; | ||
161 | } | ||
162 | |||
163 | static void | ||
164 | nvd0_sor_prepare(struct drm_encoder *encoder) | ||
165 | { | ||
166 | } | ||
167 | |||
168 | static void | ||
169 | nvd0_sor_commit(struct drm_encoder *encoder) | ||
170 | { | ||
171 | } | ||
172 | |||
173 | static void | ||
174 | nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, | ||
175 | struct drm_display_mode *adjusted_mode) | ||
176 | { | ||
177 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
178 | struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); | ||
179 | u32 mode_ctrl = (1 << nv_crtc->index); | ||
180 | u32 *push; | ||
181 | |||
182 | if (nv_encoder->dcb->sorconf.link & 1) { | ||
183 | if (adjusted_mode->clock < 165000) | ||
184 | mode_ctrl |= 0x00000100; | ||
185 | else | ||
186 | mode_ctrl |= 0x00000500; | ||
187 | } else { | ||
188 | mode_ctrl |= 0x00000200; | ||
189 | } | ||
190 | |||
191 | nvd0_sor_dpms(encoder, DRM_MODE_DPMS_ON); | ||
192 | |||
193 | push = evo_wait(encoder->dev, 0, 2); | ||
194 | if (push) { | ||
195 | evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 1); | ||
196 | evo_data(push, mode_ctrl); | ||
197 | } | ||
198 | |||
199 | nv_encoder->crtc = encoder->crtc; | ||
200 | } | ||
201 | |||
202 | static void | ||
203 | nvd0_sor_disconnect(struct drm_encoder *encoder) | ||
204 | { | ||
205 | struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); | ||
206 | struct drm_device *dev = encoder->dev; | ||
207 | |||
208 | if (nv_encoder->crtc) { | ||
209 | u32 *push = evo_wait(dev, 0, 4); | ||
210 | if (push) { | ||
211 | evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 1); | ||
212 | evo_data(push, 0x00000000); | ||
213 | evo_mthd(push, 0x0080, 1); | ||
214 | evo_data(push, 0x00000000); | ||
215 | evo_kick(push, dev, 0); | ||
216 | } | ||
217 | |||
218 | nv_encoder->crtc = NULL; | ||
219 | nv_encoder->last_dpms = DRM_MODE_DPMS_OFF; | ||
220 | } | ||
221 | } | ||
222 | |||
223 | static void | ||
224 | nvd0_sor_destroy(struct drm_encoder *encoder) | ||
225 | { | ||
226 | drm_encoder_cleanup(encoder); | ||
227 | kfree(encoder); | ||
228 | } | ||
229 | |||
230 | static const struct drm_encoder_helper_funcs nvd0_sor_hfunc = { | ||
231 | .dpms = nvd0_sor_dpms, | ||
232 | .mode_fixup = nvd0_sor_mode_fixup, | ||
233 | .prepare = nvd0_sor_prepare, | ||
234 | .commit = nvd0_sor_commit, | ||
235 | .mode_set = nvd0_sor_mode_set, | ||
236 | .disable = nvd0_sor_disconnect, | ||
237 | .get_crtc = nvd0_display_crtc_get, | ||
238 | }; | ||
239 | |||
240 | static const struct drm_encoder_funcs nvd0_sor_func = { | ||
241 | .destroy = nvd0_sor_destroy, | ||
242 | }; | ||
243 | |||
244 | static int | ||
245 | nvd0_sor_create(struct drm_connector *connector, struct dcb_entry *dcbe) | ||
246 | { | ||
247 | struct drm_device *dev = connector->dev; | ||
248 | struct nouveau_encoder *nv_encoder; | ||
249 | struct drm_encoder *encoder; | ||
250 | |||
251 | nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); | ||
252 | if (!nv_encoder) | ||
253 | return -ENOMEM; | ||
254 | nv_encoder->dcb = dcbe; | ||
255 | nv_encoder->or = ffs(dcbe->or) - 1; | ||
256 | nv_encoder->last_dpms = DRM_MODE_DPMS_OFF; | ||
257 | |||
258 | encoder = to_drm_encoder(nv_encoder); | ||
259 | encoder->possible_crtcs = dcbe->heads; | ||
260 | encoder->possible_clones = 0; | ||
261 | drm_encoder_init(dev, encoder, &nvd0_sor_func, DRM_MODE_ENCODER_TMDS); | ||
262 | drm_encoder_helper_add(encoder, &nvd0_sor_hfunc); | ||
263 | |||
264 | drm_mode_connector_attach_encoder(connector, encoder); | ||
265 | return 0; | ||
266 | } | ||
102 | 267 | ||
103 | /****************************************************************************** | 268 | /****************************************************************************** |
104 | * IRQ | 269 | * IRQ |
@@ -256,15 +421,51 @@ nvd0_display_create(struct drm_device *dev) | |||
256 | { | 421 | { |
257 | struct drm_nouveau_private *dev_priv = dev->dev_private; | 422 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
258 | struct nouveau_instmem_engine *pinstmem = &dev_priv->engine.instmem; | 423 | struct nouveau_instmem_engine *pinstmem = &dev_priv->engine.instmem; |
424 | struct dcb_table *dcb = &dev_priv->vbios.dcb; | ||
425 | struct drm_connector *connector, *tmp; | ||
259 | struct pci_dev *pdev = dev->pdev; | 426 | struct pci_dev *pdev = dev->pdev; |
260 | struct nvd0_display *disp; | 427 | struct nvd0_display *disp; |
261 | int ret; | 428 | struct dcb_entry *dcbe; |
429 | int ret, i; | ||
262 | 430 | ||
263 | disp = kzalloc(sizeof(*disp), GFP_KERNEL); | 431 | disp = kzalloc(sizeof(*disp), GFP_KERNEL); |
264 | if (!disp) | 432 | if (!disp) |
265 | return -ENOMEM; | 433 | return -ENOMEM; |
266 | dev_priv->engine.display.priv = disp; | 434 | dev_priv->engine.display.priv = disp; |
267 | 435 | ||
436 | /* create encoder/connector objects based on VBIOS DCB table */ | ||
437 | for (i = 0, dcbe = &dcb->entry[0]; i < dcb->entries; i++, dcbe++) { | ||
438 | connector = nouveau_connector_create(dev, dcbe->connector); | ||
439 | if (IS_ERR(connector)) | ||
440 | continue; | ||
441 | |||
442 | if (dcbe->location != DCB_LOC_ON_CHIP) { | ||
443 | NV_WARN(dev, "skipping off-chip encoder %d/%d\n", | ||
444 | dcbe->type, ffs(dcbe->or) - 1); | ||
445 | continue; | ||
446 | } | ||
447 | |||
448 | switch (dcbe->type) { | ||
449 | case OUTPUT_TMDS: | ||
450 | nvd0_sor_create(connector, dcbe); | ||
451 | break; | ||
452 | default: | ||
453 | NV_WARN(dev, "skipping unsupported encoder %d/%d\n", | ||
454 | dcbe->type, ffs(dcbe->or) - 1); | ||
455 | continue; | ||
456 | } | ||
457 | } | ||
458 | |||
459 | /* cull any connectors we created that don't have an encoder */ | ||
460 | list_for_each_entry_safe(connector, tmp, &dev->mode_config.connector_list, head) { | ||
461 | if (connector->encoder_ids[0]) | ||
462 | continue; | ||
463 | |||
464 | NV_WARN(dev, "%s has no encoders, removing\n", | ||
465 | drm_get_connector_name(connector)); | ||
466 | connector->funcs->destroy(connector); | ||
467 | } | ||
468 | |||
268 | /* setup interrupt handling */ | 469 | /* setup interrupt handling */ |
269 | nouveau_irq_register(dev, 26, nvd0_display_intr); | 470 | nouveau_irq_register(dev, 26, nvd0_display_intr); |
270 | 471 | ||