[MIPI CABC 2/2] drm/i915: CABC support for backlight control

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



In CABC (Content Adaptive Brightness Control) content grey level
scale can be increased while simultaneously decreasing
brightness of the backlight to achieve same perceived brightness.

The CABC is not standardized and panel vendors are free to follow
their implementation. The CABC implementaion here assumes that the
panels use standard SW register for control.

In this design there will be no PWM signal from the SoC and DCS
commands are sent to enable and control the backlight brightness.

Cc: Jani Nikula <jani.nikula@xxxxxxxxx>
Cc: Daniel Vetter <daniel.vetter@xxxxxxxxx>
Cc: Yetunde Adebisi <yetundex.adebisi@xxxxxxxxx>
Signed-off-by: Deepak M <m.deepak@xxxxxxxxx>
---
Addressed the review comments from Jani which were mentioned in
the below patch
http://lists.freedesktop.org/archives/intel-gfx/2015-September/075819.html

Went with the name CABC because the commands which are used
here are mainly for CABC and many of the panels have the same
commands for the CABC operation. For the cases where PWM is
directly from panel PWM, DCS commands may be different and
may have to add new functions to support it.

 drivers/gpu/drm/i915/intel_dsi.c   |  14 +++-
 drivers/gpu/drm/i915/intel_dsi.h   |  24 ++++++
 drivers/gpu/drm/i915/intel_panel.c | 145 +++++++++++++++++++++++++++++++++++--
 3 files changed, 177 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_dsi.c b/drivers/gpu/drm/i915/intel_dsi.c
index 170ae6f..5d1ba35 100644
--- a/drivers/gpu/drm/i915/intel_dsi.c
+++ b/drivers/gpu/drm/i915/intel_dsi.c
@@ -1175,8 +1175,20 @@ void intel_dsi_init(struct drm_device *dev)
 		intel_dsi->ports = (1 << PORT_C);
 	}
 
