xref: /linux/drivers/gpu/drm/i915/display/intel_dsi_dcs_backlight.c (revision 25489a4f556414445d342951615178368ee45cde)
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