0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
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,
0063 5,
0064 6,
0065 7,
0066 7,
0067 8,
0068 9,
0069 10
0070 };
0071
0072
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
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
0281
0282
0283
0284
0285
0286
0287
0288
0289
0290
0291
0292
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 }