Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * linux/drivers/video/fbcvt.c - VESA(TM) Coordinated Video Timings
0003  *
0004  * Copyright (C) 2005 Antonino Daplas <adaplas@pol.net>
0005  *
0006  *      Based from the VESA(TM) Coordinated Video Timing Generator by
0007  *      Graham Loveridge April 9, 2003 available at
0008  *      http://www.elo.utfsm.cl/~elo212/docs/CVTd6r1.xls
0009  *
0010  * This file is subject to the terms and conditions of the GNU General Public
0011  * License.  See the file COPYING in the main directory of this archive
0012  * for more details.
0013  *
0014  */
0015 #include <linux/fb.h>
0016 #include <linux/slab.h>
0017 
0018 #define FB_CVT_CELLSIZE               8
0019 #define FB_CVT_GTF_C                 40
0020 #define FB_CVT_GTF_J                 20
0021 #define FB_CVT_GTF_K                128
0022 #define FB_CVT_GTF_M                600
0023 #define FB_CVT_MIN_VSYNC_BP         550
0024 #define FB_CVT_MIN_VPORCH             3
0025 #define FB_CVT_MIN_BPORCH             6
0026 
0027 #define FB_CVT_RB_MIN_VBLANK        460
0028 #define FB_CVT_RB_HBLANK            160
0029 #define FB_CVT_RB_V_FPORCH            3
0030 
0031 #define FB_CVT_FLAG_REDUCED_BLANK 1
0032 #define FB_CVT_FLAG_MARGINS       2
0033 #define FB_CVT_FLAG_INTERLACED    4
0034 
0035 struct fb_cvt_data {
0036     u32 xres;
0037     u32 yres;
0038     u32 refresh;
0039     u32 f_refresh;
0040     u32 pixclock;
0041     u32 hperiod;
0042     u32 hblank;
0043     u32 hfreq;
0044     u32 htotal;
0045     u32 vtotal;
0046     u32 vsync;
0047     u32 hsync;
0048     u32 h_front_porch;
0049     u32 h_back_porch;
0050     u32 v_front_porch;
0051     u32 v_back_porch;
0052     u32 h_margin;
0053     u32 v_margin;
0054     u32 interlace;
0055     u32 aspect_ratio;
0056     u32 active_pixels;
0057     u32 flags;
0058     u32 status;
0059 };
0060 
0061 static const unsigned char fb_cvt_vbi_tab[] = {
0062     4,        /* 4:3      */
0063     5,        /* 16:9     */
0064     6,        /* 16:10    */
0065     7,        /* 5:4      */
0066     7,        /* 15:9     */
0067     8,        /* reserved */
0068     9,        /* reserved */
0069     10        /* custom   */
0070 };
0071 
0072 /* returns hperiod * 1000 */
0073 static u32 fb_cvt_hperiod(struct fb_cvt_data *cvt)
0074 {
0075     u32 num = 1000000000/cvt->f_refresh;
0076     u32 den;
0077 
0078     if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK) {
0079         num -= FB_CVT_RB_MIN_VBLANK * 1000;
0080         den = 2 * (cvt->yres/cvt->interlace + 2 * cvt->v_margin);
0081     } else {
0082         num -= FB_CVT_MIN_VSYNC_BP * 1000;
0083         den = 2 * (cvt->yres/cvt->interlace + cvt->v_margin * 2
0084                + FB_CVT_MIN_VPORCH + cvt->interlace/2);
0085     }
0086 
0087     return 2 * (num/den);
0088 }
0089 
0090 /* returns ideal duty cycle * 1000 */
0091 static u32 fb_cvt_ideal_duty_cycle(struct fb_cvt_data *cvt)
0092 {
0093     u32 c_prime = (FB_CVT_GTF_C - FB_CVT_GTF_J) *
0094         (FB_CVT_GTF_K) + 256 * FB_CVT_GTF_J;
0095     u32 m_prime = (FB_CVT_GTF_K * FB_CVT_GTF_M);
0096     u32 h_period_est = cvt->hperiod;
0097 
0098     return (1000 * c_prime  - ((m_prime * h_period_est)/1000))/256;
0099 }
0100 
0101 static u32 fb_cvt_hblank(struct fb_cvt_data *cvt)
0102 {
0103     u32 hblank = 0;
0104 
0105     if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK)
0106         hblank = FB_CVT_RB_HBLANK;
0107     else {
0108         u32 ideal_duty_cycle = fb_cvt_ideal_duty_cycle(cvt);
0109         u32 active_pixels = cvt->active_pixels;
0110 
0111         if (ideal_duty_cycle < 20000)
0112             hblank = (active_pixels * 20000)/
0113                 (100000 - 20000);
0114         else {
0115             hblank = (active_pixels * ideal_duty_cycle)/
0116                 (100000 - ideal_duty_cycle);
0117         }
0118     }
0119 
0120     hblank &= ~((2 * FB_CVT_CELLSIZE) - 1);
0121 
0122     return hblank;
0123 }
0124 
0125 static u32 fb_cvt_hsync(struct fb_cvt_data *cvt)
0126 {
0127     u32 hsync;
0128 
0129     if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK)
0130         hsync = 32;
0131     else
0132         hsync = (FB_CVT_CELLSIZE * cvt->htotal)/100;
0133 
0134     hsync &= ~(FB_CVT_CELLSIZE - 1);
0135     return hsync;
0136 }
0137 
0138 static u32 fb_cvt_vbi_lines(struct fb_cvt_data *cvt)
0139 {
0140     u32 vbi_lines, min_vbi_lines, act_vbi_lines;
0141 
0142     if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK) {
0143         vbi_lines = (1000 * FB_CVT_RB_MIN_VBLANK)/cvt->hperiod + 1;
0144         min_vbi_lines =  FB_CVT_RB_V_FPORCH + cvt->vsync +
0145             FB_CVT_MIN_BPORCH;
0146 
0147     } else {
0148         vbi_lines = (FB_CVT_MIN_VSYNC_BP * 1000)/cvt->hperiod + 1 +
0149              FB_CVT_MIN_VPORCH;
0150         min_vbi_lines = cvt->vsync + FB_CVT_MIN_BPORCH +
0151             FB_CVT_MIN_VPORCH;
0152     }
0153 
0154     if (vbi_lines < min_vbi_lines)
0155         act_vbi_lines = min_vbi_lines;
0156     else
0157         act_vbi_lines = vbi_lines;
0158 
0159     return act_vbi_lines;
0160 }
0161 
0162 static u32 fb_cvt_vtotal(struct fb_cvt_data *cvt)
0163 {
0164     u32 vtotal = cvt->yres/cvt->interlace;
0165 
0166     vtotal += 2 * cvt->v_margin + cvt->interlace/2 + fb_cvt_vbi_lines(cvt);
0167     vtotal |= cvt->interlace/2;
0168 
0169     return vtotal;
0170 }
0171 
0172 static u32 fb_cvt_pixclock(struct fb_cvt_data *cvt)
0173 {
0174     u32 pixclock;
0175 
0176     if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK)
0177         pixclock = (cvt->f_refresh * cvt->vtotal * cvt->htotal)/1000;
0178     else
0179         pixclock = (cvt->htotal * 1000000)/cvt->hperiod;
0180 
0181     pixclock /= 250;
0182     pixclock *= 250;
0183     pixclock *= 1000;
0184 
0185     return pixclock;
0186 }
0187 
0188 static u32 fb_cvt_aspect_ratio(struct fb_cvt_data *cvt)
0189 {
0190     u32 xres = cvt->xres;
0191     u32 yres = cvt->yres;
0192     u32 aspect = -1;
0193 
0194     if (xres == (yres * 4)/3 && !((yres * 4) % 3))
0195         aspect = 0;
0196     else if (xres == (yres * 16)/9 && !((yres * 16) % 9))
0197         aspect = 1;
0198     else if (xres == (yres * 16)/10 && !((yres * 16) % 10))
0199         aspect = 2;
0200     else if (xres == (yres * 5)/4 && !((yres * 5) % 4))
0201         aspect = 3;
0202     else if (xres == (yres * 15)/9 && !((yres * 15) % 9))
0203         aspect = 4;
0204     else {
0205         printk(KERN_INFO "fbcvt: Aspect ratio not CVT "
0206                "standard\n");
0207         aspect = 7;
0208         cvt->status = 1;
0209     }
0210 
0211     return aspect;
0212 }
0213 
0214 static void fb_cvt_print_name(struct fb_cvt_data *cvt)
0215 {
0216     u32 pixcount, pixcount_mod;
0217     int size = 256;
0218     int off = 0;
0219     u8 *buf;
0220 
0221     buf = kzalloc(size, GFP_KERNEL);
0222     if (!buf)
0223         return;
0224 
0225     pixcount = (cvt->xres * (cvt->yres/cvt->interlace))/1000000;
0226     pixcount_mod = (cvt->xres * (cvt->yres/cvt->interlace)) % 1000000;
0227     pixcount_mod /= 1000;
0228 
0229     off += scnprintf(buf + off, size - off, "fbcvt: %dx%d@%d: CVT Name - ",
0230                 cvt->xres, cvt->yres, cvt->refresh);
0231 
0232     if (cvt->status) {
0233         off += scnprintf(buf + off, size - off,
0234                  "Not a CVT standard - %d.%03d Mega Pixel Image\n",
0235                  pixcount, pixcount_mod);
0236     } else {
0237         if (pixcount)
0238             off += scnprintf(buf + off, size - off, "%d", pixcount);
0239 
0240         off += scnprintf(buf + off, size - off, ".%03dM", pixcount_mod);
0241 
0242         if (cvt->aspect_ratio == 0)
0243             off += scnprintf(buf + off, size - off, "3");
0244         else if (cvt->aspect_ratio == 3)
0245             off += scnprintf(buf + off, size - off, "4");
0246         else if (cvt->aspect_ratio == 1 || cvt->aspect_ratio == 4)
0247             off += scnprintf(buf + off, size - off, "9");
0248         else if (cvt->aspect_ratio == 2)
0249             off += scnprintf(buf + off, size - off, "A");
0250 
0251         if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK)
0252             off += scnprintf(buf + off, size - off, "-R");
0253     }
0254 
0255     printk(KERN_INFO "%s\n", buf);
0256     kfree(buf);
0257 }
0258 
0259 static void fb_cvt_convert_to_mode(struct fb_cvt_data *cvt,
0260                    struct fb_videomode *mode)
0261 {
0262     mode->refresh = cvt->f_refresh;
0263     mode->pixclock = KHZ2PICOS(cvt->pixclock/1000);
0264     mode->left_margin = cvt->h_back_porch;
0265     mode->right_margin = cvt->h_front_porch;
0266     mode->hsync_len = cvt->hsync;
0267     mode->upper_margin = cvt->v_back_porch;
0268     mode->lower_margin = cvt->v_front_porch;
0269     mode->vsync_len = cvt->vsync;
0270 
0271     mode->sync &= ~(FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT);
0272 
0273     if (cvt->flags & FB_CVT_FLAG_REDUCED_BLANK)
0274         mode->sync |= FB_SYNC_HOR_HIGH_ACT;
0275     else
0276         mode->sync |= FB_SYNC_VERT_HIGH_ACT;
0277 }
0278 
0279 /*
0280  * fb_find_mode_cvt - calculate mode using VESA(TM) CVT
0281  * @mode: pointer to fb_videomode; xres, yres, refresh and vmode must be
0282  *        pre-filled with the desired values
0283  * @margins: add margin to calculation (1.8% of xres and yres)
0284  * @rb: compute with reduced blanking (for flatpanels)
0285  *
0286  * RETURNS:
0287  * 0 for success
0288  * @mode is filled with computed values.  If interlaced, the refresh field
0289  * will be filled with the field rate (2x the frame rate)
0290  *
0291  * DESCRIPTION:
0292  * Computes video timings using VESA(TM) Coordinated Video Timings
0293  */
0294 int fb_find_mode_cvt(struct fb_videomode *mode, int margins, int rb)
0295 {
0296     struct fb_cvt_data cvt;
0297 
0298     memset(&cvt, 0, sizeof(cvt));
0299 
0300     if (margins)
0301         cvt.flags |= FB_CVT_FLAG_MARGINS;
0302 
0303     if (rb)
0304         cvt.flags |= FB_CVT_FLAG_REDUCED_BLANK;
0305 
0306     if (mode->vmode & FB_VMODE_INTERLACED)
0307         cvt.flags |= FB_CVT_FLAG_INTERLACED;
0308 
0309     cvt.xres = mode->xres;
0310     cvt.yres = mode->yres;
0311     cvt.refresh = mode->refresh;
0312     cvt.f_refresh = cvt.refresh;
0313     cvt.interlace = 1;
0314 
0315     if (!cvt.xres || !cvt.yres || !cvt.refresh) {
0316         printk(KERN_INFO "fbcvt: Invalid input parameters\n");
0317         return 1;
0318     }
0319 
0320     if (!(cvt.refresh == 50 || cvt.refresh == 60 || cvt.refresh == 70 ||
0321           cvt.refresh == 85)) {
0322         printk(KERN_INFO "fbcvt: Refresh rate not CVT "
0323                "standard\n");
0324         cvt.status = 1;
0325     }
0326 
0327     cvt.xres &= ~(FB_CVT_CELLSIZE - 1);
0328 
0329     if (cvt.flags & FB_CVT_FLAG_INTERLACED) {
0330         cvt.interlace = 2;
0331         cvt.f_refresh *= 2;
0332     }
0333 
0334     if (cvt.flags & FB_CVT_FLAG_REDUCED_BLANK) {
0335         if (cvt.refresh != 60) {
0336             printk(KERN_INFO "fbcvt: 60Hz refresh rate "
0337                    "advised for reduced blanking\n");
0338             cvt.status = 1;
0339         }
0340     }
0341 
0342     if (cvt.flags & FB_CVT_FLAG_MARGINS) {
0343         cvt.h_margin = (cvt.xres * 18)/1000;
0344         cvt.h_margin &= ~(FB_CVT_CELLSIZE - 1);
0345         cvt.v_margin = ((cvt.yres/cvt.interlace)* 18)/1000;
0346     }
0347 
0348     cvt.aspect_ratio = fb_cvt_aspect_ratio(&cvt);
0349     cvt.active_pixels = cvt.xres + 2 * cvt.h_margin;
0350     cvt.hperiod = fb_cvt_hperiod(&cvt);
0351     cvt.vsync = fb_cvt_vbi_tab[cvt.aspect_ratio];
0352     cvt.vtotal = fb_cvt_vtotal(&cvt);
0353     cvt.hblank = fb_cvt_hblank(&cvt);
0354     cvt.htotal = cvt.active_pixels + cvt.hblank;
0355     cvt.hsync = fb_cvt_hsync(&cvt);
0356     cvt.pixclock = fb_cvt_pixclock(&cvt);
0357     cvt.hfreq = cvt.pixclock/cvt.htotal;
0358     cvt.h_back_porch = cvt.hblank/2 + cvt.h_margin;
0359     cvt.h_front_porch = cvt.hblank - cvt.hsync - cvt.h_back_porch +
0360         2 * cvt.h_margin;
0361     cvt.v_front_porch = 3 + cvt.v_margin;
0362     cvt.v_back_porch = cvt.vtotal - cvt.yres/cvt.interlace -
0363         cvt.v_front_porch - cvt.vsync;
0364     fb_cvt_print_name(&cvt);
0365     fb_cvt_convert_to_mode(&cvt, mode);
0366 
0367     return 0;
0368 }