From 65803e53a696347e38d7f6c2c8dc186c6764ff03 Mon Sep 17 00:00:00 2001 From: Maarten Maathuis Date: Sun, 20 Jul 2008 13:49:18 +0200 Subject: modesetting-101: implement optional scaling and dithering properties --- linux-core/nv50_kms_wrapper.c | 128 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 123 insertions(+), 5 deletions(-) (limited to 'linux-core/nv50_kms_wrapper.c') diff --git a/linux-core/nv50_kms_wrapper.c b/linux-core/nv50_kms_wrapper.c index a7966e9a..b0d64340 100644 --- a/linux-core/nv50_kms_wrapper.c +++ b/linux-core/nv50_kms_wrapper.c @@ -636,10 +636,11 @@ int nv50_kms_crtc_set_config(struct drm_mode_set *set) continue; crtc->scaling_mode = connector->scaling_mode; + crtc->use_dithering = connector->use_dithering; break; } - if (crtc->scaling_mode == SCALE_PANEL) + if (crtc->scaling_mode == SCALE_NON_GPU) crtc->use_native_mode = false; else crtc->use_native_mode = true; @@ -1078,14 +1079,16 @@ static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, u } } -static bool nv50_kms_connector_set_property(struct drm_connector *connector, +static bool nv50_kms_connector_set_property(struct drm_connector *drm_connector, struct drm_property *property, uint64_t value) { - struct drm_device *dev = connector->dev; + struct drm_device *dev = drm_connector->dev; + struct nv50_connector *connector = to_nv50_connector(drm_connector); - if (property == dev->mode_config.dpms_property && connector->encoder) { - struct nv50_output *output = to_nv50_output(connector->encoder); + /* DPMS */ + if (property == dev->mode_config.dpms_property && drm_connector->encoder) { + struct nv50_output *output = to_nv50_output(drm_connector->encoder); if (!output->set_power_mode(output, (int) value)) return true; @@ -1093,6 +1096,78 @@ static bool nv50_kms_connector_set_property(struct drm_connector *connector, return false; } + /* Scaling mode */ + if (property == dev->mode_config.scaling_mode_property) { + struct nv50_crtc *crtc = NULL; + struct nv50_display *display = nv50_get_display(dev); + int internal_value = 0; + int rval = 0; + + switch (value) { + case DRM_MODE_SCALE_NON_GPU: + internal_value = SCALE_NON_GPU; + break; + case DRM_MODE_SCALE_FULLSCREEN: + internal_value = SCALE_FULLSCREEN; + break; + case DRM_MODE_SCALE_NO_SCALE: + internal_value = SCALE_NOSCALE; + break; + case DRM_MODE_SCALE_ASPECT: + internal_value = SCALE_ASPECT; + break; + default: + break; + } + + connector->scaling_mode = internal_value; + + if (drm_connector->encoder && drm_connector->encoder->crtc) + crtc = to_nv50_crtc(drm_connector->encoder->crtc); + + if (!crtc) + return true; + + crtc->scaling_mode = connector->scaling_mode; + rval = crtc->set_scale(crtc); + if (rval) + return false; + + /* process command buffer */ + display->update(display); + + return true; + } + + /* Dithering */ + if (property == dev->mode_config.dithering_mode_property) { + struct nv50_crtc *crtc = NULL; + struct nv50_display *display = nv50_get_display(dev); + int rval = 0; + + if (value == DRM_MODE_DITHERING_ON) + connector->use_dithering = true; + else + connector->use_dithering = false; + + if (drm_connector->encoder && drm_connector->encoder->crtc) + crtc = to_nv50_crtc(drm_connector->encoder->crtc); + + if (!crtc) + return true; + + /* update hw state */ + crtc->use_dithering = connector->use_dithering; + rval = crtc->set_dither(crtc); + if (rval) + return false; + + /* process command buffer */ + display->update(display); + + return true; + } + return false; } @@ -1105,12 +1180,48 @@ static const struct drm_connector_funcs nv50_kms_connector_funcs = { .set_property = nv50_kms_connector_set_property }; +static int nv50_kms_get_scaling_mode(struct drm_connector *drm_connector) +{ + struct nv50_connector *connector = NULL; + int drm_mode = 0; + + if (!drm_connector) { + DRM_ERROR("drm_connector is NULL\n"); + return 0; + } + + connector = to_nv50_connector(drm_connector); + + switch (connector->scaling_mode) { + case SCALE_NON_GPU: + drm_mode = DRM_MODE_SCALE_NON_GPU; + break; + case SCALE_FULLSCREEN: + drm_mode = DRM_MODE_SCALE_FULLSCREEN; + break; + case SCALE_NOSCALE: + drm_mode = DRM_MODE_SCALE_NO_SCALE; + break; + case SCALE_ASPECT: + drm_mode = DRM_MODE_SCALE_ASPECT; + break; + default: + break; + } + + return drm_mode; +} + static int nv50_kms_connectors_init(struct drm_device *dev) { struct nv50_display *display = nv50_get_display(dev); struct nv50_connector *connector = NULL; int i; + /* Initialise some optional connector properties. */ + drm_mode_create_scaling_mode_property(dev); + drm_mode_create_dithering_property(dev); + list_for_each_entry(connector, &display->connectors, item) { struct drm_connector *drm_connector = to_nv50_kms_connector(connector); uint32_t type = DRM_MODE_CONNECTOR_Unknown; @@ -1154,6 +1265,13 @@ static int nv50_kms_connectors_init(struct drm_device *dev) drm_connector_attach_property(drm_connector, dev->mode_config.dvi_i_select_subconnector_property, 0); } + /* If supported in the future, it will have to use the scalers internally and not expose them. */ + if (type != DRM_MODE_CONNECTOR_SVIDEO) { + drm_connector_attach_property(drm_connector, dev->mode_config.scaling_mode_property, nv50_kms_get_scaling_mode(drm_connector)); + } + + drm_connector_attach_property(drm_connector, dev->mode_config.dithering_mode_property, connector->use_dithering ? DRM_MODE_DITHERING_ON : DRM_MODE_DITHERING_OFF); + /* attach encoders, possibilities are analog + digital */ for (i = 0; i < 2; i++) { struct drm_encoder *drm_encoder = NULL; -- cgit v1.2.3 From 3ef1d05001a9e28ed52536de7e020323d8d34d83 Mon Sep 17 00:00:00 2001 From: Maarten Maathuis Date: Sun, 20 Jul 2008 14:51:22 +0200 Subject: modesetting-101: set_property should return an int, not a bool --- linux-core/nv50_kms_wrapper.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) (limited to 'linux-core/nv50_kms_wrapper.c') diff --git a/linux-core/nv50_kms_wrapper.c b/linux-core/nv50_kms_wrapper.c index b0d64340..009972c8 100644 --- a/linux-core/nv50_kms_wrapper.c +++ b/linux-core/nv50_kms_wrapper.c @@ -1079,21 +1079,21 @@ static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, u } } -static bool nv50_kms_connector_set_property(struct drm_connector *drm_connector, +static int nv50_kms_connector_set_property(struct drm_connector *drm_connector, struct drm_property *property, uint64_t value) { struct drm_device *dev = drm_connector->dev; struct nv50_connector *connector = to_nv50_connector(drm_connector); + int rval = 0; /* DPMS */ if (property == dev->mode_config.dpms_property && drm_connector->encoder) { struct nv50_output *output = to_nv50_output(drm_connector->encoder); - if (!output->set_power_mode(output, (int) value)) - return true; - else - return false; + rval = output->set_power_mode(output, (int) value); + + return rval; } /* Scaling mode */ @@ -1101,7 +1101,6 @@ static bool nv50_kms_connector_set_property(struct drm_connector *drm_connector, struct nv50_crtc *crtc = NULL; struct nv50_display *display = nv50_get_display(dev); int internal_value = 0; - int rval = 0; switch (value) { case DRM_MODE_SCALE_NON_GPU: @@ -1126,24 +1125,23 @@ static bool nv50_kms_connector_set_property(struct drm_connector *drm_connector, crtc = to_nv50_crtc(drm_connector->encoder->crtc); if (!crtc) - return true; + return 0; crtc->scaling_mode = connector->scaling_mode; rval = crtc->set_scale(crtc); if (rval) - return false; + return rval; /* process command buffer */ display->update(display); - return true; + return 0; } /* Dithering */ if (property == dev->mode_config.dithering_mode_property) { struct nv50_crtc *crtc = NULL; struct nv50_display *display = nv50_get_display(dev); - int rval = 0; if (value == DRM_MODE_DITHERING_ON) connector->use_dithering = true; @@ -1154,21 +1152,21 @@ static bool nv50_kms_connector_set_property(struct drm_connector *drm_connector, crtc = to_nv50_crtc(drm_connector->encoder->crtc); if (!crtc) - return true; + return 0; /* update hw state */ crtc->use_dithering = connector->use_dithering; rval = crtc->set_dither(crtc); if (rval) - return false; + return rval; /* process command buffer */ display->update(display); - return true; + return 0; } - return false; + return -EINVAL; } static const struct drm_connector_funcs nv50_kms_connector_funcs = { -- cgit v1.2.3 From f1e4785d4cf04b679948602ffbbef2043ce81ec0 Mon Sep 17 00:00:00 2001 From: Maarten Maathuis Date: Sun, 20 Jul 2008 14:55:59 +0200 Subject: NV50: LVDS always needs some kind of gpu scaling --- linux-core/nv50_kms_wrapper.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'linux-core/nv50_kms_wrapper.c') diff --git a/linux-core/nv50_kms_wrapper.c b/linux-core/nv50_kms_wrapper.c index 009972c8..6e0805fc 100644 --- a/linux-core/nv50_kms_wrapper.c +++ b/linux-core/nv50_kms_wrapper.c @@ -1119,6 +1119,10 @@ static int nv50_kms_connector_set_property(struct drm_connector *drm_connector, break; } + /* LVDS always needs gpu scaling */ + if (connector->type == CONNECTOR_LVDS && internal_value == SCALE_NON_GPU) + return -EINVAL; + connector->scaling_mode = internal_value; if (drm_connector->encoder && drm_connector->encoder->crtc) -- cgit v1.2.3 From 685bca02fe6b7406bb157a1a4e0f147b47ba28f8 Mon Sep 17 00:00:00 2001 From: Maarten Maathuis Date: Sun, 20 Jul 2008 15:40:40 +0200 Subject: NV50: delay changing gpu<->non-gpu scaling modes until next modeset --- linux-core/nv50_kms_wrapper.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'linux-core/nv50_kms_wrapper.c') diff --git a/linux-core/nv50_kms_wrapper.c b/linux-core/nv50_kms_wrapper.c index 6e0805fc..67836f3f 100644 --- a/linux-core/nv50_kms_wrapper.c +++ b/linux-core/nv50_kms_wrapper.c @@ -635,12 +635,12 @@ int nv50_kms_crtc_set_config(struct drm_mode_set *set) if (connector->output != output) continue; - crtc->scaling_mode = connector->scaling_mode; + crtc->requested_scaling_mode = connector->requested_scaling_mode; crtc->use_dithering = connector->use_dithering; break; } - if (crtc->scaling_mode == SCALE_NON_GPU) + if (crtc->requested_scaling_mode == SCALE_NON_GPU) crtc->use_native_mode = false; else crtc->use_native_mode = true; @@ -1086,6 +1086,7 @@ static int nv50_kms_connector_set_property(struct drm_connector *drm_connector, struct drm_device *dev = drm_connector->dev; struct nv50_connector *connector = to_nv50_connector(drm_connector); int rval = 0; + bool delay_change = false; /* DPMS */ if (property == dev->mode_config.dpms_property && drm_connector->encoder) { @@ -1123,7 +1124,7 @@ static int nv50_kms_connector_set_property(struct drm_connector *drm_connector, if (connector->type == CONNECTOR_LVDS && internal_value == SCALE_NON_GPU) return -EINVAL; - connector->scaling_mode = internal_value; + connector->requested_scaling_mode = internal_value; if (drm_connector->encoder && drm_connector->encoder->crtc) crtc = to_nv50_crtc(drm_connector->encoder->crtc); @@ -1131,7 +1132,17 @@ static int nv50_kms_connector_set_property(struct drm_connector *drm_connector, if (!crtc) return 0; - crtc->scaling_mode = connector->scaling_mode; + crtc->requested_scaling_mode = connector->requested_scaling_mode; + + /* going from and to a gpu scaled regime requires a modesetting, so wait until next modeset */ + if (crtc->scaling_mode == SCALE_NON_GPU || internal_value == SCALE_NON_GPU) { + DRM_INFO("Moving from or to a non-gpu scaled mode, this will be processed upon next modeset."); + delay_change = true; + } + + if (delay_change) + return 0; + rval = crtc->set_scale(crtc); if (rval) return rval; @@ -1194,7 +1205,7 @@ static int nv50_kms_get_scaling_mode(struct drm_connector *drm_connector) connector = to_nv50_connector(drm_connector); - switch (connector->scaling_mode) { + switch (connector->requested_scaling_mode) { case SCALE_NON_GPU: drm_mode = DRM_MODE_SCALE_NON_GPU; break; -- cgit v1.2.3 From d00644c27ddc7023ea0e442c7be6b67d9d0da047 Mon Sep 17 00:00:00 2001 From: Maarten Maathuis Date: Mon, 21 Jul 2008 14:29:13 +0200 Subject: NV50: Do detect with hpd and load detect if possible. - Appropriate error messages when an unknown situation is encountered are included. - Fallback to i2c will occur when needed. --- linux-core/nv50_kms_wrapper.c | 92 +++++++++++++++++++++++++++++-------------- 1 file changed, 63 insertions(+), 29 deletions(-) (limited to 'linux-core/nv50_kms_wrapper.c') diff --git a/linux-core/nv50_kms_wrapper.c b/linux-core/nv50_kms_wrapper.c index 67836f3f..8b27f80b 100644 --- a/linux-core/nv50_kms_wrapper.c +++ b/linux-core/nv50_kms_wrapper.c @@ -386,7 +386,7 @@ int nv50_kms_crtc_set_config(struct drm_mode_set *set) /* This is to ensure it knows the connector subtype. */ drm_connector->funcs->fill_modes(drm_connector, 0, 0); - output = connector->to_output(connector, nv50_kms_connector_is_digital(drm_connector)); + output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector)); if (!output) { DRM_ERROR("No output\n"); goto out; @@ -458,7 +458,7 @@ int nv50_kms_crtc_set_config(struct drm_mode_set *set) goto out; } - output = connector->to_output(connector, nv50_kms_connector_is_digital(drm_connector)); + output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector)); if (!output) { DRM_ERROR("No output\n"); goto out; @@ -835,7 +835,9 @@ static int nv50_kms_encoders_init(struct drm_device *dev) * Connector functions */ -bool nv50_kms_connector_is_digital(struct drm_connector *drm_connector) + +/* These 2 functions wrap the connector properties that deal with multiple encoders per connector. */ +bool nv50_kms_connector_get_digital(struct drm_connector *drm_connector) { struct drm_device *dev = drm_connector->dev; @@ -892,6 +894,33 @@ bool nv50_kms_connector_is_digital(struct drm_connector *drm_connector) return false; } +static void nv50_kms_connector_set_digital(struct drm_connector *drm_connector, int digital, bool force) +{ + struct drm_device *dev = drm_connector->dev; + + if (drm_connector->connector_type == DRM_MODE_CONNECTOR_DVII) { + uint64_t cur_value, new_value; + + int rval = drm_connector_property_get_value(drm_connector, dev->mode_config.dvi_i_subconnector_property, &cur_value); + if (rval) { + DRM_ERROR("Unable to find subconnector property\n"); + return; + } + + /* Only set when unknown or when forced to do so. */ + if (cur_value != DRM_MODE_SUBCONNECTOR_Unknown && !force) + return; + + if (digital == 1) + new_value = DRM_MODE_SUBCONNECTOR_DVID; + else if (digital == 0) + new_value = DRM_MODE_SUBCONNECTOR_DVIA; + else + new_value = DRM_MODE_SUBCONNECTOR_Unknown; + drm_connector_property_set_value(drm_connector, dev->mode_config.dvi_i_subconnector_property, new_value); + } +} + void nv50_kms_connector_detect_all(struct drm_device *dev) { struct drm_connector *drm_connector = NULL; @@ -903,16 +932,32 @@ void nv50_kms_connector_detect_all(struct drm_device *dev) static enum drm_connector_status nv50_kms_connector_detect(struct drm_connector *drm_connector) { - struct nv50_connector *connector = to_nv50_connector(drm_connector); struct drm_device *dev = drm_connector->dev; - bool connected; - int old_status; + struct nv50_connector *connector = to_nv50_connector(drm_connector); + struct nv50_output *output = NULL; + int hpd_detect = 0, load_detect = 0, i2c_detect = 0; + int old_status = drm_connector->status; - connected = connector->detect(connector); + /* hotplug detect */ + hpd_detect = connector->hpd_detect(connector); - old_status = drm_connector->status; + /* load detect */ + output = connector->to_output(connector, FALSE); /* analog */ + if (output && output->detect) + load_detect = output->detect(output); - if (connected) + if (hpd_detect < 0 || load_detect < 0) /* did an error occur? */ + i2c_detect = connector->i2c_detect(connector); + + if (load_detect == 1) { + nv50_kms_connector_set_digital(drm_connector, 0, TRUE); /* analog, forced */ + } else if (hpd_detect == 1 && load_detect == 0) { + nv50_kms_connector_set_digital(drm_connector, 1, TRUE); /* digital, forced */ + } else { + nv50_kms_connector_set_digital(drm_connector, -1, TRUE); /* unknown, forced */ + } + + if (hpd_detect == 1 || load_detect == 1 || i2c_detect == 1) drm_connector->status = connector_status_connected; else drm_connector->status = connector_status_disconnected; @@ -956,7 +1001,7 @@ static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, u struct nv50_connector *connector = to_nv50_connector(drm_connector); struct drm_device *dev = drm_connector->dev; int rval = 0; - bool connected; + bool connected = false; struct drm_display_mode *mode, *t; struct edid *edid = NULL; @@ -965,16 +1010,13 @@ static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, u list_for_each_entry_safe(mode, t, &drm_connector->modes, head) mode->status = MODE_UNVERIFIED; - connected = connector->detect(connector); + if (nv50_kms_connector_detect(drm_connector) == connector_status_connected) + connected = true; if (connected) - drm_connector->status = connector_status_connected; + NV50_DEBUG("%s is connected\n", drm_get_connector_name(drm_connector)); else - drm_connector->status = connector_status_disconnected; - - if (!connected) { NV50_DEBUG("%s is disconnected\n", drm_get_connector_name(drm_connector)); - } /* Not all connnectors have an i2c channel. */ if (connected && connector->i2c_chan) @@ -986,16 +1028,8 @@ static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, u if (edid) { rval = drm_add_edid_modes(drm_connector, edid); - /* 2 encoders per connector */ - /* eventually do this based on load detect and hot plug detect */ - if (drm_connector->connector_type == DRM_MODE_CONNECTOR_DVII) { - uint64_t subtype = 0; - if (edid->digital) - subtype = DRM_MODE_SUBCONNECTOR_DVID; - else - subtype = DRM_MODE_SUBCONNECTOR_DVIA; - drm_connector_property_set_value(drm_connector, dev->mode_config.dvi_i_subconnector_property, subtype); - } + /* Only update when relevant and when detect couldn't determine type. */ + nv50_kms_connector_set_digital(drm_connector, edid->digital ? 1 : 0, FALSE); kfree(edid); } @@ -1009,7 +1043,7 @@ static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, u list_for_each_entry_safe(mode, t, &drm_connector->modes, head) { if (mode->status == MODE_OK) { struct nouveau_hw_mode *hw_mode = nv50_kms_to_hw_mode(mode); - struct nv50_output *output = connector->to_output(connector, nv50_kms_connector_is_digital(drm_connector)); + struct nv50_output *output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector)); mode->status = output->validate_mode(output, hw_mode); /* find native mode, TODO: also check if we actually found one */ @@ -1025,7 +1059,7 @@ static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, u list_for_each_entry_safe(mode, t, &drm_connector->modes, head) { if (mode->status == MODE_OK) { struct nouveau_hw_mode *hw_mode = nv50_kms_to_hw_mode(mode); - struct nv50_output *output = connector->to_output(connector, nv50_kms_connector_is_digital(drm_connector)); + struct nv50_output *output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector)); mode->status = output->validate_mode(output, hw_mode); kfree(hw_mode); @@ -1057,7 +1091,7 @@ static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, u /* also add it as native mode */ hw_mode = nv50_kms_to_hw_mode(mode); - output = connector->to_output(connector, nv50_kms_connector_is_digital(drm_connector)); + output = connector->to_output(connector, nv50_kms_connector_get_digital(drm_connector)); if (hw_mode) *output->native_mode = *hw_mode; -- cgit v1.2.3 From 4d5b9f484885ac01457f0a8c39b24ca4aac34b5a Mon Sep 17 00:00:00 2001 From: Maarten Maathuis Date: Mon, 21 Jul 2008 16:57:25 +0200 Subject: NV50: Don't create a "native" mode for LVDS when there is none. --- linux-core/nv50_kms_wrapper.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'linux-core/nv50_kms_wrapper.c') diff --git a/linux-core/nv50_kms_wrapper.c b/linux-core/nv50_kms_wrapper.c index 8b27f80b..355d25d6 100644 --- a/linux-core/nv50_kms_wrapper.c +++ b/linux-core/nv50_kms_wrapper.c @@ -1079,6 +1079,10 @@ static void nv50_kms_connector_fill_modes(struct drm_connector *drm_connector, u NV50_DEBUG("No valid modes on %s\n", drm_get_connector_name(drm_connector)); + /* Making up native modes for LVDS is a bad idea. */ + if (drm_connector->connector_type == DRM_MODE_CONNECTOR_LVDS) + return; + /* Should we do this here ??? * When no valid EDID modes are available we end up * here and bailed in the past, now we add a standard -- cgit v1.2.3