1/*
2 * Copyright 2011, Axel D��rfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6/* Generate mode timings using the GTF Timing Standard
7 *
8 * Copyright (c) 2001, Andy Ritger  aritger@nvidia.com
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 *
15 * o Redistributions of source code must retain the above copyright
16 *   notice, this list of conditions and the following disclaimer.
17 * o Redistributions in binary form must reproduce the above copyright
18 *   notice, this list of conditions and the following disclaimer
19 *   in the documentation and/or other materials provided with the
20 *   distribution.
21 * o Neither the name of NVIDIA nor the names of its contributors
22 *   may be used to endorse or promote products derived from this
23 *   software without specific prior written permission.
24 *
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
28 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
29 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
30 * THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
32 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
33 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
34 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
36 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 *
39 *
40 *
41 * This program is based on the Generalized Timing Formula(GTF TM)
42 * Standard Version: 1.0, Revision: 1.0
43 *
44 * The GTF Document contains the following Copyright information:
45 *
46 * Copyright (c) 1994, 1995, 1996 - Video Electronics Standards
47 * Association. Duplication of this document within VESA member
48 * companies for review purposes is permitted. All other rights
49 * reserved.
50 *
51 * While every precaution has been taken in the preparation
52 * of this standard, the Video Electronics Standards Association and
53 * its contributors assume no responsibility for errors or omissions,
54 * and make no warranties, expressed or implied, of functionality
55 * of suitability for any purpose. The sample code contained within
56 * this standard may be used without restriction.
57 *
58 *
59 *
60 * The GTF EXCEL(TM) SPREADSHEET, a sample (and the definitive)
61 * implementation of the GTF Timing Standard, is available at:
62 *
63 * ftp://ftp.vesa.org/pub/GTF/GTF_V1R1.xls
64 *
65 *
66 *
67 * This program takes a desired resolution and vertical refresh rate,
68 * and computes mode timings according to the GTF Timing Standard.
69 * These mode timings can then be formatted as an XFree86 modeline
70 * or a mode description for use by fbset(8).
71 *
72 * NOTES:
73 *
74 * The GTF allows for computation of "margins" (the visible border
75 * surrounding the addressable video); on most non-overscan type
76 * systems, the margin period is zero.  I've implemented the margin
77 * computations but not enabled it because 1) I don't really have
78 * any experience with this, and 2) neither XFree86 modelines nor
79 * fbset fb.modes provide an obvious way for margin timings to be
80 * included in their mode descriptions (needs more investigation).
81 *
82 * The GTF provides for computation of interlaced mode timings;
83 * I've implemented the computations but not enabled them, yet.
84 * I should probably enable and test this at some point.
85 *
86 * TODO:
87 *
88 * o Add support for interlaced modes.
89 *
90 * o Implement the other portions of the GTF: compute mode timings
91 *   given either the desired pixel clock or the desired horizontal
92 *   frequency.
93 *
94 * o It would be nice if this were more general purpose to do things
95 *   outside the scope of the GTF: like generate double scan mode
96 *   timings, for example.
97 *
98 * o Error checking.
99 *
100 */
101
102
103#include <compute_display_timing.h>
104
105#include <math.h>
106#include <stdarg.h>
107
108
109//#define TRACE_COMPUTE
110#ifdef TRACE_COMPUTE
111#	define TRACE(x, ...)	debug_printf(x, __VA_ARGS__)
112#else
113#	define TRACE(x, ...)	;
114#endif
115
116
117#define MARGIN_PERCENT				1.8		// % of active vertical image
118#define CELL_GRANULARITY			8.0
119	// assumed character cell granularity
120#define MIN_PORCH					1		// minimum front porch
121#define V_SYNC_WIDTH				3		// width of vsync in lines
122#define H_SYNC_PERCENT				8.0		// width of hsync as % of total line
123#define MIN_VSYNC_PLUS_BACK_PORCH	550.0	// time in microsec
124
125// C' and M' are part of the Blanking Duty Cycle computation
126
127#define M					600.0	// blanking formula gradient
128#define C					40.0	// blanking formula offset
129#define K					128.0	// blanking formula scaling factor
130#define J					20.0	// blanking formula scaling factor
131#define C_PRIME				(((C - J) * K / 256.0) + J)
132#define M_PRIME				(K / 256.0 * M)
133
134
135/*!	As defined by the GTF Timing Standard, compute the Stage 1 Parameters
136	using the vertical refresh frequency. In other words: input a desired
137	resolution and desired refresh rate, and output the GTF mode timings.
138*/
139status_t
140compute_display_timing(uint32 width, uint32 height, float refresh,
141	bool interlaced, display_timing* timing)
142{
143	if (width < 320 || height < 200 || width > 65536 || height > 65536
144			|| refresh < 25 || refresh > 1000)
145		return B_BAD_VALUE;
146
147	bool margins = false;
148
149	// 1. In order to give correct results, the number of horizontal
150	// pixels requested is first processed to ensure that it is divisible
151	// by the character size, by rounding it to the nearest character
152	// cell boundary:
153	//	[H PIXELS RND] = ((ROUND([H PIXELS]/[CELL GRAN RND],0))*[CELLGRAN RND])
154	width = (uint32)(rint(width / CELL_GRANULARITY) * CELL_GRANULARITY);
155
156	// 2. If interlace is requested, the number of vertical lines assumed
157	// by the calculation must be halved, as the computation calculates
158	// the number of vertical lines per field. In either case, the
159	// number of lines is rounded to the nearest integer.
160	//	[V LINES RND] = IF([INT RQD?]="y", ROUND([V LINES]/2,0),
161	//		ROUND([V LINES],0))
162	float verticalLines = interlaced ? (double)height / 2.0 : (double)height;
163
164	// 3. Find the frame rate required:
165	//	[V FIELD RATE RQD] = IF([INT RQD?]="y", [I/P FREQ RQD]*2,
166	//		[I/P FREQ RQD])
167	float verticalFieldRate = interlaced ? refresh * 2.0 : refresh;
168
169	// 4. Find number of lines in Top margin:
170	//	[TOP MARGIN (LINES)] = IF([MARGINS RQD?]="Y",
171	//		ROUND(([MARGIN%]/100*[V LINES RND]),0), 0)
172	float topMargin = margins ? rint(MARGIN_PERCENT / 100.0 * verticalLines)
173		: 0.0;
174
175	// 5. Find number of lines in Bottom margin:
176	//	[BOT MARGIN (LINES)] = IF([MARGINS RQD?]="Y",
177	//		ROUND(([MARGIN%]/100*[V LINES RND]),0), 0)
178	float bottomMargin = margins ? rint(MARGIN_PERCENT / 100.0 * verticalLines)
179		: 0.0;
180
181	// 6. If interlace is required, then set variable [INTERLACE]=0.5:
182	//	[INTERLACE]=(IF([INT RQD?]="y",0.5,0))
183	float interlace = interlaced ? 0.5 : 0.0;
184
185	// 7. Estimate the Horizontal period
186	//	[H PERIOD EST] = ((1/[V FIELD RATE RQD]) - [MIN VSYNC+BP]/1000000)
187	//			/ ([V LINES RND] + (2*[TOP MARGIN (LINES)])
188	//				+ [MIN PORCH RND]+[INTERLACE]) * 1000000
189	float horizontalPeriodEstimate = (1.0 / verticalFieldRate
190			- MIN_VSYNC_PLUS_BACK_PORCH / 1000000.0)
191		/ (verticalLines + (2 * topMargin) + MIN_PORCH + interlace) * 1000000.0;
192
193	// 8. Find the number of lines in V sync + back porch:
194	//	[V SYNC+BP] = ROUND(([MIN VSYNC+BP]/[H PERIOD EST]),0)
195	float verticalSyncPlusBackPorch = rint(MIN_VSYNC_PLUS_BACK_PORCH
196		/ horizontalPeriodEstimate);
197
198	// 10. Find the total number of lines in Vertical field period:
199	//	[TOTAL V LINES] = [V LINES RND] + [TOP MARGIN (LINES)]
200	//		+ [BOT MARGIN (LINES)] + [V SYNC+BP] + [INTERLACE] + [MIN PORCH RND]
201	float totalVerticalLines = verticalLines + topMargin + bottomMargin
202		+ verticalSyncPlusBackPorch + interlace + MIN_PORCH;
203
204	// 11. Estimate the Vertical field frequency:
205	//	[V FIELD RATE EST] = 1 / [H PERIOD EST] / [TOTAL V LINES] * 1000000
206	float verticalFieldRateEstimate = 1.0 / horizontalPeriodEstimate
207		/ totalVerticalLines * 1000000.0;
208
209	// 12. Find the actual horizontal period:
210	//	[H PERIOD] = [H PERIOD EST] / ([V FIELD RATE RQD] / [V FIELD RATE EST])
211	float horizontalPeriod = horizontalPeriodEstimate
212		/ (verticalFieldRate / verticalFieldRateEstimate);
213
214	// 15. Find number of pixels in left margin:
215	//	[LEFT MARGIN (PIXELS)] = (IF( [MARGINS RQD?]="Y",
216	//			(ROUND( ([H PIXELS RND] * [MARGIN%] / 100 /
217	//				[CELL GRAN RND]),0)) * [CELL GRAN RND], 0))
218	float leftMargin = margins ? rint(width * MARGIN_PERCENT / 100.0
219			/ CELL_GRANULARITY) * CELL_GRANULARITY : 0.0;
220
221	// 16. Find number of pixels in right margin:
222	//	[RIGHT MARGIN (PIXELS)] = (IF( [MARGINS RQD?]="Y",
223	//			(ROUND( ([H PIXELS RND] * [MARGIN%] / 100 /
224	//				[CELL GRAN RND]),0)) * [CELL GRAN RND], 0))
225	float rightMargin = margins ? rint(width * MARGIN_PERCENT / 100.0
226			/ CELL_GRANULARITY) * CELL_GRANULARITY : 0.0;
227
228	// 17. Find total number of active pixels in image and left and right
229	// margins:
230	//	[TOTAL ACTIVE PIXELS] = [H PIXELS RND] + [LEFT MARGIN (PIXELS)]
231	//		+ [RIGHT MARGIN (PIXELS)]
232	float totalActivePixels = width + leftMargin + rightMargin;
233
234	// 18. Find the ideal blanking duty cycle from the blanking duty cycle
235	// equation:
236	//	[IDEAL DUTY CYCLE] = [C'] - ([M']*[H PERIOD]/1000)
237	float idealDutyCycle = C_PRIME - (M_PRIME * horizontalPeriod / 1000.0);
238
239	// 19. Find the number of pixels in the blanking time to the nearest
240	// double character cell:
241	//	[H BLANK (PIXELS)] = (ROUND(([TOTAL ACTIVE PIXELS]
242	//			* [IDEAL DUTY CYCLE] / (100-[IDEAL DUTY CYCLE])
243	//			/ (2*[CELL GRAN RND])), 0)) * (2*[CELL GRAN RND])
244	float horizontalBlank = rint(totalActivePixels * idealDutyCycle
245			/ (100.0 - idealDutyCycle) / (2.0 * CELL_GRANULARITY))
246		* (2.0 * CELL_GRANULARITY);
247
248	// 20. Find total number of pixels:
249	//	[TOTAL PIXELS] = [TOTAL ACTIVE PIXELS] + [H BLANK (PIXELS)]
250	float totalPixels = totalActivePixels + horizontalBlank;
251
252	// 21. Find pixel clock frequency:
253	//	[PIXEL FREQ] = [TOTAL PIXELS] / [H PERIOD]
254	float pixelFrequency = totalPixels / horizontalPeriod;
255
256	// Stage 1 computations are now complete; I should really pass
257	// the results to another function and do the Stage 2
258	// computations, but I only need a few more values so I'll just
259	// append the computations here for now */
260
261	// 17. Find the number of pixels in the horizontal sync period:
262	//	[H SYNC (PIXELS)] =(ROUND(([H SYNC%] / 100 * [TOTAL PIXELS]
263	//		/ [CELL GRAN RND]),0))*[CELL GRAN RND]
264	float horizontalSync = rint(H_SYNC_PERCENT / 100.0 * totalPixels
265			/ CELL_GRANULARITY) * CELL_GRANULARITY;
266
267	// 18. Find the number of pixels in the horizontal front porch period:
268	//	[H FRONT PORCH (PIXELS)] = ([H BLANK (PIXELS)]/2)-[H SYNC (PIXELS)]
269	float horizontalFrontPorch = (horizontalBlank / 2.0) - horizontalSync;
270
271	// 36. Find the number of lines in the odd front porch period:
272	//	[V ODD FRONT PORCH(LINES)]=([MIN PORCH RND]+[INTERLACE])
273	float verticalOddFrontPorchLines = MIN_PORCH + interlace;
274
275	// finally, pack the results in the mode struct
276
277	timing->pixel_clock = uint32(pixelFrequency * 1000);
278	timing->h_display = (uint16)width;
279	timing->h_sync_start = (uint16)(width + horizontalFrontPorch);
280	timing->h_sync_end
281		= (uint16)(width + horizontalFrontPorch + horizontalSync);
282	timing->h_total = (uint16)totalPixels;
283	timing->v_display = (uint16)verticalLines;
284	timing->v_sync_start = (uint16)(verticalLines + verticalOddFrontPorchLines);
285	timing->v_sync_end
286		= (uint16)(verticalLines + verticalOddFrontPorchLines + V_SYNC_WIDTH);
287	timing->v_total = (uint16)totalVerticalLines;
288	timing->flags = B_POSITIVE_HSYNC | B_POSITIVE_VSYNC
289		| (interlace ? B_TIMING_INTERLACED : 0);
290
291	TRACE("GTF TIMING: %lu kHz, (%u, %u, %u, %u), (%u, %u, %u, %u)\n",
292		timing->pixel_clock, timing->h_display, timing->h_sync_start,
293		timing->h_sync_end, timing->h_total, timing->v_display,
294		timing->v_sync_start, timing->v_sync_end, timing->v_total);
295
296	return B_OK;
297}
298