in fbdev/amifb.c [1128:1508]
static int ami_decode_var(struct fb_var_screeninfo *var, struct amifb_par *par,
const struct fb_info *info)
{
u_short clk_shift, line_shift;
u_long maxfetchstop, fstrt, fsize, fconst, xres_n, yres_n;
u_int htotal, vtotal;
/*
* Find a matching Pixel Clock
*/
for (clk_shift = TAG_SHRES; clk_shift <= TAG_LORES; clk_shift++)
if (var->pixclock <= pixclock[clk_shift])
break;
if (clk_shift > TAG_LORES) {
DPRINTK("pixclock too high\n");
return -EINVAL;
}
par->clk_shift = clk_shift;
/*
* Check the Geometry Values
*/
if ((par->xres = var->xres) < 64)
par->xres = 64;
if ((par->yres = var->yres) < 64)
par->yres = 64;
if ((par->vxres = var->xres_virtual) < par->xres)
par->vxres = par->xres;
if ((par->vyres = var->yres_virtual) < par->yres)
par->vyres = par->yres;
par->bpp = var->bits_per_pixel;
if (!var->nonstd) {
if (par->bpp < 1)
par->bpp = 1;
if (par->bpp > maxdepth[clk_shift]) {
if (round_down_bpp && maxdepth[clk_shift])
par->bpp = maxdepth[clk_shift];
else {
DPRINTK("invalid bpp\n");
return -EINVAL;
}
}
} else if (var->nonstd == FB_NONSTD_HAM) {
if (par->bpp < 6)
par->bpp = 6;
if (par->bpp != 6) {
if (par->bpp < 8)
par->bpp = 8;
if (par->bpp != 8 || !IS_AGA) {
DPRINTK("invalid bpp for ham mode\n");
return -EINVAL;
}
}
} else {
DPRINTK("unknown nonstd mode\n");
return -EINVAL;
}
/*
* FB_VMODE_SMOOTH_XPAN will be cleared, if one of the following
* checks failed and smooth scrolling is not possible
*/
par->vmode = var->vmode | FB_VMODE_SMOOTH_XPAN;
switch (par->vmode & FB_VMODE_MASK) {
case FB_VMODE_INTERLACED:
line_shift = 0;
break;
case FB_VMODE_NONINTERLACED:
line_shift = 1;
break;
case FB_VMODE_DOUBLE:
if (!IS_AGA) {
DPRINTK("double mode only possible with aga\n");
return -EINVAL;
}
line_shift = 2;
break;
default:
DPRINTK("unknown video mode\n");
return -EINVAL;
break;
}
par->line_shift = line_shift;
/*
* Vertical and Horizontal Timings
*/
xres_n = par->xres << clk_shift;
yres_n = par->yres << line_shift;
par->htotal = down8((var->left_margin + par->xres + var->right_margin +
var->hsync_len) << clk_shift);
par->vtotal =
down2(((var->upper_margin + par->yres + var->lower_margin +
var->vsync_len) << line_shift) + 1);
if (IS_AGA)
par->bplcon3 = sprpixmode[clk_shift];
else
par->bplcon3 = 0;
if (var->sync & FB_SYNC_BROADCAST) {
par->diwstop_h = par->htotal -
((var->right_margin - var->hsync_len) << clk_shift);
if (IS_AGA)
par->diwstop_h += mod4(var->hsync_len);
else
par->diwstop_h = down4(par->diwstop_h);
par->diwstrt_h = par->diwstop_h - xres_n;
par->diwstop_v = par->vtotal -
((var->lower_margin - var->vsync_len) << line_shift);
par->diwstrt_v = par->diwstop_v - yres_n;
if (par->diwstop_h >= par->htotal + 8) {
DPRINTK("invalid diwstop_h\n");
return -EINVAL;
}
if (par->diwstop_v > par->vtotal) {
DPRINTK("invalid diwstop_v\n");
return -EINVAL;
}
if (!IS_OCS) {
/* Initialize sync with some reasonable values for pwrsave */
par->hsstrt = 160;
par->hsstop = 320;
par->vsstrt = 30;
par->vsstop = 34;
} else {
par->hsstrt = 0;
par->hsstop = 0;
par->vsstrt = 0;
par->vsstop = 0;
}
if (par->vtotal > (PAL_VTOTAL + NTSC_VTOTAL) / 2) {
/* PAL video mode */
if (par->htotal != PAL_HTOTAL) {
DPRINTK("htotal invalid for pal\n");
return -EINVAL;
}
if (par->diwstrt_h < PAL_DIWSTRT_H) {
DPRINTK("diwstrt_h too low for pal\n");
return -EINVAL;
}
if (par->diwstrt_v < PAL_DIWSTRT_V) {
DPRINTK("diwstrt_v too low for pal\n");
return -EINVAL;
}
htotal = PAL_HTOTAL>>clk_shift;
vtotal = PAL_VTOTAL>>1;
if (!IS_OCS) {
par->beamcon0 = BMC0_PAL;
par->bplcon3 |= BPC3_BRDRBLNK;
} else if (AMIGAHW_PRESENT(AGNUS_HR_PAL) ||
AMIGAHW_PRESENT(AGNUS_HR_NTSC)) {
par->beamcon0 = BMC0_PAL;
par->hsstop = 1;
} else if (amiga_vblank != 50) {
DPRINTK("pal not supported by this chipset\n");
return -EINVAL;
}
} else {
/* NTSC video mode
* In the AGA chipset seems to be hardware bug with BPC3_BRDRBLNK
* and NTSC activated, so than better let diwstop_h <= 1812
*/
if (par->htotal != NTSC_HTOTAL) {
DPRINTK("htotal invalid for ntsc\n");
return -EINVAL;
}
if (par->diwstrt_h < NTSC_DIWSTRT_H) {
DPRINTK("diwstrt_h too low for ntsc\n");
return -EINVAL;
}
if (par->diwstrt_v < NTSC_DIWSTRT_V) {
DPRINTK("diwstrt_v too low for ntsc\n");
return -EINVAL;
}
htotal = NTSC_HTOTAL>>clk_shift;
vtotal = NTSC_VTOTAL>>1;
if (!IS_OCS) {
par->beamcon0 = 0;
par->bplcon3 |= BPC3_BRDRBLNK;
} else if (AMIGAHW_PRESENT(AGNUS_HR_PAL) ||
AMIGAHW_PRESENT(AGNUS_HR_NTSC)) {
par->beamcon0 = 0;
par->hsstop = 1;
} else if (amiga_vblank != 60) {
DPRINTK("ntsc not supported by this chipset\n");
return -EINVAL;
}
}
if (IS_OCS) {
if (par->diwstrt_h >= 1024 || par->diwstop_h < 1024 ||
par->diwstrt_v >= 512 || par->diwstop_v < 256) {
DPRINTK("invalid position for display on ocs\n");
return -EINVAL;
}
}
} else if (!IS_OCS) {
/* Programmable video mode */
par->hsstrt = var->right_margin << clk_shift;
par->hsstop = (var->right_margin + var->hsync_len) << clk_shift;
par->diwstop_h = par->htotal - mod8(par->hsstrt) + 8 - (1 << clk_shift);
if (!IS_AGA)
par->diwstop_h = down4(par->diwstop_h) - 16;
par->diwstrt_h = par->diwstop_h - xres_n;
par->hbstop = par->diwstrt_h + 4;
par->hbstrt = par->diwstop_h + 4;
if (par->hbstrt >= par->htotal + 8)
par->hbstrt -= par->htotal;
par->hcenter = par->hsstrt + (par->htotal >> 1);
par->vsstrt = var->lower_margin << line_shift;
par->vsstop = (var->lower_margin + var->vsync_len) << line_shift;
par->diwstop_v = par->vtotal;
if ((par->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED)
par->diwstop_v -= 2;
par->diwstrt_v = par->diwstop_v - yres_n;
par->vbstop = par->diwstrt_v - 2;
par->vbstrt = par->diwstop_v - 2;
if (par->vtotal > 2048) {
DPRINTK("vtotal too high\n");
return -EINVAL;
}
if (par->htotal > 2048) {
DPRINTK("htotal too high\n");
return -EINVAL;
}
par->bplcon3 |= BPC3_EXTBLKEN;
par->beamcon0 = BMC0_HARDDIS | BMC0_VARVBEN | BMC0_LOLDIS |
BMC0_VARVSYEN | BMC0_VARHSYEN | BMC0_VARBEAMEN |
BMC0_PAL | BMC0_VARCSYEN;
if (var->sync & FB_SYNC_HOR_HIGH_ACT)
par->beamcon0 |= BMC0_HSYTRUE;
if (var->sync & FB_SYNC_VERT_HIGH_ACT)
par->beamcon0 |= BMC0_VSYTRUE;
if (var->sync & FB_SYNC_COMP_HIGH_ACT)
par->beamcon0 |= BMC0_CSYTRUE;
htotal = par->htotal>>clk_shift;
vtotal = par->vtotal>>1;
} else {
DPRINTK("only broadcast modes possible for ocs\n");
return -EINVAL;
}
/*
* Checking the DMA timing
*/
fconst = 16 << maxfmode << clk_shift;
/*
* smallest window start value without turn off other dma cycles
* than sprite1-7, unless you change min_fstrt
*/
fsize = ((maxfmode + clk_shift <= 1) ? fconst : 64);
fstrt = downx(fconst, par->diwstrt_h - 4) - fsize;
if (fstrt < min_fstrt) {
DPRINTK("fetch start too low\n");
return -EINVAL;
}
/*
* smallest window start value where smooth scrolling is possible
*/
fstrt = downx(fconst, par->diwstrt_h - fconst + (1 << clk_shift) - 4) -
fsize;
if (fstrt < min_fstrt)
par->vmode &= ~FB_VMODE_SMOOTH_XPAN;
maxfetchstop = down16(par->htotal - 80);
fstrt = downx(fconst, par->diwstrt_h - 4) - 64 - fconst;
fsize = upx(fconst, xres_n +
modx(fconst, downx(1 << clk_shift, par->diwstrt_h - 4)));
if (fstrt + fsize > maxfetchstop)
par->vmode &= ~FB_VMODE_SMOOTH_XPAN;
fsize = upx(fconst, xres_n);
if (fstrt + fsize > maxfetchstop) {
DPRINTK("fetch stop too high\n");
return -EINVAL;
}
if (maxfmode + clk_shift <= 1) {
fsize = up64(xres_n + fconst - 1);
if (min_fstrt + fsize - 64 > maxfetchstop)
par->vmode &= ~FB_VMODE_SMOOTH_XPAN;
fsize = up64(xres_n);
if (min_fstrt + fsize - 64 > maxfetchstop) {
DPRINTK("fetch size too high\n");
return -EINVAL;
}
fsize -= 64;
} else
fsize -= fconst;
/*
* Check if there is enough time to update the bitplane pointers for ywrap
*/
if (par->htotal - fsize - 64 < par->bpp * 64)
par->vmode &= ~FB_VMODE_YWRAP;
/*
* Bitplane calculations and check the Memory Requirements
*/
if (amifb_ilbm) {
par->next_plane = div8(upx(16 << maxfmode, par->vxres));
par->next_line = par->bpp * par->next_plane;
if (par->next_line * par->vyres > info->fix.smem_len) {
DPRINTK("too few video mem\n");
return -EINVAL;
}
} else {
par->next_line = div8(upx(16 << maxfmode, par->vxres));
par->next_plane = par->vyres * par->next_line;
if (par->next_plane * par->bpp > info->fix.smem_len) {
DPRINTK("too few video mem\n");
return -EINVAL;
}
}
/*
* Hardware Register Values
*/
par->bplcon0 = BPC0_COLOR | bplpixmode[clk_shift];
if (!IS_OCS)
par->bplcon0 |= BPC0_ECSENA;
if (par->bpp == 8)
par->bplcon0 |= BPC0_BPU3;
else
par->bplcon0 |= par->bpp << 12;
if (var->nonstd == FB_NONSTD_HAM)
par->bplcon0 |= BPC0_HAM;
if (var->sync & FB_SYNC_EXT)
par->bplcon0 |= BPC0_ERSY;
if (IS_AGA)
par->fmode = bplfetchmode[maxfmode];
switch (par->vmode & FB_VMODE_MASK) {
case FB_VMODE_INTERLACED:
par->bplcon0 |= BPC0_LACE;
break;
case FB_VMODE_DOUBLE:
if (IS_AGA)
par->fmode |= FMODE_SSCAN2 | FMODE_BSCAN2;
break;
}
if (!((par->vmode ^ var->vmode) & FB_VMODE_YWRAP)) {
par->xoffset = var->xoffset;
par->yoffset = var->yoffset;
if (par->vmode & FB_VMODE_YWRAP) {
if (par->yoffset >= par->vyres)
par->xoffset = par->yoffset = 0;
} else {
if (par->xoffset > upx(16 << maxfmode, par->vxres - par->xres) ||
par->yoffset > par->vyres - par->yres)
par->xoffset = par->yoffset = 0;
}
} else
par->xoffset = par->yoffset = 0;
par->crsr.crsr_x = par->crsr.crsr_y = 0;
par->crsr.spot_x = par->crsr.spot_y = 0;
par->crsr.height = par->crsr.width = 0;
return 0;
}