aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/nouveau/nv17_tv.c
diff options
context:
space:
mode:
authorBen Skeggs <bskeggs@redhat.com>2009-12-11 04:24:15 -0500
committerDave Airlie <airlied@redhat.com>2009-12-11 06:29:34 -0500
commit6ee738610f41b59733f63718f0bdbcba7d3a3f12 (patch)
treeeccb9f07671998c50a1bc606a54cd6f82ba43e0a /drivers/gpu/drm/nouveau/nv17_tv.c
parentd1ede145cea25c5b6d2ebb19b167af14e374bb45 (diff)
drm/nouveau: Add DRM driver for NVIDIA GPUs
This adds a drm/kms staging non-API stable driver for GPUs from NVIDIA. This driver is a KMS-based driver and requires a compatible nouveau userspace libdrm and nouveau X.org driver. This driver requires firmware files not available in this kernel tree, interested parties can find them via the nouveau project git archive. This driver is reverse engineered, and is in no way supported by nVidia. Support for nearly the complete range of nvidia hw from nv04->g80 (nv50) is available, and the kms driver should support driving nearly all output types (displayport is under development still) along with supporting suspend/resume. This work is all from the upstream nouveau project found at nouveau.freedesktop.org. The original authors list from nouveau git tree is: Anssi Hannula <anssi.hannula@iki.fi> Ben Skeggs <bskeggs@redhat.com> Francisco Jerez <currojerez@riseup.net> Maarten Maathuis <madman2003@gmail.com> Marcin Koƛcielnicki <koriakin@0x04.net> Matthew Garrett <mjg@redhat.com> Matt Parnell <mparnell@gmail.com> Patrice Mandin <patmandin@gmail.com> Pekka Paalanen <pq@iki.fi> Xavier Chantry <shiningxc@gmail.com> along with project founder Stephane Marchesin <marchesin@icps.u-strasbg.fr> Signed-off-by: Ben Skeggs <bskeggs@redhat.com> Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/nouveau/nv17_tv.c')
-rw-r--r--drivers/gpu/drm/nouveau/nv17_tv.c681
1 files changed, 681 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nv17_tv.c b/drivers/gpu/drm/nouveau/nv17_tv.c
new file mode 100644
index 000000000000..46cfd9c60478
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nv17_tv.c
@@ -0,0 +1,681 @@
1/*
2 * Copyright (C) 2009 Francisco Jerez.
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#include "nouveau_drv.h"
30#include "nouveau_encoder.h"
31#include "nouveau_connector.h"
32#include "nouveau_crtc.h"
33#include "nouveau_hw.h"
34#include "nv17_tv.h"
35
36enum drm_connector_status nv17_tv_detect(struct drm_encoder *encoder,
37 struct drm_connector *connector,
38 uint32_t pin_mask)
39{
40 struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
41
42 tv_enc->pin_mask = pin_mask >> 28 & 0xe;
43
44 switch (tv_enc->pin_mask) {
45 case 0x2:
46 case 0x4:
47 tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Composite;
48 break;
49 case 0xc:
50 tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_SVIDEO;
51 break;
52 case 0xe:
53 if (nouveau_encoder(encoder)->dcb->tvconf.has_component_output)
54 tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Component;
55 else
56 tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_SCART;
57 break;
58 default:
59 tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
60 break;
61 }
62
63 drm_connector_property_set_value(connector,
64 encoder->dev->mode_config.tv_subconnector_property,
65 tv_enc->subconnector);
66
67 return tv_enc->subconnector ? connector_status_connected :
68 connector_status_disconnected;
69}
70
71static const struct {
72 int hdisplay;
73 int vdisplay;
74} modes[] = {
75 { 640, 400 },
76 { 640, 480 },
77 { 720, 480 },
78 { 720, 576 },
79 { 800, 600 },
80 { 1024, 768 },
81 { 1280, 720 },
82 { 1280, 1024 },
83 { 1920, 1080 }
84};
85
86static int nv17_tv_get_modes(struct drm_encoder *encoder,
87 struct drm_connector *connector)
88{
89 struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
90 struct drm_display_mode *mode;
91 struct drm_display_mode *output_mode;
92 int n = 0;
93 int i;
94
95 if (tv_norm->kind != CTV_ENC_MODE) {
96 struct drm_display_mode *tv_mode;
97
98 for (tv_mode = nv17_tv_modes; tv_mode->hdisplay; tv_mode++) {
99 mode = drm_mode_duplicate(encoder->dev, tv_mode);
100
101 mode->clock = tv_norm->tv_enc_mode.vrefresh *
102 mode->htotal / 1000 *
103 mode->vtotal / 1000;
104
105 if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
106 mode->clock *= 2;
107
108 if (mode->hdisplay == tv_norm->tv_enc_mode.hdisplay &&
109 mode->vdisplay == tv_norm->tv_enc_mode.vdisplay)
110 mode->type |= DRM_MODE_TYPE_PREFERRED;
111
112 drm_mode_probed_add(connector, mode);
113 n++;
114 }
115 return n;
116 }
117
118 /* tv_norm->kind == CTV_ENC_MODE */
119 output_mode = &tv_norm->ctv_enc_mode.mode;
120 for (i = 0; i < ARRAY_SIZE(modes); i++) {
121 if (modes[i].hdisplay > output_mode->hdisplay ||
122 modes[i].vdisplay > output_mode->vdisplay)
123 continue;
124
125 if (modes[i].hdisplay == output_mode->hdisplay &&
126 modes[i].vdisplay == output_mode->vdisplay) {
127 mode = drm_mode_duplicate(encoder->dev, output_mode);
128 mode->type |= DRM_MODE_TYPE_PREFERRED;
129 } else {
130 mode = drm_cvt_mode(encoder->dev, modes[i].hdisplay,
131 modes[i].vdisplay, 60, false,
132 output_mode->flags & DRM_MODE_FLAG_INTERLACE,
133 false);
134 }
135
136 /* CVT modes are sometimes unsuitable... */
137 if (output_mode->hdisplay <= 720
138 || output_mode->hdisplay >= 1920) {
139 mode->htotal = output_mode->htotal;
140 mode->hsync_start = (mode->hdisplay + (mode->htotal
141 - mode->hdisplay) * 9 / 10) & ~7;
142 mode->hsync_end = mode->hsync_start + 8;
143 }
144 if (output_mode->vdisplay >= 1024) {
145 mode->vtotal = output_mode->vtotal;
146 mode->vsync_start = output_mode->vsync_start;
147 mode->vsync_end = output_mode->vsync_end;
148 }
149
150 mode->type |= DRM_MODE_TYPE_DRIVER;
151 drm_mode_probed_add(connector, mode);
152 n++;
153 }
154 return n;
155}
156
157static int nv17_tv_mode_valid(struct drm_encoder *encoder,
158 struct drm_display_mode *mode)
159{
160 struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
161
162 if (tv_norm->kind == CTV_ENC_MODE) {
163 struct drm_display_mode *output_mode =
164 &tv_norm->ctv_enc_mode.mode;
165
166 if (mode->clock > 400000)
167 return MODE_CLOCK_HIGH;
168
169 if (mode->hdisplay > output_mode->hdisplay ||
170 mode->vdisplay > output_mode->vdisplay)
171 return MODE_BAD;
172
173 if ((mode->flags & DRM_MODE_FLAG_INTERLACE) !=
174 (output_mode->flags & DRM_MODE_FLAG_INTERLACE))
175 return MODE_NO_INTERLACE;
176
177 if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
178 return MODE_NO_DBLESCAN;
179
180 } else {
181 const int vsync_tolerance = 600;
182
183 if (mode->clock > 70000)
184 return MODE_CLOCK_HIGH;
185
186 if (abs(drm_mode_vrefresh(mode) * 1000 -
187 tv_norm->tv_enc_mode.vrefresh) > vsync_tolerance)
188 return MODE_VSYNC;
189
190 /* The encoder takes care of the actual interlacing */
191 if (mode->flags & DRM_MODE_FLAG_INTERLACE)
192 return MODE_NO_INTERLACE;
193 }
194
195 return MODE_OK;
196}
197
198static bool nv17_tv_mode_fixup(struct drm_encoder *encoder,
199 struct drm_display_mode *mode,
200 struct drm_display_mode *adjusted_mode)
201{
202 struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
203
204 if (tv_norm->kind == CTV_ENC_MODE)
205 adjusted_mode->clock = tv_norm->ctv_enc_mode.mode.clock;
206 else
207 adjusted_mode->clock = 90000;
208
209 return true;
210}
211
212static void nv17_tv_dpms(struct drm_encoder *encoder, int mode)
213{
214 struct drm_device *dev = encoder->dev;
215 struct nv17_tv_state *regs = &to_tv_enc(encoder)->state;
216 struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
217
218 if (nouveau_encoder(encoder)->last_dpms == mode)
219 return;
220 nouveau_encoder(encoder)->last_dpms = mode;
221
222 NV_TRACE(dev, "Setting dpms mode %d on TV encoder (output %d)\n",
223 mode, nouveau_encoder(encoder)->dcb->index);
224
225 regs->ptv_200 &= ~1;
226
227 if (tv_norm->kind == CTV_ENC_MODE) {
228 nv04_dfp_update_fp_control(encoder, mode);
229
230 } else {
231 nv04_dfp_update_fp_control(encoder, DRM_MODE_DPMS_OFF);
232
233 if (mode == DRM_MODE_DPMS_ON)
234 regs->ptv_200 |= 1;
235 }
236
237 nv_load_ptv(dev, regs, 200);
238
239 nv17_gpio_set(dev, DCB_GPIO_TVDAC1, mode == DRM_MODE_DPMS_ON);
240 nv17_gpio_set(dev, DCB_GPIO_TVDAC0, mode == DRM_MODE_DPMS_ON);
241
242 nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON);
243}
244
245static void nv17_tv_prepare(struct drm_encoder *encoder)
246{
247 struct drm_device *dev = encoder->dev;
248 struct drm_nouveau_private *dev_priv = dev->dev_private;
249 struct drm_encoder_helper_funcs *helper = encoder->helper_private;
250 struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
251 int head = nouveau_crtc(encoder->crtc)->index;
252 uint8_t *cr_lcd = &dev_priv->mode_reg.crtc_reg[head].CRTC[
253 NV_CIO_CRE_LCD__INDEX];
254 uint32_t dacclk_off = NV_PRAMDAC_DACCLK +
255 nv04_dac_output_offset(encoder);
256 uint32_t dacclk;
257
258 helper->dpms(encoder, DRM_MODE_DPMS_OFF);
259
260 nv04_dfp_disable(dev, head);
261
262 /* Unbind any FP encoders from this head if we need the FP
263 * stuff enabled. */
264 if (tv_norm->kind == CTV_ENC_MODE) {
265 struct drm_encoder *enc;
266
267 list_for_each_entry(enc, &dev->mode_config.encoder_list, head) {
268 struct dcb_entry *dcb = nouveau_encoder(enc)->dcb;
269
270 if ((dcb->type == OUTPUT_TMDS ||
271 dcb->type == OUTPUT_LVDS) &&
272 !enc->crtc &&
273 nv04_dfp_get_bound_head(dev, dcb) == head) {
274 nv04_dfp_bind_head(dev, dcb, head ^ 1,
275 dev_priv->VBIOS.fp.dual_link);
276 }
277 }
278
279 }
280
281 /* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f)
282 * at LCD__INDEX which we don't alter
283 */
284 if (!(*cr_lcd & 0x44)) {
285 if (tv_norm->kind == CTV_ENC_MODE)
286 *cr_lcd = 0x1 | (head ? 0x0 : 0x8);
287 else
288 *cr_lcd = 0;
289 }
290
291 /* Set the DACCLK register */
292 dacclk = (NVReadRAMDAC(dev, 0, dacclk_off) & ~0x30) | 0x1;
293
294 if (dev_priv->card_type == NV_40)
295 dacclk |= 0x1a << 16;
296
297 if (tv_norm->kind == CTV_ENC_MODE) {
298 dacclk |= 0x20;
299
300 if (head)
301 dacclk |= 0x100;
302 else
303 dacclk &= ~0x100;
304
305 } else {
306 dacclk |= 0x10;
307
308 }
309
310 NVWriteRAMDAC(dev, 0, dacclk_off, dacclk);
311}
312
313static void nv17_tv_mode_set(struct drm_encoder *encoder,
314 struct drm_display_mode *drm_mode,
315 struct drm_display_mode *adjusted_mode)
316{
317 struct drm_device *dev = encoder->dev;
318 struct drm_nouveau_private *dev_priv = dev->dev_private;
319 int head = nouveau_crtc(encoder->crtc)->index;
320 struct nv04_crtc_reg *regs = &dev_priv->mode_reg.crtc_reg[head];
321 struct nv17_tv_state *tv_regs = &to_tv_enc(encoder)->state;
322 struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
323 int i;
324
325 regs->CRTC[NV_CIO_CRE_53] = 0x40; /* FP_HTIMING */
326 regs->CRTC[NV_CIO_CRE_54] = 0; /* FP_VTIMING */
327 regs->ramdac_630 = 0x2; /* turn off green mode (tv test pattern?) */
328 regs->tv_setup = 1;
329 regs->ramdac_8c0 = 0x0;
330
331 if (tv_norm->kind == TV_ENC_MODE) {
332 tv_regs->ptv_200 = 0x13111100;
333 if (head)
334 tv_regs->ptv_200 |= 0x10;
335
336 tv_regs->ptv_20c = 0x808010;
337 tv_regs->ptv_304 = 0x2d00000;
338 tv_regs->ptv_600 = 0x0;
339 tv_regs->ptv_60c = 0x0;
340 tv_regs->ptv_610 = 0x1e00000;
341
342 if (tv_norm->tv_enc_mode.vdisplay == 576) {
343 tv_regs->ptv_508 = 0x1200000;
344 tv_regs->ptv_614 = 0x33;
345
346 } else if (tv_norm->tv_enc_mode.vdisplay == 480) {
347 tv_regs->ptv_508 = 0xf00000;
348 tv_regs->ptv_614 = 0x13;
349 }
350
351 if (dev_priv->card_type >= NV_30) {
352 tv_regs->ptv_500 = 0xe8e0;
353 tv_regs->ptv_504 = 0x1710;
354 tv_regs->ptv_604 = 0x0;
355 tv_regs->ptv_608 = 0x0;
356 } else {
357 if (tv_norm->tv_enc_mode.vdisplay == 576) {
358 tv_regs->ptv_604 = 0x20;
359 tv_regs->ptv_608 = 0x10;
360 tv_regs->ptv_500 = 0x19710;
361 tv_regs->ptv_504 = 0x68f0;
362
363 } else if (tv_norm->tv_enc_mode.vdisplay == 480) {
364 tv_regs->ptv_604 = 0x10;
365 tv_regs->ptv_608 = 0x20;
366 tv_regs->ptv_500 = 0x4b90;
367 tv_regs->ptv_504 = 0x1b480;
368 }
369 }
370
371 for (i = 0; i < 0x40; i++)
372 tv_regs->tv_enc[i] = tv_norm->tv_enc_mode.tv_enc[i];
373
374 } else {
375 struct drm_display_mode *output_mode =
376 &tv_norm->ctv_enc_mode.mode;
377
378 /* The registers in PRAMDAC+0xc00 control some timings and CSC
379 * parameters for the CTV encoder (It's only used for "HD" TV
380 * modes, I don't think I have enough working to guess what
381 * they exactly mean...), it's probably connected at the
382 * output of the FP encoder, but it also needs the analog
383 * encoder in its OR enabled and routed to the head it's
384 * using. It's enabled with the DACCLK register, bits [5:4].
385 */
386 for (i = 0; i < 38; i++)
387 regs->ctv_regs[i] = tv_norm->ctv_enc_mode.ctv_regs[i];
388
389 regs->fp_horiz_regs[FP_DISPLAY_END] = output_mode->hdisplay - 1;
390 regs->fp_horiz_regs[FP_TOTAL] = output_mode->htotal - 1;
391 regs->fp_horiz_regs[FP_SYNC_START] =
392 output_mode->hsync_start - 1;
393 regs->fp_horiz_regs[FP_SYNC_END] = output_mode->hsync_end - 1;
394 regs->fp_horiz_regs[FP_CRTC] = output_mode->hdisplay +
395 max((output_mode->hdisplay-600)/40 - 1, 1);
396
397 regs->fp_vert_regs[FP_DISPLAY_END] = output_mode->vdisplay - 1;
398 regs->fp_vert_regs[FP_TOTAL] = output_mode->vtotal - 1;
399 regs->fp_vert_regs[FP_SYNC_START] =
400 output_mode->vsync_start - 1;
401 regs->fp_vert_regs[FP_SYNC_END] = output_mode->vsync_end - 1;
402 regs->fp_vert_regs[FP_CRTC] = output_mode->vdisplay - 1;
403
404 regs->fp_control = NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS |
405 NV_PRAMDAC_FP_TG_CONTROL_READ_PROG |
406 NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12;
407
408 if (output_mode->flags & DRM_MODE_FLAG_PVSYNC)
409 regs->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS;
410 if (output_mode->flags & DRM_MODE_FLAG_PHSYNC)
411 regs->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS;
412
413 regs->fp_debug_0 = NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND |
414 NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND |
415 NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR |
416 NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR |
417 NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED |
418 NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE |
419 NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE;
420
421 regs->fp_debug_2 = 0;
422
423 regs->fp_margin_color = 0x801080;
424
425 }
426}
427
428static void nv17_tv_commit(struct drm_encoder *encoder)
429{
430 struct drm_device *dev = encoder->dev;
431 struct drm_nouveau_private *dev_priv = dev->dev_private;
432 struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
433 struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
434 struct drm_encoder_helper_funcs *helper = encoder->helper_private;
435
436 if (get_tv_norm(encoder)->kind == TV_ENC_MODE) {
437 nv17_tv_update_rescaler(encoder);
438 nv17_tv_update_properties(encoder);
439 } else {
440 nv17_ctv_update_rescaler(encoder);
441 }
442
443 nv17_tv_state_load(dev, &to_tv_enc(encoder)->state);
444
445 /* This could use refinement for flatpanels, but it should work */
446 if (dev_priv->chipset < 0x44)
447 NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL +
448 nv04_dac_output_offset(encoder),
449 0xf0000000);
450 else
451 NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL +
452 nv04_dac_output_offset(encoder),
453 0x00100000);
454
455 helper->dpms(encoder, DRM_MODE_DPMS_ON);
456
457 NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
458 drm_get_connector_name(
459 &nouveau_encoder_connector_get(nv_encoder)->base),
460 nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
461}
462
463static void nv17_tv_save(struct drm_encoder *encoder)
464{
465 struct drm_device *dev = encoder->dev;
466 struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
467
468 nouveau_encoder(encoder)->restore.output =
469 NVReadRAMDAC(dev, 0,
470 NV_PRAMDAC_DACCLK +
471 nv04_dac_output_offset(encoder));
472
473 nv17_tv_state_save(dev, &tv_enc->saved_state);
474
475 tv_enc->state.ptv_200 = tv_enc->saved_state.ptv_200;
476}
477
478static void nv17_tv_restore(struct drm_encoder *encoder)
479{
480 struct drm_device *dev = encoder->dev;
481
482 NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK +
483 nv04_dac_output_offset(encoder),
484 nouveau_encoder(encoder)->restore.output);
485
486 nv17_tv_state_load(dev, &to_tv_enc(encoder)->saved_state);
487}
488
489static int nv17_tv_create_resources(struct drm_encoder *encoder,
490 struct drm_connector *connector)
491{
492 struct drm_device *dev = encoder->dev;
493 struct drm_mode_config *conf = &dev->mode_config;
494 struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
495 struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
496 int num_tv_norms = dcb->tvconf.has_component_output ? NUM_TV_NORMS :
497 NUM_LD_TV_NORMS;
498 int i;
499
500 if (nouveau_tv_norm) {
501 for (i = 0; i < num_tv_norms; i++) {
502 if (!strcmp(nv17_tv_norm_names[i], nouveau_tv_norm)) {
503 tv_enc->tv_norm = i;
504 break;
505 }
506 }
507
508 if (i == num_tv_norms)
509 NV_WARN(dev, "Invalid TV norm setting \"%s\"\n",
510 nouveau_tv_norm);
511 }
512
513 drm_mode_create_tv_properties(dev, num_tv_norms, nv17_tv_norm_names);
514
515 drm_connector_attach_property(connector,
516 conf->tv_select_subconnector_property,
517 tv_enc->select_subconnector);
518 drm_connector_attach_property(connector,
519 conf->tv_subconnector_property,
520 tv_enc->subconnector);
521 drm_connector_attach_property(connector,
522 conf->tv_mode_property,
523 tv_enc->tv_norm);
524 drm_connector_attach_property(connector,
525 conf->tv_flicker_reduction_property,
526 tv_enc->flicker);
527 drm_connector_attach_property(connector,
528 conf->tv_saturation_property,
529 tv_enc->saturation);
530 drm_connector_attach_property(connector,
531 conf->tv_hue_property,
532 tv_enc->hue);
533 drm_connector_attach_property(connector,
534 conf->tv_overscan_property,
535 tv_enc->overscan);
536
537 return 0;
538}
539
540static int nv17_tv_set_property(struct drm_encoder *encoder,
541 struct drm_connector *connector,
542 struct drm_property *property,
543 uint64_t val)
544{
545 struct drm_mode_config *conf = &encoder->dev->mode_config;
546 struct drm_crtc *crtc = encoder->crtc;
547 struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
548 struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
549 bool modes_changed = false;
550
551 if (property == conf->tv_overscan_property) {
552 tv_enc->overscan = val;
553 if (encoder->crtc) {
554 if (tv_norm->kind == CTV_ENC_MODE)
555 nv17_ctv_update_rescaler(encoder);
556 else
557 nv17_tv_update_rescaler(encoder);
558 }
559
560 } else if (property == conf->tv_saturation_property) {
561 if (tv_norm->kind != TV_ENC_MODE)
562 return -EINVAL;
563
564 tv_enc->saturation = val;
565 nv17_tv_update_properties(encoder);
566
567 } else if (property == conf->tv_hue_property) {
568 if (tv_norm->kind != TV_ENC_MODE)
569 return -EINVAL;
570
571 tv_enc->hue = val;
572 nv17_tv_update_properties(encoder);
573
574 } else if (property == conf->tv_flicker_reduction_property) {
575 if (tv_norm->kind != TV_ENC_MODE)
576 return -EINVAL;
577
578 tv_enc->flicker = val;
579 if (encoder->crtc)
580 nv17_tv_update_rescaler(encoder);
581
582 } else if (property == conf->tv_mode_property) {
583 if (connector->dpms != DRM_MODE_DPMS_OFF)
584 return -EINVAL;
585
586 tv_enc->tv_norm = val;
587
588 modes_changed = true;
589
590 } else if (property == conf->tv_select_subconnector_property) {
591 if (tv_norm->kind != TV_ENC_MODE)
592 return -EINVAL;
593
594 tv_enc->select_subconnector = val;
595 nv17_tv_update_properties(encoder);
596
597 } else {
598 return -EINVAL;
599 }
600
601 if (modes_changed) {
602 drm_helper_probe_single_connector_modes(connector, 0, 0);
603
604 /* Disable the crtc to ensure a full modeset is
605 * performed whenever it's turned on again. */
606 if (crtc) {
607 struct drm_mode_set modeset = {
608 .crtc = crtc,
609 };
610
611 crtc->funcs->set_config(&modeset);
612 }
613 }
614
615 return 0;
616}
617
618static void nv17_tv_destroy(struct drm_encoder *encoder)
619{
620 struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
621
622 NV_DEBUG(encoder->dev, "\n");
623
624 drm_encoder_cleanup(encoder);
625 kfree(tv_enc);
626}
627
628static struct drm_encoder_helper_funcs nv17_tv_helper_funcs = {
629 .dpms = nv17_tv_dpms,
630 .save = nv17_tv_save,
631 .restore = nv17_tv_restore,
632 .mode_fixup = nv17_tv_mode_fixup,
633 .prepare = nv17_tv_prepare,
634 .commit = nv17_tv_commit,
635 .mode_set = nv17_tv_mode_set,
636 .detect = nv17_dac_detect,
637};
638
639static struct drm_encoder_slave_funcs nv17_tv_slave_funcs = {
640 .get_modes = nv17_tv_get_modes,
641 .mode_valid = nv17_tv_mode_valid,
642 .create_resources = nv17_tv_create_resources,
643 .set_property = nv17_tv_set_property,
644};
645
646static struct drm_encoder_funcs nv17_tv_funcs = {
647 .destroy = nv17_tv_destroy,
648};
649
650int nv17_tv_create(struct drm_device *dev, struct dcb_entry *entry)
651{
652 struct drm_encoder *encoder;
653 struct nv17_tv_encoder *tv_enc = NULL;
654
655 tv_enc = kzalloc(sizeof(*tv_enc), GFP_KERNEL);
656 if (!tv_enc)
657 return -ENOMEM;
658
659 tv_enc->overscan = 50;
660 tv_enc->flicker = 50;
661 tv_enc->saturation = 50;
662 tv_enc->hue = 0;
663 tv_enc->tv_norm = TV_NORM_PAL;
664 tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
665 tv_enc->select_subconnector = DRM_MODE_SUBCONNECTOR_Automatic;
666 tv_enc->pin_mask = 0;
667
668 encoder = to_drm_encoder(&tv_enc->base);
669
670 tv_enc->base.dcb = entry;
671 tv_enc->base.or = ffs(entry->or) - 1;
672
673 drm_encoder_init(dev, encoder, &nv17_tv_funcs, DRM_MODE_ENCODER_TVDAC);
674 drm_encoder_helper_add(encoder, &nv17_tv_helper_funcs);
675 to_encoder_slave(encoder)->slave_funcs = &nv17_tv_slave_funcs;
676
677 encoder->possible_crtcs = entry->heads;
678 encoder->possible_clones = 0;
679
680 return 0;
681}