162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Geode GX display controller.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *   Copyright (C) 2005 Arcom Control Systems Ltd.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *   Portions from AMD's original 2.4 driver:
862306a36Sopenharmony_ci *     Copyright (C) 2004 Advanced Micro Devices, Inc.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci#include <linux/spinlock.h>
1162306a36Sopenharmony_ci#include <linux/fb.h>
1262306a36Sopenharmony_ci#include <linux/delay.h>
1362306a36Sopenharmony_ci#include <asm/io.h>
1462306a36Sopenharmony_ci#include <asm/div64.h>
1562306a36Sopenharmony_ci#include <asm/delay.h>
1662306a36Sopenharmony_ci#include <linux/cs5535.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "gxfb.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ciunsigned int gx_frame_buffer_size(void)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	unsigned int val;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	if (!cs5535_has_vsa2()) {
2562306a36Sopenharmony_ci		uint32_t hi, lo;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci		/* The number of pages is (PMAX - PMIN)+1 */
2862306a36Sopenharmony_ci		rdmsr(MSR_GLIU_P2D_RO0, lo, hi);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci		/* PMAX */
3162306a36Sopenharmony_ci		val = ((hi & 0xff) << 12) | ((lo & 0xfff00000) >> 20);
3262306a36Sopenharmony_ci		/* PMIN */
3362306a36Sopenharmony_ci		val -= (lo & 0x000fffff);
3462306a36Sopenharmony_ci		val += 1;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci		/* The page size is 4k */
3762306a36Sopenharmony_ci		return (val << 12);
3862306a36Sopenharmony_ci	}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	/* FB size can be obtained from the VSA II */
4162306a36Sopenharmony_ci	/* Virtual register class = 0x02 */
4262306a36Sopenharmony_ci	/* VG_MEM_SIZE(512Kb units) = 0x00 */
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	outw(VSA_VR_UNLOCK, VSA_VRC_INDEX);
4562306a36Sopenharmony_ci	outw(VSA_VR_MEM_SIZE, VSA_VRC_INDEX);
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	val = (unsigned int)(inw(VSA_VRC_DATA)) & 0xFFl;
4862306a36Sopenharmony_ci	return (val << 19);
4962306a36Sopenharmony_ci}
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ciint gx_line_delta(int xres, int bpp)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	/* Must be a multiple of 8 bytes. */
5462306a36Sopenharmony_ci	return (xres * (bpp >> 3) + 7) & ~0x7;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_civoid gx_set_mode(struct fb_info *info)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	struct gxfb_par *par = info->par;
6062306a36Sopenharmony_ci	u32 gcfg, dcfg;
6162306a36Sopenharmony_ci	int hactive, hblankstart, hsyncstart, hsyncend, hblankend, htotal;
6262306a36Sopenharmony_ci	int vactive, vblankstart, vsyncstart, vsyncend, vblankend, vtotal;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	/* Unlock the display controller registers. */
6562306a36Sopenharmony_ci	write_dc(par, DC_UNLOCK, DC_UNLOCK_UNLOCK);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	gcfg = read_dc(par, DC_GENERAL_CFG);
6862306a36Sopenharmony_ci	dcfg = read_dc(par, DC_DISPLAY_CFG);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	/* Disable the timing generator. */
7162306a36Sopenharmony_ci	dcfg &= ~DC_DISPLAY_CFG_TGEN;
7262306a36Sopenharmony_ci	write_dc(par, DC_DISPLAY_CFG, dcfg);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	/* Wait for pending memory requests before disabling the FIFO load. */
7562306a36Sopenharmony_ci	udelay(100);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	/* Disable FIFO load and compression. */
7862306a36Sopenharmony_ci	gcfg &= ~(DC_GENERAL_CFG_DFLE | DC_GENERAL_CFG_CMPE |
7962306a36Sopenharmony_ci			DC_GENERAL_CFG_DECE);
8062306a36Sopenharmony_ci	write_dc(par, DC_GENERAL_CFG, gcfg);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	/* Setup DCLK and its divisor. */
8362306a36Sopenharmony_ci	gx_set_dclk_frequency(info);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	/*
8662306a36Sopenharmony_ci	 * Setup new mode.
8762306a36Sopenharmony_ci	 */
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	/* Clear all unused feature bits. */
9062306a36Sopenharmony_ci	gcfg &= DC_GENERAL_CFG_YUVM | DC_GENERAL_CFG_VDSE;
9162306a36Sopenharmony_ci	dcfg = 0;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	/* Set FIFO priority (default 6/5) and enable. */
9462306a36Sopenharmony_ci	/* FIXME: increase fifo priority for 1280x1024 and higher modes? */
9562306a36Sopenharmony_ci	gcfg |= (6 << DC_GENERAL_CFG_DFHPEL_SHIFT) |
9662306a36Sopenharmony_ci		(5 << DC_GENERAL_CFG_DFHPSL_SHIFT) | DC_GENERAL_CFG_DFLE;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	/* Framebuffer start offset. */
9962306a36Sopenharmony_ci	write_dc(par, DC_FB_ST_OFFSET, 0);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	/* Line delta and line buffer length. */
10262306a36Sopenharmony_ci	write_dc(par, DC_GFX_PITCH, info->fix.line_length >> 3);
10362306a36Sopenharmony_ci	write_dc(par, DC_LINE_SIZE,
10462306a36Sopenharmony_ci		((info->var.xres * info->var.bits_per_pixel/8) >> 3) + 2);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	/* Enable graphics and video data and unmask address lines. */
10862306a36Sopenharmony_ci	dcfg |= DC_DISPLAY_CFG_GDEN | DC_DISPLAY_CFG_VDEN |
10962306a36Sopenharmony_ci		DC_DISPLAY_CFG_A20M | DC_DISPLAY_CFG_A18M;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/* Set pixel format. */
11262306a36Sopenharmony_ci	switch (info->var.bits_per_pixel) {
11362306a36Sopenharmony_ci	case 8:
11462306a36Sopenharmony_ci		dcfg |= DC_DISPLAY_CFG_DISP_MODE_8BPP;
11562306a36Sopenharmony_ci		break;
11662306a36Sopenharmony_ci	case 16:
11762306a36Sopenharmony_ci		dcfg |= DC_DISPLAY_CFG_DISP_MODE_16BPP;
11862306a36Sopenharmony_ci		break;
11962306a36Sopenharmony_ci	case 32:
12062306a36Sopenharmony_ci		dcfg |= DC_DISPLAY_CFG_DISP_MODE_24BPP;
12162306a36Sopenharmony_ci		dcfg |= DC_DISPLAY_CFG_PALB;
12262306a36Sopenharmony_ci		break;
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	/* Enable timing generator. */
12662306a36Sopenharmony_ci	dcfg |= DC_DISPLAY_CFG_TGEN;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	/* Horizontal and vertical timings. */
12962306a36Sopenharmony_ci	hactive = info->var.xres;
13062306a36Sopenharmony_ci	hblankstart = hactive;
13162306a36Sopenharmony_ci	hsyncstart = hblankstart + info->var.right_margin;
13262306a36Sopenharmony_ci	hsyncend =  hsyncstart + info->var.hsync_len;
13362306a36Sopenharmony_ci	hblankend = hsyncend + info->var.left_margin;
13462306a36Sopenharmony_ci	htotal = hblankend;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	vactive = info->var.yres;
13762306a36Sopenharmony_ci	vblankstart = vactive;
13862306a36Sopenharmony_ci	vsyncstart = vblankstart + info->var.lower_margin;
13962306a36Sopenharmony_ci	vsyncend =  vsyncstart + info->var.vsync_len;
14062306a36Sopenharmony_ci	vblankend = vsyncend + info->var.upper_margin;
14162306a36Sopenharmony_ci	vtotal = vblankend;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	write_dc(par, DC_H_ACTIVE_TIMING, (hactive - 1)    |
14462306a36Sopenharmony_ci			((htotal - 1) << 16));
14562306a36Sopenharmony_ci	write_dc(par, DC_H_BLANK_TIMING, (hblankstart - 1) |
14662306a36Sopenharmony_ci			((hblankend - 1) << 16));
14762306a36Sopenharmony_ci	write_dc(par, DC_H_SYNC_TIMING, (hsyncstart - 1)   |
14862306a36Sopenharmony_ci			((hsyncend - 1) << 16));
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	write_dc(par, DC_V_ACTIVE_TIMING, (vactive - 1)    |
15162306a36Sopenharmony_ci			((vtotal - 1) << 16));
15262306a36Sopenharmony_ci	write_dc(par, DC_V_BLANK_TIMING, (vblankstart - 1) |
15362306a36Sopenharmony_ci			((vblankend - 1) << 16));
15462306a36Sopenharmony_ci	write_dc(par, DC_V_SYNC_TIMING, (vsyncstart - 1)   |
15562306a36Sopenharmony_ci			((vsyncend - 1) << 16));
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	/* Write final register values. */
15862306a36Sopenharmony_ci	write_dc(par, DC_DISPLAY_CFG, dcfg);
15962306a36Sopenharmony_ci	write_dc(par, DC_GENERAL_CFG, gcfg);
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	gx_configure_display(info);
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	/* Relock display controller registers */
16462306a36Sopenharmony_ci	write_dc(par, DC_UNLOCK, DC_UNLOCK_LOCK);
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_civoid gx_set_hw_palette_reg(struct fb_info *info, unsigned regno,
16862306a36Sopenharmony_ci		unsigned red, unsigned green, unsigned blue)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	struct gxfb_par *par = info->par;
17162306a36Sopenharmony_ci	int val;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	/* Hardware palette is in RGB 8-8-8 format. */
17462306a36Sopenharmony_ci	val  = (red   << 8) & 0xff0000;
17562306a36Sopenharmony_ci	val |= (green)      & 0x00ff00;
17662306a36Sopenharmony_ci	val |= (blue  >> 8) & 0x0000ff;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	write_dc(par, DC_PAL_ADDRESS, regno);
17962306a36Sopenharmony_ci	write_dc(par, DC_PAL_DATA, val);
18062306a36Sopenharmony_ci}
181