Commit 5b1714d3 authored by Alex Deucher's avatar Alex Deucher Committed by Dave Airlie

drm/radeon/kms: enable underscan option for digital connectors

This connector attribute allows you to enable or disable underscan
on a digital output to compensate for panels that automatically
overscan (e.g., many HDMI TVs).  Valid values for the attribute are:

off - forces underscan off
on - forces underscan on
auto - enables underscan if an HDMI TV is connected, off otherwise

default value is auto.
Signed-off-by: default avatarAlex Deucher <alexdeucher@gmail.com>
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent d65d65b1
...@@ -44,10 +44,6 @@ static void atombios_overscan_setup(struct drm_crtc *crtc, ...@@ -44,10 +44,6 @@ static void atombios_overscan_setup(struct drm_crtc *crtc,
memset(&args, 0, sizeof(args)); memset(&args, 0, sizeof(args));
args.usOverscanRight = 0;
args.usOverscanLeft = 0;
args.usOverscanBottom = 0;
args.usOverscanTop = 0;
args.ucCRTC = radeon_crtc->crtc_id; args.ucCRTC = radeon_crtc->crtc_id;
switch (radeon_crtc->rmx_type) { switch (radeon_crtc->rmx_type) {
...@@ -56,7 +52,6 @@ static void atombios_overscan_setup(struct drm_crtc *crtc, ...@@ -56,7 +52,6 @@ static void atombios_overscan_setup(struct drm_crtc *crtc,
args.usOverscanBottom = (adjusted_mode->crtc_vdisplay - mode->crtc_vdisplay) / 2; args.usOverscanBottom = (adjusted_mode->crtc_vdisplay - mode->crtc_vdisplay) / 2;
args.usOverscanLeft = (adjusted_mode->crtc_hdisplay - mode->crtc_hdisplay) / 2; args.usOverscanLeft = (adjusted_mode->crtc_hdisplay - mode->crtc_hdisplay) / 2;
args.usOverscanRight = (adjusted_mode->crtc_hdisplay - mode->crtc_hdisplay) / 2; args.usOverscanRight = (adjusted_mode->crtc_hdisplay - mode->crtc_hdisplay) / 2;
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
break; break;
case RMX_ASPECT: case RMX_ASPECT:
a1 = mode->crtc_vdisplay * adjusted_mode->crtc_hdisplay; a1 = mode->crtc_vdisplay * adjusted_mode->crtc_hdisplay;
...@@ -69,17 +64,16 @@ static void atombios_overscan_setup(struct drm_crtc *crtc, ...@@ -69,17 +64,16 @@ static void atombios_overscan_setup(struct drm_crtc *crtc,
args.usOverscanLeft = (adjusted_mode->crtc_vdisplay - (a1 / mode->crtc_hdisplay)) / 2; args.usOverscanLeft = (adjusted_mode->crtc_vdisplay - (a1 / mode->crtc_hdisplay)) / 2;
args.usOverscanRight = (adjusted_mode->crtc_vdisplay - (a1 / mode->crtc_hdisplay)) / 2; args.usOverscanRight = (adjusted_mode->crtc_vdisplay - (a1 / mode->crtc_hdisplay)) / 2;
} }
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
break; break;
case RMX_FULL: case RMX_FULL:
default: default:
args.usOverscanRight = 0; args.usOverscanRight = radeon_crtc->h_border;
args.usOverscanLeft = 0; args.usOverscanLeft = radeon_crtc->h_border;
args.usOverscanBottom = 0; args.usOverscanBottom = radeon_crtc->v_border;
args.usOverscanTop = 0; args.usOverscanTop = radeon_crtc->v_border;
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
break; break;
} }
atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
} }
static void atombios_scaler_setup(struct drm_crtc *crtc) static void atombios_scaler_setup(struct drm_crtc *crtc)
...@@ -282,22 +276,22 @@ atombios_set_crtc_dtd_timing(struct drm_crtc *crtc, ...@@ -282,22 +276,22 @@ atombios_set_crtc_dtd_timing(struct drm_crtc *crtc,
u16 misc = 0; u16 misc = 0;
memset(&args, 0, sizeof(args)); memset(&args, 0, sizeof(args));
args.usH_Size = cpu_to_le16(mode->crtc_hdisplay); args.usH_Size = cpu_to_le16(mode->crtc_hdisplay - (radeon_crtc->h_border * 2));
args.usH_Blanking_Time = args.usH_Blanking_Time =
cpu_to_le16(mode->crtc_hblank_end - mode->crtc_hdisplay); cpu_to_le16(mode->crtc_hblank_end - mode->crtc_hdisplay + (radeon_crtc->h_border * 2));
args.usV_Size = cpu_to_le16(mode->crtc_vdisplay); args.usV_Size = cpu_to_le16(mode->crtc_vdisplay - (radeon_crtc->v_border * 2));
args.usV_Blanking_Time = args.usV_Blanking_Time =
cpu_to_le16(mode->crtc_vblank_end - mode->crtc_vdisplay); cpu_to_le16(mode->crtc_vblank_end - mode->crtc_vdisplay + (radeon_crtc->v_border * 2));
args.usH_SyncOffset = args.usH_SyncOffset =
cpu_to_le16(mode->crtc_hsync_start - mode->crtc_hdisplay); cpu_to_le16(mode->crtc_hsync_start - mode->crtc_hdisplay + radeon_crtc->h_border);
args.usH_SyncWidth = args.usH_SyncWidth =
cpu_to_le16(mode->crtc_hsync_end - mode->crtc_hsync_start); cpu_to_le16(mode->crtc_hsync_end - mode->crtc_hsync_start);
args.usV_SyncOffset = args.usV_SyncOffset =
cpu_to_le16(mode->crtc_vsync_start - mode->crtc_vdisplay); cpu_to_le16(mode->crtc_vsync_start - mode->crtc_vdisplay + radeon_crtc->v_border);
args.usV_SyncWidth = args.usV_SyncWidth =
cpu_to_le16(mode->crtc_vsync_end - mode->crtc_vsync_start); cpu_to_le16(mode->crtc_vsync_end - mode->crtc_vsync_start);
/*args.ucH_Border = mode->hborder;*/ args.ucH_Border = radeon_crtc->h_border;
/*args.ucV_Border = mode->vborder;*/ args.ucV_Border = radeon_crtc->v_border;
if (mode->flags & DRM_MODE_FLAG_NVSYNC) if (mode->flags & DRM_MODE_FLAG_NVSYNC)
misc |= ATOM_VSYNC_POLARITY; misc |= ATOM_VSYNC_POLARITY;
...@@ -1176,10 +1170,8 @@ int atombios_crtc_mode_set(struct drm_crtc *crtc, ...@@ -1176,10 +1170,8 @@ int atombios_crtc_mode_set(struct drm_crtc *crtc,
atombios_crtc_set_pll(crtc, adjusted_mode); atombios_crtc_set_pll(crtc, adjusted_mode);
atombios_enable_ss(crtc); atombios_enable_ss(crtc);
if (ASIC_IS_DCE4(rdev)) if (ASIC_IS_AVIVO(rdev))
atombios_set_crtc_dtd_timing(crtc, adjusted_mode); atombios_set_crtc_dtd_timing(crtc, adjusted_mode);
else if (ASIC_IS_AVIVO(rdev))
atombios_crtc_set_timing(crtc, adjusted_mode);
else { else {
atombios_crtc_set_timing(crtc, adjusted_mode); atombios_crtc_set_timing(crtc, adjusted_mode);
if (radeon_crtc->crtc_id == 0) if (radeon_crtc->crtc_id == 0)
......
...@@ -312,6 +312,20 @@ int radeon_connector_set_property(struct drm_connector *connector, struct drm_pr ...@@ -312,6 +312,20 @@ int radeon_connector_set_property(struct drm_connector *connector, struct drm_pr
} }
} }
if (property == rdev->mode_info.underscan_property) {
/* need to find digital encoder on connector */
encoder = radeon_find_encoder(connector, DRM_MODE_ENCODER_TMDS);
if (!encoder)
return 0;
radeon_encoder = to_radeon_encoder(encoder);
if (radeon_encoder->underscan_type != val) {
radeon_encoder->underscan_type = val;
radeon_property_change_mode(&radeon_encoder->base);
}
}
if (property == rdev->mode_info.tv_std_property) { if (property == rdev->mode_info.tv_std_property) {
encoder = radeon_find_encoder(connector, DRM_MODE_ENCODER_TVDAC); encoder = radeon_find_encoder(connector, DRM_MODE_ENCODER_TVDAC);
if (!encoder) { if (!encoder) {
...@@ -1120,6 +1134,9 @@ radeon_add_atom_connector(struct drm_device *dev, ...@@ -1120,6 +1134,9 @@ radeon_add_atom_connector(struct drm_device *dev,
drm_connector_attach_property(&radeon_connector->base, drm_connector_attach_property(&radeon_connector->base,
rdev->mode_info.coherent_mode_property, rdev->mode_info.coherent_mode_property,
1); 1);
drm_connector_attach_property(&radeon_connector->base,
rdev->mode_info.underscan_property,
UNDERSCAN_AUTO);
if (connector_type == DRM_MODE_CONNECTOR_DVII) { if (connector_type == DRM_MODE_CONNECTOR_DVII) {
radeon_connector->dac_load_detect = true; radeon_connector->dac_load_detect = true;
drm_connector_attach_property(&radeon_connector->base, drm_connector_attach_property(&radeon_connector->base,
...@@ -1145,6 +1162,9 @@ radeon_add_atom_connector(struct drm_device *dev, ...@@ -1145,6 +1162,9 @@ radeon_add_atom_connector(struct drm_device *dev,
drm_connector_attach_property(&radeon_connector->base, drm_connector_attach_property(&radeon_connector->base,
rdev->mode_info.coherent_mode_property, rdev->mode_info.coherent_mode_property,
1); 1);
drm_connector_attach_property(&radeon_connector->base,
rdev->mode_info.underscan_property,
UNDERSCAN_AUTO);
subpixel_order = SubPixelHorizontalRGB; subpixel_order = SubPixelHorizontalRGB;
break; break;
case DRM_MODE_CONNECTOR_DisplayPort: case DRM_MODE_CONNECTOR_DisplayPort:
...@@ -1176,6 +1196,9 @@ radeon_add_atom_connector(struct drm_device *dev, ...@@ -1176,6 +1196,9 @@ radeon_add_atom_connector(struct drm_device *dev,
drm_connector_attach_property(&radeon_connector->base, drm_connector_attach_property(&radeon_connector->base,
rdev->mode_info.coherent_mode_property, rdev->mode_info.coherent_mode_property,
1); 1);
drm_connector_attach_property(&radeon_connector->base,
rdev->mode_info.underscan_property,
UNDERSCAN_AUTO);
break; break;
case DRM_MODE_CONNECTOR_SVIDEO: case DRM_MODE_CONNECTOR_SVIDEO:
case DRM_MODE_CONNECTOR_Composite: case DRM_MODE_CONNECTOR_Composite:
......
...@@ -921,6 +921,12 @@ static struct drm_prop_enum_list radeon_tv_std_enum_list[] = ...@@ -921,6 +921,12 @@ static struct drm_prop_enum_list radeon_tv_std_enum_list[] =
{ TV_STD_SECAM, "secam" }, { TV_STD_SECAM, "secam" },
}; };
static struct drm_prop_enum_list radeon_underscan_enum_list[] =
{ { UNDERSCAN_OFF, "off" },
{ UNDERSCAN_ON, "on" },
{ UNDERSCAN_AUTO, "auto" },
};
static int radeon_modeset_create_props(struct radeon_device *rdev) static int radeon_modeset_create_props(struct radeon_device *rdev)
{ {
int i, sz; int i, sz;
...@@ -974,6 +980,18 @@ static int radeon_modeset_create_props(struct radeon_device *rdev) ...@@ -974,6 +980,18 @@ static int radeon_modeset_create_props(struct radeon_device *rdev)
radeon_tv_std_enum_list[i].name); radeon_tv_std_enum_list[i].name);
} }
sz = ARRAY_SIZE(radeon_underscan_enum_list);
rdev->mode_info.underscan_property =
drm_property_create(rdev->ddev,
DRM_MODE_PROP_ENUM,
"underscan", sz);
for (i = 0; i < sz; i++) {
drm_property_add_enum(rdev->mode_info.underscan_property,
i,
radeon_underscan_enum_list[i].type,
radeon_underscan_enum_list[i].name);
}
return 0; return 0;
} }
...@@ -1069,17 +1087,26 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc, ...@@ -1069,17 +1087,26 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
struct drm_display_mode *adjusted_mode) struct drm_display_mode *adjusted_mode)
{ {
struct drm_device *dev = crtc->dev; struct drm_device *dev = crtc->dev;
struct radeon_device *rdev = dev->dev_private;
struct drm_encoder *encoder; struct drm_encoder *encoder;
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
struct radeon_encoder *radeon_encoder; struct radeon_encoder *radeon_encoder;
struct drm_connector *connector;
struct radeon_connector *radeon_connector;
bool first = true; bool first = true;
u32 src_v = 1, dst_v = 1; u32 src_v = 1, dst_v = 1;
u32 src_h = 1, dst_h = 1; u32 src_h = 1, dst_h = 1;
radeon_crtc->h_border = 0;
radeon_crtc->v_border = 0;
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
if (encoder->crtc != crtc) if (encoder->crtc != crtc)
continue; continue;
radeon_encoder = to_radeon_encoder(encoder); radeon_encoder = to_radeon_encoder(encoder);
connector = radeon_get_connector_for_encoder(encoder);
radeon_connector = to_radeon_connector(connector);
if (first) { if (first) {
/* set scaling */ /* set scaling */
if (radeon_encoder->rmx_type == RMX_OFF) if (radeon_encoder->rmx_type == RMX_OFF)
...@@ -1097,6 +1124,20 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc, ...@@ -1097,6 +1124,20 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
memcpy(&radeon_crtc->native_mode, memcpy(&radeon_crtc->native_mode,
&radeon_encoder->native_mode, &radeon_encoder->native_mode,
sizeof(struct drm_display_mode)); sizeof(struct drm_display_mode));
/* fix up for overscan on hdmi */
if (ASIC_IS_AVIVO(rdev) &&
((radeon_encoder->underscan_type == UNDERSCAN_ON) ||
((radeon_encoder->underscan_type == UNDERSCAN_AUTO) &&
drm_detect_hdmi_monitor(radeon_connector->edid)))) {
radeon_crtc->h_border = (mode->hdisplay >> 5) + 16;
radeon_crtc->v_border = (mode->vdisplay >> 5) + 16;
radeon_crtc->rmx_type = RMX_FULL;
src_v = crtc->mode.vdisplay;
dst_v = crtc->mode.vdisplay - (radeon_crtc->v_border * 2);
src_h = crtc->mode.hdisplay;
dst_h = crtc->mode.hdisplay - (radeon_crtc->h_border * 2);
}
first = false; first = false;
} else { } else {
if (radeon_crtc->rmx_type != radeon_encoder->rmx_type) { if (radeon_crtc->rmx_type != radeon_encoder->rmx_type) {
......
...@@ -212,7 +212,7 @@ void radeon_encoder_set_active_device(struct drm_encoder *encoder) ...@@ -212,7 +212,7 @@ void radeon_encoder_set_active_device(struct drm_encoder *encoder)
} }
} }
static struct drm_connector * struct drm_connector *
radeon_get_connector_for_encoder(struct drm_encoder *encoder) radeon_get_connector_for_encoder(struct drm_encoder *encoder)
{ {
struct drm_device *dev = encoder->dev; struct drm_device *dev = encoder->dev;
...@@ -1694,6 +1694,7 @@ radeon_add_atom_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t su ...@@ -1694,6 +1694,7 @@ radeon_add_atom_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t su
radeon_encoder->encoder_id = encoder_id; radeon_encoder->encoder_id = encoder_id;
radeon_encoder->devices = supported_device; radeon_encoder->devices = supported_device;
radeon_encoder->rmx_type = RMX_OFF; radeon_encoder->rmx_type = RMX_OFF;
radeon_encoder->underscan_type = UNDERSCAN_OFF;
switch (radeon_encoder->encoder_id) { switch (radeon_encoder->encoder_id) {
case ENCODER_OBJECT_ID_INTERNAL_LVDS: case ENCODER_OBJECT_ID_INTERNAL_LVDS:
...@@ -1707,6 +1708,7 @@ radeon_add_atom_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t su ...@@ -1707,6 +1708,7 @@ radeon_add_atom_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t su
} else { } else {
drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_TMDS); drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_TMDS);
radeon_encoder->enc_priv = radeon_atombios_set_dig_info(radeon_encoder); radeon_encoder->enc_priv = radeon_atombios_set_dig_info(radeon_encoder);
radeon_encoder->underscan_type = UNDERSCAN_AUTO;
} }
drm_encoder_helper_add(encoder, &radeon_atom_dig_helper_funcs); drm_encoder_helper_add(encoder, &radeon_atom_dig_helper_funcs);
break; break;
...@@ -1736,6 +1738,7 @@ radeon_add_atom_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t su ...@@ -1736,6 +1738,7 @@ radeon_add_atom_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t su
} else { } else {
drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_TMDS); drm_encoder_init(dev, encoder, &radeon_atom_enc_funcs, DRM_MODE_ENCODER_TMDS);
radeon_encoder->enc_priv = radeon_atombios_set_dig_info(radeon_encoder); radeon_encoder->enc_priv = radeon_atombios_set_dig_info(radeon_encoder);
radeon_encoder->underscan_type = UNDERSCAN_AUTO;
} }
drm_encoder_helper_add(encoder, &radeon_atom_dig_helper_funcs); drm_encoder_helper_add(encoder, &radeon_atom_dig_helper_funcs);
break; break;
......
...@@ -66,6 +66,12 @@ enum radeon_tv_std { ...@@ -66,6 +66,12 @@ enum radeon_tv_std {
TV_STD_PAL_N, TV_STD_PAL_N,
}; };
enum radeon_underscan_type {
UNDERSCAN_OFF,
UNDERSCAN_ON,
UNDERSCAN_AUTO,
};
enum radeon_hpd_id { enum radeon_hpd_id {
RADEON_HPD_1 = 0, RADEON_HPD_1 = 0,
RADEON_HPD_2, RADEON_HPD_2,
...@@ -226,10 +232,12 @@ struct radeon_mode_info { ...@@ -226,10 +232,12 @@ struct radeon_mode_info {
struct drm_property *coherent_mode_property; struct drm_property *coherent_mode_property;
/* DAC enable load detect */ /* DAC enable load detect */
struct drm_property *load_detect_property; struct drm_property *load_detect_property;
/* TV standard load detect */ /* TV standard */
struct drm_property *tv_std_property; struct drm_property *tv_std_property;
/* legacy TMDS PLL detect */ /* legacy TMDS PLL detect */
struct drm_property *tmds_pll_property; struct drm_property *tmds_pll_property;
/* underscan */
struct drm_property *underscan_property;
/* hardcoded DFP edid from BIOS */ /* hardcoded DFP edid from BIOS */
struct edid *bios_hardcoded_edid; struct edid *bios_hardcoded_edid;
...@@ -266,6 +274,8 @@ struct radeon_crtc { ...@@ -266,6 +274,8 @@ struct radeon_crtc {
uint32_t legacy_display_base_addr; uint32_t legacy_display_base_addr;
uint32_t legacy_cursor_offset; uint32_t legacy_cursor_offset;
enum radeon_rmx_type rmx_type; enum radeon_rmx_type rmx_type;
u8 h_border;
u8 v_border;
fixed20_12 vsc; fixed20_12 vsc;
fixed20_12 hsc; fixed20_12 hsc;
struct drm_display_mode native_mode; struct drm_display_mode native_mode;
...@@ -354,6 +364,7 @@ struct radeon_encoder { ...@@ -354,6 +364,7 @@ struct radeon_encoder {
uint32_t flags; uint32_t flags;
uint32_t pixel_clock; uint32_t pixel_clock;
enum radeon_rmx_type rmx_type; enum radeon_rmx_type rmx_type;
enum radeon_underscan_type underscan_type;
struct drm_display_mode native_mode; struct drm_display_mode native_mode;
void *enc_priv; void *enc_priv;
int audio_polling_active; int audio_polling_active;
...@@ -392,7 +403,7 @@ struct radeon_connector { ...@@ -392,7 +403,7 @@ struct radeon_connector {
uint32_t connector_id; uint32_t connector_id;
uint32_t devices; uint32_t devices;
struct radeon_i2c_chan *ddc_bus; struct radeon_i2c_chan *ddc_bus;
/* some systems have a an hdmi and vga port with a shared ddc line */ /* some systems have an hdmi and vga port with a shared ddc line */
bool shared_ddc; bool shared_ddc;
bool use_digital; bool use_digital;
/* we need to mind the EDID between detect /* we need to mind the EDID between detect
...@@ -414,6 +425,9 @@ radeon_combios_get_tv_info(struct radeon_device *rdev); ...@@ -414,6 +425,9 @@ radeon_combios_get_tv_info(struct radeon_device *rdev);
extern enum radeon_tv_std extern enum radeon_tv_std
radeon_atombios_get_tv_info(struct radeon_device *rdev); radeon_atombios_get_tv_info(struct radeon_device *rdev);
extern struct drm_connector *
radeon_get_connector_for_encoder(struct drm_encoder *encoder);
extern void radeon_connector_hotplug(struct drm_connector *connector); extern void radeon_connector_hotplug(struct drm_connector *connector);
extern bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector); extern bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector);
extern int radeon_dp_mode_valid_helper(struct radeon_connector *radeon_connector, extern int radeon_dp_mode_valid_helper(struct radeon_connector *radeon_connector,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment