| /* |
| * 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 "intel_drv.h" |
| #include "intel_dsi.h" |
| #include "i915_drv.h" |
| #include <video/mipi_display.h> |
| #include <drm/drm_mipi_dsi.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) |
| { |
| struct intel_encoder *encoder = connector->encoder; |
| struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base); |
| struct mipi_dsi_device *dsi_device; |
| u8 data = 0; |
| enum port port; |
| |
| /* FIXME: Need to take care of 16 bit brightness level */ |
| for_each_dsi_port(port, intel_dsi->dcs_backlight_ports) { |
| dsi_device = intel_dsi->dsi_hosts[port]->device; |
| mipi_dsi_dcs_read(dsi_device, MIPI_DCS_GET_DISPLAY_BRIGHTNESS, |
| &data, sizeof(data)); |
| break; |
| } |
| |
| return data; |
| } |
| |
| static void dcs_set_backlight(const struct drm_connector_state *conn_state, u32 level) |
| { |
| struct intel_dsi *intel_dsi = enc_to_intel_dsi(conn_state->best_encoder); |
| struct mipi_dsi_device *dsi_device; |
| u8 data = level; |
| enum port port; |
| |
| /* FIXME: Need to take care of 16 bit brightness level */ |
| for_each_dsi_port(port, intel_dsi->dcs_backlight_ports) { |
| dsi_device = intel_dsi->dsi_hosts[port]->device; |
| mipi_dsi_dcs_write(dsi_device, MIPI_DCS_SET_DISPLAY_BRIGHTNESS, |
| &data, sizeof(data)); |
| } |
| } |
| |
| static void dcs_disable_backlight(const struct drm_connector_state *conn_state) |
| { |
| struct intel_dsi *intel_dsi = enc_to_intel_dsi(conn_state->best_encoder); |
| struct mipi_dsi_device *dsi_device; |
| enum port port; |
| |
| dcs_set_backlight(conn_state, 0); |
| |
| for_each_dsi_port(port, intel_dsi->dcs_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, intel_dsi->dcs_backlight_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) |
| { |
| struct intel_dsi *intel_dsi = enc_to_intel_dsi(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, intel_dsi->dcs_backlight_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, intel_dsi->dcs_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, panel->backlight.level); |
| } |
| |
| static int dcs_setup_backlight(struct intel_connector *connector, |
| enum pipe unused) |
| { |
| struct intel_panel *panel = &connector->panel; |
| |
| panel->backlight.max = PANEL_PWM_MAX_VALUE; |
| panel->backlight.level = PANEL_PWM_MAX_VALUE; |
| |
| return 0; |
| } |
| |
| int intel_dsi_dcs_init_backlight_funcs(struct intel_connector *intel_connector) |
| { |
| struct drm_device *dev = intel_connector->base.dev; |
| struct drm_i915_private *dev_priv = to_i915(dev); |
| struct intel_encoder *encoder = intel_connector->encoder; |
| struct intel_panel *panel = &intel_connector->panel; |
| |
| if (dev_priv->vbt.backlight.type != INTEL_BACKLIGHT_DSI_DCS) |
| return -ENODEV; |
| |
| if (WARN_ON(encoder->type != INTEL_OUTPUT_DSI)) |
| return -EINVAL; |
| |
| panel->backlight.setup = dcs_setup_backlight; |
| panel->backlight.enable = dcs_enable_backlight; |
| panel->backlight.disable = dcs_disable_backlight; |
| panel->backlight.set = dcs_set_backlight; |
| panel->backlight.get = dcs_get_backlight; |
| |
| return 0; |
| } |