Branch data Line data Source code
1 : : /* 2 : : * linux/drivers/video/fbcvt.c - VESA(TM) Coordinated Video Timings 3 : : * 4 : : * Copyright (C) 2005 Antonino Daplas <adaplas@pol.net> 5 : : * 6 : : * Based from the VESA(TM) Coordinated Video Timing Generator by 7 : : * Graham Loveridge April 9, 2003 available at 8 : : * http://www.elo.utfsm.cl/~elo212/docs/CVTd6r1.xls 9 : : * 10 : : * This file is subject to the terms and conditions of the GNU General Public 11 : : * License. See the file COPYING in the main directory of this archive 12 : : * for more details. 13 : : * 14 : : */ 15 : : #include <linux/fb.h> 16 : : #include <linux/slab.h> 17 : : 18 : : #define FB_CVT_CELLSIZE 8 19 : : #define FB_CVT_GTF_C 40 20 : : #define FB_CVT_GTF_J 20 21 : : #define FB_CVT_GTF_K 128 22 : : #define FB_CVT_GTF_M 600 23 : : #define FB_CVT_MIN_VSYNC_BP 550 24 : : #define FB_CVT_MIN_VPORCH 3 25 : : #define FB_CVT_MIN_BPORCH 6 26 : : 27 : : #define FB_CVT_RB_MIN_VBLANK 460 28 : : #define FB_CVT_RB_HBLANK 160 29 : : #define FB_CVT_RB_V_FPORCH 3 30 : : 31 : : #define FB_CVT_FLAG_REDUCED_BLANK 1 32 : : #define FB_CVT_FLAG_MARGINS 2 33 : : #define FB_CVT_FLAG_INTERLACED 4 34 : : 35 : : struct fb_cvt_data { 36 : : u32 xres; 37 : : u32 yres; 38 : : u32 refresh; 39 : : u32 f_refresh; 40 : : u32 pixclock; 41 : : u32 hperiod; 42 : : u32 hblank; 43 : : u32 hfreq; 44 : : u32 htotal; 45 : : u32 vtotal; 46 : : u32 vsync; 47 : : u32 hsync; 48 : : u32 h_front_porch; 49 : : u32 h_back_porch; 50 : : u32 v_front_porch; 51 : : u32 v_back_porch; 52 : : u32 h_margin; 53 : : u32 v_margin; 54 : : u32 interlace; 55 : : u32 aspect_ratio; 56 : : u32 active_pixels; 57 : : u32 flags; 58 : : u32 status; 59 : : }; 60 : : 61 : : static const unsigned char fb_cvt_vbi_tab[] = { 62 : : 4, /* 4:3 */ 63 : : 5, /* 16:9 */ 64 : : 6, /* 16:10 */ 65 : : 7, /* 5:4 */ 66 : : 7, /* 15:9 */ 67 : : 8, /* reserved */ 68 : : 9, /* reserved */ 69 : : 10 /* custom */ 70 : : }; 71 : : 72 : : /* returns hperiod * 1000 */ 73 : 0 : static u32 fb_cvt_hperiod(struct fb_cvt_data *cvt) 74 : : { 75 : 0 : u32 num = 1000000000/cvt->f_refresh; 76 : : u32 den; 77 : : 78 : 0 : if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK) { 79 : 0 : num -= FB_CVT_RB_MIN_VBLANK * 1000; 80 : 0 : den = 2 * (cvt->yres/cvt->interlace + 2 * cvt->v_margin); 81 : : } else { 82 : 0 : num -= FB_CVT_MIN_VSYNC_BP * 1000; 83 : 0 : den = 2 * (cvt->yres/cvt->interlace + cvt->v_margin * 2 84 : 0 : + FB_CVT_MIN_VPORCH + cvt->interlace/2); 85 : : } 86 : : 87 : 0 : return 2 * (num/den); 88 : : } 89 : : 90 : : /* returns ideal duty cycle * 1000 */ 91 : : static u32 fb_cvt_ideal_duty_cycle(struct fb_cvt_data *cvt) 92 : : { 93 : : u32 c_prime = (FB_CVT_GTF_C - FB_CVT_GTF_J) * 94 : : (FB_CVT_GTF_K) + 256 * FB_CVT_GTF_J; 95 : : u32 m_prime = (FB_CVT_GTF_K * FB_CVT_GTF_M); 96 : 0 : u32 h_period_est = cvt->hperiod; 97 : : 98 : 0 : return (1000 * c_prime - ((m_prime * h_period_est)/1000))/256; 99 : : } 100 : : 101 : 0 : static u32 fb_cvt_hblank(struct fb_cvt_data *cvt) 102 : : { 103 : : u32 hblank = 0; 104 : : 105 : 0 : if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK) 106 : : hblank = FB_CVT_RB_HBLANK; 107 : : else { 108 : : u32 ideal_duty_cycle = fb_cvt_ideal_duty_cycle(cvt); 109 : 0 : u32 active_pixels = cvt->active_pixels; 110 : : 111 : 0 : if (ideal_duty_cycle < 20000) 112 : 0 : hblank = (active_pixels * 20000)/ 113 : : (100000 - 20000); 114 : : else { 115 : 0 : hblank = (active_pixels * ideal_duty_cycle)/ 116 : 0 : (100000 - ideal_duty_cycle); 117 : : } 118 : : } 119 : : 120 : 0 : hblank &= ~((2 * FB_CVT_CELLSIZE) - 1); 121 : : 122 : 0 : return hblank; 123 : : } 124 : : 125 : : static u32 fb_cvt_hsync(struct fb_cvt_data *cvt) 126 : : { 127 : : u32 hsync; 128 : : 129 : 0 : if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK) 130 : : hsync = 32; 131 : : else 132 : 0 : hsync = (FB_CVT_CELLSIZE * cvt->htotal)/100; 133 : : 134 : 0 : hsync &= ~(FB_CVT_CELLSIZE - 1); 135 : : return hsync; 136 : : } 137 : : 138 : : static u32 fb_cvt_vbi_lines(struct fb_cvt_data *cvt) 139 : : { 140 : : u32 vbi_lines, min_vbi_lines, act_vbi_lines; 141 : : 142 : 0 : if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK) { 143 : 0 : vbi_lines = (1000 * FB_CVT_RB_MIN_VBLANK)/cvt->hperiod + 1; 144 : 0 : min_vbi_lines = FB_CVT_RB_V_FPORCH + cvt->vsync + 145 : : FB_CVT_MIN_BPORCH; 146 : : 147 : : } else { 148 : 0 : vbi_lines = (FB_CVT_MIN_VSYNC_BP * 1000)/cvt->hperiod + 1 + 149 : : FB_CVT_MIN_VPORCH; 150 : 0 : min_vbi_lines = cvt->vsync + FB_CVT_MIN_BPORCH + 151 : : FB_CVT_MIN_VPORCH; 152 : : } 153 : : 154 : 0 : if (vbi_lines < min_vbi_lines) 155 : : act_vbi_lines = min_vbi_lines; 156 : : else 157 : : act_vbi_lines = vbi_lines; 158 : : 159 : : return act_vbi_lines; 160 : : } 161 : : 162 : 0 : static u32 fb_cvt_vtotal(struct fb_cvt_data *cvt) 163 : : { 164 : 0 : u32 vtotal = cvt->yres/cvt->interlace; 165 : : 166 : 0 : vtotal += 2 * cvt->v_margin + cvt->interlace/2 + fb_cvt_vbi_lines(cvt); 167 : 0 : vtotal |= cvt->interlace/2; 168 : : 169 : 0 : return vtotal; 170 : : } 171 : : 172 : : static u32 fb_cvt_pixclock(struct fb_cvt_data *cvt) 173 : : { 174 : : u32 pixclock; 175 : : 176 : 0 : if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK) 177 : 0 : pixclock = (cvt->f_refresh * cvt->vtotal * cvt->htotal)/1000; 178 : : else 179 : 0 : pixclock = (cvt->htotal * 1000000)/cvt->hperiod; 180 : : 181 : 0 : pixclock /= 250; 182 : 0 : pixclock *= 250; 183 : 0 : pixclock *= 1000; 184 : : 185 : : return pixclock; 186 : : } 187 : : 188 : 0 : static u32 fb_cvt_aspect_ratio(struct fb_cvt_data *cvt) 189 : : { 190 : 0 : u32 xres = cvt->xres; 191 : 0 : u32 yres = cvt->yres; 192 : : u32 aspect = -1; 193 : : 194 : 0 : if (xres == (yres * 4)/3 && !((yres * 4) % 3)) 195 : : aspect = 0; 196 : 0 : else if (xres == (yres * 16)/9 && !((yres * 16) % 9)) 197 : : aspect = 1; 198 : 0 : else if (xres == (yres * 16)/10 && !((yres * 16) % 10)) 199 : : aspect = 2; 200 : 0 : else if (xres == (yres * 5)/4 && !((yres * 5) % 4)) 201 : : aspect = 3; 202 : 0 : else if (xres == (yres * 15)/9 && !((yres * 15) % 9)) 203 : : aspect = 4; 204 : : else { 205 : 0 : printk(KERN_INFO "fbcvt: Aspect ratio not CVT " 206 : : "standard\n"); 207 : : aspect = 7; 208 : 0 : cvt->status = 1; 209 : : } 210 : : 211 : 0 : return aspect; 212 : : } 213 : : 214 : 0 : static void fb_cvt_print_name(struct fb_cvt_data *cvt) 215 : : { 216 : : u32 pixcount, pixcount_mod; 217 : : int cnt = 255, offset = 0, read = 0; 218 : 0 : u8 *buf = kzalloc(256, GFP_KERNEL); 219 : : 220 : 0 : if (!buf) 221 : 0 : return; 222 : : 223 : 0 : pixcount = (cvt->xres * (cvt->yres/cvt->interlace))/1000000; 224 : 0 : pixcount_mod = (cvt->xres * (cvt->yres/cvt->interlace)) % 1000000; 225 : 0 : pixcount_mod /= 1000; 226 : : 227 : 0 : read = snprintf(buf+offset, cnt, "fbcvt: %dx%d@%d: CVT Name - ", 228 : : cvt->xres, cvt->yres, cvt->refresh); 229 : : offset += read; 230 : 0 : cnt -= read; 231 : : 232 : 0 : if (cvt->status) 233 : 0 : snprintf(buf+offset, cnt, "Not a CVT standard - %d.%03d Mega " 234 : : "Pixel Image\n", pixcount, pixcount_mod); 235 : : else { 236 : 0 : if (pixcount) { 237 : 0 : read = snprintf(buf+offset, cnt, "%d", pixcount); 238 : 0 : cnt -= read; 239 : 0 : offset += read; 240 : : } 241 : : 242 : 0 : read = snprintf(buf+offset, cnt, ".%03dM", pixcount_mod); 243 : 0 : cnt -= read; 244 : 0 : offset += read; 245 : : 246 : 0 : if (cvt->aspect_ratio == 0) 247 : 0 : read = snprintf(buf+offset, cnt, "3"); 248 : 0 : else if (cvt->aspect_ratio == 3) 249 : 0 : read = snprintf(buf+offset, cnt, "4"); 250 : 0 : else if (cvt->aspect_ratio == 1 || cvt->aspect_ratio == 4) 251 : 0 : read = snprintf(buf+offset, cnt, "9"); 252 : 0 : else if (cvt->aspect_ratio == 2) 253 : 0 : read = snprintf(buf+offset, cnt, "A"); 254 : : else 255 : : read = 0; 256 : 0 : cnt -= read; 257 : 0 : offset += read; 258 : : 259 : 0 : if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK) { 260 : 0 : read = snprintf(buf+offset, cnt, "-R"); 261 : : cnt -= read; 262 : : offset += read; 263 : : } 264 : : } 265 : : 266 : 0 : printk(KERN_INFO "%s\n", buf); 267 : 0 : kfree(buf); 268 : : } 269 : : 270 : 0 : static void fb_cvt_convert_to_mode(struct fb_cvt_data *cvt, 271 : : struct fb_videomode *mode) 272 : : { 273 : 0 : mode->refresh = cvt->f_refresh; 274 : 0 : mode->pixclock = KHZ2PICOS(cvt->pixclock/1000); 275 : 0 : mode->left_margin = cvt->h_back_porch; 276 : 0 : mode->right_margin = cvt->h_front_porch; 277 : 0 : mode->hsync_len = cvt->hsync; 278 : 0 : mode->upper_margin = cvt->v_back_porch; 279 : 0 : mode->lower_margin = cvt->v_front_porch; 280 : 0 : mode->vsync_len = cvt->vsync; 281 : : 282 : 0 : mode->sync &= ~(FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT); 283 : : 284 : 0 : if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK) 285 : 0 : mode->sync |= FB_SYNC_HOR_HIGH_ACT; 286 : : else 287 : 0 : mode->sync |= FB_SYNC_VERT_HIGH_ACT; 288 : 0 : } 289 : : 290 : : /* 291 : : * fb_find_mode_cvt - calculate mode using VESA(TM) CVT 292 : : * @mode: pointer to fb_videomode; xres, yres, refresh and vmode must be 293 : : * pre-filled with the desired values 294 : : * @margins: add margin to calculation (1.8% of xres and yres) 295 : : * @rb: compute with reduced blanking (for flatpanels) 296 : : * 297 : : * RETURNS: 298 : : * 0 for success 299 : : * @mode is filled with computed values. If interlaced, the refresh field 300 : : * will be filled with the field rate (2x the frame rate) 301 : : * 302 : : * DESCRIPTION: 303 : : * Computes video timings using VESA(TM) Coordinated Video Timings 304 : : */ 305 : 0 : int fb_find_mode_cvt(struct fb_videomode *mode, int margins, int rb) 306 : : { 307 : : struct fb_cvt_data cvt; 308 : : 309 : 0 : memset(&cvt, 0, sizeof(cvt)); 310 : : 311 : 0 : if (margins) 312 : 0 : cvt.flags |= FB_CVT_FLAG_MARGINS; 313 : : 314 : 0 : if (rb) 315 : 0 : cvt.flags |= FB_CVT_FLAG_REDUCED_BLANK; 316 : : 317 : 0 : if (mode->vmode & FB_VMODE_INTERLACED) 318 : 0 : cvt.flags |= FB_CVT_FLAG_INTERLACED; 319 : : 320 : 0 : cvt.xres = mode->xres; 321 : 0 : cvt.yres = mode->yres; 322 : 0 : cvt.refresh = mode->refresh; 323 : 0 : cvt.f_refresh = cvt.refresh; 324 : 0 : cvt.interlace = 1; 325 : : 326 : 0 : if (!cvt.xres || !cvt.yres || !cvt.refresh) { 327 : 0 : printk(KERN_INFO "fbcvt: Invalid input parameters\n"); 328 : 0 : return 1; 329 : : } 330 : : 331 : 0 : if (!(cvt.refresh == 50 || cvt.refresh == 60 || cvt.refresh == 70 || 332 : : cvt.refresh == 85)) { 333 : 0 : printk(KERN_INFO "fbcvt: Refresh rate not CVT " 334 : : "standard\n"); 335 : 0 : cvt.status = 1; 336 : : } 337 : : 338 : 0 : cvt.xres &= ~(FB_CVT_CELLSIZE - 1); 339 : : 340 : 0 : if (cvt.flags & FB_CVT_FLAG_INTERLACED) { 341 : 0 : cvt.interlace = 2; 342 : 0 : cvt.f_refresh *= 2; 343 : : } 344 : : 345 : 0 : if (cvt.flags & FB_CVT_FLAG_REDUCED_BLANK) { 346 : 0 : if (cvt.refresh != 60) { 347 : 0 : printk(KERN_INFO "fbcvt: 60Hz refresh rate " 348 : : "advised for reduced blanking\n"); 349 : 0 : cvt.status = 1; 350 : : } 351 : : } 352 : : 353 : 0 : if (cvt.flags & FB_CVT_FLAG_MARGINS) { 354 : 0 : cvt.h_margin = (cvt.xres * 18)/1000; 355 : 0 : cvt.h_margin &= ~(FB_CVT_CELLSIZE - 1); 356 : 0 : cvt.v_margin = ((cvt.yres/cvt.interlace)* 18)/1000; 357 : : } 358 : : 359 : 0 : cvt.aspect_ratio = fb_cvt_aspect_ratio(&cvt); 360 : 0 : cvt.active_pixels = cvt.xres + 2 * cvt.h_margin; 361 : 0 : cvt.hperiod = fb_cvt_hperiod(&cvt); 362 : 0 : cvt.vsync = fb_cvt_vbi_tab[cvt.aspect_ratio]; 363 : 0 : cvt.vtotal = fb_cvt_vtotal(&cvt); 364 : 0 : cvt.hblank = fb_cvt_hblank(&cvt); 365 : 0 : cvt.htotal = cvt.active_pixels + cvt.hblank; 366 : 0 : cvt.hsync = fb_cvt_hsync(&cvt); 367 : 0 : cvt.pixclock = fb_cvt_pixclock(&cvt); 368 : 0 : cvt.hfreq = cvt.pixclock/cvt.htotal; 369 : 0 : cvt.h_back_porch = cvt.hblank/2 + cvt.h_margin; 370 : 0 : cvt.h_front_porch = cvt.hblank - cvt.hsync - cvt.h_back_porch + 371 : : 2 * cvt.h_margin; 372 : 0 : cvt.v_front_porch = 3 + cvt.v_margin; 373 : 0 : cvt.v_back_porch = cvt.vtotal - cvt.yres/cvt.interlace - 374 : 0 : cvt.v_front_porch - cvt.vsync; 375 : 0 : fb_cvt_print_name(&cvt); 376 : 0 : fb_cvt_convert_to_mode(&cvt, mode); 377 : : 378 : 0 : return 0; 379 : : }