]>
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 WD |
167 | |
168 | if ((p = getenv (s)) != NULL) | |
eeb1b77b | 169 | s = p; |
40ac78a9 WD |
170 | |
171 | /* | |
172 | * in case of the bootargs line, we have to start | |
eeb1b77b WD |
173 | * after "video=ctfb:" |
174 | */ | |
175 | i = video_search_param (s, "video=ctfb:"); | |
176 | if (i >= 0) { | |
177 | s += i; | |
178 | s += strlen ("video=ctfb:"); | |
179 | } | |
180 | /* search for mode as a default value */ | |
181 | p = s; | |
eeb1b77b | 182 | mode = 0; /* default */ |
40ac78a9 | 183 | |
eeb1b77b WD |
184 | while ((i = video_get_param_len (p, ',')) != 0) { |
185 | GET_OPTION ("mode:", mode) | |
186 | p += i; | |
187 | if (*p != 0) | |
188 | p++; /* skip ',' */ | |
189 | } | |
40ac78a9 | 190 | |
eeb1b77b WD |
191 | if (mode >= RES_MODES_COUNT) |
192 | mode = 0; | |
40ac78a9 | 193 | |
eeb1b77b WD |
194 | *pPar = res_mode_init[mode]; /* copy default values */ |
195 | bpp = 24 - ((mode % 3) * 8); | |
196 | p = s; /* restart */ | |
40ac78a9 | 197 | |
eeb1b77b WD |
198 | while ((i = video_get_param_len (p, ',')) != 0) { |
199 | GET_OPTION ("x:", pPar->xres) | |
200 | GET_OPTION ("y:", pPar->yres) | |
0c91d257 | 201 | GET_OPTION ("refresh:", pPar->refresh) |
eeb1b77b WD |
202 | GET_OPTION ("le:", pPar->left_margin) |
203 | GET_OPTION ("ri:", pPar->right_margin) | |
204 | GET_OPTION ("up:", pPar->upper_margin) | |
205 | GET_OPTION ("lo:", pPar->lower_margin) | |
206 | GET_OPTION ("hs:", pPar->hsync_len) | |
207 | GET_OPTION ("vs:", pPar->vsync_len) | |
208 | GET_OPTION ("sync:", pPar->sync) | |
209 | GET_OPTION ("vmode:", pPar->vmode) | |
210 | GET_OPTION ("pclk:", pPar->pixclock) | |
0c91d257 | 211 | GET_OPTION ("pclk_khz:", pPar->pixclock_khz) |
eeb1b77b WD |
212 | GET_OPTION ("depth:", bpp) |
213 | p += i; | |
214 | if (*p != 0) | |
215 | p++; /* skip ',' */ | |
216 | } | |
217 | return bpp; | |
218 | } | |
a5dbdc81 TT |
219 | |
220 | /* | |
221 | * Parse the 'video-mode' environment variable | |
222 | * | |
223 | * Example: "video-mode=fslfb:1280x1024-32@60,monitor=dvi". See | |
224 | * doc/README.video for more information on how to set the variable. | |
225 | * | |
226 | * @xres: returned value of X-resolution | |
227 | * @yres: returned value of Y-resolution | |
228 | * @depth: returned value of color depth | |
229 | * @freq: returned value of monitor frequency | |
230 | * @options: pointer to any remaining options, or NULL | |
231 | * | |
232 | * Returns 1 if valid values were found, 0 otherwise | |
233 | */ | |
234 | int video_get_video_mode(unsigned int *xres, unsigned int *yres, | |
235 | unsigned int *depth, unsigned int *freq, const char **options) | |
236 | { | |
237 | char *p = getenv("video-mode"); | |
238 | if (!p) | |
239 | return 0; | |
240 | ||
241 | /* Skip over the driver name, which we don't care about. */ | |
242 | p = strchr(p, ':'); | |
243 | if (!p) | |
244 | return 0; | |
245 | ||
246 | /* Get the X-resolution*/ | |
247 | while (*p && !isdigit(*p)) | |
248 | p++; | |
249 | *xres = simple_strtoul(p, &p, 10); | |
250 | if (!*xres) | |
251 | return 0; | |
252 | ||
253 | /* Get the Y-resolution */ | |
254 | while (*p && !isdigit(*p)) | |
255 | p++; | |
256 | *yres = simple_strtoul(p, &p, 10); | |
257 | if (!*yres) | |
258 | return 0; | |
259 | ||
260 | /* Get the depth */ | |
261 | while (*p && !isdigit(*p)) | |
262 | p++; | |
263 | *depth = simple_strtoul(p, &p, 10); | |
264 | if (!*depth) | |
265 | return 0; | |
266 | ||
267 | /* Get the frequency */ | |
268 | while (*p && !isdigit(*p)) | |
269 | p++; | |
270 | *freq = simple_strtoul(p, &p, 10); | |
271 | if (!*freq) | |
272 | return 0; | |
273 | ||
274 | /* Find the extra options, if any */ | |
275 | p = strchr(p, ','); | |
276 | *options = p ? p + 1 : NULL; | |
277 | ||
278 | return 1; | |
279 | } | |
e976b868 HG |
280 | |
281 | /* | |
282 | * Parse the 'video-mode' environment variable using video_get_video_mode() | |
283 | * and lookup the matching ctfb_res_modes in res_mode_init. | |
284 | * | |
285 | * @default_mode: RES_MODE_##x## define for the mode to store in mode_ret | |
286 | * when 'video-mode' is not set or does not contain a valid mode | |
287 | * @default_depth: depth to set when 'video-mode' is not set | |
288 | * @mode_ret: pointer where the mode will be stored | |
289 | * @depth_ret: pointer where the depth will be stored | |
290 | * @options: pointer to any remaining options, or NULL | |
291 | */ | |
292 | void video_get_ctfb_res_modes(int default_mode, unsigned int default_depth, | |
293 | const struct ctfb_res_modes **mode_ret, | |
294 | unsigned int *depth_ret, | |
295 | const char **options) | |
296 | { | |
297 | unsigned int i, xres, yres, depth, refresh; | |
298 | ||
299 | *mode_ret = &res_mode_init[default_mode]; | |
300 | *depth_ret = default_depth; | |
301 | *options = NULL; | |
302 | ||
303 | if (!video_get_video_mode(&xres, &yres, &depth, &refresh, options)) | |
304 | return; | |
305 | ||
306 | for (i = 0; i < RES_MODES_COUNT; i++) { | |
307 | if (res_mode_init[i].xres == xres && | |
308 | res_mode_init[i].yres == yres && | |
309 | res_mode_init[i].refresh == refresh) { | |
310 | *mode_ret = &res_mode_init[i]; | |
311 | *depth_ret = depth; | |
312 | return; | |
313 | } | |
314 | } | |
315 | ||
316 | printf("video-mode %dx%d-%d@%d not available, falling back to %dx%d-%d@%d\n", | |
317 | xres, yres, depth, refresh, (*mode_ret)->xres, | |
318 | (*mode_ret)->yres, *depth_ret, (*mode_ret)->refresh); | |
319 | } | |
eb3c0cf8 HG |
320 | |
321 | /* | |
322 | * Find the named string option within the ',' separated options string, and | |
323 | * store its value in dest. | |
324 | * | |
325 | * @options: ',' separated options string | |
326 | * @name: name of the option to look for | |
327 | * @dest: destination buffer to store the value of the option in | |
328 | * @dest_len: length of dest | |
329 | * @def: value to store in dest if the option is not present in options | |
330 | */ | |
331 | void video_get_option_string(const char *options, const char *name, | |
332 | char *dest, int dest_len, const char *def) | |
333 | { | |
334 | const char *p = options; | |
335 | const int name_len = strlen(name); | |
336 | int i, len; | |
337 | ||
338 | while (p && (i = video_get_param_len(p, ',')) != 0) { | |
339 | if (strncmp(p, name, name_len) == 0 && p[name_len] == '=') { | |
340 | len = i - (name_len + 1); | |
341 | if (len >= dest_len) | |
342 | len = dest_len - 1; | |
343 | memcpy(dest, &p[name_len + 1], len); | |
344 | dest[len] = 0; | |
345 | return; | |
346 | } | |
347 | p += i; | |
348 | if (*p != 0) | |
349 | p++; /* skip ',' */ | |
350 | } | |
351 | strcpy(dest, def); | |
352 | } | |
353 | ||
354 | /* | |
355 | * Find the named integer option within the ',' separated options string, and | |
356 | * return its value. | |
357 | * | |
358 | * @options: ',' separated options string | |
359 | * @name: name of the option to look for | |
360 | * @def: value to return if the option is not present in options | |
361 | */ | |
362 | int video_get_option_int(const char *options, const char *name, int def) | |
363 | { | |
364 | const char *p = options; | |
365 | const int name_len = strlen(name); | |
366 | int i; | |
367 | ||
368 | while (p && (i = video_get_param_len(p, ',')) != 0) { | |
369 | if (strncmp(p, name, name_len) == 0 && p[name_len] == '=') | |
370 | return simple_strtoul(&p[name_len + 1], NULL, 10); | |
371 | ||
372 | p += i; | |
373 | if (*p != 0) | |
374 | p++; /* skip ',' */ | |
375 | } | |
376 | return def; | |
377 | } | |
b7ce12dd HG |
378 | |
379 | /** | |
380 | * Convert an EDID detailed timing to a struct ctfb_res_modes | |
381 | * | |
382 | * @param t The EDID detailed timing to be converted | |
383 | * @param mode Returns the converted timing | |
384 | * | |
385 | * @return 0 on success, or a negative errno on error | |
386 | */ | |
387 | int video_edid_dtd_to_ctfb_res_modes(struct edid_detailed_timing *t, | |
388 | struct ctfb_res_modes *mode) | |
389 | { | |
390 | int margin, h_total, v_total; | |
391 | ||
392 | /* Check all timings are non 0 */ | |
393 | if (EDID_DETAILED_TIMING_PIXEL_CLOCK(*t) == 0 || | |
394 | EDID_DETAILED_TIMING_HORIZONTAL_ACTIVE(*t) == 0 || | |
395 | EDID_DETAILED_TIMING_HORIZONTAL_BLANKING(*t) == 0 || | |
396 | EDID_DETAILED_TIMING_VERTICAL_ACTIVE(*t) == 0 || | |
397 | EDID_DETAILED_TIMING_VERTICAL_BLANKING(*t) == 0 || | |
398 | EDID_DETAILED_TIMING_HSYNC_OFFSET(*t) == 0 || | |
399 | EDID_DETAILED_TIMING_HSYNC_PULSE_WIDTH(*t) == 0 || | |
400 | EDID_DETAILED_TIMING_VSYNC_OFFSET(*t) == 0 || | |
401 | EDID_DETAILED_TIMING_VSYNC_PULSE_WIDTH(*t) == 0 || | |
402 | /* 3d formats are not supported*/ | |
403 | EDID_DETAILED_TIMING_FLAG_STEREO(*t) != 0) | |
404 | return -EINVAL; | |
405 | ||
406 | mode->xres = EDID_DETAILED_TIMING_HORIZONTAL_ACTIVE(*t); | |
407 | mode->yres = EDID_DETAILED_TIMING_VERTICAL_ACTIVE(*t); | |
408 | ||
409 | h_total = mode->xres + EDID_DETAILED_TIMING_HORIZONTAL_BLANKING(*t); | |
410 | v_total = mode->yres + EDID_DETAILED_TIMING_VERTICAL_BLANKING(*t); | |
411 | mode->refresh = EDID_DETAILED_TIMING_PIXEL_CLOCK(*t) / | |
412 | (h_total * v_total); | |
413 | ||
414 | mode->pixclock_khz = EDID_DETAILED_TIMING_PIXEL_CLOCK(*t) / 1000; | |
415 | mode->pixclock = 1000000000L / mode->pixclock_khz; | |
416 | ||
417 | mode->right_margin = EDID_DETAILED_TIMING_HSYNC_OFFSET(*t); | |
418 | mode->hsync_len = EDID_DETAILED_TIMING_HSYNC_PULSE_WIDTH(*t); | |
419 | margin = EDID_DETAILED_TIMING_HORIZONTAL_BLANKING(*t) - | |
420 | (mode->right_margin + mode->hsync_len); | |
421 | if (margin <= 0) | |
422 | return -EINVAL; | |
423 | ||
424 | mode->left_margin = margin; | |
425 | ||
426 | mode->lower_margin = EDID_DETAILED_TIMING_VSYNC_OFFSET(*t); | |
427 | mode->vsync_len = EDID_DETAILED_TIMING_VSYNC_PULSE_WIDTH(*t); | |
428 | margin = EDID_DETAILED_TIMING_VERTICAL_BLANKING(*t) - | |
429 | (mode->lower_margin + mode->vsync_len); | |
430 | if (margin <= 0) | |
431 | return -EINVAL; | |
432 | ||
433 | mode->upper_margin = margin; | |
434 | ||
435 | mode->sync = 0; | |
436 | if (EDID_DETAILED_TIMING_FLAG_HSYNC_POLARITY(*t)) | |
437 | mode->sync |= FB_SYNC_HOR_HIGH_ACT; | |
438 | if (EDID_DETAILED_TIMING_FLAG_VSYNC_POLARITY(*t)) | |
439 | mode->sync |= FB_SYNC_VERT_HIGH_ACT; | |
440 | ||
441 | if (EDID_DETAILED_TIMING_FLAG_INTERLACED(*t)) | |
442 | mode->vmode = FB_VMODE_INTERLACED; | |
443 | else | |
444 | mode->vmode = FB_VMODE_NONINTERLACED; | |
445 | ||
446 | return 0; | |
447 | } |