aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2009-08-13 16:32:14 +1000
committerDave Airlie <airlied@redhat.com>2009-09-08 09:24:37 +1000
commit4ce001abafafe77e5dd943d1480fc9f87894e96f (patch)
tree4a22b42c58a80450992fcf5d7625b19fe045855b /drivers
parent551ebd837c75fc75df81811a18b7136c39cab487 (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/Makefile2
-rw-r--r--drivers/gpu/drm/radeon/atombios.h11
-rw-r--r--drivers/gpu/drm/radeon/atombios_crtc.c99
-rw-r--r--drivers/gpu/drm/radeon/radeon.h1
-rw-r--r--drivers/gpu/drm/radeon/radeon_atombios.c75
-rw-r--r--drivers/gpu/drm/radeon/radeon_connectors.c215
-rw-r--r--drivers/gpu/drm/radeon/radeon_display.c13
-rw-r--r--drivers/gpu/drm/radeon/radeon_drv.c4
-rw-r--r--drivers/gpu/drm/radeon/radeon_encoders.c116
-rw-r--r--drivers/gpu/drm/radeon/radeon_legacy_crtc.c44
-rw-r--r--drivers/gpu/drm/radeon/radeon_legacy_encoders.c339
-rw-r--r--drivers/gpu/drm/radeon/radeon_legacy_tv.c904
-rw-r--r--drivers/gpu/drm/radeon/radeon_mode.h50
-rw-r--r--drivers/gpu/drm/radeon/radeon_reg.h4
-rw-r--r--drivers/gpu/drm/radeon/rv515.c452
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