From 65803e53a696347e38d7f6c2c8dc186c6764ff03 Mon Sep 17 00:00:00 2001 From: Maarten Maathuis Date: Sun, 20 Jul 2008 13:49:18 +0200 Subject: [PATCH] modesetting-101: implement optional scaling and dithering properties --- linux-core/drm_crtc.c | 69 ++++++++++++++++++ linux-core/drm_crtc.h | 8 ++- linux-core/nv50_connector.c | 4 +- linux-core/nv50_connector.h | 1 + linux-core/nv50_crtc.c | 2 +- linux-core/nv50_display.h | 2 +- linux-core/nv50_kms_wrapper.c | 128 ++++++++++++++++++++++++++++++++-- shared-core/drm.h | 10 +++ 8 files changed, 215 insertions(+), 9 deletions(-) diff --git a/linux-core/drm_crtc.c b/linux-core/drm_crtc.c index fc8d1fe8..c984209a 100644 --- a/linux-core/drm_crtc.c +++ b/linux-core/drm_crtc.c @@ -60,6 +60,26 @@ char *drm_get_dpms_name(int val) return "unknown"; } +/* + * Optional properties + */ +static struct drm_prop_enum_list drm_scaling_mode_enum_list[] = +{ + { DRM_MODE_SCALE_NON_GPU, "Non-GPU" }, + { DRM_MODE_SCALE_FULLSCREEN, "Fullscreen" }, + { DRM_MODE_SCALE_NO_SCALE, "No scale" }, + { DRM_MODE_SCALE_ASPECT, "Aspect" }, +}; + +static struct drm_prop_enum_list drm_dithering_mode_enum_list[] = +{ + { DRM_MODE_DITHERING_OFF, "Off" }, + { DRM_MODE_DITHERING_ON, "On" }, +}; + +/* + * Non-global properties, but "required" for certain connectors. + */ static struct drm_prop_enum_list drm_select_subconnector_enum_list[] = { { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ @@ -102,6 +122,9 @@ char *drm_get_subconnector_name(int val) return "unknown"; } +/* + * Connector and encoder types. + */ static struct drm_prop_enum_list drm_connector_enum_list[] = { { DRM_MODE_CONNECTOR_Unknown, "Unknown" }, { DRM_MODE_CONNECTOR_VGA, "VGA" }, @@ -646,6 +669,52 @@ int drm_mode_create_tv_properties(struct drm_device *dev, int num_modes, } EXPORT_SYMBOL(drm_mode_create_tv_properties); +/** + * drm_mode_create_scaling_mode_property - create scaling mode property + * @dev: DRM device + * + * Called by a driver the first time it's needed, must be attached to desired connectors. + */ +int drm_mode_create_scaling_mode_property(struct drm_device *dev) +{ + int i; + + if (dev->mode_config.scaling_mode_property) /* already done */ + return 0; + + dev->mode_config.scaling_mode_property = + drm_property_create(dev, DRM_MODE_PROP_ENUM, + "scaling mode", ARRAY_SIZE(drm_scaling_mode_enum_list)); + for (i = 0; i < ARRAY_SIZE(drm_scaling_mode_enum_list); i++) + drm_property_add_enum(dev->mode_config.scaling_mode_property, i, drm_scaling_mode_enum_list[i].type, drm_scaling_mode_enum_list[i].name); + + return 0; +} +EXPORT_SYMBOL(drm_mode_create_scaling_mode_property); + +/** + * drm_mode_create_dithering_property - create dithering property + * @dev: DRM device + * + * Called by a driver the first time it's needed, must be attached to desired connectors. + */ +int drm_mode_create_dithering_property(struct drm_device *dev) +{ + int i; + + if (dev->mode_config.dithering_mode_property) /* already done */ + return 0; + + dev->mode_config.dithering_mode_property = + drm_property_create(dev, DRM_MODE_PROP_ENUM, + "dithering", ARRAY_SIZE(drm_dithering_mode_enum_list)); + for (i = 0; i < ARRAY_SIZE(drm_dithering_mode_enum_list); i++) + drm_property_add_enum(dev->mode_config.dithering_mode_property, i, drm_dithering_mode_enum_list[i].type, drm_dithering_mode_enum_list[i].name); + + return 0; +} +EXPORT_SYMBOL(drm_mode_create_dithering_property); + /** * drm_mode_config_init - initialize DRM mode_configuration structure * @dev: DRM device diff --git a/linux-core/drm_crtc.h b/linux-core/drm_crtc.h index d4bb8794..d88c6149 100644 --- a/linux-core/drm_crtc.h +++ b/linux-core/drm_crtc.h @@ -536,7 +536,7 @@ struct drm_mode_config { struct drm_property *edid_property; struct drm_property *dpms_property; - /* optional properties */ + /* DVI-I properties */ struct drm_property *dvi_i_subconnector_property; struct drm_property *dvi_i_select_subconnector_property; @@ -549,6 +549,10 @@ struct drm_mode_config { struct drm_property *tv_top_margin_property; struct drm_property *tv_bottom_margin_property; + /* Optional properties */ + struct drm_property *scaling_mode_property; + struct drm_property *dithering_mode_property; + /* hotplug */ uint32_t hotplug_counter; }; @@ -650,6 +654,8 @@ extern int drm_property_add_enum(struct drm_property *property, int index, extern int drm_mode_create_dvi_i_properties(struct drm_device *dev); extern int drm_mode_create_tv_properties(struct drm_device *dev, int num_formats, char *formats[]); +extern int drm_mode_create_scaling_mode_property(struct drm_device *dev); +extern int drm_mode_create_dithering_property(struct drm_device *dev); extern char *drm_get_encoder_name(struct drm_encoder *encoder); extern int drm_mode_connector_attach_encoder(struct drm_connector *connector, diff --git a/linux-core/nv50_connector.c b/linux-core/nv50_connector.c index ac5194c0..be133de3 100644 --- a/linux-core/nv50_connector.c +++ b/linux-core/nv50_connector.c @@ -187,7 +187,9 @@ int nv50_connector_create(struct drm_device *dev, int bus, int i2c_index, int ty if (type == CONNECTOR_DVI_D || type == CONNECTOR_DVI_I || type == CONNECTOR_LVDS) connector->scaling_mode = SCALE_FULLSCREEN; else - connector->scaling_mode = SCALE_PANEL; + connector->scaling_mode = SCALE_NON_GPU; + + connector->use_dithering = false; if (i2c_index < 0xf) connector->i2c_chan = nv50_i2c_channel_create(dev, i2c_index); diff --git a/linux-core/nv50_connector.h b/linux-core/nv50_connector.h index ebd6eac6..02b1561e 100644 --- a/linux-core/nv50_connector.h +++ b/linux-core/nv50_connector.h @@ -48,6 +48,7 @@ struct nv50_connector { struct nv50_output *output; int scaling_mode; + bool use_dithering; bool (*detect) (struct nv50_connector *connector); int (*destroy) (struct nv50_connector *connector); diff --git a/linux-core/nv50_crtc.c b/linux-core/nv50_crtc.c index 6c3d404f..ffb976f4 100644 --- a/linux-core/nv50_crtc.c +++ b/linux-core/nv50_crtc.c @@ -264,7 +264,7 @@ static int nv50_crtc_set_scale(struct nv50_crtc *crtc) outY = crtc->native_mode->vdisplay; break; case SCALE_NOSCALE: - case SCALE_PANEL: + case SCALE_NON_GPU: default: outX = crtc->mode->hdisplay; outY = crtc->mode->vdisplay; diff --git a/linux-core/nv50_display.h b/linux-core/nv50_display.h index f20e67da..3c2ee1c9 100644 --- a/linux-core/nv50_display.h +++ b/linux-core/nv50_display.h @@ -68,7 +68,7 @@ struct nv50_display { }; enum scaling_modes { - SCALE_PANEL, + SCALE_NON_GPU, SCALE_FULLSCREEN, SCALE_ASPECT, SCALE_NOSCALE, 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; diff --git a/shared-core/drm.h b/shared-core/drm.h index 2e4d2a94..41190640 100644 --- a/shared-core/drm.h +++ b/shared-core/drm.h @@ -1034,6 +1034,16 @@ struct drm_mm_info_arg { #define DRM_MODE_DPMS_SUSPEND 2 #define DRM_MODE_DPMS_OFF 3 +/* Scaling mode options */ +#define DRM_MODE_SCALE_NON_GPU 0 +#define DRM_MODE_SCALE_FULLSCREEN 1 +#define DRM_MODE_SCALE_NO_SCALE 2 +#define DRM_MODE_SCALE_ASPECT 3 + +/* Dithering mode options */ +#define DRM_MODE_DITHERING_OFF 0 +#define DRM_MODE_DITHERING_ON 1 + struct drm_mode_modeinfo { unsigned int clock; unsigned short hdisplay, hsync_start, hsync_end, htotal, hskew;