206 lines · c
1/*2 * Copyright © 2016 Intel Corporation3 *4 * Permission is hereby granted, free of charge, to any person obtaining a5 * copy of this software and associated documentation files (the "Software"),6 * to deal in the Software without restriction, including without limitation7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,8 * and/or sell copies of the Software, and to permit persons to whom the9 * Software is furnished to do so, subject to the following conditions:10 *11 * The above copyright notice and this permission notice (including the next12 * paragraph) shall be included in all copies or substantial portions of the13 * Software.14 *15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER21 * DEALINGS IN THE SOFTWARE.22 *23 * Author: Deepak M <m.deepak at intel.com>24 */25 26#include <drm/drm_mipi_dsi.h>27#include <video/mipi_display.h>28 29#include "i915_drv.h"30#include "intel_display_types.h"31#include "intel_dsi.h"32#include "intel_dsi_dcs_backlight.h"33 34#define CONTROL_DISPLAY_BCTRL (1 << 5)35#define CONTROL_DISPLAY_DD (1 << 3)36#define CONTROL_DISPLAY_BL (1 << 2)37 38#define POWER_SAVE_OFF (0 << 0)39#define POWER_SAVE_LOW (1 << 0)40#define POWER_SAVE_MEDIUM (2 << 0)41#define POWER_SAVE_HIGH (3 << 0)42#define POWER_SAVE_OUTDOOR_MODE (4 << 0)43 44#define PANEL_PWM_MAX_VALUE 0xFF45 46static u32 dcs_get_backlight(struct intel_connector *connector, enum pipe unused)47{48 struct intel_encoder *encoder = intel_attached_encoder(connector);49 struct intel_dsi *intel_dsi = enc_to_intel_dsi(encoder);50 struct intel_panel *panel = &connector->panel;51 struct mipi_dsi_device *dsi_device;52 u8 data[2] = {};53 enum port port;54 size_t len = panel->backlight.max > U8_MAX ? 2 : 1;55 56 for_each_dsi_port(port, panel->vbt.dsi.bl_ports) {57 dsi_device = intel_dsi->dsi_hosts[port]->device;58 mipi_dsi_dcs_read(dsi_device, MIPI_DCS_GET_DISPLAY_BRIGHTNESS,59 &data, len);60 break;61 }62 63 return (data[1] << 8) | data[0];64}65 66static void dcs_set_backlight(const struct drm_connector_state *conn_state, u32 level)67{68 struct intel_dsi *intel_dsi = enc_to_intel_dsi(to_intel_encoder(conn_state->best_encoder));69 struct intel_panel *panel = &to_intel_connector(conn_state->connector)->panel;70 struct mipi_dsi_device *dsi_device;71 u8 data[2] = {};72 enum port port;73 size_t len = panel->backlight.max > U8_MAX ? 2 : 1;74 unsigned long mode_flags;75 76 if (len == 1) {77 data[0] = level;78 } else {79 data[0] = level >> 8;80 data[1] = level;81 }82 83 for_each_dsi_port(port, panel->vbt.dsi.bl_ports) {84 dsi_device = intel_dsi->dsi_hosts[port]->device;85 mode_flags = dsi_device->mode_flags;86 dsi_device->mode_flags &= ~MIPI_DSI_MODE_LPM;87 mipi_dsi_dcs_write(dsi_device, MIPI_DCS_SET_DISPLAY_BRIGHTNESS,88 &data, len);89 dsi_device->mode_flags = mode_flags;90 }91}92 93static void dcs_disable_backlight(const struct drm_connector_state *conn_state, u32 level)94{95 struct intel_dsi *intel_dsi = enc_to_intel_dsi(to_intel_encoder(conn_state->best_encoder));96 struct intel_panel *panel = &to_intel_connector(conn_state->connector)->panel;97 struct mipi_dsi_device *dsi_device;98 enum port port;99 100 dcs_set_backlight(conn_state, 0);101 102 for_each_dsi_port(port, panel->vbt.dsi.cabc_ports) {103 u8 cabc = POWER_SAVE_OFF;104 105 dsi_device = intel_dsi->dsi_hosts[port]->device;106 mipi_dsi_dcs_write(dsi_device, MIPI_DCS_WRITE_POWER_SAVE,107 &cabc, sizeof(cabc));108 }109 110 for_each_dsi_port(port, panel->vbt.dsi.bl_ports) {111 u8 ctrl = 0;112 113 dsi_device = intel_dsi->dsi_hosts[port]->device;114 115 mipi_dsi_dcs_read(dsi_device, MIPI_DCS_GET_CONTROL_DISPLAY,116 &ctrl, sizeof(ctrl));117 118 ctrl &= ~CONTROL_DISPLAY_BL;119 ctrl &= ~CONTROL_DISPLAY_DD;120 ctrl &= ~CONTROL_DISPLAY_BCTRL;121 122 mipi_dsi_dcs_write(dsi_device, MIPI_DCS_WRITE_CONTROL_DISPLAY,123 &ctrl, sizeof(ctrl));124 }125}126 127static void dcs_enable_backlight(const struct intel_crtc_state *crtc_state,128 const struct drm_connector_state *conn_state, u32 level)129{130 struct intel_dsi *intel_dsi = enc_to_intel_dsi(to_intel_encoder(conn_state->best_encoder));131 struct intel_panel *panel = &to_intel_connector(conn_state->connector)->panel;132 struct mipi_dsi_device *dsi_device;133 enum port port;134 135 for_each_dsi_port(port, panel->vbt.dsi.bl_ports) {136 u8 ctrl = 0;137 138 dsi_device = intel_dsi->dsi_hosts[port]->device;139 140 mipi_dsi_dcs_read(dsi_device, MIPI_DCS_GET_CONTROL_DISPLAY,141 &ctrl, sizeof(ctrl));142 143 ctrl |= CONTROL_DISPLAY_BL;144 ctrl |= CONTROL_DISPLAY_DD;145 ctrl |= CONTROL_DISPLAY_BCTRL;146 147 mipi_dsi_dcs_write(dsi_device, MIPI_DCS_WRITE_CONTROL_DISPLAY,148 &ctrl, sizeof(ctrl));149 }150 151 for_each_dsi_port(port, panel->vbt.dsi.cabc_ports) {152 u8 cabc = POWER_SAVE_MEDIUM;153 154 dsi_device = intel_dsi->dsi_hosts[port]->device;155 mipi_dsi_dcs_write(dsi_device, MIPI_DCS_WRITE_POWER_SAVE,156 &cabc, sizeof(cabc));157 }158 159 dcs_set_backlight(conn_state, level);160}161 162static int dcs_setup_backlight(struct intel_connector *connector,163 enum pipe unused)164{165 struct drm_i915_private *i915 = to_i915(connector->base.dev);166 struct intel_panel *panel = &connector->panel;167 168 if (panel->vbt.backlight.brightness_precision_bits > 8)169 panel->backlight.max = (1 << panel->vbt.backlight.brightness_precision_bits) - 1;170 else171 panel->backlight.max = PANEL_PWM_MAX_VALUE;172 173 panel->backlight.level = panel->backlight.max;174 175 drm_dbg_kms(&i915->drm,176 "[CONNECTOR:%d:%s] Using DCS for backlight control\n",177 connector->base.base.id, connector->base.name);178 179 return 0;180}181 182static const struct intel_panel_bl_funcs dcs_bl_funcs = {183 .setup = dcs_setup_backlight,184 .enable = dcs_enable_backlight,185 .disable = dcs_disable_backlight,186 .set = dcs_set_backlight,187 .get = dcs_get_backlight,188};189 190int intel_dsi_dcs_init_backlight_funcs(struct intel_connector *intel_connector)191{192 struct drm_device *dev = intel_connector->base.dev;193 struct intel_encoder *encoder = intel_attached_encoder(intel_connector);194 struct intel_panel *panel = &intel_connector->panel;195 196 if (panel->vbt.backlight.type != INTEL_BACKLIGHT_DSI_DCS)197 return -ENODEV;198 199 if (drm_WARN_ON(dev, encoder->type != INTEL_OUTPUT_DSI))200 return -EINVAL;201 202 panel->backlight.funcs = &dcs_bl_funcs;203 204 return 0;205}206