Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/gpu/drm/i915/intel_panel.c
15113 views
1
/*
2
* Copyright © 2006-2010 Intel Corporation
3
* Copyright (c) 2006 Dave Airlie <[email protected]>
4
*
5
* Permission is hereby granted, free of charge, to any person obtaining a
6
* copy of this software and associated documentation files (the "Software"),
7
* to deal in the Software without restriction, including without limitation
8
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
9
* and/or sell copies of the Software, and to permit persons to whom the
10
* Software is furnished to do so, subject to the following conditions:
11
*
12
* The above copyright notice and this permission notice (including the next
13
* paragraph) shall be included in all copies or substantial portions of the
14
* Software.
15
*
16
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22
* DEALINGS IN THE SOFTWARE.
23
*
24
* Authors:
25
* Eric Anholt <[email protected]>
26
* Dave Airlie <[email protected]>
27
* Jesse Barnes <[email protected]>
28
* Chris Wilson <[email protected]>
29
*/
30
31
#include "intel_drv.h"
32
33
#define PCI_LBPC 0xf4 /* legacy/combination backlight modes */
34
35
void
36
intel_fixed_panel_mode(struct drm_display_mode *fixed_mode,
37
struct drm_display_mode *adjusted_mode)
38
{
39
adjusted_mode->hdisplay = fixed_mode->hdisplay;
40
adjusted_mode->hsync_start = fixed_mode->hsync_start;
41
adjusted_mode->hsync_end = fixed_mode->hsync_end;
42
adjusted_mode->htotal = fixed_mode->htotal;
43
44
adjusted_mode->vdisplay = fixed_mode->vdisplay;
45
adjusted_mode->vsync_start = fixed_mode->vsync_start;
46
adjusted_mode->vsync_end = fixed_mode->vsync_end;
47
adjusted_mode->vtotal = fixed_mode->vtotal;
48
49
adjusted_mode->clock = fixed_mode->clock;
50
51
drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V);
52
}
53
54
/* adjusted_mode has been preset to be the panel's fixed mode */
55
void
56
intel_pch_panel_fitting(struct drm_device *dev,
57
int fitting_mode,
58
struct drm_display_mode *mode,
59
struct drm_display_mode *adjusted_mode)
60
{
61
struct drm_i915_private *dev_priv = dev->dev_private;
62
int x, y, width, height;
63
64
x = y = width = height = 0;
65
66
/* Native modes don't need fitting */
67
if (adjusted_mode->hdisplay == mode->hdisplay &&
68
adjusted_mode->vdisplay == mode->vdisplay)
69
goto done;
70
71
switch (fitting_mode) {
72
case DRM_MODE_SCALE_CENTER:
73
width = mode->hdisplay;
74
height = mode->vdisplay;
75
x = (adjusted_mode->hdisplay - width + 1)/2;
76
y = (adjusted_mode->vdisplay - height + 1)/2;
77
break;
78
79
case DRM_MODE_SCALE_ASPECT:
80
/* Scale but preserve the aspect ratio */
81
{
82
u32 scaled_width = adjusted_mode->hdisplay * mode->vdisplay;
83
u32 scaled_height = mode->hdisplay * adjusted_mode->vdisplay;
84
if (scaled_width > scaled_height) { /* pillar */
85
width = scaled_height / mode->vdisplay;
86
x = (adjusted_mode->hdisplay - width + 1) / 2;
87
y = 0;
88
height = adjusted_mode->vdisplay;
89
} else if (scaled_width < scaled_height) { /* letter */
90
height = scaled_width / mode->hdisplay;
91
y = (adjusted_mode->vdisplay - height + 1) / 2;
92
x = 0;
93
width = adjusted_mode->hdisplay;
94
} else {
95
x = y = 0;
96
width = adjusted_mode->hdisplay;
97
height = adjusted_mode->vdisplay;
98
}
99
}
100
break;
101
102
default:
103
case DRM_MODE_SCALE_FULLSCREEN:
104
x = y = 0;
105
width = adjusted_mode->hdisplay;
106
height = adjusted_mode->vdisplay;
107
break;
108
}
109
110
done:
111
dev_priv->pch_pf_pos = (x << 16) | y;
112
dev_priv->pch_pf_size = (width << 16) | height;
113
}
114
115
static int is_backlight_combination_mode(struct drm_device *dev)
116
{
117
struct drm_i915_private *dev_priv = dev->dev_private;
118
119
if (INTEL_INFO(dev)->gen >= 4)
120
return I915_READ(BLC_PWM_CTL2) & BLM_COMBINATION_MODE;
121
122
if (IS_GEN2(dev))
123
return I915_READ(BLC_PWM_CTL) & BLM_LEGACY_MODE;
124
125
return 0;
126
}
127
128
static u32 i915_read_blc_pwm_ctl(struct drm_i915_private *dev_priv)
129
{
130
u32 val;
131
132
/* Restore the CTL value if it lost, e.g. GPU reset */
133
134
if (HAS_PCH_SPLIT(dev_priv->dev)) {
135
val = I915_READ(BLC_PWM_PCH_CTL2);
136
if (dev_priv->saveBLC_PWM_CTL2 == 0) {
137
dev_priv->saveBLC_PWM_CTL2 = val;
138
} else if (val == 0) {
139
I915_WRITE(BLC_PWM_PCH_CTL2,
140
dev_priv->saveBLC_PWM_CTL);
141
val = dev_priv->saveBLC_PWM_CTL;
142
}
143
} else {
144
val = I915_READ(BLC_PWM_CTL);
145
if (dev_priv->saveBLC_PWM_CTL == 0) {
146
dev_priv->saveBLC_PWM_CTL = val;
147
dev_priv->saveBLC_PWM_CTL2 = I915_READ(BLC_PWM_CTL2);
148
} else if (val == 0) {
149
I915_WRITE(BLC_PWM_CTL,
150
dev_priv->saveBLC_PWM_CTL);
151
I915_WRITE(BLC_PWM_CTL2,
152
dev_priv->saveBLC_PWM_CTL2);
153
val = dev_priv->saveBLC_PWM_CTL;
154
}
155
}
156
157
return val;
158
}
159
160
u32 intel_panel_get_max_backlight(struct drm_device *dev)
161
{
162
struct drm_i915_private *dev_priv = dev->dev_private;
163
u32 max;
164
165
max = i915_read_blc_pwm_ctl(dev_priv);
166
if (max == 0) {
167
/* XXX add code here to query mode clock or hardware clock
168
* and program max PWM appropriately.
169
*/
170
printk_once(KERN_WARNING "fixme: max PWM is zero.\n");
171
return 1;
172
}
173
174
if (HAS_PCH_SPLIT(dev)) {
175
max >>= 16;
176
} else {
177
if (IS_PINEVIEW(dev)) {
178
max >>= 17;
179
} else {
180
max >>= 16;
181
if (INTEL_INFO(dev)->gen < 4)
182
max &= ~1;
183
}
184
185
if (is_backlight_combination_mode(dev))
186
max *= 0xff;
187
}
188
189
DRM_DEBUG_DRIVER("max backlight PWM = %d\n", max);
190
return max;
191
}
192
193
u32 intel_panel_get_backlight(struct drm_device *dev)
194
{
195
struct drm_i915_private *dev_priv = dev->dev_private;
196
u32 val;
197
198
if (HAS_PCH_SPLIT(dev)) {
199
val = I915_READ(BLC_PWM_CPU_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
200
} else {
201
val = I915_READ(BLC_PWM_CTL) & BACKLIGHT_DUTY_CYCLE_MASK;
202
if (IS_PINEVIEW(dev))
203
val >>= 1;
204
205
if (is_backlight_combination_mode(dev)){
206
u8 lbpc;
207
208
val &= ~1;
209
pci_read_config_byte(dev->pdev, PCI_LBPC, &lbpc);
210
val *= lbpc;
211
}
212
}
213
214
DRM_DEBUG_DRIVER("get backlight PWM = %d\n", val);
215
return val;
216
}
217
218
static void intel_pch_panel_set_backlight(struct drm_device *dev, u32 level)
219
{
220
struct drm_i915_private *dev_priv = dev->dev_private;
221
u32 val = I915_READ(BLC_PWM_CPU_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK;
222
I915_WRITE(BLC_PWM_CPU_CTL, val | level);
223
}
224
225
void intel_panel_set_backlight(struct drm_device *dev, u32 level)
226
{
227
struct drm_i915_private *dev_priv = dev->dev_private;
228
u32 tmp;
229
230
DRM_DEBUG_DRIVER("set backlight PWM = %d\n", level);
231
232
if (HAS_PCH_SPLIT(dev))
233
return intel_pch_panel_set_backlight(dev, level);
234
235
if (is_backlight_combination_mode(dev)){
236
u32 max = intel_panel_get_max_backlight(dev);
237
u8 lbpc;
238
239
lbpc = level * 0xfe / max + 1;
240
level /= lbpc;
241
pci_write_config_byte(dev->pdev, PCI_LBPC, lbpc);
242
}
243
244
tmp = I915_READ(BLC_PWM_CTL);
245
if (IS_PINEVIEW(dev)) {
246
tmp &= ~(BACKLIGHT_DUTY_CYCLE_MASK - 1);
247
level <<= 1;
248
} else
249
tmp &= ~BACKLIGHT_DUTY_CYCLE_MASK;
250
I915_WRITE(BLC_PWM_CTL, tmp | level);
251
}
252
253
void intel_panel_disable_backlight(struct drm_device *dev)
254
{
255
struct drm_i915_private *dev_priv = dev->dev_private;
256
257
if (dev_priv->backlight_enabled) {
258
dev_priv->backlight_level = intel_panel_get_backlight(dev);
259
dev_priv->backlight_enabled = false;
260
}
261
262
intel_panel_set_backlight(dev, 0);
263
}
264
265
void intel_panel_enable_backlight(struct drm_device *dev)
266
{
267
struct drm_i915_private *dev_priv = dev->dev_private;
268
269
if (dev_priv->backlight_level == 0)
270
dev_priv->backlight_level = intel_panel_get_max_backlight(dev);
271
272
intel_panel_set_backlight(dev, dev_priv->backlight_level);
273
dev_priv->backlight_enabled = true;
274
}
275
276
void intel_panel_setup_backlight(struct drm_device *dev)
277
{
278
struct drm_i915_private *dev_priv = dev->dev_private;
279
280
dev_priv->backlight_level = intel_panel_get_backlight(dev);
281
dev_priv->backlight_enabled = dev_priv->backlight_level != 0;
282
}
283
284
enum drm_connector_status
285
intel_panel_detect(struct drm_device *dev)
286
{
287
#if 0
288
struct drm_i915_private *dev_priv = dev->dev_private;
289
#endif
290
291
if (i915_panel_ignore_lid)
292
return i915_panel_ignore_lid > 0 ?
293
connector_status_connected :
294
connector_status_disconnected;
295
296
/* opregion lid state on HP 2540p is wrong at boot up,
297
* appears to be either the BIOS or Linux ACPI fault */
298
#if 0
299
/* Assume that the BIOS does not lie through the OpRegion... */
300
if (dev_priv->opregion.lid_state)
301
return ioread32(dev_priv->opregion.lid_state) & 0x1 ?
302
connector_status_connected :
303
connector_status_disconnected;
304
#endif
305
306
return connector_status_unknown;
307
}
308
309