diff options
author | Dave Airlie <airlied@redhat.com> | 2009-08-13 02:32:14 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2009-09-07 19:24:37 -0400 |
commit | 4ce001abafafe77e5dd943d1480fc9f87894e96f (patch) | |
tree | 4a22b42c58a80450992fcf5d7625b19fe045855b /drivers/gpu/drm/radeon/radeon_connectors.c | |
parent | 551ebd837c75fc75df81811a18b7136c39cab487 (diff) |
drm/radeon/kms: add initial radeon tv-out support.
This ports the tv-out code from the DDX to KMS.
adds a radeon.tv module option, radeon.tv=0 to disable tv
Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/radeon/radeon_connectors.c')
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_connectors.c | 215 |
1 files changed, 178 insertions, 37 deletions
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index 70ede6a52d4e..6a2b0296adff 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c | |||
@@ -94,6 +94,54 @@ struct drm_encoder *radeon_best_single_encoder(struct drm_connector *connector) | |||
94 | return NULL; | 94 | return NULL; |
95 | } | 95 | } |
96 | 96 | ||
97 | |||
98 | /* | ||
99 | * radeon_connector_analog_encoder_conflict_solve | ||
100 | * - search for other connectors sharing this encoder | ||
101 | * if priority is true, then set them disconnected if this is connected | ||
102 | * if priority is false, set us disconnected if they are connected | ||
103 | */ | ||
104 | static enum drm_connector_status | ||
105 | radeon_connector_analog_encoder_conflict_solve(struct drm_connector *connector, | ||
106 | struct drm_encoder *encoder, | ||
107 | enum drm_connector_status current_status, | ||
108 | bool priority) | ||
109 | { | ||
110 | struct drm_device *dev = connector->dev; | ||
111 | struct drm_connector *conflict; | ||
112 | int i; | ||
113 | |||
114 | list_for_each_entry(conflict, &dev->mode_config.connector_list, head) { | ||
115 | if (conflict == connector) | ||
116 | continue; | ||
117 | |||
118 | for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { | ||
119 | if (conflict->encoder_ids[i] == 0) | ||
120 | break; | ||
121 | |||
122 | /* if the IDs match */ | ||
123 | if (conflict->encoder_ids[i] == encoder->base.id) { | ||
124 | if (conflict->status != connector_status_connected) | ||
125 | continue; | ||
126 | |||
127 | if (priority == true) { | ||
128 | DRM_INFO("1: conflicting encoders switching off %s\n", drm_get_connector_name(conflict)); | ||
129 | DRM_INFO("in favor of %s\n", drm_get_connector_name(connector)); | ||
130 | conflict->status = connector_status_disconnected; | ||
131 | radeon_connector_update_scratch_regs(conflict, connector_status_disconnected); | ||
132 | } else { | ||
133 | DRM_INFO("2: conflicting encoders switching off %s\n", drm_get_connector_name(connector)); | ||
134 | DRM_INFO("in favor of %s\n", drm_get_connector_name(conflict)); | ||
135 | current_status = connector_status_disconnected; | ||
136 | } | ||
137 | break; | ||
138 | } | ||
139 | } | ||
140 | } | ||
141 | return current_status; | ||
142 | |||
143 | } | ||
144 | |||
97 | static struct drm_display_mode *radeon_fp_native_mode(struct drm_encoder *encoder) | 145 | static struct drm_display_mode *radeon_fp_native_mode(struct drm_encoder *encoder) |
98 | { | 146 | { |
99 | struct drm_device *dev = encoder->dev; | 147 | struct drm_device *dev = encoder->dev; |
@@ -213,7 +261,6 @@ static int radeon_vga_get_modes(struct drm_connector *connector) | |||
213 | static int radeon_vga_mode_valid(struct drm_connector *connector, | 261 | static int radeon_vga_mode_valid(struct drm_connector *connector, |
214 | struct drm_display_mode *mode) | 262 | struct drm_display_mode *mode) |
215 | { | 263 | { |
216 | |||
217 | return MODE_OK; | 264 | return MODE_OK; |
218 | } | 265 | } |
219 | 266 | ||
@@ -225,22 +272,22 @@ static enum drm_connector_status radeon_vga_detect(struct drm_connector *connect | |||
225 | bool dret; | 272 | bool dret; |
226 | enum drm_connector_status ret = connector_status_disconnected; | 273 | enum drm_connector_status ret = connector_status_disconnected; |
227 | 274 | ||
275 | encoder = radeon_best_single_encoder(connector); | ||
276 | if (!encoder) | ||
277 | ret = connector_status_disconnected; | ||
278 | |||
228 | radeon_i2c_do_lock(radeon_connector, 1); | 279 | radeon_i2c_do_lock(radeon_connector, 1); |
229 | dret = radeon_ddc_probe(radeon_connector); | 280 | dret = radeon_ddc_probe(radeon_connector); |
230 | radeon_i2c_do_lock(radeon_connector, 0); | 281 | radeon_i2c_do_lock(radeon_connector, 0); |
231 | if (dret) | 282 | if (dret) |
232 | ret = connector_status_connected; | 283 | ret = connector_status_connected; |
233 | else { | 284 | else { |
234 | /* if EDID fails to a load detect */ | 285 | encoder_funcs = encoder->helper_private; |
235 | encoder = radeon_best_single_encoder(connector); | 286 | ret = encoder_funcs->detect(encoder, connector); |
236 | if (!encoder) | ||
237 | ret = connector_status_disconnected; | ||
238 | else { | ||
239 | encoder_funcs = encoder->helper_private; | ||
240 | ret = encoder_funcs->detect(encoder, connector); | ||
241 | } | ||
242 | } | 287 | } |
243 | 288 | ||
289 | if (ret == connector_status_connected) | ||
290 | ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, true); | ||
244 | radeon_connector_update_scratch_regs(connector, ret); | 291 | radeon_connector_update_scratch_regs(connector, ret); |
245 | return ret; | 292 | return ret; |
246 | } | 293 | } |
@@ -259,21 +306,87 @@ struct drm_connector_funcs radeon_vga_connector_funcs = { | |||
259 | .set_property = radeon_connector_set_property, | 306 | .set_property = radeon_connector_set_property, |
260 | }; | 307 | }; |
261 | 308 | ||
309 | static struct drm_display_mode tv_fixed_mode = { | ||
310 | DRM_MODE("800x600", DRM_MODE_TYPE_DEFAULT, 38250, 800, 832, | ||
311 | 912, 1024, 0, 600, 603, 607, 624, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), | ||
312 | }; | ||
313 | |||
314 | static int radeon_tv_get_modes(struct drm_connector *connector) | ||
315 | { | ||
316 | struct drm_device *dev = connector->dev; | ||
317 | struct drm_display_mode *tv_mode; | ||
318 | |||
319 | tv_mode = drm_mode_duplicate(dev, &tv_fixed_mode); | ||
320 | tv_mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; | ||
321 | |||
322 | drm_mode_probed_add(connector, tv_mode); | ||
323 | |||
324 | return 1; | ||
325 | } | ||
326 | |||
327 | static int radeon_tv_mode_valid(struct drm_connector *connector, | ||
328 | struct drm_display_mode *mode) | ||
329 | { | ||
330 | return MODE_OK; | ||
331 | } | ||
332 | |||
333 | static enum drm_connector_status radeon_tv_detect(struct drm_connector *connector) | ||
334 | { | ||
335 | struct drm_encoder *encoder; | ||
336 | struct drm_encoder_helper_funcs *encoder_funcs; | ||
337 | int ret; | ||
338 | |||
339 | encoder = radeon_best_single_encoder(connector); | ||
340 | if (!encoder) | ||
341 | ret = connector_status_disconnected; | ||
342 | else { | ||
343 | encoder_funcs = encoder->helper_private; | ||
344 | ret = encoder_funcs->detect(encoder, connector); | ||
345 | } | ||
346 | if (ret == connector_status_connected) | ||
347 | ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, false); | ||
348 | radeon_connector_update_scratch_regs(connector, ret); | ||
349 | return ret; | ||
350 | } | ||
351 | |||
352 | struct drm_connector_helper_funcs radeon_tv_connector_helper_funcs = { | ||
353 | .get_modes = radeon_tv_get_modes, | ||
354 | .mode_valid = radeon_tv_mode_valid, | ||
355 | .best_encoder = radeon_best_single_encoder, | ||
356 | }; | ||
357 | |||
358 | struct drm_connector_funcs radeon_tv_connector_funcs = { | ||
359 | .dpms = drm_helper_connector_dpms, | ||
360 | .detect = radeon_tv_detect, | ||
361 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
362 | .destroy = radeon_connector_destroy, | ||
363 | .set_property = radeon_connector_set_property, | ||
364 | }; | ||
365 | |||
262 | static int radeon_dvi_get_modes(struct drm_connector *connector) | 366 | static int radeon_dvi_get_modes(struct drm_connector *connector) |
263 | { | 367 | { |
264 | struct radeon_connector *radeon_connector = to_radeon_connector(connector); | 368 | struct radeon_connector *radeon_connector = to_radeon_connector(connector); |
265 | int ret; | 369 | int ret; |
266 | 370 | ||
267 | ret = radeon_ddc_get_modes(radeon_connector); | 371 | ret = radeon_ddc_get_modes(radeon_connector); |
268 | /* reset scratch regs here since radeon_dvi_detect doesn't check digital bit */ | ||
269 | radeon_connector_update_scratch_regs(connector, connector_status_connected); | ||
270 | return ret; | 372 | return ret; |
271 | } | 373 | } |
272 | 374 | ||
375 | /* | ||
376 | * DVI is complicated | ||
377 | * Do a DDC probe, if DDC probe passes, get the full EDID so | ||
378 | * we can do analog/digital monitor detection at this point. | ||
379 | * If the monitor is an analog monitor or we got no DDC, | ||
380 | * we need to find the DAC encoder object for this connector. | ||
381 | * If we got no DDC, we do load detection on the DAC encoder object. | ||
382 | * If we got analog DDC or load detection passes on the DAC encoder | ||
383 | * we have to check if this analog encoder is shared with anyone else (TV) | ||
384 | * if its shared we have to set the other connector to disconnected. | ||
385 | */ | ||
273 | static enum drm_connector_status radeon_dvi_detect(struct drm_connector *connector) | 386 | static enum drm_connector_status radeon_dvi_detect(struct drm_connector *connector) |
274 | { | 387 | { |
275 | struct radeon_connector *radeon_connector = to_radeon_connector(connector); | 388 | struct radeon_connector *radeon_connector = to_radeon_connector(connector); |
276 | struct drm_encoder *encoder; | 389 | struct drm_encoder *encoder = NULL; |
277 | struct drm_encoder_helper_funcs *encoder_funcs; | 390 | struct drm_encoder_helper_funcs *encoder_funcs; |
278 | struct drm_mode_object *obj; | 391 | struct drm_mode_object *obj; |
279 | int i; | 392 | int i; |
@@ -283,32 +396,58 @@ static enum drm_connector_status radeon_dvi_detect(struct drm_connector *connect | |||
283 | radeon_i2c_do_lock(radeon_connector, 1); | 396 | radeon_i2c_do_lock(radeon_connector, 1); |
284 | dret = radeon_ddc_probe(radeon_connector); | 397 | dret = radeon_ddc_probe(radeon_connector); |
285 | radeon_i2c_do_lock(radeon_connector, 0); | 398 | radeon_i2c_do_lock(radeon_connector, 0); |
286 | if (dret) | 399 | if (dret) { |
287 | ret = connector_status_connected; | 400 | radeon_i2c_do_lock(radeon_connector, 1); |
288 | else { | 401 | radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter); |
289 | for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { | 402 | radeon_i2c_do_lock(radeon_connector, 0); |
290 | if (connector->encoder_ids[i] == 0) | 403 | |
291 | break; | 404 | if (!radeon_connector->edid) { |
405 | DRM_ERROR("DDC responded but not EDID found for %s\n", | ||
406 | drm_get_connector_name(connector)); | ||
407 | } else { | ||
408 | radeon_connector->use_digital = !!(radeon_connector->edid->input & DRM_EDID_INPUT_DIGITAL); | ||
409 | |||
410 | /* if this isn't a digital monitor | ||
411 | then we need to make sure we don't have any | ||
412 | TV conflicts */ | ||
413 | ret = connector_status_connected; | ||
414 | } | ||
415 | } | ||
416 | |||
417 | if ((ret == connector_status_connected) && (radeon_connector->use_digital == true)) | ||
418 | goto out; | ||
419 | |||
420 | /* find analog encoder */ | ||
421 | for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { | ||
422 | if (connector->encoder_ids[i] == 0) | ||
423 | break; | ||
292 | 424 | ||
293 | obj = drm_mode_object_find(connector->dev, | 425 | obj = drm_mode_object_find(connector->dev, |
294 | connector->encoder_ids[i], | 426 | connector->encoder_ids[i], |
295 | DRM_MODE_OBJECT_ENCODER); | 427 | DRM_MODE_OBJECT_ENCODER); |
296 | if (!obj) | 428 | if (!obj) |
297 | continue; | 429 | continue; |
298 | 430 | ||
299 | encoder = obj_to_encoder(obj); | 431 | encoder = obj_to_encoder(obj); |
300 | 432 | ||
301 | encoder_funcs = encoder->helper_private; | 433 | encoder_funcs = encoder->helper_private; |
302 | if (encoder_funcs->detect) { | 434 | if (encoder_funcs->detect) { |
435 | if (ret != connector_status_connected) { | ||
303 | ret = encoder_funcs->detect(encoder, connector); | 436 | ret = encoder_funcs->detect(encoder, connector); |
304 | if (ret == connector_status_connected) { | 437 | if (ret == connector_status_connected) { |
305 | radeon_connector->use_digital = 0; | 438 | radeon_connector->use_digital = false; |
306 | break; | ||
307 | } | 439 | } |
308 | } | 440 | } |
441 | break; | ||
309 | } | 442 | } |
310 | } | 443 | } |
311 | 444 | ||
445 | if ((ret == connector_status_connected) && (radeon_connector->use_digital == false) && | ||
446 | encoder) { | ||
447 | ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, true); | ||
448 | } | ||
449 | |||
450 | out: | ||
312 | /* updated in get modes as well since we need to know if it's analog or digital */ | 451 | /* updated in get modes as well since we need to know if it's analog or digital */ |
313 | radeon_connector_update_scratch_regs(connector, ret); | 452 | radeon_connector_update_scratch_regs(connector, ret); |
314 | return ret; | 453 | return ret; |
@@ -332,7 +471,7 @@ struct drm_encoder *radeon_dvi_encoder(struct drm_connector *connector) | |||
332 | 471 | ||
333 | encoder = obj_to_encoder(obj); | 472 | encoder = obj_to_encoder(obj); |
334 | 473 | ||
335 | if (radeon_connector->use_digital) { | 474 | if (radeon_connector->use_digital == true) { |
336 | if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS) | 475 | if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS) |
337 | return encoder; | 476 | return encoder; |
338 | } else { | 477 | } else { |
@@ -385,10 +524,7 @@ radeon_add_atom_connector(struct drm_device *dev, | |||
385 | uint32_t subpixel_order = SubPixelNone; | 524 | uint32_t subpixel_order = SubPixelNone; |
386 | 525 | ||
387 | /* fixme - tv/cv/din */ | 526 | /* fixme - tv/cv/din */ |
388 | if ((connector_type == DRM_MODE_CONNECTOR_Unknown) || | 527 | if (connector_type == DRM_MODE_CONNECTOR_Unknown) |
389 | (connector_type == DRM_MODE_CONNECTOR_SVIDEO) || | ||
390 | (connector_type == DRM_MODE_CONNECTOR_Composite) || | ||
391 | (connector_type == DRM_MODE_CONNECTOR_9PinDIN)) | ||
392 | return; | 528 | return; |
393 | 529 | ||
394 | /* see if we already added it */ | 530 | /* see if we already added it */ |
@@ -480,6 +616,10 @@ radeon_add_atom_connector(struct drm_device *dev, | |||
480 | case DRM_MODE_CONNECTOR_SVIDEO: | 616 | case DRM_MODE_CONNECTOR_SVIDEO: |
481 | case DRM_MODE_CONNECTOR_Composite: | 617 | case DRM_MODE_CONNECTOR_Composite: |
482 | case DRM_MODE_CONNECTOR_9PinDIN: | 618 | case DRM_MODE_CONNECTOR_9PinDIN: |
619 | if (radeon_tv == 1) { | ||
620 | drm_connector_init(dev, &radeon_connector->base, &radeon_tv_connector_funcs, connector_type); | ||
621 | drm_connector_helper_add(&radeon_connector->base, &radeon_tv_connector_helper_funcs); | ||
622 | } | ||
483 | break; | 623 | break; |
484 | case DRM_MODE_CONNECTOR_LVDS: | 624 | case DRM_MODE_CONNECTOR_LVDS: |
485 | radeon_dig_connector = kzalloc(sizeof(struct radeon_connector_atom_dig), GFP_KERNEL); | 625 | radeon_dig_connector = kzalloc(sizeof(struct radeon_connector_atom_dig), GFP_KERNEL); |
@@ -522,10 +662,7 @@ radeon_add_legacy_connector(struct drm_device *dev, | |||
522 | uint32_t subpixel_order = SubPixelNone; | 662 | uint32_t subpixel_order = SubPixelNone; |
523 | 663 | ||
524 | /* fixme - tv/cv/din */ | 664 | /* fixme - tv/cv/din */ |
525 | if ((connector_type == DRM_MODE_CONNECTOR_Unknown) || | 665 | if (connector_type == DRM_MODE_CONNECTOR_Unknown) |
526 | (connector_type == DRM_MODE_CONNECTOR_SVIDEO) || | ||
527 | (connector_type == DRM_MODE_CONNECTOR_Composite) || | ||
528 | (connector_type == DRM_MODE_CONNECTOR_9PinDIN)) | ||
529 | return; | 666 | return; |
530 | 667 | ||
531 | /* see if we already added it */ | 668 | /* see if we already added it */ |
@@ -578,6 +715,10 @@ radeon_add_legacy_connector(struct drm_device *dev, | |||
578 | case DRM_MODE_CONNECTOR_SVIDEO: | 715 | case DRM_MODE_CONNECTOR_SVIDEO: |
579 | case DRM_MODE_CONNECTOR_Composite: | 716 | case DRM_MODE_CONNECTOR_Composite: |
580 | case DRM_MODE_CONNECTOR_9PinDIN: | 717 | case DRM_MODE_CONNECTOR_9PinDIN: |
718 | if (radeon_tv == 1) { | ||
719 | drm_connector_init(dev, &radeon_connector->base, &radeon_tv_connector_funcs, connector_type); | ||
720 | drm_connector_helper_add(&radeon_connector->base, &radeon_tv_connector_helper_funcs); | ||
721 | } | ||
581 | break; | 722 | break; |
582 | case DRM_MODE_CONNECTOR_LVDS: | 723 | case DRM_MODE_CONNECTOR_LVDS: |
583 | drm_connector_init(dev, &radeon_connector->base, &radeon_lvds_connector_funcs, connector_type); | 724 | drm_connector_init(dev, &radeon_connector->base, &radeon_lvds_connector_funcs, connector_type); |