Subversion Repositories Kolibri OS

Rev

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

Rev Author Line No. Line
2351 Serge 1
/*
2
 * Copyright © 2006 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 FROM,
20
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
 * SOFTWARE.
22
 *
23
 * Authors:
24
 *    Eric Anholt 
25
 *
26
 */
27
#include 
3031 serge 28
#include 
29
#include 
2351 Serge 30
#include "i915_drv.h"
31
#include "intel_bios.h"
32
 
33
#define	SLAVE_ADDR1	0x70
34
#define	SLAVE_ADDR2	0x72
35
 
36
static int panel_type;
37
 
38
static void *
39
find_section(struct bdb_header *bdb, int section_id)
40
{
41
	u8 *base = (u8 *)bdb;
42
	int index = 0;
43
	u16 total, current_size;
44
	u8 current_id;
45
 
46
	/* skip to first section */
47
	index += bdb->header_size;
48
	total = bdb->bdb_size;
49
 
50
	/* walk the sections looking for section_id */
51
	while (index < total) {
52
		current_id = *(base + index);
53
		index++;
54
		current_size = *((u16 *)(base + index));
55
		index += 2;
56
		if (current_id == section_id)
57
			return base + index;
58
		index += current_size;
59
	}
60
 
61
	return NULL;
62
}
63
 
64
static u16
65
get_blocksize(void *p)
66
{
67
	u16 *block_ptr, block_size;
68
 
69
	block_ptr = (u16 *)((char *)p - 2);
70
	block_size = *block_ptr;
71
	return block_size;
72
}
73
 
74
static void
75
fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode,
76
			const struct lvds_dvo_timing *dvo_timing)
77
{
78
	panel_fixed_mode->hdisplay = (dvo_timing->hactive_hi << 8) |
79
		dvo_timing->hactive_lo;
80
	panel_fixed_mode->hsync_start = panel_fixed_mode->hdisplay +
81
		((dvo_timing->hsync_off_hi << 8) | dvo_timing->hsync_off_lo);
82
	panel_fixed_mode->hsync_end = panel_fixed_mode->hsync_start +
83
		dvo_timing->hsync_pulse_width;
84
	panel_fixed_mode->htotal = panel_fixed_mode->hdisplay +
85
		((dvo_timing->hblank_hi << 8) | dvo_timing->hblank_lo);
86
 
87
	panel_fixed_mode->vdisplay = (dvo_timing->vactive_hi << 8) |
88
		dvo_timing->vactive_lo;
89
	panel_fixed_mode->vsync_start = panel_fixed_mode->vdisplay +
90
		dvo_timing->vsync_off;
91
	panel_fixed_mode->vsync_end = panel_fixed_mode->vsync_start +
92
		dvo_timing->vsync_pulse_width;
93
	panel_fixed_mode->vtotal = panel_fixed_mode->vdisplay +
94
		((dvo_timing->vblank_hi << 8) | dvo_timing->vblank_lo);
95
	panel_fixed_mode->clock = dvo_timing->clock * 10;
96
	panel_fixed_mode->type = DRM_MODE_TYPE_PREFERRED;
97
 
98
	if (dvo_timing->hsync_positive)
99
		panel_fixed_mode->flags |= DRM_MODE_FLAG_PHSYNC;
100
	else
101
		panel_fixed_mode->flags |= DRM_MODE_FLAG_NHSYNC;
102
 
103
	if (dvo_timing->vsync_positive)
104
		panel_fixed_mode->flags |= DRM_MODE_FLAG_PVSYNC;
105
	else
106
		panel_fixed_mode->flags |= DRM_MODE_FLAG_NVSYNC;
107
 
108
	/* Some VBTs have bogus h/vtotal values */
109
	if (panel_fixed_mode->hsync_end > panel_fixed_mode->htotal)
110
		panel_fixed_mode->htotal = panel_fixed_mode->hsync_end + 1;
111
	if (panel_fixed_mode->vsync_end > panel_fixed_mode->vtotal)
112
		panel_fixed_mode->vtotal = panel_fixed_mode->vsync_end + 1;
113
 
114
	drm_mode_set_name(panel_fixed_mode);
115
}
116
 
117
static bool
118
lvds_dvo_timing_equal_size(const struct lvds_dvo_timing *a,
119
			   const struct lvds_dvo_timing *b)
120
{
121
	if (a->hactive_hi != b->hactive_hi ||
122
	    a->hactive_lo != b->hactive_lo)
123
		return false;
124
 
125
	if (a->hsync_off_hi != b->hsync_off_hi ||
126
	    a->hsync_off_lo != b->hsync_off_lo)
127
		return false;
128
 
129
	if (a->hsync_pulse_width != b->hsync_pulse_width)
130
		return false;
131
 
132
	if (a->hblank_hi != b->hblank_hi ||
133
	    a->hblank_lo != b->hblank_lo)
134
		return false;
135
 
136
	if (a->vactive_hi != b->vactive_hi ||
137
	    a->vactive_lo != b->vactive_lo)
138
		return false;
139
 
140
	if (a->vsync_off != b->vsync_off)
141
		return false;
142
 
143
	if (a->vsync_pulse_width != b->vsync_pulse_width)
144
		return false;
145
 
146
	if (a->vblank_hi != b->vblank_hi ||
147
	    a->vblank_lo != b->vblank_lo)
148
		return false;
149
 
150
	return true;
151
}
152
 
