diff options
Diffstat (limited to 'drivers/gpu/drm/i915/display/intel_dsi_dcs_backlight.c')
-rw-r--r-- | drivers/gpu/drm/i915/display/intel_dsi_dcs_backlight.c | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/drivers/gpu/drm/i915/display/intel_dsi_dcs_backlight.c b/drivers/gpu/drm/i915/display/intel_dsi_dcs_backlight.c new file mode 100644 index 0000000000..0494432453 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_dsi_dcs_backlight.c @@ -0,0 +1,205 @@ +/* + * Copyright © 2016 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Deepak M <m.deepak at intel.com> + */ + +#include <drm/drm_mipi_dsi.h> +#include <video/mipi_display.h> + +#include "i915_drv.h" +#include "intel_display_types.h" +#include "intel_dsi.h" +#include "intel_dsi_dcs_backlight.h" + +#define CONTROL_DISPLAY_BCTRL (1 << 5) +#define CONTROL_DISPLAY_DD (1 << 3) +#define CONTROL_DISPLAY_BL (1 << 2) + +#define POWER_SAVE_OFF (0 << 0) +#define POWER_SAVE_LOW (1 << 0) +#define POWER_SAVE_MEDIUM (2 << 0) +#define POWER_SAVE_HIGH (3 << 0) +#define POWER_SAVE_OUTDOOR_MODE (4 << 0) + +#define PANEL_PWM_MAX_VALUE 0xFF + +static u32 dcs_get_backlight(struct intel_connector *connector, enum pipe unused) +{ + struct intel_encoder *encoder = intel_attached_encoder(connector); + struct intel_dsi *intel_dsi = enc_to_intel_dsi(encoder); + struct intel_panel *panel = &connector->panel; + struct mipi_dsi_device *dsi_device; + u8 data[2] = {}; + enum port port; + size_t len = panel->backlight.max > U8_MAX ? 2 : 1; + + for_each_dsi_port(port, panel->vbt.dsi.bl_ports) { + dsi_device = intel_dsi->dsi_hosts[port]->device; + mipi_dsi_dcs_read(dsi_device, MIPI_DCS_GET_DISPLAY_BRIGHTNESS, + &data, len); + break; + } + + return (data[1] << 8) | data[0]; +} + +static void dcs_set_backlight(const struct drm_connector_state *conn_state, u32 level) +{ + struct intel_dsi *intel_dsi = enc_to_intel_dsi(to_intel_encoder(conn_state->best_encoder)); + struct intel_panel *panel = &to_intel_connector(conn_state->connector)->panel; + struct mipi_dsi_device *dsi_device; + u8 data[2] = {}; + enum port port; + size_t len = panel->backlight.max > U8_MAX ? 2 : 1; + unsigned long mode_flags; + + if (len == 1) { + data[0] = level; + } else { + data[0] = level >> 8; + data[1] = level; + } + + for_each_dsi_port(port, panel->vbt.dsi.bl_ports) { + dsi_device = intel_dsi->dsi_hosts[port]->device; + mode_flags = dsi_device->mode_flags; + dsi_device->mode_flags &= ~MIPI_DSI_MODE_LPM; + mipi_dsi_dcs_write(dsi_device, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, + &data, len); + dsi_device->mode_flags = mode_flags; + } +} + +static void dcs_disable_backlight(const struct drm_connector_state *conn_state, u32 level) +{ + struct intel_dsi *intel_dsi = enc_to_intel_dsi(to_intel_encoder(conn_state->best_encoder)); + struct intel_panel *panel = &to_intel_connector(conn_state->connector)->panel; + struct mipi_dsi_device *dsi_device; + enum port port; + + dcs_set_backlight(conn_state, 0); + + for_each_dsi_port(port, panel->vbt.dsi.cabc_ports) { + u8 cabc = POWER_SAVE_OFF; + + dsi_device = intel_dsi->dsi_hosts[port]->device; + mipi_dsi_dcs_write(dsi_device, MIPI_DCS_WRITE_POWER_SAVE, + &cabc, sizeof(cabc)); + } + + for_each_dsi_port(port, panel->vbt.dsi.bl_ports) { + u8 ctrl = 0; + + dsi_device = intel_dsi->dsi_hosts[port]->device; + + mipi_dsi_dcs_read(dsi_device, MIPI_DCS_GET_CONTROL_DISPLAY, + &ctrl, sizeof(ctrl)); + + ctrl &= ~CONTROL_DISPLAY_BL; + ctrl &= ~CONTROL_DISPLAY_DD; + ctrl &= ~CONTROL_DISPLAY_BCTRL; + + mipi_dsi_dcs_write(dsi_device, MIPI_DCS_WRITE_CONTROL_DISPLAY, + &ctrl, sizeof(ctrl)); + } +} + +static void dcs_enable_backlight(const struct intel_crtc_state *crtc_state, + const struct drm_connector_state *conn_state, u32 level) +{ + struct intel_dsi *intel_dsi = enc_to_intel_dsi(to_intel_encoder(conn_state->best_encoder)); + struct intel_panel *panel = &to_intel_connector(conn_state->connector)->panel; + struct mipi_dsi_device *dsi_device; + enum port port; + + for_each_dsi_port(port, panel->vbt.dsi.bl_ports) { + u8 ctrl = 0; + + dsi_device = intel_dsi->dsi_hosts[port]->device; + + mipi_dsi_dcs_read(dsi_device, MIPI_DCS_GET_CONTROL_DISPLAY, + &ctrl, sizeof(ctrl)); + + ctrl |= CONTROL_DISPLAY_BL; + ctrl |= CONTROL_DISPLAY_DD; + ctrl |= CONTROL_DISPLAY_BCTRL; + + mipi_dsi_dcs_write(dsi_device, MIPI_DCS_WRITE_CONTROL_DISPLAY, + &ctrl, sizeof(ctrl)); + } + + for_each_dsi_port(port, panel->vbt.dsi.cabc_ports) { + u8 cabc = POWER_SAVE_MEDIUM; + + dsi_device = intel_dsi->dsi_hosts[port]->device; + mipi_dsi_dcs_write(dsi_device, MIPI_DCS_WRITE_POWER_SAVE, + &cabc, sizeof(cabc)); + } + + dcs_set_backlight(conn_state, level); +} + +static int dcs_setup_backlight(struct intel_connector *connector, + enum pipe unused) +{ + struct drm_i915_private *i915 = to_i915(connector->base.dev); + struct intel_panel *panel = &connector->panel; + + if (panel->vbt.backlight.brightness_precision_bits > 8) + panel->backlight.max = (1 << panel->vbt.backlight.brightness_precision_bits) - 1; + else + panel->backlight.max = PANEL_PWM_MAX_VALUE; + + panel->backlight.level = panel->backlight.max; + + drm_dbg_kms(&i915->drm, + "[CONNECTOR:%d:%s] Using DCS for backlight control\n", + connector->base.base.id, connector->base.name); + + return 0; +} + +static const struct intel_panel_bl_funcs dcs_bl_funcs = { + .setup = dcs_setup_backlight, + .enable = dcs_enable_backlight, + .disable = dcs_disable_backlight, + .set = dcs_set_backlight, + .get = dcs_get_backlight, +}; + +int intel_dsi_dcs_init_backlight_funcs(struct intel_connector *intel_connector) +{ + struct drm_device *dev = intel_connector->base.dev; + struct intel_encoder *encoder = intel_attached_encoder(intel_connector); + struct intel_panel *panel = &intel_connector->panel; + + if (panel->vbt.backlight.type != INTEL_BACKLIGHT_DSI_DCS) + return -ENODEV; + + if (drm_WARN_ON(dev, encoder->type != INTEL_OUTPUT_DSI)) + return -EINVAL; + + panel->backlight.funcs = &dcs_bl_funcs; + + return 0; +} |