Subversion Repositories Kolibri OS

Rev

Rev 2342 | Rev 3031 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2330 Serge 1
/*
2
 * Copyright © 2006-2007 Intel Corporation
3
 * Copyright (c) 2006 Dave Airlie 
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 
26
 *      Dave Airlie 
27
 *      Jesse Barnes 
28
 */
29
 
30
//#include 
31
//#include 
32
#include 
33
#include 
34
#include "drmP.h"
35
#include "drm.h"
36
#include "drm_crtc.h"
37
#include "drm_edid.h"
38
#include "intel_drv.h"
39
#include "i915_drm.h"
40
#include "i915_drv.h"
41
//#include 
42
 
43
/* Private structure for the integrated LVDS support */
44
struct intel_lvds {
45
	struct intel_encoder base;
46
 
47
	struct edid *edid;
48
 
49
	int fitting_mode;
50
	u32 pfit_control;
51
	u32 pfit_pgm_ratios;
52
	bool pfit_dirty;
53
 
54
	struct drm_display_mode *fixed_mode;
55
};
56
 
57
static struct intel_lvds *to_intel_lvds(struct drm_encoder *encoder)
58
{
59
	return container_of(encoder, struct intel_lvds, base.base);
60
}
61
 
62
static struct intel_lvds *intel_attached_lvds(struct drm_connector *connector)
63
{
64
	return container_of(intel_attached_encoder(connector),
65
			    struct intel_lvds, base);
66
}
67
 
68
/**
69
 * Sets the power state for the panel.
70
 */
71
static void intel_lvds_enable(struct intel_lvds *intel_lvds)
72
{
73
	struct drm_device *dev = intel_lvds->base.base.dev;
74
	struct drm_i915_private *dev_priv = dev->dev_private;
75
	u32 ctl_reg, lvds_reg, stat_reg;
76
 
77
	if (HAS_PCH_SPLIT(dev)) {
78
		ctl_reg = PCH_PP_CONTROL;
79
		lvds_reg = PCH_LVDS;
80
		stat_reg = PCH_PP_STATUS;
81
	} else {
82
		ctl_reg = PP_CONTROL;
83
		lvds_reg = LVDS;
84
		stat_reg = PP_STATUS;
85
	}
86
 
87
	I915_WRITE(lvds_reg, I915_READ(lvds_reg) | LVDS_PORT_EN);
88
 
89
	if (intel_lvds->pfit_dirty) {
90
		/*
91
		 * Enable automatic panel scaling so that non-native modes
92
		 * fill the screen.  The panel fitter should only be
93
		 * adjusted whilst the pipe is disabled, according to
94
		 * register description and PRM.
95
		 */
96
		DRM_DEBUG_KMS("applying panel-fitter: %x, %x\n",
97
			      intel_lvds->pfit_control,
98
			      intel_lvds->pfit_pgm_ratios);
99
 
100
		I915_WRITE(PFIT_PGM_RATIOS, intel_lvds->pfit_pgm_ratios);
101
		I915_WRITE(PFIT_CONTROL, intel_lvds->pfit_control);
102
		intel_lvds->pfit_dirty = false;
103
	}
104
 
105
	I915_WRITE(ctl_reg, I915_READ(ctl_reg) | POWER_TARGET_ON);
106
	POSTING_READ(lvds_reg);
107
	if (wait_for((I915_READ(stat_reg) & PP_ON) != 0, 1000))
108
		DRM_ERROR("timed out waiting for panel to power on\n");
109
 
110
	intel_panel_enable_backlight(dev);
111
}
112
 
113
static void intel_lvds_disable(struct intel_lvds *intel_lvds)
114
{
115
	struct drm_device *dev = intel_lvds->base.base.dev;
116
	struct drm_i915_private *dev_priv = dev->dev_private;
117
	u32 ctl_reg, lvds_reg, stat_reg;
118
 
119
	if (HAS_PCH_SPLIT(dev)) {
120
		ctl_reg = PCH_PP_CONTROL;
121
		lvds_reg = PCH_LVDS;
122
		stat_reg = PCH_PP_STATUS;
123
	} else {
124
		ctl_reg = PP_CONTROL;
125
		lvds_reg = LVDS;
126
		stat_reg = PP_STATUS;
127
	}
128
 
129
	intel_panel_disable_backlight(dev);
130
 
131
	I915_WRITE(ctl_reg, I915_READ(ctl_reg) & ~POWER_TARGET_ON);
132
	if (wait_for((I915_READ(stat_reg) & PP_ON) == 0, 1000))
133
		DRM_ERROR("timed out waiting for panel to power off\n");
134
 
135
	if (intel_lvds->pfit_control) {
136
		I915_WRITE(PFIT_CONTROL, 0);
137
		intel_lvds->pfit_dirty = true;
138
	}
139
 
140
	I915_WRITE(lvds_reg, I915_READ(lvds_reg) & ~LVDS_PORT_EN);
141
	POSTING_READ(lvds_reg);
142
}
143
 