153
static const struct lvds_dvo_timing *
154
get_lvds_dvo_timing(const struct bdb_lvds_lfp_data *lvds_lfp_data,
155
		    const struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs,
156
		    int index)
157
{
158
	/*
159
	 * the size of fp_timing varies on the different platform.
160
	 * So calculate the DVO timing relative offset in LVDS data
161
	 * entry to get the DVO timing entry
162
	 */
163
 
164
	int lfp_data_size =
165
		lvds_lfp_data_ptrs->ptr[1].dvo_timing_offset -
166
		lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset;
167
	int dvo_timing_offset =
168
		lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset -
169
		lvds_lfp_data_ptrs->ptr[0].fp_timing_offset;
170
	char *entry = (char *)lvds_lfp_data->data + lfp_data_size * index;
171
 
172
	return (struct lvds_dvo_timing *)(entry + dvo_timing_offset);
173
}
174
 
3031 serge 175
/* get lvds_fp_timing entry
176
 * this function may return NULL if the corresponding entry is invalid
177
 */
178
static const struct lvds_fp_timing *
179
get_lvds_fp_timing(const struct bdb_header *bdb,
180
		   const struct bdb_lvds_lfp_data *data,
181
		   const struct bdb_lvds_lfp_data_ptrs *ptrs,
182
		   int index)
183
{
184
	size_t data_ofs = (const u8 *)data - (const u8 *)bdb;
185
	u16 data_size = ((const u16 *)data)[-1]; /* stored in header */
186
	size_t ofs;
187
 
188
	if (index >= ARRAY_SIZE(ptrs->ptr))
189
		return NULL;
190
	ofs = ptrs->ptr[index].fp_timing_offset;
191
	if (ofs < data_ofs ||
192
	    ofs + sizeof(struct lvds_fp_timing) > data_ofs + data_size)
193
		return NULL;
194
	return (const struct lvds_fp_timing *)((const u8 *)bdb + ofs);
195
}
196
 
2351 Serge 197
/* Try to find integrated panel data */
198
static void
199
parse_lfp_panel_data(struct drm_i915_private *dev_priv,
200
			    struct bdb_header *bdb)
201
{
202
	const struct bdb_lvds_options *lvds_options;
203
	const struct bdb_lvds_lfp_data *lvds_lfp_data;
204
	const struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs;
205
	const struct lvds_dvo_timing *panel_dvo_timing;
3031 serge 206
	const struct lvds_fp_timing *fp_timing;
2351 Serge 207
	struct drm_display_mode *panel_fixed_mode;
208
	int i, downclock;
209
 
210
	lvds_options = find_section(bdb, BDB_LVDS_OPTIONS);
211
	if (!lvds_options)
212
		return;
213
 
214
	dev_priv->lvds_dither = lvds_options->pixel_dither;
215
	if (lvds_options->panel_type == 0xff)
216
		return;
217
 
218
	panel_type = lvds_options->panel_type;
219
 
220
	lvds_lfp_data = find_section(bdb, BDB_LVDS_LFP_DATA);
221
	if (!lvds_lfp_data)
222
		return;
223
 
224
	lvds_lfp_data_ptrs = find_section(bdb, BDB_LVDS_LFP_DATA_PTRS);
225
	if (!lvds_lfp_data_ptrs)
226
		return;
227
 
228
	dev_priv->lvds_vbt = 1;
229
 
230
	panel_dvo_timing = get_lvds_dvo_timing(lvds_lfp_data,
231
					       lvds_lfp_data_ptrs,
232
					       lvds_options->panel_type);
233
 
234
	panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), GFP_KERNEL);
235
	if (!panel_fixed_mode)
236
		return;
237
 
238
	fill_detail_timing_data(panel_fixed_mode, panel_dvo_timing);
239
 
240
	dev_priv->lfp_lvds_vbt_mode = panel_fixed_mode;
241
 
242
	DRM_DEBUG_KMS("Found panel mode in BIOS VBT tables:\n");
243
	drm_mode_debug_printmodeline(panel_fixed_mode);
244
 
245
	/*
246
	 * Iterate over the LVDS panel timing info to find the lowest clock
247
	 * for the native resolution.
248
	 */
249
	downclock = panel_dvo_timing->clock;
250
	for (i = 0; i < 16; i++) {
251
		const struct lvds_dvo_timing *dvo_timing;
252
 
253
		dvo_timing = get_lvds_dvo_timing(lvds_lfp_data,
254
						 lvds_lfp_data_ptrs,
255
						 i);
256
		if (lvds_dvo_timing_equal_size(dvo_timing, panel_dvo_timing) &&
257
		    dvo_timing->clock < downclock)
258
			downclock = dvo_timing->clock;
259
	}
260
 
261
	if (downclock < panel_dvo_timing->clock && i915_lvds_downclock) {
262
		dev_priv->lvds_downclock_avail = 1;