static int ami_decode_var()

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;
}