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