static int radeonfb_set_par()

in fbdev/aty/radeon_base.c [1637:1949]


static int radeonfb_set_par(struct fb_info *info)
{
	struct radeonfb_info *rinfo = info->par;
	struct fb_var_screeninfo *mode = &info->var;
	struct radeon_regs *newmode;
	int hTotal, vTotal, hSyncStart, hSyncEnd,
	    vSyncStart, vSyncEnd;
	u8 hsync_adj_tab[] = {0, 0x12, 9, 9, 6, 5};
	u8 hsync_fudge_fp[] = {2, 2, 0, 0, 5, 5};
	u32 sync, h_sync_pol, v_sync_pol, dotClock, pixClock;
	int i, freq;
	int format = 0;
	int nopllcalc = 0;
	int hsync_start, hsync_fudge, hsync_wid, vsync_wid;
	int primary_mon = PRIMARY_MONITOR(rinfo);
	int depth = var_to_depth(mode);
	int use_rmx = 0;

	newmode = kmalloc(sizeof(struct radeon_regs), GFP_KERNEL);
	if (!newmode)
		return -ENOMEM;

	/* We always want engine to be idle on a mode switch, even
	 * if we won't actually change the mode
	 */
	radeon_engine_idle();

	hSyncStart = mode->xres + mode->right_margin;
	hSyncEnd = hSyncStart + mode->hsync_len;
	hTotal = hSyncEnd + mode->left_margin;

	vSyncStart = mode->yres + mode->lower_margin;
	vSyncEnd = vSyncStart + mode->vsync_len;
	vTotal = vSyncEnd + mode->upper_margin;
	pixClock = mode->pixclock;

	sync = mode->sync;
	h_sync_pol = sync & FB_SYNC_HOR_HIGH_ACT ? 0 : 1;
	v_sync_pol = sync & FB_SYNC_VERT_HIGH_ACT ? 0 : 1;

	if (primary_mon == MT_DFP || primary_mon == MT_LCD) {
		if (rinfo->panel_info.xres < mode->xres)
			mode->xres = rinfo->panel_info.xres;
		if (rinfo->panel_info.yres < mode->yres)
			mode->yres = rinfo->panel_info.yres;

		hTotal = mode->xres + rinfo->panel_info.hblank;
		hSyncStart = mode->xres + rinfo->panel_info.hOver_plus;
		hSyncEnd = hSyncStart + rinfo->panel_info.hSync_width;

		vTotal = mode->yres + rinfo->panel_info.vblank;
		vSyncStart = mode->yres + rinfo->panel_info.vOver_plus;
		vSyncEnd = vSyncStart + rinfo->panel_info.vSync_width;

		h_sync_pol = !rinfo->panel_info.hAct_high;
		v_sync_pol = !rinfo->panel_info.vAct_high;

		pixClock = 100000000 / rinfo->panel_info.clock;

		if (rinfo->panel_info.use_bios_dividers) {
			nopllcalc = 1;
			newmode->ppll_div_3 = rinfo->panel_info.fbk_divider |
				(rinfo->panel_info.post_divider << 16);
			newmode->ppll_ref_div = rinfo->panel_info.ref_divider;
		}
	}
	dotClock = 1000000000 / pixClock;
	freq = dotClock / 10; /* x100 */

	pr_debug("hStart = %d, hEnd = %d, hTotal = %d\n",
		hSyncStart, hSyncEnd, hTotal);
	pr_debug("vStart = %d, vEnd = %d, vTotal = %d\n",
		vSyncStart, vSyncEnd, vTotal);

	hsync_wid = (hSyncEnd - hSyncStart) / 8;
	vsync_wid = vSyncEnd - vSyncStart;
	if (hsync_wid == 0)
		hsync_wid = 1;
	else if (hsync_wid > 0x3f)	/* max */
		hsync_wid = 0x3f;

	if (vsync_wid == 0)
		vsync_wid = 1;
	else if (vsync_wid > 0x1f)	/* max */
		vsync_wid = 0x1f;

	format = radeon_get_dstbpp(depth);

	if ((primary_mon == MT_DFP) || (primary_mon == MT_LCD))
		hsync_fudge = hsync_fudge_fp[format-1];
	else
		hsync_fudge = hsync_adj_tab[format-1];

	hsync_start = hSyncStart - 8 + hsync_fudge;

	newmode->crtc_gen_cntl = CRTC_EXT_DISP_EN | CRTC_EN |
				(format << 8);

	/* Clear auto-center etc... */
	newmode->crtc_more_cntl = rinfo->init_state.crtc_more_cntl;
	newmode->crtc_more_cntl &= 0xfffffff0;
	
	if ((primary_mon == MT_DFP) || (primary_mon == MT_LCD)) {
		newmode->crtc_ext_cntl = VGA_ATI_LINEAR | XCRT_CNT_EN;
		if (mirror)
			newmode->crtc_ext_cntl |= CRTC_CRT_ON;

		newmode->crtc_gen_cntl &= ~(CRTC_DBL_SCAN_EN |
					   CRTC_INTERLACE_EN);
	} else {
		newmode->crtc_ext_cntl = VGA_ATI_LINEAR | XCRT_CNT_EN |
					CRTC_CRT_ON;
	}

	newmode->dac_cntl = /* INREG(DAC_CNTL) | */ DAC_MASK_ALL | DAC_VGA_ADR_EN |
			   DAC_8BIT_EN;

	newmode->crtc_h_total_disp = ((((hTotal / 8) - 1) & 0x3ff) |
				     (((mode->xres / 8) - 1) << 16));

	newmode->crtc_h_sync_strt_wid = ((hsync_start & 0x1fff) |
					(hsync_wid << 16) | (h_sync_pol << 23));

	newmode->crtc_v_total_disp = ((vTotal - 1) & 0xffff) |
				    ((mode->yres - 1) << 16);

	newmode->crtc_v_sync_strt_wid = (((vSyncStart - 1) & 0xfff) |
					 (vsync_wid << 16) | (v_sync_pol  << 23));

	if (!(info->flags & FBINFO_HWACCEL_DISABLED)) {
		/* We first calculate the engine pitch */
		rinfo->pitch = ((mode->xres_virtual * ((mode->bits_per_pixel + 1) / 8) + 0x3f)
 				& ~(0x3f)) >> 6;

		/* Then, re-multiply it to get the CRTC pitch */
		newmode->crtc_pitch = (rinfo->pitch << 3) / ((mode->bits_per_pixel + 1) / 8);
	} else
		newmode->crtc_pitch = (mode->xres_virtual >> 3);

	newmode->crtc_pitch |= (newmode->crtc_pitch << 16);

	/*
	 * It looks like recent chips have a problem with SURFACE_CNTL,
	 * setting SURF_TRANSLATION_DIS completely disables the
	 * swapper as well, so we leave it unset now.
	 */
	newmode->surface_cntl = 0;

#if defined(__BIG_ENDIAN)

	/* Setup swapping on both apertures, though we currently
	 * only use aperture 0, enabling swapper on aperture 1
	 * won't harm
	 */
	switch (mode->bits_per_pixel) {
		case 16:
			newmode->surface_cntl |= NONSURF_AP0_SWP_16BPP;
			newmode->surface_cntl |= NONSURF_AP1_SWP_16BPP;
			break;
		case 24:	
		case 32:
			newmode->surface_cntl |= NONSURF_AP0_SWP_32BPP;
			newmode->surface_cntl |= NONSURF_AP1_SWP_32BPP;
			break;
	}
#endif

	/* Clear surface registers */
	for (i=0; i<8; i++) {
		newmode->surf_lower_bound[i] = 0;
		newmode->surf_upper_bound[i] = 0x1f;
		newmode->surf_info[i] = 0;
	}

	pr_debug("h_total_disp = 0x%x\t   hsync_strt_wid = 0x%x\n",
		newmode->crtc_h_total_disp, newmode->crtc_h_sync_strt_wid);
	pr_debug("v_total_disp = 0x%x\t   vsync_strt_wid = 0x%x\n",
		newmode->crtc_v_total_disp, newmode->crtc_v_sync_strt_wid);

	rinfo->bpp = mode->bits_per_pixel;
	rinfo->depth = depth;

	pr_debug("pixclock = %lu\n", (unsigned long)pixClock);
	pr_debug("freq = %lu\n", (unsigned long)freq);

	/* We use PPLL_DIV_3 */
	newmode->clk_cntl_index = 0x300;

	/* Calculate PPLL value if necessary */
	if (!nopllcalc)
		radeon_calc_pll_regs(rinfo, newmode, freq);

	newmode->vclk_ecp_cntl = rinfo->init_state.vclk_ecp_cntl;

	if ((primary_mon == MT_DFP) || (primary_mon == MT_LCD)) {
		unsigned int hRatio, vRatio;

		if (mode->xres > rinfo->panel_info.xres)
			mode->xres = rinfo->panel_info.xres;
		if (mode->yres > rinfo->panel_info.yres)
			mode->yres = rinfo->panel_info.yres;

		newmode->fp_horz_stretch = (((rinfo->panel_info.xres / 8) - 1)
					   << HORZ_PANEL_SHIFT);
		newmode->fp_vert_stretch = ((rinfo->panel_info.yres - 1)
					   << VERT_PANEL_SHIFT);

		if (mode->xres != rinfo->panel_info.xres) {
			hRatio = round_div(mode->xres * HORZ_STRETCH_RATIO_MAX,
					   rinfo->panel_info.xres);
			newmode->fp_horz_stretch = (((((unsigned long)hRatio) & HORZ_STRETCH_RATIO_MASK)) |
						   (newmode->fp_horz_stretch &
						    (HORZ_PANEL_SIZE | HORZ_FP_LOOP_STRETCH |
						     HORZ_AUTO_RATIO_INC)));
			newmode->fp_horz_stretch |= (HORZ_STRETCH_BLEND |
						    HORZ_STRETCH_ENABLE);
			use_rmx = 1;
		}
		newmode->fp_horz_stretch &= ~HORZ_AUTO_RATIO;

		if (mode->yres != rinfo->panel_info.yres) {
			vRatio = round_div(mode->yres * VERT_STRETCH_RATIO_MAX,
					   rinfo->panel_info.yres);
			newmode->fp_vert_stretch = (((((unsigned long)vRatio) & VERT_STRETCH_RATIO_MASK)) |
						   (newmode->fp_vert_stretch &
						   (VERT_PANEL_SIZE | VERT_STRETCH_RESERVED)));
			newmode->fp_vert_stretch |= (VERT_STRETCH_BLEND |
						    VERT_STRETCH_ENABLE);
			use_rmx = 1;
		}
		newmode->fp_vert_stretch &= ~VERT_AUTO_RATIO_EN;

		newmode->fp_gen_cntl = (rinfo->init_state.fp_gen_cntl & (u32)
				       ~(FP_SEL_CRTC2 |
					 FP_RMX_HVSYNC_CONTROL_EN |
					 FP_DFP_SYNC_SEL |
					 FP_CRT_SYNC_SEL |
					 FP_CRTC_LOCK_8DOT |
					 FP_USE_SHADOW_EN |
					 FP_CRTC_USE_SHADOW_VEND |
					 FP_CRT_SYNC_ALT));

		newmode->fp_gen_cntl |= (FP_CRTC_DONT_SHADOW_VPAR |
					FP_CRTC_DONT_SHADOW_HEND |
					FP_PANEL_FORMAT);

		if (IS_R300_VARIANT(rinfo) ||
		    (rinfo->family == CHIP_FAMILY_R200)) {
			newmode->fp_gen_cntl &= ~R200_FP_SOURCE_SEL_MASK;
			if (use_rmx)
				newmode->fp_gen_cntl |= R200_FP_SOURCE_SEL_RMX;
			else
				newmode->fp_gen_cntl |= R200_FP_SOURCE_SEL_CRTC1;
		} else
			newmode->fp_gen_cntl |= FP_SEL_CRTC1;

		newmode->lvds_gen_cntl = rinfo->init_state.lvds_gen_cntl;
		newmode->lvds_pll_cntl = rinfo->init_state.lvds_pll_cntl;
		newmode->tmds_crc = rinfo->init_state.tmds_crc;
		newmode->tmds_transmitter_cntl = rinfo->init_state.tmds_transmitter_cntl;

		if (primary_mon == MT_LCD) {
			newmode->lvds_gen_cntl |= (LVDS_ON | LVDS_BLON);
			newmode->fp_gen_cntl &= ~(FP_FPON | FP_TMDS_EN);
		} else {
			/* DFP */
			newmode->fp_gen_cntl |= (FP_FPON | FP_TMDS_EN);
			newmode->tmds_transmitter_cntl &= ~(TMDS_PLLRST);
			/* TMDS_PLL_EN bit is reversed on RV (and mobility) chips */
			if (IS_R300_VARIANT(rinfo) ||
			    (rinfo->family == CHIP_FAMILY_R200) || !rinfo->has_CRTC2)
				newmode->tmds_transmitter_cntl &= ~TMDS_PLL_EN;
			else
				newmode->tmds_transmitter_cntl |= TMDS_PLL_EN;
			newmode->crtc_ext_cntl &= ~CRTC_CRT_ON;
		}

		newmode->fp_crtc_h_total_disp = (((rinfo->panel_info.hblank / 8) & 0x3ff) |
				(((mode->xres / 8) - 1) << 16));
		newmode->fp_crtc_v_total_disp = (rinfo->panel_info.vblank & 0xffff) |
				((mode->yres - 1) << 16);
		newmode->fp_h_sync_strt_wid = ((rinfo->panel_info.hOver_plus & 0x1fff) |
				(hsync_wid << 16) | (h_sync_pol << 23));
		newmode->fp_v_sync_strt_wid = ((rinfo->panel_info.vOver_plus & 0xfff) |
				(vsync_wid << 16) | (v_sync_pol  << 23));
	}

	/* do it! */
	if (!rinfo->asleep) {
		memcpy(&rinfo->state, newmode, sizeof(*newmode));
		radeon_write_mode (rinfo, newmode, 0);
		/* (re)initialize the engine */
		if (!(info->flags & FBINFO_HWACCEL_DISABLED))
			radeonfb_engine_init (rinfo);
	}
	/* Update fix */
	if (!(info->flags & FBINFO_HWACCEL_DISABLED))
        	info->fix.line_length = rinfo->pitch*64;
        else
		info->fix.line_length = mode->xres_virtual
			* ((mode->bits_per_pixel + 1) / 8);
        info->fix.visual = rinfo->depth == 8 ? FB_VISUAL_PSEUDOCOLOR
		: FB_VISUAL_DIRECTCOLOR;

#ifdef CONFIG_BOOTX_TEXT
	/* Update debug text engine */
	btext_update_display(rinfo->fb_base_phys, mode->xres, mode->yres,
			     rinfo->depth, info->fix.line_length);
#endif

	kfree(newmode);
	return 0;
}