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