modesetting-101: implement optional scaling and dithering properties
authorMaarten Maathuis <madman2003@gmail.com>
Sun, 20 Jul 2008 11:49:18 +0000 (13:49 +0200)
committerMaarten Maathuis <madman2003@gmail.com>
Sun, 20 Jul 2008 11:49:18 +0000 (13:49 +0200)
linux-core/drm_crtc.c
linux-core/drm_crtc.h
linux-core/nv50_connector.c
linux-core/nv50_connector.h
linux-core/nv50_crtc.c
linux-core/nv50_display.h
linux-core/nv50_kms_wrapper.c
shared-core/drm.h

index fc8d1fe..c984209 100644 (file)
@@ -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" },
@@ -647,6 +670,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
  *
index d4bb879..d88c614 100644 (file)
@@ -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,
index ac5194c..be133de 100644 (file)
@@ -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);
index ebd6eac..02b1561 100644 (file)
@@ -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);
index 6c3d404..ffb976f 100644 (file)
@@ -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;
index f20e67d..3c2ee1c 100644 (file)
@@ -68,7 +68,7 @@ struct nv50_display {
 };
 
 enum scaling_modes {
-       SCALE_PANEL,
+       SCALE_NON_GPU,
        SCALE_FULLSCREEN,
        SCALE_ASPECT,
        SCALE_NOSCALE,
index a7966e9..b0d6434 100644 (file)
@@ -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;
index 2e4d2a9..4119064 100644 (file)
@@ -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;