From e0adf2c9bf553538521a84ba01d33a8177802866 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Fri, 12 Aug 2011 12:52:26 +0100 Subject: [PATCH] drm/kms: add support for memory bandwidth considerations (v2) Some GPUs (mostly in the lower server end of the market) have memory bandwidth limitations that hit in the real world, however memory bw calcs require bit depth to be worked out and at the moment, the KMS interface doesn't know about bit depth until we set the mode. This "overloads" the connector_type_id in the get connector ioctl to pass a depth value to the kernel code. A new libdrm API is added to utilise this. libdrm has always set this to 0 up until now. This depth value is what the kms user proposed to use, the actual API in libdrm is drmModeGetConnectorWithDepth and will follow along. This patch also adds RN50 support to the radeon kms to check the memory bandwidth calcs and refuse the higher modes on these chips. v2: rebased onto latest kernel - fixup radeon usage. Signed-off-by: Dave Airlie --- drivers/gpu/drm/drm_crtc.c | 3 +++ drivers/gpu/drm/drm_modes.c | 16 ++++++++++++++++ drivers/gpu/drm/radeon/radeon_connectors.c | 10 ++++++++-- include/drm/drm_crtc.h | 4 ++++ include/drm/drm_mode.h | 2 ++ 5 files changed, 33 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 82db185..6f5a3a2 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -1345,6 +1345,9 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, } if (out_resp->count_modes == 0) { + /* set the proposed depth up */ + if (out_resp->connector_type_id) + connector->proposed_depth = out_resp->connector_type_id; connector->funcs->fill_modes(connector, dev->mode_config.max_width, dev->mode_config.max_height); diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index ad74fb4..a0565f2 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -544,6 +544,22 @@ void drm_mode_list_concat(struct list_head *head, struct list_head *new) } EXPORT_SYMBOL(drm_mode_list_concat); +u32 drm_mode_bandwidth(struct drm_display_mode *mode, int depth) +{ + u32 a_active, a_total, active_percent, pixels_per_second; + int bytes_per_pixel = depth / 8; + + if (!mode->htotal || !mode->vtotal || !mode->clock) + return 0; + + a_active = mode->hdisplay * mode->vdisplay; + a_total = mode->htotal * mode->vtotal; + active_percent = (a_active * 1000) / a_total; + pixels_per_second = active_percent * mode->clock; + return (u32)(pixels_per_second * bytes_per_pixel / (1024 * 1024)); +} +EXPORT_SYMBOL(drm_mode_bandwidth); + /** * drm_mode_width - get the width of a mode * @mode: mode diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index 441e070..94bf73b 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -689,7 +689,10 @@ static int radeon_vga_mode_valid(struct drm_connector *connector, struct drm_device *dev = connector->dev; struct radeon_device *rdev = dev->dev_private; - /* XXX check mode bandwidth */ + if (ASIC_IS_RN50(rdev)) { + if (drm_mode_bandwidth(mode, connector->proposed_depth) > 300) + return MODE_BANDWIDTH; + } if ((mode->clock / 10) > rdev->clock.max_pixel_clock) return MODE_CLOCK_HIGH; @@ -1065,7 +1068,10 @@ static int radeon_dvi_mode_valid(struct drm_connector *connector, struct radeon_device *rdev = dev->dev_private; struct radeon_connector *radeon_connector = to_radeon_connector(connector); - /* XXX check mode bandwidth */ + if (ASIC_IS_RN50(rdev)) { + if (drm_mode_bandwidth(mode, connector->proposed_depth) > 300) + return MODE_BANDWIDTH; + } /* clocks over 135 MHz have heat issues with DVI on RV100 */ if (radeon_connector->use_digital && diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 44335e5..a8320b3 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -94,6 +94,7 @@ enum drm_mode_status { MODE_ONE_HEIGHT, /* only one height is supported */ MODE_ONE_SIZE, /* only one resolution is supported */ MODE_NO_REDUCED, /* monitor doesn't accept reduced blanking */ + MODE_BANDWIDTH, /* mode requires too much memory bandwidth */ MODE_UNVERIFIED = -3, /* mode needs to reverified */ MODE_BAD = -2, /* unspecified reason */ MODE_ERROR = -1 /* error condition */ @@ -524,6 +525,8 @@ struct drm_connector { struct drm_encoder *encoder; /* currently active encoder */ int null_edid_counter; /* needed to workaround some HW bugs where we get all 0s */ + + uint32_t proposed_depth; /* depth to be used in mode bw calcs */ }; /** @@ -676,6 +679,7 @@ extern void drm_mode_set_name(struct drm_display_mode *mode); extern bool drm_mode_equal(struct drm_display_mode *mode1, struct drm_display_mode *mode2); extern int drm_mode_width(struct drm_display_mode *mode); extern int drm_mode_height(struct drm_display_mode *mode); +extern u32 drm_mode_bandwidth(struct drm_display_mode *mode, int depth); /* for us by fb module */ extern int drm_mode_attachmode_crtc(struct drm_device *dev, diff --git a/include/drm/drm_mode.h b/include/drm/drm_mode.h index c4961ea..f7ef233 100644 --- a/include/drm/drm_mode.h +++ b/include/drm/drm_mode.h @@ -177,6 +177,8 @@ struct drm_mode_get_connector { __u32 encoder_id; /**< Current Encoder */ __u32 connector_id; /**< Id */ __u32 connector_type; + /* connector type id is also used an input for the proposed + depth - all libdrm pass in 0 for this up until this change */ __u32 connector_type_id; __u32 connection; -- 1.7.6