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; |
||