#include <linux/tty.h>
#include <linux/fb.h>
#include <linux/module.h>
+#include <linux/pci.h>
#include <video/edid.h>
#ifdef CONFIG_PPC_OF
-#include <linux/pci.h>
#include <asm/prom.h>
#include <asm/pci-bridge.h>
#endif
static void calc_mode_timings(int xres, int yres, int refresh,
struct fb_videomode *mode)
{
- struct fb_var_screeninfo var;
- struct fb_info info;
+ struct fb_var_screeninfo *var;
- memset(&var, 0, sizeof(struct fb_var_screeninfo));
- var.xres = xres;
- var.yres = yres;
- fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON,
- refresh, &var, &info);
- mode->xres = xres;
- mode->yres = yres;
- mode->pixclock = var.pixclock;
- mode->refresh = refresh;
- mode->left_margin = var.left_margin;
- mode->right_margin = var.right_margin;
- mode->upper_margin = var.upper_margin;
- mode->lower_margin = var.lower_margin;
- mode->hsync_len = var.hsync_len;
- mode->vsync_len = var.vsync_len;
- mode->vmode = 0;
- mode->sync = 0;
+ var = kzalloc(sizeof(struct fb_var_screeninfo), GFP_KERNEL);
+
+ if (var) {
+ var->xres = xres;
+ var->yres = yres;
+ fb_get_mode(FB_VSYNCTIMINGS | FB_IGNOREMON,
+ refresh, var, NULL);
+ mode->xres = xres;
+ mode->yres = yres;
+ mode->pixclock = var->pixclock;
+ mode->refresh = refresh;
+ mode->left_margin = var->left_margin;
+ mode->right_margin = var->right_margin;
+ mode->upper_margin = var->upper_margin;
+ mode->lower_margin = var->lower_margin;
+ mode->hsync_len = var->hsync_len;
+ mode->vsync_len = var->vsync_len;
+ mode->vmode = 0;
+ mode->sync = 0;
+ kfree(var);
+ }
}
static int get_est_timing(unsigned char *block, struct fb_videomode *mode)
unsigned char *block;
int num = 0, i;
- mode = kmalloc(50 * sizeof(struct fb_videomode), GFP_KERNEL);
+ mode = kzalloc(50 * sizeof(struct fb_videomode), GFP_KERNEL);
if (mode == NULL)
return NULL;
- memset(mode, 0, 50 * sizeof(struct fb_videomode));
if (edid == NULL || !edid_checksum(edid) ||
!edid_check_header(edid)) {
block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
DPRINTK(" Monitor Operating Limits: ");
+
for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) {
if (edid_is_limits_block(block)) {
specs->hfmin = H_MIN_RATE * 1000;
break;
}
}
-
+
/* estimate monitor limits based on modes supported */
if (retval) {
- struct fb_videomode *modes;
+ struct fb_videomode *modes, *mode;
int num_modes, i, hz, hscan, pixclock;
+ int vtotal, htotal;
modes = fb_create_modedb(edid, &num_modes);
if (!modes) {
retval = 0;
for (i = 0; i < num_modes; i++) {
- hz = modes[i].refresh;
+ mode = &modes[i];
pixclock = PICOS2KHZ(modes[i].pixclock) * 1000;
- hscan = (modes[i].yres * 105 * hz + 5000)/100;
+ htotal = mode->xres + mode->right_margin + mode->hsync_len
+ + mode->left_margin;
+ vtotal = mode->yres + mode->lower_margin + mode->vsync_len
+ + mode->upper_margin;
+
+ if (mode->vmode & FB_VMODE_INTERLACED)
+ vtotal /= 2;
+
+ if (mode->vmode & FB_VMODE_DOUBLE)
+ vtotal *= 2;
+
+ hscan = (pixclock + htotal / 2) / htotal;
+ hscan = (hscan + 500) / 1000 * 1000;
+ hz = (hscan + vtotal / 2) / vtotal;
if (specs->dclkmax == 0 || specs->dclkmax < pixclock)
specs->dclkmax = pixclock;
+
if (specs->dclkmin == 0 || specs->dclkmin > pixclock)
specs->dclkmin = pixclock;
+
if (specs->hfmax == 0 || specs->hfmax < hscan)
specs->hfmax = hscan;
+
if (specs->hfmin == 0 || specs->hfmin > hscan)
specs->hfmin = hscan;
+
if (specs->vfmax == 0 || specs->vfmax < hz)
specs->vfmax = hz;
+
if (specs->vfmin == 0 || specs->vfmin > hz)
specs->vfmin = hz;
}
*/
int fb_get_mode(int flags, u32 val, struct fb_var_screeninfo *var, struct fb_info *info)
{
- struct __fb_timings timings;
+ struct __fb_timings *timings;
u32 interlace = 1, dscan = 1;
- u32 hfmin, hfmax, vfmin, vfmax, dclkmin, dclkmax;
+ u32 hfmin, hfmax, vfmin, vfmax, dclkmin, dclkmax, err = 0;
+
+
+ timings = kzalloc(sizeof(struct __fb_timings), GFP_KERNEL);
+
+ if (!timings)
+ return -ENOMEM;
/*
* If monspecs are invalid, use values that are enough
* for 640x480@60
*/
- if (!info->monspecs.hfmax || !info->monspecs.vfmax ||
+ if (!info || !info->monspecs.hfmax || !info->monspecs.vfmax ||
!info->monspecs.dclkmax ||
info->monspecs.hfmax < info->monspecs.hfmin ||
info->monspecs.vfmax < info->monspecs.vfmin ||
dclkmax = info->monspecs.dclkmax;
}
- memset(&timings, 0, sizeof(struct __fb_timings));
- timings.hactive = var->xres;
- timings.vactive = var->yres;
+ timings->hactive = var->xres;
+ timings->vactive = var->yres;
if (var->vmode & FB_VMODE_INTERLACED) {
- timings.vactive /= 2;
+ timings->vactive /= 2;
interlace = 2;
}
if (var->vmode & FB_VMODE_DOUBLE) {
- timings.vactive *= 2;
+ timings->vactive *= 2;
dscan = 2;
}
switch (flags & ~FB_IGNOREMON) {
case FB_MAXTIMINGS: /* maximize refresh rate */
- timings.hfreq = hfmax;
- fb_timings_hfreq(&timings);
- if (timings.vfreq > vfmax) {
- timings.vfreq = vfmax;
- fb_timings_vfreq(&timings);
+ timings->hfreq = hfmax;
+ fb_timings_hfreq(timings);
+ if (timings->vfreq > vfmax) {
+ timings->vfreq = vfmax;
+ fb_timings_vfreq(timings);
}
- if (timings.dclk > dclkmax) {
- timings.dclk = dclkmax;
- fb_timings_dclk(&timings);
+ if (timings->dclk > dclkmax) {
+ timings->dclk = dclkmax;
+ fb_timings_dclk(timings);
}
break;
case FB_VSYNCTIMINGS: /* vrefresh driven */
- timings.vfreq = val;
- fb_timings_vfreq(&timings);
+ timings->vfreq = val;
+ fb_timings_vfreq(timings);
break;
case FB_HSYNCTIMINGS: /* hsync driven */
- timings.hfreq = val;
- fb_timings_hfreq(&timings);
+ timings->hfreq = val;
+ fb_timings_hfreq(timings);
break;
case FB_DCLKTIMINGS: /* pixelclock driven */
- timings.dclk = PICOS2KHZ(val) * 1000;
- fb_timings_dclk(&timings);
+ timings->dclk = PICOS2KHZ(val) * 1000;
+ fb_timings_dclk(timings);
break;
default:
- return -EINVAL;
+ err = -EINVAL;
}
- if (!(flags & FB_IGNOREMON) &&
- (timings.vfreq < vfmin || timings.vfreq > vfmax ||
- timings.hfreq < hfmin || timings.hfreq > hfmax ||
- timings.dclk < dclkmin || timings.dclk > dclkmax))
- return -EINVAL;
-
- var->pixclock = KHZ2PICOS(timings.dclk/1000);
- var->hsync_len = (timings.htotal * 8)/100;
- var->right_margin = (timings.hblank/2) - var->hsync_len;
- var->left_margin = timings.hblank - var->right_margin - var->hsync_len;
-
- var->vsync_len = (3 * interlace)/dscan;
- var->lower_margin = (1 * interlace)/dscan;
- var->upper_margin = (timings.vblank * interlace)/dscan -
- (var->vsync_len + var->lower_margin);
+ if (err || (!(flags & FB_IGNOREMON) &&
+ (timings->vfreq < vfmin || timings->vfreq > vfmax ||
+ timings->hfreq < hfmin || timings->hfreq > hfmax ||
+ timings->dclk < dclkmin || timings->dclk > dclkmax))) {
+ err = -EINVAL;
+ } else {
+ var->pixclock = KHZ2PICOS(timings->dclk/1000);
+ var->hsync_len = (timings->htotal * 8)/100;
+ var->right_margin = (timings->hblank/2) - var->hsync_len;
+ var->left_margin = timings->hblank - var->right_margin -
+ var->hsync_len;
+ var->vsync_len = (3 * interlace)/dscan;
+ var->lower_margin = (1 * interlace)/dscan;
+ var->upper_margin = (timings->vblank * interlace)/dscan -
+ (var->vsync_len + var->lower_margin);
+ }
- return 0;
+ kfree(timings);
+ return err;
}
#else
int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var)
-EINVAL : 0;
}
-#if defined(__i386__)
-#include <linux/pci.h>
+#if defined(CONFIG_FIRMWARE_EDID) && defined(CONFIG_X86)
/*
* We need to ensure that the EDID block is only returned for
{
return NULL;
}
-#endif /* _i386_ */
+#endif
+EXPORT_SYMBOL(fb_firmware_edid);
EXPORT_SYMBOL(fb_parse_edid);
EXPORT_SYMBOL(fb_edid_to_monspecs);
-EXPORT_SYMBOL(fb_firmware_edid);
EXPORT_SYMBOL(fb_get_mode);
EXPORT_SYMBOL(fb_validate_mode);
EXPORT_SYMBOL(fb_destroy_modedb);