144
static void intel_lvds_dpms(struct drm_encoder *encoder, int mode)
145
{
146
	struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
147
 
148
	if (mode == DRM_MODE_DPMS_ON)
149
		intel_lvds_enable(intel_lvds);
150
	else
151
		intel_lvds_disable(intel_lvds);
152
 
153
	/* XXX: We never power down the LVDS pairs. */
154
}
155
 
156
static int intel_lvds_mode_valid(struct drm_connector *connector,
157
				 struct drm_display_mode *mode)
158
{
159
	struct intel_lvds *intel_lvds = intel_attached_lvds(connector);
160
	struct drm_display_mode *fixed_mode = intel_lvds->fixed_mode;
161
 
162
	if (mode->hdisplay > fixed_mode->hdisplay)
163
		return MODE_PANEL;
164
	if (mode->vdisplay > fixed_mode->vdisplay)
165
		return MODE_PANEL;
166
 
167
	return MODE_OK;
168
}
169
 
170
static void
171
centre_horizontally(struct drm_display_mode *mode,
172
		    int width)
173
{
174
	u32 border, sync_pos, blank_width, sync_width;
175
 
176
	/* keep the hsync and hblank widths constant */
177
	sync_width = mode->crtc_hsync_end - mode->crtc_hsync_start;
178
	blank_width = mode->crtc_hblank_end - mode->crtc_hblank_start;
179
	sync_pos = (blank_width - sync_width + 1) / 2;
180
 
181
	border = (mode->hdisplay - width + 1) / 2;
182
	border += border & 1; /* make the border even */
183
 
184
	mode->crtc_hdisplay = width;
185
	mode->crtc_hblank_start = width + border;
186
	mode->crtc_hblank_end = mode->crtc_hblank_start + blank_width;
187
 
188
	mode->crtc_hsync_start = mode->crtc_hblank_start + sync_pos;
189
	mode->crtc_hsync_end = mode->crtc_hsync_start + sync_width;
190
}
191
 
192
static void
193
centre_vertically(struct drm_display_mode *mode,
194
		  int height)
195
{
196
	u32 border, sync_pos, blank_width, sync_width;
197
 
198
	/* keep the vsync and vblank widths constant */
199
	sync_width = mode->crtc_vsync_end - mode->crtc_vsync_start;
200
	blank_width = mode->crtc_vblank_end - mode->crtc_vblank_start;
201
	sync_pos = (blank_width - sync_width + 1) / 2;
202
 
203
	border = (mode->vdisplay - height + 1) / 2;
204
 
205
	mode->crtc_vdisplay = height;
206
	mode->crtc_vblank_start = height + border;
207
	mode->crtc_vblank_end = mode->crtc_vblank_start + blank_width;
208
 
209
	mode->crtc_vsync_start = mode->crtc_vblank_start + sync_pos;
210
	mode->crtc_vsync_end = mode->crtc_vsync_start + sync_width;
211
}
212
 
213
static inline u32 panel_fitter_scaling(u32 source, u32 target)
214
{
215
	/*
216
	 * Floating point operation is not supported. So the FACTOR
217
	 * is defined, which can avoid the floating point computation
218
	 * when calculating the panel ratio.
219
	 */
220
#define ACCURACY 12
221
#define FACTOR (1 << ACCURACY)
222
	u32 ratio = source * FACTOR / target;
223
	return (FACTOR * ratio + FACTOR/2) / FACTOR;
224
}
225
 
226
static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
227
				  struct drm_display_mode *mode,
228
				  struct drm_display_mode *adjusted_mode)
229
{
230
	struct drm_device *dev = encoder->dev;
231
	struct drm_i915_private *dev_priv = dev->dev_private;
232
	struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
233
	struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
234
	struct drm_encoder *tmp_encoder;
235
	u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0;
236
	int pipe;
237
 
238
	/* Should never happen!! */
239
	if (INTEL_INFO(dev)->gen < 4 && intel_crtc->pipe == 0) {
240
		DRM_ERROR("Can't support LVDS on pipe A\n");
241
		return false;
242
	}
243
 
244
	/* Should never happen!! */
245
	list_for_each_entry(tmp_encoder, &dev->mode_config.encoder_list, head) {
246
		if (tmp_encoder != encoder && tmp_encoder->crtc == encoder->crtc) {
247
			DRM_ERROR("Can't enable LVDS and another "
248
			       "encoder on the same pipe\n");
249
			return false;
250
		}
251
	}
252
 
253
	/*
254
	 * We have timings from the BIOS for the panel, put them in
255
	 * to the adjusted mode.  The CRTC will be set up for this mode,
256
	 * with the panel scaling set up to source from the H/VDisplay
257
	 * of the original mode.
258
	 */
259
	intel_fixed_panel_mode(intel_lvds->fixed_mode, adjusted_mode);
260
 
261
	if (HAS_PCH_SPLIT(dev)) {
262
		intel_pch_panel_fitting(dev, intel_lvds->fitting_mode,
263
					mode, adjusted_mode);
264
		return true;
265
	}
266
 
267
	/* Native modes don't need fitting */