Merge tag 'drm-intel-next-2019-05-24' of git://anongit.freedesktop.org/drm/drm-intel...
[linux-2.6-microblaze.git] / drivers / gpu / drm / i915 / intel_dp_aux_backlight.c
1 /*
2  * Copyright © 2015 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 DEALINGS
21  * IN THE SOFTWARE.
22  *
23  */
24
25 #include "intel_dp_aux_backlight.h"
26 #include "intel_drv.h"
27
28 static void set_aux_backlight_enable(struct intel_dp *intel_dp, bool enable)
29 {
30         u8 reg_val = 0;
31
32         /* Early return when display use other mechanism to enable backlight. */
33         if (!(intel_dp->edp_dpcd[1] & DP_EDP_BACKLIGHT_AUX_ENABLE_CAP))
34                 return;
35
36         if (drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER,
37                               &reg_val) < 0) {
38                 DRM_DEBUG_KMS("Failed to read DPCD register 0x%x\n",
39                               DP_EDP_DISPLAY_CONTROL_REGISTER);
40                 return;
41         }
42         if (enable)
43                 reg_val |= DP_EDP_BACKLIGHT_ENABLE;
44         else
45                 reg_val &= ~(DP_EDP_BACKLIGHT_ENABLE);
46
47         if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_EDP_DISPLAY_CONTROL_REGISTER,
48                                reg_val) != 1) {
49                 DRM_DEBUG_KMS("Failed to %s aux backlight\n",
50                               enable ? "enable" : "disable");
51         }
52 }
53
54 /*
55  * Read the current backlight value from DPCD register(s) based
56  * on if 8-bit(MSB) or 16-bit(MSB and LSB) values are supported
57  */
58 static u32 intel_dp_aux_get_backlight(struct intel_connector *connector)
59 {
60         struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
61         u8 read_val[2] = { 0x0 };
62         u16 level = 0;
63
64         if (drm_dp_dpcd_read(&intel_dp->aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB,
65                              &read_val, sizeof(read_val)) < 0) {
66                 DRM_DEBUG_KMS("Failed to read DPCD register 0x%x\n",
67                               DP_EDP_BACKLIGHT_BRIGHTNESS_MSB);
68                 return 0;
69         }
70         level = read_val[0];
71         if (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT)
72                 level = (read_val[0] << 8 | read_val[1]);
73
74         return level;
75 }
76
77 /*
78  * Sends the current backlight level over the aux channel, checking if its using
79  * 8-bit or 16 bit value (MSB and LSB)
80  */
81 static void
82 intel_dp_aux_set_backlight(const struct drm_connector_state *conn_state, u32 level)
83 {
84         struct intel_connector *connector = to_intel_connector(conn_state->connector);
85         struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
86         u8 vals[2] = { 0x0 };
87
88         vals[0] = level;
89
90         /* Write the MSB and/or LSB */
91         if (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT) {
92                 vals[0] = (level & 0xFF00) >> 8;
93                 vals[1] = (level & 0xFF);
94         }
95         if (drm_dp_dpcd_write(&intel_dp->aux, DP_EDP_BACKLIGHT_BRIGHTNESS_MSB,
96                               vals, sizeof(vals)) < 0) {
97                 DRM_DEBUG_KMS("Failed to write aux backlight level\n");
98                 return;
99         }
100 }
101
102 /*
103  * Set PWM Frequency divider to match desired frequency in vbt.
104  * The PWM Frequency is calculated as 27Mhz / (F x P).
105  * - Where F = PWM Frequency Pre-Divider value programmed by field 7:0 of the
106  *             EDP_BACKLIGHT_FREQ_SET register (DPCD Address 00728h)
107  * - Where P = 2^Pn, where Pn is the value programmed by field 4:0 of the
108  *             EDP_PWMGEN_BIT_COUNT register (DPCD Address 00724h)
109  */
110 static bool intel_dp_aux_set_pwm_freq(struct intel_connector *connector)
111 {
112         struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
113         struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
114         int freq, fxp, fxp_min, fxp_max, fxp_actual, f = 1;
115         u8 pn, pn_min, pn_max;
116
117         /* Find desired value of (F x P)
118          * Note that, if F x P is out of supported range, the maximum value or
119          * minimum value will applied automatically. So no need to check that.
120          */
121         freq = dev_priv->vbt.backlight.pwm_freq_hz;
122         DRM_DEBUG_KMS("VBT defined backlight frequency %u Hz\n", freq);
123         if (!freq) {
124                 DRM_DEBUG_KMS("Use panel default backlight frequency\n");
125                 return false;
126         }
127
128         fxp = DIV_ROUND_CLOSEST(KHz(DP_EDP_BACKLIGHT_FREQ_BASE_KHZ), freq);
129
130         /* Use highest possible value of Pn for more granularity of brightness
131          * adjustment while satifying the conditions below.
132          * - Pn is in the range of Pn_min and Pn_max
133          * - F is in the range of 1 and 255
134          * - FxP is within 25% of desired value.
135          *   Note: 25% is arbitrary value and may need some tweak.
136          */
137         if (drm_dp_dpcd_readb(&intel_dp->aux,
138                                DP_EDP_PWMGEN_BIT_COUNT_CAP_MIN, &pn_min) != 1) {
139                 DRM_DEBUG_KMS("Failed to read pwmgen bit count cap min\n");
140                 return false;
141         }
142         if (drm_dp_dpcd_readb(&intel_dp->aux,
143                                DP_EDP_PWMGEN_BIT_COUNT_CAP_MAX, &pn_max) != 1) {
144                 DRM_DEBUG_KMS("Failed to read pwmgen bit count cap max\n");
145                 return false;
146         }
147         pn_min &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
148         pn_max &= DP_EDP_PWMGEN_BIT_COUNT_MASK;
149
150         fxp_min = DIV_ROUND_CLOSEST(fxp * 3, 4);
151         fxp_max = DIV_ROUND_CLOSEST(fxp * 5, 4);
152         if (fxp_min < (1 << pn_min) || (255 << pn_max) < fxp_max) {
153                 DRM_DEBUG_KMS("VBT defined backlight frequency out of range\n");
154                 return false;
155         }
156
157         for (pn = pn_max; pn >= pn_min; pn--) {
158                 f = clamp(DIV_ROUND_CLOSEST(fxp, 1 << pn), 1, 255);
159                 fxp_actual = f << pn;
160                 if (fxp_min <= fxp_actual && fxp_actual <= fxp_max)
161                         break;
162         }
163
164         if (drm_dp_dpcd_writeb(&intel_dp->aux,
165                                DP_EDP_PWMGEN_BIT_COUNT, pn) < 0) {
166                 DRM_DEBUG_KMS("Failed to write aux pwmgen bit count\n");
167                 return false;
168         }
169         if (drm_dp_dpcd_writeb(&intel_dp->aux,
170                                DP_EDP_BACKLIGHT_FREQ_SET, (u8) f) < 0) {
171                 DRM_DEBUG_KMS("Failed to write aux backlight freq\n");
172                 return false;
173         }
174         return true;
175 }
176
177 static void intel_dp_aux_enable_backlight(const struct intel_crtc_state *crtc_state,
178                                           const struct drm_connector_state *conn_state)
179 {
180         struct intel_connector *connector = to_intel_connector(conn_state->connector);
181         struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
182         u8 dpcd_buf, new_dpcd_buf, edp_backlight_mode;
183
184         if (drm_dp_dpcd_readb(&intel_dp->aux,
185                         DP_EDP_BACKLIGHT_MODE_SET_REGISTER, &dpcd_buf) != 1) {
186                 DRM_DEBUG_KMS("Failed to read DPCD register 0x%x\n",
187                               DP_EDP_BACKLIGHT_MODE_SET_REGISTER);
188                 return;
189         }
190
191         new_dpcd_buf = dpcd_buf;
192         edp_backlight_mode = dpcd_buf & DP_EDP_BACKLIGHT_CONTROL_MODE_MASK;
193
194         switch (edp_backlight_mode) {
195         case DP_EDP_BACKLIGHT_CONTROL_MODE_PWM:
196         case DP_EDP_BACKLIGHT_CONTROL_MODE_PRESET:
197         case DP_EDP_BACKLIGHT_CONTROL_MODE_PRODUCT:
198                 new_dpcd_buf &= ~DP_EDP_BACKLIGHT_CONTROL_MODE_MASK;
199                 new_dpcd_buf |= DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD;
200                 break;
201
202         /* Do nothing when it is already DPCD mode */
203         case DP_EDP_BACKLIGHT_CONTROL_MODE_DPCD:
204         default:
205                 break;
206         }
207
208         if (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_FREQ_AUX_SET_CAP)
209                 if (intel_dp_aux_set_pwm_freq(connector))
210                         new_dpcd_buf |= DP_EDP_BACKLIGHT_FREQ_AUX_SET_ENABLE;
211
212         if (new_dpcd_buf != dpcd_buf) {
213                 if (drm_dp_dpcd_writeb(&intel_dp->aux,
214                         DP_EDP_BACKLIGHT_MODE_SET_REGISTER, new_dpcd_buf) < 0) {
215                         DRM_DEBUG_KMS("Failed to write aux backlight mode\n");
216                 }
217         }
218
219         set_aux_backlight_enable(intel_dp, true);
220         intel_dp_aux_set_backlight(conn_state, connector->panel.backlight.level);
221 }
222
223 static void intel_dp_aux_disable_backlight(const struct drm_connector_state *old_conn_state)
224 {
225         set_aux_backlight_enable(enc_to_intel_dp(old_conn_state->best_encoder), false);
226 }
227
228 static int intel_dp_aux_setup_backlight(struct intel_connector *connector,
229                                         enum pipe pipe)
230 {
231         struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
232         struct intel_panel *panel = &connector->panel;
233
234         if (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_BYTE_COUNT)
235                 panel->backlight.max = 0xFFFF;
236         else
237                 panel->backlight.max = 0xFF;
238
239         panel->backlight.min = 0;
240         panel->backlight.level = intel_dp_aux_get_backlight(connector);
241
242         panel->backlight.enabled = panel->backlight.level != 0;
243
244         return 0;
245 }
246
247 static bool
248 intel_dp_aux_display_control_capable(struct intel_connector *connector)
249 {
250         struct intel_dp *intel_dp = enc_to_intel_dp(&connector->encoder->base);
251
252         /* Check the eDP Display control capabilities registers to determine if
253          * the panel can support backlight control over the aux channel
254          */
255         if (intel_dp->edp_dpcd[1] & DP_EDP_TCON_BACKLIGHT_ADJUSTMENT_CAP &&
256             (intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_AUX_SET_CAP) &&
257             !(intel_dp->edp_dpcd[2] & DP_EDP_BACKLIGHT_BRIGHTNESS_PWM_PIN_CAP)) {
258                 DRM_DEBUG_KMS("AUX Backlight Control Supported!\n");
259                 return true;
260         }
261         return false;
262 }
263
264 int intel_dp_aux_init_backlight_funcs(struct intel_connector *intel_connector)
265 {
266         struct intel_panel *panel = &intel_connector->panel;
267
268         if (!i915_modparams.enable_dpcd_backlight)
269                 return -ENODEV;
270
271         if (!intel_dp_aux_display_control_capable(intel_connector))
272                 return -ENODEV;
273
274         panel->backlight.setup = intel_dp_aux_setup_backlight;
275         panel->backlight.enable = intel_dp_aux_enable_backlight;
276         panel->backlight.disable = intel_dp_aux_disable_backlight;
277         panel->backlight.set = intel_dp_aux_set_backlight;
278         panel->backlight.get = intel_dp_aux_get_backlight;
279
280         return 0;
281 }