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