-	if (dev_priv->vbt.dsi.config->dual_link)
+	if (dev_priv->vbt.dsi.config->dual_link) {
 		intel_dsi->ports = ((1 << PORT_A) | (1 << PORT_C));
+		switch (dev_priv->vbt.dsi.config->dl_cabc_port) {
+		case CABC_PORT_A:
+			intel_dsi->bkl_dcs_ports = (1 << PORT_A);
+		case CABC_PORT_C:
+			intel_dsi->bkl_dcs_ports = (1 << PORT_C);
+		case CABC_PORT_A_AND_C:
+			intel_dsi->bkl_dcs_ports = intel_dsi->ports;
+		default:
+			DRM_ERROR("Unknown MIPI ports for sending DCS\n");
+		}
+	} else
+		intel_dsi->bkl_dcs_ports = intel_dsi->ports;
 
 	/* Create a DSI host (and a device) for each port. */
 	for_each_dsi_port(port, intel_dsi->ports) {
diff --git a/drivers/gpu/drm/i915/intel_dsi.h b/drivers/gpu/drm/i915/intel_dsi.h
index 4fde83b..4bcee40 100644
--- a/drivers/gpu/drm/i915/intel_dsi.h
+++ b/drivers/gpu/drm/i915/intel_dsi.h
@@ -34,6 +34,30 @@
 #define DSI_DUAL_LINK_FRONT_BACK	1
 #define DSI_DUAL_LINK_PIXEL_ALT		2
 
+#define CABC_OFF			(0 << 0)
+#define CABC_USER_INTERFACE_IMAGE	(1 << 0)
+#define CABC_STILL_PICTURE		(2 << 0)
+#define CABC_VIDEO_MODE			(3 << 0)
+
+#define CABC_BACKLIGHT			(1 << 2)
+#define CABC_DIMMING_DISPLAY		(1 << 3)
+#define CABC_BCTRL			(1 << 5)
+
+#define CABC_PORT_A			0x00
+#define CABC_PORT_C			0x01
+#define CABC_PORT_A_AND_C		0x02
+
+#define CABC_MAX_VALUE			0xFF
+
+#define MIPI_DCS_CABC_LEVEL_RD		0x52
+#define MIPI_DCS_CABC_MIN_BRIGHTNESS_RD	0x5F
+#define MIPI_DCS_CABC_CONTROL_RD	0x56
+#define MIPI_DCS_CABC_CONTROL_BRIGHT_RD	0x54
+#define MIPI_DCS_CABC_LEVEL_WR		0x51
+#define MIPI_DCS_CABC_MIN_BRIGHTNESS_WR	0x5E
+#define MIPI_DCS_CABC_CONTROL_WR	0x55
+#define MIPI_DCS_CABC_CONTROL_BRIGHT_WR	0x53
+
 struct intel_dsi_host;
 
 struct intel_dsi {
diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
index a24df35..085d9a6 100644
--- a/drivers/gpu/drm/i915/intel_panel.c
+++ b/drivers/gpu/drm/i915/intel_panel.c
@@ -34,6 +34,7 @@
 #include <linux/moduleparam.h>
 #include <linux/pwm.h>
 #include "intel_drv.h"
+#include "intel_dsi.h"
 
 #define CRC_PMIC_PWM_PERIOD_NS	21333
 
@@ -533,6 +534,30 @@ static u32 vlv_get_backlight(struct intel_connector *connector)
 	return _vlv_get_backlight(dev, pipe);
 }
 
+static u32 cabc_get_backlight(struct intel_connector *connector)
+{
+	struct intel_dsi *intel_dsi = NULL;
+	struct intel_encoder *encoder = NULL;
+	struct mipi_dsi_device *dsi_device;
+	u8 data[2] = {0};
+	enum port port;
+
+	encoder = connector->encoder;
+	if (encoder->type == INTEL_OUTPUT_DSI)
+		intel_dsi = enc_to_intel_dsi(&encoder->base);
+	else {
+		DRM_ERROR("Use DSI encoder for CABC\n");
+		return -EINVAL;
+	}
+
+	for_each_dsi_port(port, intel_dsi->bkl_dcs_ports) {
+		dsi_device = intel_dsi->dsi_hosts[port]->device;
+		mipi_dsi_dcs_read(dsi_device, MIPI_DCS_CABC_LEVEL_RD, data, 2);
+	}
+
+	return data[1];
+}
+
 static u32 bxt_get_backlight(struct intel_connector *connector)
 {
 	struct drm_device *dev = connector->base.dev;
@@ -631,6 +656,30 @@ static void vlv_set_backlight(struct intel_connector *connector, u32 level)
 	I915_WRITE(VLV_BLC_PWM_CTL(pipe), tmp | level);
 }
 
+static void cabc_set_backlight(struct intel_connector *connector, u32 level)
+{
+	struct intel_dsi *intel_dsi = NULL;
+	struct intel_encoder *encoder = NULL;
+	struct mipi_dsi_device *dsi_device;
+	u8 data[2] = {0};
+	enum port port;
+
+	encoder = connector->encoder;
+	if (encoder->type == INTEL_OUTPUT_DSI)
+		intel_dsi = enc_to_intel_dsi(&encoder->base);
+	else {
+		DRM_ERROR("Use DSI encoder for CABC\n");
+		return;
+	}
+
+	for_each_dsi_port(port, intel_dsi->bkl_dcs_ports) {
+		dsi_device = intel_dsi->dsi_hosts[port]->device;
+		data[1] = level;
+		data[0] = MIPI_DCS_CABC_LEVEL_WR;
+		mipi_dsi_dcs_write_buffer(dsi_device, data, 2);
+	}
+}
+
 static void bxt_set_backlight(struct intel_connector *connector, u32 level)
 {
 	struct drm_device *dev = connector->base.dev;
@@ -798,6 +847,34 @@ static void vlv_disable_backlight(struct intel_connector *connector)
 	I915_WRITE(VLV_BLC_PWM_CTL2(pipe), tmp & ~BLM_PWM_ENABLE);
 }
 
+static void cabc_disable_backlight(struct intel_connector *connector)
+{
+	struct intel_dsi *intel_dsi = NULL;
+	struct intel_encoder *encoder = NULL;
+	struct mipi_dsi_device *dsi_device;
+	enum port port;
+	u8 data[2] = {0};
+
+	encoder = connector->encoder;
+	if (encoder->type == INTEL_OUTPUT_DSI)
+		intel_dsi = enc_to_intel_dsi(&encoder->base);
+	else {
+		DRM_ERROR("Use DSI encoder for CABC\n");
+		return;
+	}
+
+	intel_panel_actually_set_backlight(connector, 0);
+
+	for_each_dsi_port(port, intel_dsi->bkl_dcs_ports) {
+		dsi_device = intel_dsi->dsi_hosts[port]->device;
+		data[1] = CABC_OFF;
+		data[0] = MIPI_DCS_CABC_CONTROL_WR;
+		mipi_dsi_dcs_write_buffer(dsi_device, data, 2);
+		data[0] = MIPI_DCS_CABC_CONTROL_BRIGHT_WR;
+		mipi_dsi_dcs_write_buffer(dsi_device, data, 2);
+	}
+}
+
 static void bxt_disable_backlight(struct intel_connector *connector)
 {
 	struct drm_device *dev = connector->base.dev;
@@ -1133,6 +1210,36 @@ void intel_panel_enable_backlight(struct intel_connector *connector)
 	mutex_unlock(&dev_priv->backlight_lock);
 }
 
+static void cabc_enable_backlight(struct intel_connector *connector)
+{
+	struct intel_dsi *intel_dsi = NULL;
+	struct intel_encoder *encoder = NULL;
+	struct intel_panel *panel = &connector->panel;
+	struct mipi_dsi_device *dsi_device;
+	enum port port;
+	u8 data[2] = {0};
+
+	encoder = connector->encoder;
+	if (encoder->type == INTEL_OUTPUT_DSI)
+		intel_dsi = enc_to_intel_dsi(&encoder->base);
+	else {
+		DRM_ERROR("Use DSI encoder for the CABC\n");
+		return;
+	}
+
+	for_each_dsi_port(port, intel_dsi->bkl_dcs_ports) {
+		dsi_device = intel_dsi->dsi_hosts[port]->device;
+		data[0] = MIPI_DCS_CABC_CONTROL_BRIGHT_WR;
+		data[1] = CABC_BACKLIGHT | CABC_DIMMING_DISPLAY | CABC_BCTRL;
+		mipi_dsi_dcs_write_buffer(dsi_device, data, 2);
+		data[0] = MIPI_DCS_CABC_CONTROL_WR;
+		data[1] = CABC_STILL_PICTURE;
+		mipi_dsi_dcs_write_buffer(dsi_device, data, 2);
+	}
+
+	intel_panel_actually_set_backlight(connector, panel->backlight.level);
+}
+
 #if IS_ENABLED(CONFIG_BACKLIGHT_CLASS_DEVICE)
 static int intel_backlight_device_update_status(struct backlight_device *bd)
 {
@@ -1601,6 +1708,26 @@ static int vlv_setup_backlight(struct intel_connector *connector, enum pipe pipe
 	return 0;
 }
 
+static int cabc_setup_backlight(struct intel_connector *connector,
+		enum pipe unused)
+{
+	struct drm_device *dev = connector->base.dev;
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	struct intel_panel *panel = &connector->panel;
+
+	if (dev_priv->vbt.backlight.present)
+		panel->backlight.present = true;
+	else {
+		DRM_ERROR("no backlight present per VBT\n");
+		return 0;
+	}
+
+	panel->backlight.max = CABC_MAX_VALUE;
+	panel->backlight.level = CABC_MAX_VALUE;
+
+	return 0;
+}
+
 static int
 bxt_setup_backlight(struct intel_connector *connector, enum pipe unused)
 {
@@ -1745,11 +1872,19 @@ intel_panel_init_backlight_funcs(struct intel_panel *panel)
 	struct drm_i915_private *dev_priv = dev->dev_private;
 
 	if (IS_BROXTON(dev)) {
-		panel->backlight.setup = bxt_setup_backlight;
-		panel->backlight.enable = bxt_enable_backlight;
-		panel->backlight.disable = bxt_disable_backlight;
-		panel->backlight.set = bxt_set_backlight;
-		panel->backlight.get = bxt_get_backlight;
+		if (dev_priv->vbt.dsi.config->cabc_supported) {
+			panel->backlight.setup = cabc_setup_backlight;
+			panel->backlight.enable = cabc_enable_backlight;
+			panel->backlight.disable = cabc_disable_backlight;
+			panel->backlight.set = cabc_set_backlight;
+			panel->backlight.get = cabc_get_backlight;
+		} else {
+			panel->backlight.setup = bxt_setup_backlight;
+			panel->backlight.enable = bxt_enable_backlight;
+			panel->backlight.disable = bxt_disable_backlight;
+			panel->backlight.set = bxt_set_backlight;
+			panel->backlight.get = bxt_get_backlight;
+		}
 	} else if (HAS_PCH_LPT(dev) || HAS_PCH_SPT(dev)) {
 		panel->backlight.setup = lpt_setup_backlight;
 		panel->backlight.enable = lpt_enable_backlight;
-- 
1.9.1

_______________________________________________
Intel-gfx mailing list
Intel-gfx@xxxxxxxxxxxxxxxxxxxxx
http://lists.freedesktop.org/mailman/listinfo/intel-gfx




[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]
  Powered by Linux