]>
Commit | Line | Data |
---|---|---|
eeb1b77b WD |
1 | /* |
2 | * (C) Copyright 2004 | |
3 | * Pierre Aubert, Staubli Faverges , <[email protected]> | |
a5dbdc81 | 4 | * Copyright 2011 Freescale Semiconductor, Inc. |
eeb1b77b | 5 | * |
3765b3e7 | 6 | * SPDX-License-Identifier: GPL-2.0+ |
eeb1b77b WD |
7 | */ |
8 | ||
9 | /************************************************************************ | |
10 | Get Parameters for the video mode: | |
6d0f6bcf | 11 | The default video mode can be defined in CONFIG_SYS_DEFAULT_VIDEO_MODE. |
eeb1b77b WD |
12 | If undefined, default video mode is set to 0x301 |
13 | Parameters can be set via the variable "videomode" in the environment. | |
14 | 2 diferent ways are possible: | |
15 | "videomode=301" - 301 is a hexadecimal number describing the VESA | |
16 | mode. Following modes are implemented: | |
17 | ||
18 | Colors 640x480 800x600 1024x768 1152x864 1280x1024 | |
19 | --------+--------------------------------------------- | |
20 | 8 bits | 0x301 0x303 0x305 0x161 0x307 | |
21 | 15 bits | 0x310 0x313 0x316 0x162 0x319 | |
22 | 16 bits | 0x311 0x314 0x317 0x163 0x31A | |
23 | 24 bits | 0x312 0x315 0x318 ? 0x31B | |
24 | --------+--------------------------------------------- | |
25 | "videomode=bootargs" | |
26 | - the parameters are parsed from the bootargs. | |
27 | The format is "NAME:VALUE,NAME:VALUE" etc. | |
28 | Ex.: | |
29 | "bootargs=video=ctfb:x:800,y:600,depth:16,pclk:25000" | |
30 | Parameters not included in the list will be taken from | |
31 | the default mode, which is one of the following: | |
32 | mode:0 640x480x24 | |
33 | mode:1 800x600x16 | |
34 | mode:2 1024x768x8 | |
35 | mode:3 960x720x24 | |
36 | mode:4 1152x864x16 | |
37 | mode:5 1280x1024x8 | |
38 | ||
39 | if "mode" is not provided within the parameter list, | |
40 | mode:0 is assumed. | |
41 | Following parameters are supported: | |
42 | x xres = visible resolution horizontal | |
43 | y yres = visible resolution vertical | |
44 | pclk pixelclocks in pico sec | |
45 | le left_marging time from sync to picture in pixelclocks | |
46 | ri right_marging time from picture to sync in pixelclocks | |
47 | up upper_margin time from sync to picture | |
48 | lo lower_margin | |
49 | hs hsync_len length of horizontal sync | |
50 | vs vsync_len length of vertical sync | |
51 | sync see FB_SYNC_* | |
52 | vmode see FB_VMODE_* | |
53 | depth Color depth in bits per pixel | |
54 | All other parameters in the variable bootargs are ignored. | |
55 | It is also possible to set the parameters direct in the | |
56 | variable "videomode", or in another variable i.e. | |
57 | "myvideo" and setting the variable "videomode=myvideo".. | |
58 | ****************************************************************************/ | |
59 | ||
60 | #include <common.h> | |
b7ce12dd HG |
61 | #include <edid.h> |
62 | #include <errno.h> | |
a5dbdc81 TT |
63 | #include <linux/ctype.h> |
64 | ||
eeb1b77b WD |
65 | #include "videomodes.h" |
66 | ||
67 | const struct ctfb_vesa_modes vesa_modes[VESA_MODES_COUNT] = { | |
68 | {0x301, RES_MODE_640x480, 8}, | |
69 | {0x310, RES_MODE_640x480, 15}, | |
70 | {0x311, RES_MODE_640x480, 16}, | |
71 | {0x312, RES_MODE_640x480, 24}, | |
72 | {0x303, RES_MODE_800x600, 8}, | |
73 | {0x313, RES_MODE_800x600, 15}, | |
74 | {0x314, RES_MODE_800x600, 16}, | |
75 | {0x315, RES_MODE_800x600, 24}, | |
76 | {0x305, RES_MODE_1024x768, 8}, | |
77 | {0x316, RES_MODE_1024x768, 15}, | |
78 | {0x317, RES_MODE_1024x768, 16}, | |
79 | {0x318, RES_MODE_1024x768, 24}, | |
80 | {0x161, RES_MODE_1152x864, 8}, | |
81 | {0x162, RES_MODE_1152x864, 15}, | |
82 | {0x163, RES_MODE_1152x864, 16}, | |
83 | {0x307, RES_MODE_1280x1024, 8}, | |
84 | {0x319, RES_MODE_1280x1024, 15}, | |
85 | {0x31A, RES_MODE_1280x1024, 16}, | |
86 | {0x31B, RES_MODE_1280x1024, 24}, | |
87 | }; | |
88 | const struct ctfb_res_modes res_mode_init[RES_MODES_COUNT] = { | |
0c91d257 | 89 | /* x y hz pixclk ps/kHz le ri up lo hs vs s vmode */ |
92a88c33 | 90 | #ifndef CONFIG_VIDEO_STD_TIMINGS |
0c91d257 HG |
91 | { 640, 480, 60, 39721, 25180, 40, 24, 32, 11, 96, 2, 0, FB_VMODE_NONINTERLACED}, |
92 | { 800, 600, 60, 27778, 36000, 64, 24, 22, 1, 72, 2, 0, FB_VMODE_NONINTERLACED}, | |
93 | {1024, 768, 60, 15384, 65000, 168, 8, 29, 3, 144, 4, 0, FB_VMODE_NONINTERLACED}, | |
94 | { 960, 720, 80, 13100, 76335, 160, 40, 32, 8, 80, 4, 0, FB_VMODE_NONINTERLACED}, | |
95 | {1152, 864, 60, 12004, 83300, 200, 64, 32, 16, 80, 4, 0, FB_VMODE_NONINTERLACED}, | |
96 | {1280, 1024, 60, 9090, 110000, 200, 48, 26, 1, 184, 3, 0, FB_VMODE_NONINTERLACED}, | |
92a88c33 HG |
97 | #else |
98 | { 640, 480, 60, 39683, 25200, 48, 16, 33, 10, 96, 2, 0, FB_VMODE_NONINTERLACED}, | |
99 | { 800, 600, 60, 25000, 40000, 88, 40, 23, 1, 128, 4, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED}, | |
100 | {1024, 768, 60, 15384, 65000, 160, 24, 29, 3, 136, 6, 0, FB_VMODE_NONINTERLACED}, | |
101 | { 960, 720, 75, 13468, 74250, 176, 72, 27, 1, 112, 2, 0, FB_VMODE_NONINTERLACED}, | |
102 | {1152, 864, 75, 9259, 108000, 256, 64, 32, 1, 128, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED}, | |
103 | {1280, 1024, 60, 9259, 108000, 248, 48, 38, 1, 112, 3, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED}, | |
104 | #endif | |
59bb6109 HG |
105 | {1280, 720, 60, 13468, 74250, 220, 110, 20, 5, 40, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED}, |
106 | {1360, 768, 60, 11696, 85500, 256, 64, 17, 3, 112, 7, 0, FB_VMODE_NONINTERLACED}, | |
107 | {1920, 1080, 60, 6734, 148500, 148, 88, 36, 4, 44, 5, FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED}, | |
108 | {1920, 1200, 60, 6494, 154000, 80, 48, 26, 3, 32, 6, FB_SYNC_HOR_HIGH_ACT, FB_VMODE_NONINTERLACED}, | |
eeb1b77b WD |
109 | }; |
110 | ||
111 | /************************************************************************ | |
112 | * Get Parameters for the video mode: | |
113 | */ | |
114 | /********************************************************************* | |
115 | * returns the length to the next seperator | |
116 | */ | |
117 | static int | |
eb3c0cf8 | 118 | video_get_param_len(const char *start, char sep) |
eeb1b77b WD |
119 | { |
120 | int i = 0; | |
121 | while ((*start != 0) && (*start != sep)) { | |
122 | start++; | |
123 | i++; | |
124 | } | |
125 | return i; | |
126 | } | |
127 | ||
128 | static int | |
129 | video_search_param (char *start, char *param) | |
130 | { | |
131 | int len, totallen, i; | |
132 | char *p = start; | |
133 | len = strlen (param); | |
134 | totallen = len + strlen (start); | |
135 | for (i = 0; i < totallen; i++) { | |
136 | if (strncmp (p++, param, len) == 0) | |
137 | return (i); | |
138 | } | |
139 | return -1; | |
140 | } | |
141 | ||
142 | /*************************************************************** | |
143 | * Get parameter via the environment as it is done for the | |
144 | * linux kernel i.e: | |
145 | * video=ctfb:x:800,xv:1280,y:600,yv:1024,depth:16,mode:0,pclk:25000, | |
146 | * le:56,ri:48,up:26,lo:5,hs:152,vs:2,sync:0,vmode:0,accel:0 | |
147 | * | |
148 | * penv is a pointer to the environment, containing the string, or the name of | |
149 | * another environment variable. It could even be the term "bootargs" | |
150 | */ | |
151 | ||
152 | #define GET_OPTION(name,var) \ | |
153 | if(strncmp(p,name,strlen(name))==0) { \ | |
154 | val_s=p+strlen(name); \ | |
155 | var=simple_strtoul(val_s, NULL, 10); \ | |
156 | } | |
157 | ||
158 | int video_get_params (struct ctfb_res_modes *pPar, char *penv) | |
159 | { | |
160 | char *p, *s, *val_s; | |
40ac78a9 | 161 | int i = 0; |
eeb1b77b WD |
162 | int bpp; |
163 | int mode; | |
40ac78a9 | 164 | |
eeb1b77b WD |
165 | /* first search for the environment containing the real param string */ |
166 | s = penv; | |
40ac78a9 | 167 | |
00caae6d SG |
168 | p = env_get(s); |
169 | if (p) | |
eeb1b77b | 170 | s = p; |
40ac78a9 WD |
171 | |
172 | /* | |
173 | * in case of the bootargs line, we have to start | |
eeb1b77b WD |
174 | * after "video=ctfb:" |
175 | */ | |
176 | i = video_search_param (s, "video=ctfb:"); | |
177 | if (i >= 0) { | |
178 | s += i; | |
179 | s += strlen ("video=ctfb:"); | |
180 | } | |
181 | /* search for mode as a default value */ | |
182 | p = s; | |
eeb1b77b | 183 | mode = 0; /* default */ |
40ac78a9 | 184 | |
eeb1b77b WD |
185 | while ((i = video_get_param_len (p, ',')) != 0) { |
186 | GET_OPTION ("mode:", mode) | |
187 | p += i; | |
188 | if (*p != 0) | |
189 | p++; /* skip ',' */ | |
190 | } | |
40ac78a9 | 191 | |
eeb1b77b WD |
192 | if (mode >= RES_MODES_COUNT) |
193 | mode = 0; | |
40ac78a9 | 194 | |
eeb1b77b WD |
195 | *pPar = res_mode_init[mode]; /* copy default values */ |
196 | bpp = 24 - ((mode % 3) * 8); | |
197 | p = s; /* restart */ | |
40ac78a9 | 198 | |
eeb1b77b WD |
199 | while ((i = video_get_param_len (p, ',')) != 0) { |
200 | GET_OPTION ("x:", pPar->xres) | |
201 | GET_OPTION ("y:", pPar->yres) | |
0c91d257 | 202 | GET_OPTION ("refresh:", pPar->refresh) |
eeb1b77b WD |
203 | GET_OPTION ("le:", pPar->left_margin) |
204 | GET_OPTION ("ri:", pPar->right_margin) | |
205 | GET_OPTION ("up:", pPar->upper_margin) | |
206 | GET_OPTION ("lo:", pPar->lower_margin) | |
207 | GET_OPTION ("hs:", pPar->hsync_len) | |
208 | GET_OPTION ("vs:", pPar->vsync_len) | |
209 | GET_OPTION ("sync:", pPar->sync) | |
210 | GET_OPTION ("vmode:", pPar->vmode) | |
211 | GET_OPTION ("pclk:", pPar->pixclock) | |
0c91d257 | 212 | GET_OPTION ("pclk_khz:", pPar->pixclock_khz) |
eeb1b77b WD |
213 | GET_OPTION ("depth:", bpp) |
214 | p += i; | |
215 | if (*p != 0) | |
216 | p++; /* skip ',' */ | |
217 | } | |
218 | return bpp; | |
219 | } | |
a5dbdc81 TT |
220 | |
221 | /* | |
222 | * Parse the 'video-mode' environment variable | |
223 | * | |
224 | * Example: "video-mode=fslfb:1280x1024-32@60,monitor=dvi". See | |
225 | * doc/README.video for more information on how to set the variable. | |
226 | * | |
227 | * @xres: returned value of X-resolution | |
228 | * @yres: returned value of Y-resolution | |
229 | * @depth: returned value of color depth | |
230 | * @freq: returned value of monitor frequency | |
231 | * @options: pointer to any remaining options, or NULL | |
232 | * | |
233 | * Returns 1 if valid values were found, 0 otherwise | |
234 | */ | |
235 | int video_get_video_mode(unsigned int *xres, unsigned int *yres, | |
236 | unsigned int *depth, unsigned int *freq, const char **options) | |
237 | { | |
00caae6d | 238 | char *p = env_get("video-mode"); |
a5dbdc81 TT |
239 | if (!p) |
240 | return 0; | |
241 | ||
242 | /* Skip over the driver name, which we don't care about. */ | |
243 | p = strchr(p, ':'); | |
244 | if (!p) | |
245 | return 0; | |
246 | ||
247 | /* Get the X-resolution*/ | |
248 | while (*p && !isdigit(*p)) | |
249 | p++; | |
250 | *xres = simple_strtoul(p, &p, 10); | |
251 | if (!*xres) | |
252 | return 0; | |
253 | ||
254 | /* Get the Y-resolution */ | |
255 | while (*p && !isdigit(*p)) | |
256 | p++; | |
257 | *yres = simple_strtoul(p, &p, 10); | |
258 | if (!*yres) | |
259 | return 0; | |
260 | ||
261 | /* Get the depth */ | |
262 | while (*p && !isdigit(*p)) | |
263 | p++; | |
264 | *depth = simple_strtoul(p, &p, 10); | |
265 | if (!*depth) | |
266 | return 0; | |
267 | ||
268 | /* Get the frequency */ | |
269 | while (*p && !isdigit(*p)) | |
270 | p++; | |
271 | *freq = simple_strtoul(p, &p, 10); | |
272 | if (!*freq) | |
273 | return 0; | |
274 | ||
275 | /* Find the extra options, if any */ | |
276 | p = strchr(p, ','); | |
277 | *options = p ? p + 1 : NULL; | |
278 | ||
279 | return 1; | |
280 | } | |
e976b868 HG |
281 | |
282 | /* | |
283 | * Parse the 'video-mode' environment variable using video_get_video_mode() | |
284 | * and lookup the matching ctfb_res_modes in res_mode_init. | |
285 | * | |
286 | * @default_mode: RES_MODE_##x## define for the mode to store in mode_ret | |
287 | * when 'video-mode' is not set or does not contain a valid mode | |
288 | * @default_depth: depth to set when 'video-mode' is not set | |
289 | * @mode_ret: pointer where the mode will be stored | |
290 | * @depth_ret: pointer where the depth will be stored | |
291 | * @options: pointer to any remaining options, or NULL | |
292 | */ | |
293 | void video_get_ctfb_res_modes(int default_mode, unsigned int default_depth, | |
294 | const struct ctfb_res_modes **mode_ret, | |
295 | unsigned int *depth_ret, | |
296 | const char **options) | |
297 | { | |
298 | unsigned int i, xres, yres, depth, refresh; | |
299 | ||
300 | *mode_ret = &res_mode_init[default_mode]; | |
301 | *depth_ret = default_depth; | |
302 | *options = NULL; | |
303 | ||
304 | if (!video_get_video_mode(&xres, &yres, &depth, &refresh, options)) | |
305 | return; | |
306 | ||
307 | for (i = 0; i < RES_MODES_COUNT; i++) { | |
308 | if (res_mode_init[i].xres == xres && | |
309 | res_mode_init[i].yres == yres && | |
310 | res_mode_init[i].refresh == refresh) { | |
311 | *mode_ret = &res_mode_init[i]; | |
312 | *depth_ret = depth; | |
313 | return; | |
314 | } | |
315 | } | |
316 | ||
317 | printf("video-mode %dx%d-%d@%d not available, falling back to %dx%d-%d@%d\n", | |
318 | xres, yres, depth, refresh, (*mode_ret)->xres, | |
319 | (*mode_ret)->yres, *depth_ret, (*mode_ret)->refresh); | |
320 | } | |
eb3c0cf8 HG |
321 | |
322 | /* | |
323 | * Find the named string option within the ',' separated options string, and | |
324 | * store its value in dest. | |
325 | * | |
326 | * @options: ',' separated options string | |
327 | * @name: name of the option to look for | |
328 | * @dest: destination buffer to store the value of the option in | |
329 | * @dest_len: length of dest | |
330 | * @def: value to store in dest if the option is not present in options | |
331 | */ | |
332 | void video_get_option_string(const char *options, const char *name, | |
333 | char *dest, int dest_len, const char *def) | |
334 | { | |
335 | const char *p = options; | |
336 | const int name_len = strlen(name); | |
337 | int i, len; | |
338 | ||
339 | while (p && (i = video_get_param_len(p, ',')) != 0) { | |
340 | if (strncmp(p, name, name_len) == 0 && p[name_len] == '=') { | |
341 | len = i - (name_len + 1); | |
342 | if (len >= dest_len) | |
343 | len = dest_len - 1; | |
344 | memcpy(dest, &p[name_len + 1], len); | |
345 | dest[len] = 0; | |
346 | return; | |
347 | } | |
348 | p += i; | |
349 | if (*p != 0) | |
350 | p++; /* skip ',' */ | |
351 | } | |
352 | strcpy(dest, def); | |
353 | } | |
354 | ||
355 | /* | |
356 | * Find the named integer option within the ',' separated options string, and | |
357 | * return its value. | |
358 | * | |
359 | * @options: ',' separated options string | |
360 | * @name: name of the option to look for | |
361 | * @def: value to return if the option is not present in options | |
362 | */ | |
363 | int video_get_option_int(const char *options, const char *name, int def) | |
364 | { | |
365 | const char *p = options; | |
366 | const int name_len = strlen(name); | |
367 | int i; | |
368 | ||
369 | while (p && (i = video_get_param_len(p, ',')) != 0) { | |
370 | if (strncmp(p, name, name_len) == 0 && p[name_len] == '=') | |
371 | return simple_strtoul(&p[name_len + 1], NULL, 10); | |
372 | ||
373 | p += i; | |
374 | if (*p != 0) | |
375 | p++; /* skip ',' */ | |
376 | } | |
377 | return def; | |
378 | } | |
b7ce12dd HG |
379 | |
380 | /** | |
381 | * Convert an EDID detailed timing to a struct ctfb_res_modes | |
382 | * | |
383 | * @param t The EDID detailed timing to be converted | |
384 | * @param mode Returns the converted timing | |
385 | * | |
386 | * @return 0 on success, or a negative errno on error | |
387 | */ | |
388 | int video_edid_dtd_to_ctfb_res_modes(struct edid_detailed_timing *t, | |
389 | struct ctfb_res_modes *mode) | |
390 | { | |
391 | int margin, h_total, v_total; | |
392 | ||
393 | /* Check all timings are non 0 */ | |
394 | if (EDID_DETAILED_TIMING_PIXEL_CLOCK(*t) == 0 || | |
395 | EDID_DETAILED_TIMING_HORIZONTAL_ACTIVE(*t) == 0 || | |
396 | EDID_DETAILED_TIMING_HORIZONTAL_BLANKING(*t) == 0 || | |
397 | EDID_DETAILED_TIMING_VERTICAL_ACTIVE(*t) == 0 || | |
398 | EDID_DETAILED_TIMING_VERTICAL_BLANKING(*t) == 0 || | |
399 | EDID_DETAILED_TIMING_HSYNC_OFFSET(*t) == 0 || | |
400 | EDID_DETAILED_TIMING_HSYNC_PULSE_WIDTH(*t) == 0 || | |
401 | EDID_DETAILED_TIMING_VSYNC_OFFSET(*t) == 0 || | |
402 | EDID_DETAILED_TIMING_VSYNC_PULSE_WIDTH(*t) == 0 || | |
403 | /* 3d formats are not supported*/ | |
404 | EDID_DETAILED_TIMING_FLAG_STEREO(*t) != 0) | |
405 | return -EINVAL; | |
406 | ||
407 | mode->xres = EDID_DETAILED_TIMING_HORIZONTAL_ACTIVE(*t); | |
408 | mode->yres = EDID_DETAILED_TIMING_VERTICAL_ACTIVE(*t); | |
409 | ||
410 | h_total = mode->xres + EDID_DETAILED_TIMING_HORIZONTAL_BLANKING(*t); | |
411 | v_total = mode->yres + EDID_DETAILED_TIMING_VERTICAL_BLANKING(*t); | |
412 | mode->refresh = EDID_DETAILED_TIMING_PIXEL_CLOCK(*t) / | |
413 | (h_total * v_total); | |
414 | ||
415 | mode->pixclock_khz = EDID_DETAILED_TIMING_PIXEL_CLOCK(*t) / 1000; | |
416 | mode->pixclock = 1000000000L / mode->pixclock_khz; | |
417 | ||
418 | mode->right_margin = EDID_DETAILED_TIMING_HSYNC_OFFSET(*t); | |
419 | mode->hsync_len = EDID_DETAILED_TIMING_HSYNC_PULSE_WIDTH(*t); | |
420 | margin = EDID_DETAILED_TIMING_HORIZONTAL_BLANKING(*t) - | |
421 | (mode->right_margin + mode->hsync_len); | |
422 | if (margin <= 0) | |
423 | return -EINVAL; | |
424 | ||
425 | mode->left_margin = margin; | |
426 | ||
427 | mode->lower_margin = EDID_DETAILED_TIMING_VSYNC_OFFSET(*t); | |
428 | mode->vsync_len = EDID_DETAILED_TIMING_VSYNC_PULSE_WIDTH(*t); | |
429 | margin = EDID_DETAILED_TIMING_VERTICAL_BLANKING(*t) - | |
430 | (mode->lower_margin + mode->vsync_len); | |
431 | if (margin <= 0) | |
432 | return -EINVAL; | |
433 | ||
434 | mode->upper_margin = margin; | |
435 | ||
436 | mode->sync = 0; | |
437 | if (EDID_DETAILED_TIMING_FLAG_HSYNC_POLARITY(*t)) | |
438 | mode->sync |= FB_SYNC_HOR_HIGH_ACT; | |
439 | if (EDID_DETAILED_TIMING_FLAG_VSYNC_POLARITY(*t)) | |
440 | mode->sync |= FB_SYNC_VERT_HIGH_ACT; | |
441 | ||
442 | if (EDID_DETAILED_TIMING_FLAG_INTERLACED(*t)) | |
443 | mode->vmode = FB_VMODE_INTERLACED; | |
444 | else | |
445 | mode->vmode = FB_VMODE_NONINTERLACED; | |
446 | ||
447 | return 0; | |
448 | } |