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 */
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" },
}
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
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;
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;
};
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,
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);
struct nv50_output *output;
int scaling_mode;
+ bool use_dithering;
bool (*detect) (struct nv50_connector *connector);
int (*destroy) (struct nv50_connector *connector);
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;
};
enum scaling_modes {
- SCALE_PANEL,
+ SCALE_NON_GPU,
SCALE_FULLSCREEN,
SCALE_ASPECT,
SCALE_NOSCALE,
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;
}
}
-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;
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;
}
.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;
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;
#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;