diff options
author | Dave Airlie <airlied@redhat.com> | 2009-08-13 16:32:14 +1000 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2009-09-08 09:24:37 +1000 |
commit | 4ce001abafafe77e5dd943d1480fc9f87894e96f (patch) | |
tree | 4a22b42c58a80450992fcf5d7625b19fe045855b /drivers | |
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')
-rw-r--r-- | drivers/gpu/drm/radeon/Makefile | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/atombios.h | 11 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/atombios_crtc.c | 99 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon.h | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_atombios.c | 75 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_connectors.c | 215 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_display.c | 13 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_drv.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_encoders.c | 116 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_legacy_crtc.c | 44 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_legacy_encoders.c | 339 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_legacy_tv.c | 904 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_mode.h | 50 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_reg.h | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/rv515.c | 452 |
15 files changed, 1941 insertions, 388 deletions
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index 6fb84296249..c5db0c4fe78 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -47,7 +47,7 @@ radeon-$(CONFIG_DRM_RADEON_KMS) += radeon_device.o radeon_kms.o \ radeon_clocks.o radeon_fb.o radeon_gem.o radeon_ring.o radeon_irq_kms.o \ radeon_cs.o radeon_bios.o radeon_benchmark.o r100.o r300.o r420.o \ rs400.o rs600.o rs690.o rv515.o r520.o r600.o rs780.o rv770.o \ - radeon_test.o r200.o + radeon_test.o r200.o radeon_legacy_tv.o radeon-$(CONFIG_COMPAT) += radeon_ioc32.o diff --git a/drivers/gpu/drm/radeon/atombios.h b/drivers/gpu/drm/radeon/atombios.h index cf67928abbc..5d402086bc4 100644 --- a/drivers/gpu/drm/radeon/atombios.h +++ b/drivers/gpu/drm/radeon/atombios.h @@ -2374,6 +2374,17 @@ typedef struct _ATOM_ANALOG_TV_INFO { ATOM_MODE_TIMING aModeTimings[MAX_SUPPORTED_TV_TIMING]; } ATOM_ANALOG_TV_INFO; +#define MAX_SUPPORTED_TV_TIMING_V1_2 3 + +typedef struct _ATOM_ANALOG_TV_INFO_V1_2 { + ATOM_COMMON_TABLE_HEADER sHeader; + UCHAR ucTV_SupportedStandard; + UCHAR ucTV_BootUpDefaultStandard; + UCHAR ucExt_TV_ASIC_ID; + UCHAR ucExt_TV_ASIC_SlaveAddr; + ATOM_DTD_FORMAT aModeTimings[MAX_SUPPORTED_TV_TIMING]; +} ATOM_ANALOG_TV_INFO_V1_2; + /**************************************************************************/ /* VRAM usage and their defintions */ diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index 74d034f77c6..8e31e992ec5 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -31,6 +31,10 @@ #include "atom.h" #include "atom-bits.h" +/* evil but including atombios.h is much worse */ +bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index, + SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION *crtc_timing, + int32_t *pixel_clock); static void atombios_overscan_setup(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) @@ -89,17 +93,32 @@ static void atombios_scaler_setup(struct drm_crtc *crtc) struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); ENABLE_SCALER_PS_ALLOCATION args; int index = GetIndexIntoMasterTable(COMMAND, EnableScaler); + /* fixme - fill in enc_priv for atom dac */ enum radeon_tv_std tv_std = TV_STD_NTSC; + bool is_tv = false, is_cv = false; + struct drm_encoder *encoder; if (!ASIC_IS_AVIVO(rdev) && radeon_crtc->crtc_id) return; + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + /* find tv std */ + if (encoder->crtc == crtc) { + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + if (radeon_encoder->active_device & ATOM_DEVICE_TV_SUPPORT) { + struct radeon_encoder_atom_dac *tv_dac = radeon_encoder->enc_priv; + tv_std = tv_dac->tv_std; + is_tv = true; + } + } + } + memset(&args, 0, sizeof(args)); args.ucScaler = radeon_crtc->crtc_id; - if (radeon_crtc->devices & (ATOM_DEVICE_TV_SUPPORT)) { + if (is_tv) { switch (tv_std) { case TV_STD_NTSC: default: @@ -128,7 +147,7 @@ static void atombios_scaler_setup(struct drm_crtc *crtc) break; } args.ucEnable = SCALER_ENABLE_MULTITAP_MODE; - } else if (radeon_crtc->devices & (ATOM_DEVICE_CV_SUPPORT)) { + } else if (is_cv) { args.ucTVStandard = ATOM_TV_CV; args.ucEnable = SCALER_ENABLE_MULTITAP_MODE; } else { @@ -151,9 +170,9 @@ static void atombios_scaler_setup(struct drm_crtc *crtc) } } atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); - if (radeon_crtc->devices & (ATOM_DEVICE_CV_SUPPORT | ATOM_DEVICE_TV_SUPPORT) - && rdev->family >= CHIP_RV515 && rdev->family <= CHIP_RV570) { - atom_rv515_force_tv_scaler(rdev); + if ((is_tv || is_cv) + && rdev->family >= CHIP_RV515 && rdev->family <= CHIP_R580) { + atom_rv515_force_tv_scaler(rdev, radeon_crtc); } } @@ -551,42 +570,68 @@ int atombios_crtc_mode_set(struct drm_crtc *crtc, struct radeon_device *rdev = dev->dev_private; struct drm_encoder *encoder; SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION crtc_timing; + int need_tv_timings = 0; + bool ret; /* TODO color tiling */ memset(&crtc_timing, 0, sizeof(crtc_timing)); - /* TODO tv */ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - + /* find tv std */ + if (encoder->crtc == crtc) { + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + + if (radeon_encoder->active_device & ATOM_DEVICE_TV_SUPPORT) { + struct radeon_encoder_atom_dac *tv_dac = radeon_encoder->enc_priv; + if (tv_dac) { + if (tv_dac->tv_std == TV_STD_NTSC || + tv_dac->tv_std == TV_STD_NTSC_J || + tv_dac->tv_std == TV_STD_PAL_M) + need_tv_timings = 1; + else + need_tv_timings = 2; + break; + } + } + } } crtc_timing.ucCRTC = radeon_crtc->crtc_id; - crtc_timing.usH_Total = adjusted_mode->crtc_htotal; - crtc_timing.usH_Disp = adjusted_mode->crtc_hdisplay; - crtc_timing.usH_SyncStart = adjusted_mode->crtc_hsync_start; - crtc_timing.usH_SyncWidth = - adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start; + if (need_tv_timings) { + ret = radeon_atom_get_tv_timings(rdev, need_tv_timings - 1, + &crtc_timing, &adjusted_mode->clock); + if (ret == false) + need_tv_timings = 0; + } + + if (!need_tv_timings) { + crtc_timing.usH_Total = adjusted_mode->crtc_htotal; + crtc_timing.usH_Disp = adjusted_mode->crtc_hdisplay; + crtc_timing.usH_SyncStart = adjusted_mode->crtc_hsync_start; + crtc_timing.usH_SyncWidth = + adjusted_mode->crtc_hsync_end - adjusted_mode->crtc_hsync_start; - crtc_timing.usV_Total = adjusted_mode->crtc_vtotal; - crtc_timing.usV_Disp = adjusted_mode->crtc_vdisplay; - crtc_timing.usV_SyncStart = adjusted_mode->crtc_vsync_start; - crtc_timing.usV_SyncWidth = - adjusted_mode->crtc_vsync_end - adjusted_mode->crtc_vsync_start; + crtc_timing.usV_Total = adjusted_mode->crtc_vtotal; + crtc_timing.usV_Disp = adjusted_mode->crtc_vdisplay; + crtc_timing.usV_SyncStart = adjusted_mode->crtc_vsync_start; + crtc_timing.usV_SyncWidth = + adjusted_mode->crtc_vsync_end - adjusted_mode->crtc_vsync_start; - if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) - crtc_timing.susModeMiscInfo.usAccess |= ATOM_VSYNC_POLARITY; + if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) + crtc_timing.susModeMiscInfo.usAccess |= ATOM_VSYNC_POLARITY; - if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) - crtc_timing.susModeMiscInfo.usAccess |= ATOM_HSYNC_POLARITY; + if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) + crtc_timing.susModeMiscInfo.usAccess |= ATOM_HSYNC_POLARITY; - if (adjusted_mode->flags & DRM_MODE_FLAG_CSYNC) - crtc_timing.susModeMiscInfo.usAccess |= ATOM_COMPOSITESYNC; + if (adjusted_mode->flags & DRM_MODE_FLAG_CSYNC) + crtc_timing.susModeMiscInfo.usAccess |= ATOM_COMPOSITESYNC; - if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) - crtc_timing.susModeMiscInfo.usAccess |= ATOM_INTERLACE; + if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) + crtc_timing.susModeMiscInfo.usAccess |= ATOM_INTERLACE; - if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) - crtc_timing.susModeMiscInfo.usAccess |= ATOM_DOUBLE_CLOCK_MODE; + if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) + crtc_timing.susModeMiscInfo.usAccess |= ATOM_DOUBLE_CLOCK_MODE; + } atombios_crtc_set_pll(crtc, adjusted_mode); atombios_crtc_set_timing(crtc, &crtc_timing); diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 6c35c3c2919..e47f2fc294c 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -66,6 +66,7 @@ extern int radeon_gart_size; extern int radeon_benchmarking; extern int radeon_testing; extern int radeon_connector_table; +extern int radeon_tv; /* * Copy from radeon_drv.h so we don't have to include both and have conflicting diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c index fcfe5c02d74..bba9b4bd8f5 100644 --- a/drivers/gpu/drm/radeon/radeon_atombios.c +++ b/drivers/gpu/drm/radeon/radeon_atombios.c @@ -471,11 +471,6 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct continue; } - if (i == ATOM_DEVICE_TV1_INDEX) { - DRM_DEBUG("Skipping TV Out\n"); - continue; - } - bios_connectors[i].connector_type = supported_devices_connector_convert[ci.sucConnectorInfo. sbfAccess. @@ -858,6 +853,72 @@ radeon_atombios_get_primary_dac_info(struct radeon_encoder *encoder) return p_dac; } +bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index, + SET_CRTC_TIMING_PARAMETERS_PS_ALLOCATION *crtc_timing, + int32_t *pixel_clock) +{ + struct radeon_mode_info *mode_info = &rdev->mode_info; + ATOM_ANALOG_TV_INFO *tv_info; + ATOM_ANALOG_TV_INFO_V1_2 *tv_info_v1_2; + ATOM_DTD_FORMAT *dtd_timings; + int data_index = GetIndexIntoMasterTable(DATA, AnalogTV_Info); + u8 frev, crev; + uint16_t data_offset; + + atom_parse_data_header(mode_info->atom_context, data_index, NULL, &frev, &crev, &data_offset); + + switch (crev) { + case 1: + tv_info = (ATOM_ANALOG_TV_INFO *)(mode_info->atom_context->bios + data_offset); + if (index > MAX_SUPPORTED_TV_TIMING) + return false; + + crtc_timing->usH_Total = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_H_Total); + crtc_timing->usH_Disp = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_H_Disp); + crtc_timing->usH_SyncStart = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_H_SyncStart); + crtc_timing->usH_SyncWidth = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_H_SyncWidth); + + crtc_timing->usV_Total = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_V_Total); + crtc_timing->usV_Disp = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_V_Disp); + crtc_timing->usV_SyncStart = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_V_SyncStart); + crtc_timing->usV_SyncWidth = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_V_SyncWidth); + + crtc_timing->susModeMiscInfo = tv_info->aModeTimings[index].susModeMiscInfo; + + crtc_timing->ucOverscanRight = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_OverscanRight); + crtc_timing->ucOverscanLeft = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_OverscanLeft); + crtc_timing->ucOverscanBottom = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_OverscanBottom); + crtc_timing->ucOverscanTop = le16_to_cpu(tv_info->aModeTimings[index].usCRTC_OverscanTop); + *pixel_clock = le16_to_cpu(tv_info->aModeTimings[index].usPixelClock) * 10; + + if (index == 1) { + /* PAL timings appear to have wrong values for totals */ + crtc_timing->usH_Total -= 1; + crtc_timing->usV_Total -= 1; + } + break; + case 2: + tv_info_v1_2 = (ATOM_ANALOG_TV_INFO_V1_2 *)(mode_info->atom_context->bios + data_offset); + if (index > MAX_SUPPORTED_TV_TIMING_V1_2) + return false; + + dtd_timings = &tv_info_v1_2->aModeTimings[index]; + crtc_timing->usH_Total = le16_to_cpu(dtd_timings->usHActive) + le16_to_cpu(dtd_timings->usHBlanking_Time); + crtc_timing->usH_Disp = le16_to_cpu(dtd_timings->usHActive); + crtc_timing->usH_SyncStart = le16_to_cpu(dtd_timings->usHActive) + le16_to_cpu(dtd_timings->usHSyncOffset); + crtc_timing->usH_SyncWidth = le16_to_cpu(dtd_timings->usHSyncWidth); + crtc_timing->usV_Total = le16_to_cpu(dtd_timings->usVActive) + le16_to_cpu(dtd_timings->usVBlanking_Time); + crtc_timing->usV_Disp = le16_to_cpu(dtd_timings->usVActive); + crtc_timing->usV_SyncStart = le16_to_cpu(dtd_timings->usVActive) + le16_to_cpu(dtd_timings->usVSyncOffset); + crtc_timing->usV_SyncWidth = le16_to_cpu(dtd_timings->usVSyncWidth); + + crtc_timing->susModeMiscInfo.usAccess = le16_to_cpu(dtd_timings->susModeMiscInfo.usAccess); + *pixel_clock = le16_to_cpu(dtd_timings->usPixClk) * 10; + break; + } + return true; +} + struct radeon_encoder_tv_dac * radeon_atombios_get_tv_dac_info(struct radeon_encoder *encoder) { @@ -948,10 +1009,10 @@ void radeon_atom_initialize_bios_scratch_regs(struct drm_device *dev) uint32_t bios_2_scratch, bios_6_scratch; if (rdev->family >= CHIP_R600) { - bios_2_scratch = RREG32(R600_BIOS_0_SCRATCH); + bios_2_scratch = RREG32(R600_BIOS_2_SCRATCH); bios_6_scratch = RREG32(R600_BIOS_6_SCRATCH); } else { - bios_2_scratch = RREG32(RADEON_BIOS_0_SCRATCH); + bios_2_scratch = RREG32(RADEON_BIOS_2_SCRATCH); bios_6_scratch = RREG32(RADEON_BIOS_6_SCRATCH); } diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index 70ede6a52d4..6a2b0296adf 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) return NULL; } + +/* + * radeon_connector_analog_encoder_conflict_solve + * - search for other connectors sharing this encoder + * if priority is true, then set them disconnected if this is connected + * if priority is false, set us disconnected if they are connected + */ +static enum drm_connector_status +radeon_connector_analog_encoder_conflict_solve(struct drm_connector *connector, + struct drm_encoder *encoder, + enum drm_connector_status current_status, + bool priority) +{ + struct drm_device *dev = connector->dev; + struct drm_connector *conflict; + int i; + + list_for_each_entry(conflict, &dev->mode_config.connector_list, head) { + if (conflict == connector) + continue; + + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { + if (conflict->encoder_ids[i] == 0) + break; + + /* if the IDs match */ + if (conflict->encoder_ids[i] == encoder->base.id) { + if (conflict->status != connector_status_connected) + continue; + + if (priority == true) { + DRM_INFO("1: conflicting encoders switching off %s\n", drm_get_connector_name(conflict)); + DRM_INFO("in favor of %s\n", drm_get_connector_name(connector)); + conflict->status = connector_status_disconnected; + radeon_connector_update_scratch_regs(conflict, connector_status_disconnected); + } else { + DRM_INFO("2: conflicting encoders switching off %s\n", drm_get_connector_name(connector)); + DRM_INFO("in favor of %s\n", drm_get_connector_name(conflict)); + current_status = connector_status_disconnected; + } + break; + } + } + } + return current_status; + +} + static struct drm_display_mode *radeon_fp_native_mode(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; @@ -213,7 +261,6 @@ static int radeon_vga_get_modes(struct drm_connector *connector) static int radeon_vga_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { - return MODE_OK; } @@ -225,22 +272,22 @@ static enum drm_connector_status radeon_vga_detect(struct drm_connector *connect bool dret; enum drm_connector_status ret = connector_status_disconnected; + encoder = radeon_best_single_encoder(connector); + if (!encoder) + ret = connector_status_disconnected; + radeon_i2c_do_lock(radeon_connector, 1); dret = radeon_ddc_probe(radeon_connector); radeon_i2c_do_lock(radeon_connector, 0); if (dret) ret = connector_status_connected; else { - /* if EDID fails to a load detect */ - encoder = radeon_best_single_encoder(connector); - if (!encoder) - ret = connector_status_disconnected; - else { - encoder_funcs = encoder->helper_private; - ret = encoder_funcs->detect(encoder, connector); - } + encoder_funcs = encoder->helper_private; + ret = encoder_funcs->detect(encoder, connector); } + if (ret == connector_status_connected) + ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, true); radeon_connector_update_scratch_regs(connector, ret); return ret; } @@ -259,21 +306,87 @@ struct drm_connector_funcs radeon_vga_connector_funcs = { .set_property = radeon_connector_set_property, }; +static struct drm_display_mode tv_fixed_mode = { + DRM_MODE("800x600", DRM_MODE_TYPE_DEFAULT, 38250, 800, 832, + 912, 1024, 0, 600, 603, 607, 624, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC), +}; + +static int radeon_tv_get_modes(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_display_mode *tv_mode; + + tv_mode = drm_mode_duplicate(dev, &tv_fixed_mode); + tv_mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + + drm_mode_probed_add(connector, tv_mode); + + return 1; +} + +static int radeon_tv_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + return MODE_OK; +} + +static enum drm_connector_status radeon_tv_detect(struct drm_connector *connector) +{ + struct drm_encoder *encoder; + struct drm_encoder_helper_funcs *encoder_funcs; + int ret; + + encoder = radeon_best_single_encoder(connector); + if (!encoder) + ret = connector_status_disconnected; + else { + encoder_funcs = encoder->helper_private; + ret = encoder_funcs->detect(encoder, connector); + } + if (ret == connector_status_connected) + ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, false); + radeon_connector_update_scratch_regs(connector, ret); + return ret; +} + +struct drm_connector_helper_funcs radeon_tv_connector_helper_funcs = { + .get_modes = radeon_tv_get_modes, + .mode_valid = radeon_tv_mode_valid, + .best_encoder = radeon_best_single_encoder, +}; + +struct drm_connector_funcs radeon_tv_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = radeon_tv_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = radeon_connector_destroy, + .set_property = radeon_connector_set_property, +}; + static int radeon_dvi_get_modes(struct drm_connector *connector) { struct radeon_connector *radeon_connector = to_radeon_connector(connector); int ret; ret = radeon_ddc_get_modes(radeon_connector); - /* reset scratch regs here since radeon_dvi_detect doesn't check digital bit */ - radeon_connector_update_scratch_regs(connector, connector_status_connected); return ret; } +/* + * DVI is complicated + * Do a DDC probe, if DDC probe passes, get the full EDID so + * we can do analog/digital monitor detection at this point. + * If the monitor is an analog monitor or we got no DDC, + * we need to find the DAC encoder object for this connector. + * If we got no DDC, we do load detection on the DAC encoder object. + * If we got analog DDC or load detection passes on the DAC encoder + * we have to check if this analog encoder is shared with anyone else (TV) + * if its shared we have to set the other connector to disconnected. + */ static enum drm_connector_status radeon_dvi_detect(struct drm_connector *connector) { struct radeon_connector *radeon_connector = to_radeon_connector(connector); - struct drm_encoder *encoder; + struct drm_encoder *encoder = NULL; struct drm_encoder_helper_funcs *encoder_funcs; struct drm_mode_object *obj; int i; @@ -283,32 +396,58 @@ static enum drm_connector_status radeon_dvi_detect(struct drm_connector *connect radeon_i2c_do_lock(radeon_connector, 1); dret = radeon_ddc_probe(radeon_connector); radeon_i2c_do_lock(radeon_connector, 0); - if (dret) - ret = connector_status_connected; - else { - for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { - if (connector->encoder_ids[i] == 0) - break; + if (dret) { + radeon_i2c_do_lock(radeon_connector, 1); + radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter); + radeon_i2c_do_lock(radeon_connector, 0); + + if (!radeon_connector->edid) { + DRM_ERROR("DDC responded but not EDID found for %s\n", + drm_get_connector_name(connector)); + } else { + radeon_connector->use_digital = !!(radeon_connector->edid->input & DRM_EDID_INPUT_DIGITAL); + + /* if this isn't a digital monitor + then we need to make sure we don't have any + TV conflicts */ + ret = connector_status_connected; + } + } + + if ((ret == connector_status_connected) && (radeon_connector->use_digital == true)) + goto out; + + /* find analog encoder */ + for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { + if (connector->encoder_ids[i] == 0) + break; - obj = drm_mode_object_find(connector->dev, - connector->encoder_ids[i], - DRM_MODE_OBJECT_ENCODER); - if (!obj) - continue; + obj = drm_mode_object_find(connector->dev, + connector->encoder_ids[i], + DRM_MODE_OBJECT_ENCODER); + if (!obj) + continue; - encoder = obj_to_encoder(obj); + encoder = obj_to_encoder(obj); - encoder_funcs = encoder->helper_private; - if (encoder_funcs->detect) { + encoder_funcs = encoder->helper_private; + if (encoder_funcs->detect) { + if (ret != connector_status_connected) { ret = encoder_funcs->detect(encoder, connector); if (ret == connector_status_connected) { - radeon_connector->use_digital = 0; - break; + radeon_connector->use_digital = false; } } + break; } } + if ((ret == connector_status_connected) && (radeon_connector->use_digital == false) && + encoder) { + ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, true); + } + +out: /* updated in get modes as well since we need to know if it's analog or digital */ radeon_connector_update_scratch_regs(connector, ret); return ret; @@ -332,7 +471,7 @@ struct drm_encoder *radeon_dvi_encoder(struct drm_connector *connector) encoder = obj_to_encoder(obj); - if (radeon_connector->use_digital) { + if (radeon_connector->use_digital == true) { if (encoder->encoder_type == DRM_MODE_ENCODER_TMDS) return encoder; } else { @@ -385,10 +524,7 @@ radeon_add_atom_connector(struct drm_device *dev, uint32_t subpixel_order = SubPixelNone; /* fixme - tv/cv/din */ - if ((connector_type == DRM_MODE_CONNECTOR_Unknown) || - (connector_type == DRM_MODE_CONNECTOR_SVIDEO) || - (connector_type == DRM_MODE_CONNECTOR_Composite) || - (connector_type == DRM_MODE_CONNECTOR_9PinDIN)) + if (connector_type == DRM_MODE_CONNECTOR_Unknown) return; /* see if we already added it */ @@ -480,6 +616,10 @@ radeon_add_atom_connector(struct drm_device *dev, case DRM_MODE_CONNECTOR_SVIDEO: case DRM_MODE_CONNECTOR_Composite: case DRM_MODE_CONNECTOR_9PinDIN: + if (radeon_tv == 1) { + drm_connector_init(dev, &radeon_connector->base, &radeon_tv_connector_funcs, connector_type); + drm_connector_helper_add(&radeon_connector->base, &radeon_tv_connector_helper_funcs); + } break; case DRM_MODE_CONNECTOR_LVDS: radeon_dig_connector = kzalloc(sizeof(struct radeon_connector_atom_dig), GFP_KERNEL); @@ -522,10 +662,7 @@ radeon_add_legacy_connector(struct drm_device *dev, uint32_t subpixel_order = SubPixelNone; /* fixme - tv/cv/din */ - if ((connector_type == DRM_MODE_CONNECTOR_Unknown) || - (connector_type == DRM_MODE_CONNECTOR_SVIDEO) || - (connector_type == DRM_MODE_CONNECTOR_Composite) || - (connector_type == DRM_MODE_CONNECTOR_9PinDIN)) + if (connector_type == DRM_MODE_CONNECTOR_Unknown) return; /* see if we already added it */ @@ -578,6 +715,10 @@ radeon_add_legacy_connector(struct drm_device *dev, case DRM_MODE_CONNECTOR_SVIDEO: case DRM_MODE_CONNECTOR_Composite: case DRM_MODE_CONNECTOR_9PinDIN: + if (radeon_tv == 1) { + drm_connector_init(dev, &radeon_connector->base, &radeon_tv_connector_funcs, connector_type); + drm_connector_helper_add(&radeon_connector->base, &radeon_tv_connector_helper_funcs); + } break; case DRM_MODE_CONNECTOR_LVDS: drm_connector_init(dev, &radeon_connector->base, &radeon_lvds_connector_funcs, connector_type); diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index af035605d14..9d817a62e7f 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -312,7 +312,7 @@ static void radeon_print_display_setup(struct drm_device *dev) } } -bool radeon_setup_enc_conn(struct drm_device *dev) +static bool radeon_setup_enc_conn(struct drm_device *dev) { struct radeon_device *rdev = dev->dev_private; struct drm_connector *drm_connector; @@ -346,9 +346,13 @@ int radeon_ddc_get_modes(struct radeon_connector *radeon_connector) if (!radeon_connector->ddc_bus) return -1; - radeon_i2c_do_lock(radeon_connector, 1); - edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter); - radeon_i2c_do_lock(radeon_connector, 0); + if (!radeon_connector->edid) { + radeon_i2c_do_lock(radeon_connector, 1); + edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter); + radeon_i2c_do_lock(radeon_connector, 0); + } else + edid = radeon_connector->edid; + if (edid) { /* update digital bits here */ if (edid->input & DRM_EDID_INPUT_DIGITAL) @@ -677,7 +681,6 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc, continue; if (first) { radeon_crtc->rmx_type = radeon_encoder->rmx_type; - radeon_crtc->devices = radeon_encoder->devices; memcpy(&radeon_crtc->native_mode, &radeon_encoder->native_mode, sizeof(struct radeon_native_mode)); diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 0bd5879a495..133e975dbf0 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -91,6 +91,7 @@ int radeon_gart_size = 512; /* default gart size */ int radeon_benchmarking = 0; int radeon_testing = 0; int radeon_connector_table = 0; +int radeon_tv = 1; #endif MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers"); @@ -123,6 +124,9 @@ module_param_named(test, radeon_testing, int, 0444); MODULE_PARM_DESC(connector_table, "Force connector table"); module_param_named(connector_table, radeon_connector_table, int, 0444); + +MODULE_PARM_DESC(tv, "TV enable (0 = disable)"); +module_param_named(tv, radeon_tv, int, 0444); #endif static int radeon_suspend(struct drm_device *dev, pm_message_t state) diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c b/drivers/gpu/drm/radeon/radeon_encoders.c index 0a92706eac1..5c4ede7c990 100644 --- a/drivers/gpu/drm/radeon/radeon_encoders.c +++ b/drivers/gpu/drm/radeon/radeon_encoders.c @@ -126,6 +126,23 @@ radeon_link_encoder_connector(struct drm_device *dev) } } +void radeon_encoder_set_active_device(struct drm_encoder *encoder) +{ + struct drm_device *dev = encoder->dev; + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct drm_connector *connector; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (connector->encoder == encoder) { + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + radeon_encoder->active_device = radeon_encoder->devices & radeon_connector->devices; + DRM_INFO("setting active device to %08x from %08x %08x for encoder %d\n", + radeon_encoder->active_device, radeon_encoder->devices, + radeon_connector->devices, encoder->encoder_type); + } + } +} + static struct drm_connector * radeon_get_connector_for_encoder(struct drm_encoder *encoder) { @@ -244,9 +261,9 @@ atombios_dac_setup(struct drm_encoder *encoder, int action) args.ucAction = action; - if (radeon_encoder->devices & (ATOM_DEVICE_CRT_SUPPORT)) + if (radeon_encoder->active_device & (ATOM_DEVICE_CRT_SUPPORT)) args.ucDacStandard = ATOM_DAC1_PS2; - else if (radeon_encoder->devices & (ATOM_DEVICE_CV_SUPPORT)) + else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) args.ucDacStandard = ATOM_DAC1_CV; else { switch (tv_std) { @@ -288,7 +305,7 @@ atombios_tv_setup(struct drm_encoder *encoder, int action) args.sTVEncoder.ucAction = action; - if (radeon_encoder->devices & (ATOM_DEVICE_CV_SUPPORT)) + if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) args.sTVEncoder.ucTvStandard = ATOM_TV_CV; else { switch (tv_std) { @@ -825,10 +842,10 @@ atombios_yuv_setup(struct drm_encoder *encoder, bool enable) /* XXX: fix up scratch reg handling */ temp = RREG32(reg); - if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT)) + if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) WREG32(reg, (ATOM_S3_TV1_ACTIVE | (radeon_crtc->crtc_id << 18))); - else if (radeon_encoder->devices & (ATOM_DEVICE_CV_SUPPORT)) + else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) WREG32(reg, (ATOM_S3_CV_ACTIVE | (radeon_crtc->crtc_id << 24))); else WREG32(reg, 0); @@ -851,9 +868,19 @@ radeon_atom_encoder_dpms(struct drm_encoder *encoder, int mode) DISPLAY_DEVICE_OUTPUT_CONTROL_PS_ALLOCATION args; int index = 0; bool is_dig = false; + int devices; memset(&args, 0, sizeof(args)); + /* on DPMS off we have no idea if active device is meaningful */ + if (mode != DRM_MODE_DPMS_ON && !radeon_encoder->active_device) + devices = radeon_encoder->devices; + else + devices = radeon_encoder->active_device; + + DRM_INFO("encoder dpms %d to mode %d, devices %08x, active_devices %08x\n", + radeon_encoder->encoder_id, mode, radeon_encoder->devices, + radeon_encoder->active_device); switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_INTERNAL_TMDS1: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: @@ -881,18 +908,18 @@ radeon_atom_encoder_dpms(struct drm_encoder *encoder, int mode) break; case ENCODER_OBJECT_ID_INTERNAL_DAC1: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: - if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT)) + if (devices & (ATOM_DEVICE_TV_SUPPORT)) index = GetIndexIntoMasterTable(COMMAND, TV1OutputControl); - else if (radeon_encoder->devices & (ATOM_DEVICE_CV_SUPPORT)) + else if (devices & (ATOM_DEVICE_CV_SUPPORT)) index = GetIndexIntoMasterTable(COMMAND, CV1OutputControl); else index = GetIndexIntoMasterTable(COMMAND, DAC1OutputControl); break; case ENCODER_OBJECT_ID_INTERNAL_DAC2: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: - if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT)) + if (devices & (ATOM_DEVICE_TV_SUPPORT)) index = GetIndexIntoMasterTable(COMMAND, TV1OutputControl); - else if (radeon_encoder->devices & (ATOM_DEVICE_CV_SUPPORT)) + else if (devices & (ATOM_DEVICE_CV_SUPPORT)) index = GetIndexIntoMasterTable(COMMAND, CV1OutputControl); else index = GetIndexIntoMasterTable(COMMAND, DAC2OutputControl); @@ -979,18 +1006,18 @@ atombios_set_encoder_crtc_source(struct drm_encoder *encoder) break; case ENCODER_OBJECT_ID_INTERNAL_DAC1: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: - if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT)) + if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) args.v1.ucDevice = ATOM_DEVICE_TV1_INDEX; - else if (radeon_encoder->devices & (ATOM_DEVICE_CV_SUPPORT)) + else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) args.v1.ucDevice = ATOM_DEVICE_CV_INDEX; else args.v1.ucDevice = ATOM_DEVICE_CRT1_INDEX; break; case ENCODER_OBJECT_ID_INTERNAL_DAC2: case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2: - if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT)) + if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) args.v1.ucDevice = ATOM_DEVICE_TV1_INDEX; - else if (radeon_encoder->devices & (ATOM_DEVICE_CV_SUPPORT)) + else if (radeon_encoder->active_device & (ATOM_DEVICE_CV_SUPPORT)) args.v1.ucDevice = ATOM_DEVICE_CV_INDEX; else args.v1.ucDevice = ATOM_DEVICE_CRT2_INDEX; @@ -1019,17 +1046,17 @@ atombios_set_encoder_crtc_source(struct drm_encoder *encoder) args.v2.ucEncoderID = ASIC_INT_DIG2_ENCODER_ID; break; case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1: - if (radeon_encoder->devices & (ATOM_DEVICE_TV_SUPPORT)) + if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPP |