drm/radeon: add a connector property for dither

Allows you to enable dither in the display hardware
when the monitor supports lower a lower bpc than the
current framebuffer format.

Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
diff --git a/drivers/gpu/drm/radeon/cik.c b/drivers/gpu/drm/radeon/cik.c
index ccd0871..6e3c098 100644
--- a/drivers/gpu/drm/radeon/cik.c
+++ b/drivers/gpu/drm/radeon/cik.c
@@ -7427,10 +7427,13 @@
 	struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
 	int bpc = 0;
 	u32 tmp = 0;
-	bool dither = false;
+	enum radeon_connector_dither dither = RADEON_FMT_DITHER_DISABLE;
 
-	if (connector)
+	if (connector) {
+		struct radeon_connector *radeon_connector = to_radeon_connector(connector);
 		bpc = radeon_get_monitor_bpc(connector);
+		dither = radeon_connector->dither;
+	}
 
 	/* LVDS/eDP FMT is set up by atom */
 	if (radeon_encoder->devices & ATOM_DEVICE_LCD_SUPPORT)
@@ -7446,7 +7449,7 @@
 
 	switch (bpc) {
 	case 6:
-		if (dither)
+		if (dither == RADEON_FMT_DITHER_ENABLE)
 			/* XXX sort out optimal dither settings */
 			tmp |= (FMT_FRAME_RANDOM_ENABLE | FMT_HIGHPASS_RANDOM_ENABLE |
 				FMT_SPATIAL_DITHER_EN | FMT_SPATIAL_DITHER_DEPTH(0));
@@ -7454,7 +7457,7 @@
 			tmp |= (FMT_TRUNCATE_EN | FMT_TRUNCATE_DEPTH(0));
 		break;
 	case 8:
-		if (dither)
+		if (dither == RADEON_FMT_DITHER_ENABLE)
 			/* XXX sort out optimal dither settings */
 			tmp |= (FMT_FRAME_RANDOM_ENABLE | FMT_HIGHPASS_RANDOM_ENABLE |
 				FMT_RGB_RANDOM_ENABLE |
@@ -7463,7 +7466,7 @@
 			tmp |= (FMT_TRUNCATE_EN | FMT_TRUNCATE_DEPTH(1));
 		break;
 	case 10:
-		if (dither)
+		if (dither == RADEON_FMT_DITHER_ENABLE)
 			/* XXX sort out optimal dither settings */
 			tmp |= (FMT_FRAME_RANDOM_ENABLE | FMT_HIGHPASS_RANDOM_ENABLE |
 				FMT_RGB_RANDOM_ENABLE |
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
index feedfcc..52fa3be 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -1202,10 +1202,13 @@
 	struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
 	int bpc = 0;
 	u32 tmp = 0;
-	bool dither = false;
+	enum radeon_connector_dither dither = RADEON_FMT_DITHER_DISABLE;
 
-	if (connector)
+	if (connector) {
+		struct radeon_connector *radeon_connector = to_radeon_connector(connector);
 		bpc = radeon_get_monitor_bpc(connector);
+		dither = radeon_connector->dither;
+	}
 
 	/* LVDS/eDP FMT is set up by atom */
 	if (radeon_encoder->devices & ATOM_DEVICE_LCD_SUPPORT)
@@ -1221,7 +1224,7 @@
 
 	switch (bpc) {
 	case 6:
-		if (dither)
+		if (dither == RADEON_FMT_DITHER_ENABLE)
 			/* XXX sort out optimal dither settings */
 			tmp |= (FMT_FRAME_RANDOM_ENABLE | FMT_HIGHPASS_RANDOM_ENABLE |
 				FMT_SPATIAL_DITHER_EN);
@@ -1229,7 +1232,7 @@
 			tmp |= FMT_TRUNCATE_EN;
 		break;
 	case 8:
-		if (dither)
+		if (dither == RADEON_FMT_DITHER_ENABLE)
 			/* XXX sort out optimal dither settings */
 			tmp |= (FMT_FRAME_RANDOM_ENABLE | FMT_HIGHPASS_RANDOM_ENABLE |
 				FMT_RGB_RANDOM_ENABLE |
diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c
index bdf4b80..af922e2 100644
--- a/drivers/gpu/drm/radeon/r600.c
+++ b/drivers/gpu/drm/radeon/r600.c
@@ -133,10 +133,13 @@
 	struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
 	int bpc = 0;
 	u32 tmp = 0;
-	bool dither = false;
+	enum radeon_connector_dither dither = RADEON_FMT_DITHER_DISABLE;
 
-	if (connector)
+	if (connector) {
+		struct radeon_connector *radeon_connector = to_radeon_connector(connector);
 		bpc = radeon_get_monitor_bpc(connector);
+		dither = radeon_connector->dither;
+	}
 
 	/* LVDS FMT is set up by atom */
 	if (radeon_encoder->devices & ATOM_DEVICE_LCD_SUPPORT)
@@ -152,14 +155,14 @@
 
 	switch (bpc) {
 	case 6:
-		if (dither)
+		if (dither == RADEON_FMT_DITHER_ENABLE)
 			/* XXX sort out optimal dither settings */
 			tmp |= FMT_SPATIAL_DITHER_EN;
 		else
 			tmp |= FMT_TRUNCATE_EN;
 		break;
 	case 8:
-		if (dither)
+		if (dither == RADEON_FMT_DITHER_ENABLE)
 			/* XXX sort out optimal dither settings */
 			tmp |= (FMT_SPATIAL_DITHER_EN | FMT_SPATIAL_DITHER_DEPTH);
 		else
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index 79159b5..43f7600 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -411,6 +411,21 @@
 		}
 	}
 
+	if (property == rdev->mode_info.dither_property) {
+		struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+		/* need to find digital encoder on connector */
+		encoder = radeon_find_encoder(connector, DRM_MODE_ENCODER_TMDS);
+		if (!encoder)
+			return 0;
+
+		radeon_encoder = to_radeon_encoder(encoder);
+
+		if (radeon_connector->dither != val) {
+			radeon_connector->dither = val;
+			radeon_property_change_mode(&radeon_encoder->base);
+		}
+	}
+
 	if (property == rdev->mode_info.underscan_property) {
 		/* need to find digital encoder on connector */
 		encoder = radeon_find_encoder(connector, DRM_MODE_ENCODER_TMDS);
@@ -1661,6 +1676,9 @@
 			drm_object_attach_property(&radeon_connector->base.base,
 						   rdev->mode_info.audio_property,
 						   RADEON_AUDIO_DISABLE);
+			drm_object_attach_property(&radeon_connector->base.base,
+						   rdev->mode_info.dither_property,
+						   RADEON_FMT_DITHER_DISABLE);
 			subpixel_order = SubPixelHorizontalRGB;
 			connector->interlace_allowed = true;
 			if (connector_type == DRM_MODE_CONNECTOR_HDMIB)
@@ -1759,6 +1777,11 @@
 							      rdev->mode_info.audio_property,
 							      RADEON_AUDIO_DISABLE);
 			}
+			if (ASIC_IS_AVIVO(rdev)) {
+				drm_object_attach_property(&radeon_connector->base.base,
+							   rdev->mode_info.dither_property,
+							   RADEON_FMT_DITHER_DISABLE);
+			}
 			if (connector_type == DRM_MODE_CONNECTOR_DVII) {
 				radeon_connector->dac_load_detect = true;
 				drm_object_attach_property(&radeon_connector->base.base,
@@ -1804,6 +1827,11 @@
 							      rdev->mode_info.audio_property,
 							      RADEON_AUDIO_DISABLE);
 			}
+			if (ASIC_IS_AVIVO(rdev)) {
+				drm_object_attach_property(&radeon_connector->base.base,
+							   rdev->mode_info.dither_property,
+							   RADEON_FMT_DITHER_DISABLE);
+			}
 			subpixel_order = SubPixelHorizontalRGB;
 			connector->interlace_allowed = true;
 			if (connector_type == DRM_MODE_CONNECTOR_HDMIB)
@@ -1848,6 +1876,11 @@
 							      rdev->mode_info.audio_property,
 							      RADEON_AUDIO_DISABLE);
 			}
+			if (ASIC_IS_AVIVO(rdev)) {
+				drm_object_attach_property(&radeon_connector->base.base,
+							   rdev->mode_info.dither_property,
+							   RADEON_FMT_DITHER_DISABLE);
+			}
 			connector->interlace_allowed = true;
 			/* in theory with a DP to VGA converter... */
 			connector->doublescan_allowed = false;
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index 0d1aa05..7adbb74 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -1178,6 +1178,12 @@
 	{ RADEON_AUDIO_AUTO, "auto" },
 };
 
+/* XXX support different dither options? spatial, temporal, both, etc. */
+static struct drm_prop_enum_list radeon_dither_enum_list[] =
+{	{ RADEON_FMT_DITHER_DISABLE, "off" },
+	{ RADEON_FMT_DITHER_ENABLE, "on" },
+};
+
 static int radeon_modeset_create_props(struct radeon_device *rdev)
 {
 	int sz;
@@ -1234,6 +1240,12 @@
 					 "audio",
 					 radeon_audio_enum_list, sz);
 
+	sz = ARRAY_SIZE(radeon_dither_enum_list);
+	rdev->mode_info.dither_property =
+		drm_property_create_enum(rdev->ddev, 0,
+					 "dither",
+					 radeon_dither_enum_list, sz);
+
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index 03f74b0..8b4e712 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -249,6 +249,8 @@
 	struct drm_property *underscan_vborder_property;
 	/* audio */
 	struct drm_property *audio_property;
+	/* FMT dithering */
+	struct drm_property *dither_property;
 	/* hardcoded DFP edid from BIOS */
 	struct edid *bios_hardcoded_edid;
 	int bios_hardcoded_edid_size;
@@ -479,6 +481,11 @@
 	RADEON_AUDIO_AUTO = 2
 };
 
+enum radeon_connector_dither {
+	RADEON_FMT_DITHER_DISABLE = 0,
+	RADEON_FMT_DITHER_ENABLE = 1,
+};
+
 struct radeon_connector {
 	struct drm_connector base;
 	uint32_t connector_id;
@@ -498,6 +505,7 @@
 	struct radeon_router router;
 	struct radeon_i2c_chan *router_bus;
 	enum radeon_connector_audio audio;
+	enum radeon_connector_dither dither;
 };
 
 struct radeon_framebuffer {
diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c
index df01aa39..76cc8d3 100644
--- a/drivers/gpu/drm/radeon/rs600.c
+++ b/drivers/gpu/drm/radeon/rs600.c
@@ -161,10 +161,13 @@
 	struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
 	int bpc = 0;
 	u32 tmp = 0;
-	bool dither = false;
+	enum radeon_connector_dither dither = RADEON_FMT_DITHER_DISABLE;
 
-	if (connector)
+	if (connector) {
+		struct radeon_connector *radeon_connector = to_radeon_connector(connector);
 		bpc = radeon_get_monitor_bpc(connector);
+		dither = radeon_connector->dither;
+	}
 
 	/* LVDS FMT is set up by atom */
 	if (radeon_encoder->devices & ATOM_DEVICE_LCD_SUPPORT)
@@ -175,14 +178,14 @@
 
 	switch (bpc) {
 	case 6:
-		if (dither)
+		if (dither == RADEON_FMT_DITHER_ENABLE)
 			/* XXX sort out optimal dither settings */
 			tmp |= AVIVO_TMDS_BIT_DEPTH_CONTROL_SPATIAL_DITHER_EN;
 		else
 			tmp |= AVIVO_TMDS_BIT_DEPTH_CONTROL_TRUNCATE_EN;
 		break;
 	case 8:
-		if (dither)
+		if (dither == RADEON_FMT_DITHER_ENABLE)
 			/* XXX sort out optimal dither settings */
 			tmp |= (AVIVO_TMDS_BIT_DEPTH_CONTROL_SPATIAL_DITHER_EN |
 				AVIVO_TMDS_BIT_DEPTH_CONTROL_SPATIAL_DITHER_DEPTH);