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