162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Portions Copyright (c) 2001 Matrox Graphics Inc.
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * Version: 1.65 2002/08/14
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * See matroxfb_base.c for contributors.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "matroxfb_base.h"
1762306a36Sopenharmony_ci#include "matroxfb_misc.h"
1862306a36Sopenharmony_ci#include "matroxfb_DAC1064.h"
1962306a36Sopenharmony_ci#include "g450_pll.h"
2062306a36Sopenharmony_ci#include <linux/matroxfb.h>
2162306a36Sopenharmony_ci#include <asm/div64.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include "matroxfb_g450.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/* Definition of the various controls */
2662306a36Sopenharmony_cistruct mctl {
2762306a36Sopenharmony_ci	struct v4l2_queryctrl desc;
2862306a36Sopenharmony_ci	size_t control;
2962306a36Sopenharmony_ci};
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define BLMIN	0xF3
3262306a36Sopenharmony_ci#define WLMAX	0x3FF
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic const struct mctl g450_controls[] =
3562306a36Sopenharmony_ci{	{ { V4L2_CID_BRIGHTNESS, V4L2_CTRL_TYPE_INTEGER,
3662306a36Sopenharmony_ci	  "brightness",
3762306a36Sopenharmony_ci	  0, WLMAX-BLMIN, 1, 370-BLMIN,
3862306a36Sopenharmony_ci	  0,
3962306a36Sopenharmony_ci	}, offsetof(struct matrox_fb_info, altout.tvo_params.brightness) },
4062306a36Sopenharmony_ci	{ { V4L2_CID_CONTRAST, V4L2_CTRL_TYPE_INTEGER,
4162306a36Sopenharmony_ci	  "contrast",
4262306a36Sopenharmony_ci	  0, 1023, 1, 127,
4362306a36Sopenharmony_ci	  0,
4462306a36Sopenharmony_ci	}, offsetof(struct matrox_fb_info, altout.tvo_params.contrast) },
4562306a36Sopenharmony_ci	{ { V4L2_CID_SATURATION, V4L2_CTRL_TYPE_INTEGER,
4662306a36Sopenharmony_ci	  "saturation",
4762306a36Sopenharmony_ci	  0, 255, 1, 165,
4862306a36Sopenharmony_ci	  0,
4962306a36Sopenharmony_ci	}, offsetof(struct matrox_fb_info, altout.tvo_params.saturation) },
5062306a36Sopenharmony_ci	{ { V4L2_CID_HUE, V4L2_CTRL_TYPE_INTEGER,
5162306a36Sopenharmony_ci	  "hue",
5262306a36Sopenharmony_ci	  0, 255, 1, 0,
5362306a36Sopenharmony_ci	  0,
5462306a36Sopenharmony_ci	}, offsetof(struct matrox_fb_info, altout.tvo_params.hue) },
5562306a36Sopenharmony_ci	{ { MATROXFB_CID_TESTOUT, V4L2_CTRL_TYPE_BOOLEAN,
5662306a36Sopenharmony_ci	  "test output",
5762306a36Sopenharmony_ci	  0, 1, 1, 0,
5862306a36Sopenharmony_ci	  0,
5962306a36Sopenharmony_ci	}, offsetof(struct matrox_fb_info, altout.tvo_params.testout) },
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci#define G450CTRLS ARRAY_SIZE(g450_controls)
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/* Return: positive number: id found
6562306a36Sopenharmony_ci           -EINVAL:         id not found, return failure
6662306a36Sopenharmony_ci	   -ENOENT:         id not found, create fake disabled control */
6762306a36Sopenharmony_cistatic int get_ctrl_id(__u32 v4l2_id) {
6862306a36Sopenharmony_ci	int i;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	for (i = 0; i < G450CTRLS; i++) {
7162306a36Sopenharmony_ci		if (v4l2_id < g450_controls[i].desc.id) {
7262306a36Sopenharmony_ci			if (g450_controls[i].desc.id == 0x08000000) {
7362306a36Sopenharmony_ci				return -EINVAL;
7462306a36Sopenharmony_ci			}
7562306a36Sopenharmony_ci			return -ENOENT;
7662306a36Sopenharmony_ci		}
7762306a36Sopenharmony_ci		if (v4l2_id == g450_controls[i].desc.id) {
7862306a36Sopenharmony_ci			return i;
7962306a36Sopenharmony_ci		}
8062306a36Sopenharmony_ci	}
8162306a36Sopenharmony_ci	return -EINVAL;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic inline int *get_ctrl_ptr(struct matrox_fb_info *minfo, unsigned int idx)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	return (int*)((char*)minfo + g450_controls[idx].control);
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic void tvo_fill_defaults(struct matrox_fb_info *minfo)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	unsigned int i;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	for (i = 0; i < G450CTRLS; i++) {
9462306a36Sopenharmony_ci		*get_ctrl_ptr(minfo, i) = g450_controls[i].desc.default_value;
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic int cve2_get_reg(struct matrox_fb_info *minfo, int reg)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	unsigned long flags;
10162306a36Sopenharmony_ci	int val;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	matroxfb_DAC_lock_irqsave(flags);
10462306a36Sopenharmony_ci	matroxfb_DAC_out(minfo, 0x87, reg);
10562306a36Sopenharmony_ci	val = matroxfb_DAC_in(minfo, 0x88);
10662306a36Sopenharmony_ci	matroxfb_DAC_unlock_irqrestore(flags);
10762306a36Sopenharmony_ci	return val;
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic void cve2_set_reg(struct matrox_fb_info *minfo, int reg, int val)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	unsigned long flags;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	matroxfb_DAC_lock_irqsave(flags);
11562306a36Sopenharmony_ci	matroxfb_DAC_out(minfo, 0x87, reg);
11662306a36Sopenharmony_ci	matroxfb_DAC_out(minfo, 0x88, val);
11762306a36Sopenharmony_ci	matroxfb_DAC_unlock_irqrestore(flags);
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic void cve2_set_reg10(struct matrox_fb_info *minfo, int reg, int val)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	unsigned long flags;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	matroxfb_DAC_lock_irqsave(flags);
12562306a36Sopenharmony_ci	matroxfb_DAC_out(minfo, 0x87, reg);
12662306a36Sopenharmony_ci	matroxfb_DAC_out(minfo, 0x88, val >> 2);
12762306a36Sopenharmony_ci	matroxfb_DAC_out(minfo, 0x87, reg + 1);
12862306a36Sopenharmony_ci	matroxfb_DAC_out(minfo, 0x88, val & 3);
12962306a36Sopenharmony_ci	matroxfb_DAC_unlock_irqrestore(flags);
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic void g450_compute_bwlevel(const struct matrox_fb_info *minfo, int *bl,
13362306a36Sopenharmony_ci				 int *wl)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	const int b = minfo->altout.tvo_params.brightness + BLMIN;
13662306a36Sopenharmony_ci	const int c = minfo->altout.tvo_params.contrast;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	*bl = max(b - c, BLMIN);
13962306a36Sopenharmony_ci	*wl = min(b + c, WLMAX);
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_cistatic int g450_query_ctrl(void* md, struct v4l2_queryctrl *p) {
14362306a36Sopenharmony_ci	int i;
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	i = get_ctrl_id(p->id);
14662306a36Sopenharmony_ci	if (i >= 0) {
14762306a36Sopenharmony_ci		*p = g450_controls[i].desc;
14862306a36Sopenharmony_ci		return 0;
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci	if (i == -ENOENT) {
15162306a36Sopenharmony_ci		static const struct v4l2_queryctrl disctrl =
15262306a36Sopenharmony_ci			{ .flags = V4L2_CTRL_FLAG_DISABLED };
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci		i = p->id;
15562306a36Sopenharmony_ci		*p = disctrl;
15662306a36Sopenharmony_ci		p->id = i;
15762306a36Sopenharmony_ci		sprintf(p->name, "Ctrl #%08X", i);
15862306a36Sopenharmony_ci		return 0;
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci	return -EINVAL;
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic int g450_set_ctrl(void* md, struct v4l2_control *p) {
16462306a36Sopenharmony_ci	int i;
16562306a36Sopenharmony_ci	struct matrox_fb_info *minfo = md;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	i = get_ctrl_id(p->id);
16862306a36Sopenharmony_ci	if (i < 0) return -EINVAL;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	/*
17162306a36Sopenharmony_ci	 * Check if changed.
17262306a36Sopenharmony_ci	 */
17362306a36Sopenharmony_ci	if (p->value == *get_ctrl_ptr(minfo, i)) return 0;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	/*
17662306a36Sopenharmony_ci	 * Check limits.
17762306a36Sopenharmony_ci	 */
17862306a36Sopenharmony_ci	if (p->value > g450_controls[i].desc.maximum) return -EINVAL;
17962306a36Sopenharmony_ci	if (p->value < g450_controls[i].desc.minimum) return -EINVAL;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	/*
18262306a36Sopenharmony_ci	 * Store new value.
18362306a36Sopenharmony_ci	 */
18462306a36Sopenharmony_ci	*get_ctrl_ptr(minfo, i) = p->value;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	switch (p->id) {
18762306a36Sopenharmony_ci		case V4L2_CID_BRIGHTNESS:
18862306a36Sopenharmony_ci		case V4L2_CID_CONTRAST:
18962306a36Sopenharmony_ci			{
19062306a36Sopenharmony_ci				int blacklevel, whitelevel;
19162306a36Sopenharmony_ci				g450_compute_bwlevel(minfo, &blacklevel, &whitelevel);
19262306a36Sopenharmony_ci				cve2_set_reg10(minfo, 0x0e, blacklevel);
19362306a36Sopenharmony_ci				cve2_set_reg10(minfo, 0x1e, whitelevel);
19462306a36Sopenharmony_ci			}
19562306a36Sopenharmony_ci			break;
19662306a36Sopenharmony_ci		case V4L2_CID_SATURATION:
19762306a36Sopenharmony_ci			cve2_set_reg(minfo, 0x20, p->value);
19862306a36Sopenharmony_ci			cve2_set_reg(minfo, 0x22, p->value);
19962306a36Sopenharmony_ci			break;
20062306a36Sopenharmony_ci		case V4L2_CID_HUE:
20162306a36Sopenharmony_ci			cve2_set_reg(minfo, 0x25, p->value);
20262306a36Sopenharmony_ci			break;
20362306a36Sopenharmony_ci		case MATROXFB_CID_TESTOUT:
20462306a36Sopenharmony_ci			{
20562306a36Sopenharmony_ci				unsigned char val = cve2_get_reg(minfo, 0x05);
20662306a36Sopenharmony_ci				if (p->value) val |=  0x02;
20762306a36Sopenharmony_ci				else          val &= ~0x02;
20862306a36Sopenharmony_ci				cve2_set_reg(minfo, 0x05, val);
20962306a36Sopenharmony_ci			}
21062306a36Sopenharmony_ci			break;
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	return 0;
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic int g450_get_ctrl(void* md, struct v4l2_control *p) {
21862306a36Sopenharmony_ci	int i;
21962306a36Sopenharmony_ci	struct matrox_fb_info *minfo = md;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	i = get_ctrl_id(p->id);
22262306a36Sopenharmony_ci	if (i < 0) return -EINVAL;
22362306a36Sopenharmony_ci	p->value = *get_ctrl_ptr(minfo, i);
22462306a36Sopenharmony_ci	return 0;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistruct output_desc {
22862306a36Sopenharmony_ci	unsigned int	h_vis;
22962306a36Sopenharmony_ci	unsigned int	h_f_porch;
23062306a36Sopenharmony_ci	unsigned int	h_sync;
23162306a36Sopenharmony_ci	unsigned int	h_b_porch;
23262306a36Sopenharmony_ci	unsigned long long int	chromasc;
23362306a36Sopenharmony_ci	unsigned int	burst;
23462306a36Sopenharmony_ci	unsigned int	v_total;
23562306a36Sopenharmony_ci};
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic void computeRegs(struct matrox_fb_info *minfo, struct mavenregs *r,
23862306a36Sopenharmony_ci			struct my_timming *mt, const struct output_desc *outd)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	u_int32_t chromasc;
24162306a36Sopenharmony_ci	u_int32_t hlen;
24262306a36Sopenharmony_ci	u_int32_t hsl;
24362306a36Sopenharmony_ci	u_int32_t hbp;
24462306a36Sopenharmony_ci	u_int32_t hfp;
24562306a36Sopenharmony_ci	u_int32_t hvis;
24662306a36Sopenharmony_ci	unsigned int pixclock;
24762306a36Sopenharmony_ci	unsigned long long piic;
24862306a36Sopenharmony_ci	int mnp;
24962306a36Sopenharmony_ci	int over;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	r->regs[0x80] = 0x03;	/* | 0x40 for SCART */
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	hvis = ((mt->HDisplay << 1) + 3) & ~3;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	if (hvis >= 2048) {
25662306a36Sopenharmony_ci		hvis = 2044;
25762306a36Sopenharmony_ci	}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	piic = 1000000000ULL * hvis;
26062306a36Sopenharmony_ci	do_div(piic, outd->h_vis);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	dprintk(KERN_DEBUG "Want %u kHz pixclock\n", (unsigned int)piic);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	mnp = matroxfb_g450_setclk(minfo, piic, M_VIDEO_PLL);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	mt->mnp = mnp;
26762306a36Sopenharmony_ci	mt->pixclock = g450_mnp2f(minfo, mnp);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	dprintk(KERN_DEBUG "MNP=%08X\n", mnp);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	pixclock = 1000000000U / mt->pixclock;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	dprintk(KERN_DEBUG "Got %u ps pixclock\n", pixclock);
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	piic = outd->chromasc;
27662306a36Sopenharmony_ci	do_div(piic, mt->pixclock);
27762306a36Sopenharmony_ci	chromasc = piic;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	dprintk(KERN_DEBUG "Chroma is %08X\n", chromasc);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	r->regs[0] = piic >> 24;
28262306a36Sopenharmony_ci	r->regs[1] = piic >> 16;
28362306a36Sopenharmony_ci	r->regs[2] = piic >>  8;
28462306a36Sopenharmony_ci	r->regs[3] = piic >>  0;
28562306a36Sopenharmony_ci	hbp = (((outd->h_b_porch + pixclock) / pixclock)) & ~1;
28662306a36Sopenharmony_ci	hfp = (((outd->h_f_porch + pixclock) / pixclock)) & ~1;
28762306a36Sopenharmony_ci	hsl = (((outd->h_sync + pixclock) / pixclock)) & ~1;
28862306a36Sopenharmony_ci	hlen = hvis + hfp + hsl + hbp;
28962306a36Sopenharmony_ci	over = hlen & 0x0F;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	dprintk(KERN_DEBUG "WL: vis=%u, hf=%u, hs=%u, hb=%u, total=%u\n", hvis, hfp, hsl, hbp, hlen);
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	if (over) {
29462306a36Sopenharmony_ci		hfp -= over;
29562306a36Sopenharmony_ci		hlen -= over;
29662306a36Sopenharmony_ci		if (over <= 2) {
29762306a36Sopenharmony_ci		} else if (over < 10) {
29862306a36Sopenharmony_ci			hfp += 4;
29962306a36Sopenharmony_ci			hlen += 4;
30062306a36Sopenharmony_ci		} else {
30162306a36Sopenharmony_ci			hfp += 16;
30262306a36Sopenharmony_ci			hlen += 16;
30362306a36Sopenharmony_ci		}
30462306a36Sopenharmony_ci	}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	/* maybe cve2 has requirement 800 < hlen < 1184 */
30762306a36Sopenharmony_ci	r->regs[0x08] = hsl;
30862306a36Sopenharmony_ci	r->regs[0x09] = (outd->burst + pixclock - 1) / pixclock;	/* burst length */
30962306a36Sopenharmony_ci	r->regs[0x0A] = hbp;
31062306a36Sopenharmony_ci	r->regs[0x2C] = hfp;
31162306a36Sopenharmony_ci	r->regs[0x31] = hvis / 8;
31262306a36Sopenharmony_ci	r->regs[0x32] = hvis & 7;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	dprintk(KERN_DEBUG "PG: vis=%04X, hf=%02X, hs=%02X, hb=%02X, total=%04X\n", hvis, hfp, hsl, hbp, hlen);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	r->regs[0x84] = 1;	/* x sync point */
31762306a36Sopenharmony_ci	r->regs[0x85] = 0;
31862306a36Sopenharmony_ci	hvis = hvis >> 1;
31962306a36Sopenharmony_ci	hlen = hlen >> 1;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	dprintk(KERN_DEBUG "hlen=%u hvis=%u\n", hlen, hvis);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	mt->interlaced = 1;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	mt->HDisplay = hvis & ~7;
32662306a36Sopenharmony_ci	mt->HSyncStart = mt->HDisplay + 8;
32762306a36Sopenharmony_ci	mt->HSyncEnd = (hlen & ~7) - 8;
32862306a36Sopenharmony_ci	mt->HTotal = hlen;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	{
33162306a36Sopenharmony_ci		int upper;
33262306a36Sopenharmony_ci		unsigned int vtotal;
33362306a36Sopenharmony_ci		unsigned int vsyncend;
33462306a36Sopenharmony_ci		unsigned int vdisplay;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci		vtotal = mt->VTotal;
33762306a36Sopenharmony_ci		vsyncend = mt->VSyncEnd;
33862306a36Sopenharmony_ci		vdisplay = mt->VDisplay;
33962306a36Sopenharmony_ci		if (vtotal < outd->v_total) {
34062306a36Sopenharmony_ci			unsigned int yovr = outd->v_total - vtotal;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci			vsyncend += yovr >> 1;
34362306a36Sopenharmony_ci		} else if (vtotal > outd->v_total) {
34462306a36Sopenharmony_ci			vdisplay = outd->v_total - 4;
34562306a36Sopenharmony_ci			vsyncend = outd->v_total;
34662306a36Sopenharmony_ci		}
34762306a36Sopenharmony_ci		upper = (outd->v_total - vsyncend) >> 1;	/* in field lines */
34862306a36Sopenharmony_ci		r->regs[0x17] = outd->v_total / 4;
34962306a36Sopenharmony_ci		r->regs[0x18] = outd->v_total & 3;
35062306a36Sopenharmony_ci		r->regs[0x33] = upper - 1;	/* upper blanking */
35162306a36Sopenharmony_ci		r->regs[0x82] = upper;		/* y sync point */
35262306a36Sopenharmony_ci		r->regs[0x83] = upper >> 8;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci		mt->VDisplay = vdisplay;
35562306a36Sopenharmony_ci		mt->VSyncStart = outd->v_total - 2;
35662306a36Sopenharmony_ci		mt->VSyncEnd = outd->v_total;
35762306a36Sopenharmony_ci		mt->VTotal = outd->v_total;
35862306a36Sopenharmony_ci	}
35962306a36Sopenharmony_ci}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_cistatic void cve2_init_TVdata(int norm, struct mavenregs* data, const struct output_desc** outd) {
36262306a36Sopenharmony_ci	static const struct output_desc paloutd = {
36362306a36Sopenharmony_ci		.h_vis	   = 52148148,	// ps
36462306a36Sopenharmony_ci		.h_f_porch =  1407407,	// ps
36562306a36Sopenharmony_ci		.h_sync    =  4666667,	// ps
36662306a36Sopenharmony_ci		.h_b_porch =  5777778,	// ps
36762306a36Sopenharmony_ci		.chromasc  = 19042247534182ULL,	// 4433618.750 Hz
36862306a36Sopenharmony_ci		.burst     =  2518518,	// ps
36962306a36Sopenharmony_ci		.v_total   =      625,
37062306a36Sopenharmony_ci	};
37162306a36Sopenharmony_ci	static const struct output_desc ntscoutd = {
37262306a36Sopenharmony_ci		.h_vis     = 52888889,	// ps
37362306a36Sopenharmony_ci		.h_f_porch =  1333333,	// ps
37462306a36Sopenharmony_ci		.h_sync    =  4666667,	// ps
37562306a36Sopenharmony_ci		.h_b_porch =  4666667,	// ps
37662306a36Sopenharmony_ci		.chromasc  = 15374030659475ULL,	// 3579545.454 Hz
37762306a36Sopenharmony_ci		.burst     =  2418418,	// ps
37862306a36Sopenharmony_ci		.v_total   =      525,	// lines
37962306a36Sopenharmony_ci	};
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	static const struct mavenregs palregs = { {
38262306a36Sopenharmony_ci		0x2A, 0x09, 0x8A, 0xCB,	/* 00: chroma subcarrier */
38362306a36Sopenharmony_ci		0x00,
38462306a36Sopenharmony_ci		0x00,	/* test */
38562306a36Sopenharmony_ci		0xF9,	/* modified by code (F9 written...) */
38662306a36Sopenharmony_ci		0x00,	/* ? not written */
38762306a36Sopenharmony_ci		0x7E,	/* 08 */
38862306a36Sopenharmony_ci		0x44,	/* 09 */
38962306a36Sopenharmony_ci		0x9C,	/* 0A */
39062306a36Sopenharmony_ci		0x2E,	/* 0B */
39162306a36Sopenharmony_ci		0x21,	/* 0C */
39262306a36Sopenharmony_ci		0x00,	/* ? not written */
39362306a36Sopenharmony_ci//		0x3F, 0x03, /* 0E-0F */
39462306a36Sopenharmony_ci		0x3C, 0x03,
39562306a36Sopenharmony_ci		0x3C, 0x03, /* 10-11 */
39662306a36Sopenharmony_ci		0x1A,	/* 12 */
39762306a36Sopenharmony_ci		0x2A,	/* 13 */
39862306a36Sopenharmony_ci		0x1C, 0x3D, 0x14, /* 14-16 */
39962306a36Sopenharmony_ci		0x9C, 0x01, /* 17-18 */
40062306a36Sopenharmony_ci		0x00,	/* 19 */
40162306a36Sopenharmony_ci		0xFE,	/* 1A */
40262306a36Sopenharmony_ci		0x7E,	/* 1B */
40362306a36Sopenharmony_ci		0x60,	/* 1C */
40462306a36Sopenharmony_ci		0x05,	/* 1D */
40562306a36Sopenharmony_ci//		0x89, 0x03, /* 1E-1F */
40662306a36Sopenharmony_ci		0xAD, 0x03,
40762306a36Sopenharmony_ci//		0x72,	/* 20 */
40862306a36Sopenharmony_ci		0xA5,
40962306a36Sopenharmony_ci		0x07,	/* 21 */
41062306a36Sopenharmony_ci//		0x72,	/* 22 */
41162306a36Sopenharmony_ci		0xA5,
41262306a36Sopenharmony_ci		0x00,	/* 23 */
41362306a36Sopenharmony_ci		0x00,	/* 24 */
41462306a36Sopenharmony_ci		0x00,	/* 25 */
41562306a36Sopenharmony_ci		0x08,	/* 26 */
41662306a36Sopenharmony_ci		0x04,	/* 27 */
41762306a36Sopenharmony_ci		0x00,	/* 28 */
41862306a36Sopenharmony_ci		0x1A,	/* 29 */
41962306a36Sopenharmony_ci		0x55, 0x01, /* 2A-2B */
42062306a36Sopenharmony_ci		0x26,	/* 2C */
42162306a36Sopenharmony_ci		0x07, 0x7E, /* 2D-2E */
42262306a36Sopenharmony_ci		0x02, 0x54, /* 2F-30 */
42362306a36Sopenharmony_ci		0xB0, 0x00, /* 31-32 */
42462306a36Sopenharmony_ci		0x14,	/* 33 */
42562306a36Sopenharmony_ci		0x49,	/* 34 */
42662306a36Sopenharmony_ci		0x00,	/* 35 written multiple times */
42762306a36Sopenharmony_ci		0x00,	/* 36 not written */
42862306a36Sopenharmony_ci		0xA3,	/* 37 */
42962306a36Sopenharmony_ci		0xC8,	/* 38 */
43062306a36Sopenharmony_ci		0x22,	/* 39 */
43162306a36Sopenharmony_ci		0x02,	/* 3A */
43262306a36Sopenharmony_ci		0x22,	/* 3B */
43362306a36Sopenharmony_ci		0x3F, 0x03, /* 3C-3D */
43462306a36Sopenharmony_ci		0x00,	/* 3E written multiple times */
43562306a36Sopenharmony_ci		0x00,	/* 3F not written */
43662306a36Sopenharmony_ci	} };
43762306a36Sopenharmony_ci	static const struct mavenregs ntscregs = { {
43862306a36Sopenharmony_ci		0x21, 0xF0, 0x7C, 0x1F,	/* 00: chroma subcarrier */
43962306a36Sopenharmony_ci		0x00,
44062306a36Sopenharmony_ci		0x00,	/* test */
44162306a36Sopenharmony_ci		0xF9,	/* modified by code (F9 written...) */
44262306a36Sopenharmony_ci		0x00,	/* ? not written */
44362306a36Sopenharmony_ci		0x7E,	/* 08 */
44462306a36Sopenharmony_ci		0x43,	/* 09 */
44562306a36Sopenharmony_ci		0x7E,	/* 0A */
44662306a36Sopenharmony_ci		0x3D,	/* 0B */
44762306a36Sopenharmony_ci		0x00,	/* 0C */
44862306a36Sopenharmony_ci		0x00,	/* ? not written */
44962306a36Sopenharmony_ci		0x41, 0x00, /* 0E-0F */
45062306a36Sopenharmony_ci		0x3C, 0x00, /* 10-11 */
45162306a36Sopenharmony_ci		0x17,	/* 12 */
45262306a36Sopenharmony_ci		0x21,	/* 13 */
45362306a36Sopenharmony_ci		0x1B, 0x1B, 0x24, /* 14-16 */
45462306a36Sopenharmony_ci		0x83, 0x01, /* 17-18 */
45562306a36Sopenharmony_ci		0x00,	/* 19 */
45662306a36Sopenharmony_ci		0x0F,	/* 1A */
45762306a36Sopenharmony_ci		0x0F,	/* 1B */
45862306a36Sopenharmony_ci		0x60,	/* 1C */
45962306a36Sopenharmony_ci		0x05,	/* 1D */
46062306a36Sopenharmony_ci		//0x89, 0x02, /* 1E-1F */
46162306a36Sopenharmony_ci		0xC0, 0x02, /* 1E-1F */
46262306a36Sopenharmony_ci		//0x5F,	/* 20 */
46362306a36Sopenharmony_ci		0x9C,	/* 20 */
46462306a36Sopenharmony_ci		0x04,	/* 21 */
46562306a36Sopenharmony_ci		//0x5F,	/* 22 */
46662306a36Sopenharmony_ci		0x9C,	/* 22 */
46762306a36Sopenharmony_ci		0x01,	/* 23 */
46862306a36Sopenharmony_ci		0x02,	/* 24 */
46962306a36Sopenharmony_ci		0x00,	/* 25 */
47062306a36Sopenharmony_ci		0x0A,	/* 26 */
47162306a36Sopenharmony_ci		0x05,	/* 27 */
47262306a36Sopenharmony_ci		0x00,	/* 28 */
47362306a36Sopenharmony_ci		0x10,	/* 29 */
47462306a36Sopenharmony_ci		0xFF, 0x03, /* 2A-2B */
47562306a36Sopenharmony_ci		0x24,	/* 2C */
47662306a36Sopenharmony_ci		0x0F, 0x78, /* 2D-2E */
47762306a36Sopenharmony_ci		0x00, 0x00, /* 2F-30 */
47862306a36Sopenharmony_ci		0xB2, 0x04, /* 31-32 */
47962306a36Sopenharmony_ci		0x14,	/* 33 */
48062306a36Sopenharmony_ci		0x02,	/* 34 */
48162306a36Sopenharmony_ci		0x00,	/* 35 written multiple times */
48262306a36Sopenharmony_ci		0x00,	/* 36 not written */
48362306a36Sopenharmony_ci		0xA3,	/* 37 */
48462306a36Sopenharmony_ci		0xC8,	/* 38 */
48562306a36Sopenharmony_ci		0x15,	/* 39 */
48662306a36Sopenharmony_ci		0x05,	/* 3A */
48762306a36Sopenharmony_ci		0x3B,	/* 3B */
48862306a36Sopenharmony_ci		0x3C, 0x00, /* 3C-3D */
48962306a36Sopenharmony_ci		0x00,	/* 3E written multiple times */
49062306a36Sopenharmony_ci		0x00,	/* never written */
49162306a36Sopenharmony_ci	} };
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	if (norm == MATROXFB_OUTPUT_MODE_PAL) {
49462306a36Sopenharmony_ci		*data = palregs;
49562306a36Sopenharmony_ci		*outd = &paloutd;
49662306a36Sopenharmony_ci	} else {
49762306a36Sopenharmony_ci  		*data = ntscregs;
49862306a36Sopenharmony_ci		*outd = &ntscoutd;
49962306a36Sopenharmony_ci	}
50062306a36Sopenharmony_ci 	return;
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci#define LR(x) cve2_set_reg(minfo, (x), m->regs[(x)])
50462306a36Sopenharmony_cistatic void cve2_init_TV(struct matrox_fb_info *minfo,
50562306a36Sopenharmony_ci			 const struct mavenregs *m)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	int i;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	LR(0x80);
51062306a36Sopenharmony_ci	LR(0x82); LR(0x83);
51162306a36Sopenharmony_ci	LR(0x84); LR(0x85);
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	cve2_set_reg(minfo, 0x3E, 0x01);
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	for (i = 0; i < 0x3E; i++) {
51662306a36Sopenharmony_ci		LR(i);
51762306a36Sopenharmony_ci	}
51862306a36Sopenharmony_ci	cve2_set_reg(minfo, 0x3E, 0x00);
51962306a36Sopenharmony_ci}
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_cistatic int matroxfb_g450_compute(void* md, struct my_timming* mt) {
52262306a36Sopenharmony_ci	struct matrox_fb_info *minfo = md;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	dprintk(KERN_DEBUG "Computing, mode=%u\n", minfo->outputs[1].mode);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	if (mt->crtc == MATROXFB_SRC_CRTC2 &&
52762306a36Sopenharmony_ci	    minfo->outputs[1].mode != MATROXFB_OUTPUT_MODE_MONITOR) {
52862306a36Sopenharmony_ci		const struct output_desc* outd;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci		cve2_init_TVdata(minfo->outputs[1].mode, &minfo->hw.maven, &outd);
53162306a36Sopenharmony_ci		{
53262306a36Sopenharmony_ci			int blacklevel, whitelevel;
53362306a36Sopenharmony_ci			g450_compute_bwlevel(minfo, &blacklevel, &whitelevel);
53462306a36Sopenharmony_ci			minfo->hw.maven.regs[0x0E] = blacklevel >> 2;
53562306a36Sopenharmony_ci			minfo->hw.maven.regs[0x0F] = blacklevel & 3;
53662306a36Sopenharmony_ci			minfo->hw.maven.regs[0x1E] = whitelevel >> 2;
53762306a36Sopenharmony_ci			minfo->hw.maven.regs[0x1F] = whitelevel & 3;
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci			minfo->hw.maven.regs[0x20] =
54062306a36Sopenharmony_ci			minfo->hw.maven.regs[0x22] = minfo->altout.tvo_params.saturation;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci			minfo->hw.maven.regs[0x25] = minfo->altout.tvo_params.hue;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci			if (minfo->altout.tvo_params.testout) {
54562306a36Sopenharmony_ci				minfo->hw.maven.regs[0x05] |= 0x02;
54662306a36Sopenharmony_ci			}
54762306a36Sopenharmony_ci		}
54862306a36Sopenharmony_ci		computeRegs(minfo, &minfo->hw.maven, mt, outd);
54962306a36Sopenharmony_ci	} else if (mt->mnp < 0) {
55062306a36Sopenharmony_ci		/* We must program clocks before CRTC2, otherwise interlaced mode
55162306a36Sopenharmony_ci		   startup may fail */
55262306a36Sopenharmony_ci		mt->mnp = matroxfb_g450_setclk(minfo, mt->pixclock, (mt->crtc == MATROXFB_SRC_CRTC1) ? M_PIXEL_PLL_C : M_VIDEO_PLL);
55362306a36Sopenharmony_ci		mt->pixclock = g450_mnp2f(minfo, mt->mnp);
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci	dprintk(KERN_DEBUG "Pixclock = %u\n", mt->pixclock);
55662306a36Sopenharmony_ci	return 0;
55762306a36Sopenharmony_ci}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_cistatic int matroxfb_g450_program(void* md) {
56062306a36Sopenharmony_ci	struct matrox_fb_info *minfo = md;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	if (minfo->outputs[1].mode != MATROXFB_OUTPUT_MODE_MONITOR) {
56362306a36Sopenharmony_ci		cve2_init_TV(minfo, &minfo->hw.maven);
56462306a36Sopenharmony_ci	}
56562306a36Sopenharmony_ci	return 0;
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_cistatic int matroxfb_g450_verify_mode(void* md, u_int32_t arg) {
56962306a36Sopenharmony_ci	switch (arg) {
57062306a36Sopenharmony_ci		case MATROXFB_OUTPUT_MODE_PAL:
57162306a36Sopenharmony_ci		case MATROXFB_OUTPUT_MODE_NTSC:
57262306a36Sopenharmony_ci		case MATROXFB_OUTPUT_MODE_MONITOR:
57362306a36Sopenharmony_ci			return 0;
57462306a36Sopenharmony_ci	}
57562306a36Sopenharmony_ci	return -EINVAL;
57662306a36Sopenharmony_ci}
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_cistatic int g450_dvi_compute(void* md, struct my_timming* mt) {
57962306a36Sopenharmony_ci	struct matrox_fb_info *minfo = md;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	if (mt->mnp < 0) {
58262306a36Sopenharmony_ci		mt->mnp = matroxfb_g450_setclk(minfo, mt->pixclock, (mt->crtc == MATROXFB_SRC_CRTC1) ? M_PIXEL_PLL_C : M_VIDEO_PLL);
58362306a36Sopenharmony_ci		mt->pixclock = g450_mnp2f(minfo, mt->mnp);
58462306a36Sopenharmony_ci	}
58562306a36Sopenharmony_ci	return 0;
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_cistatic struct matrox_altout matroxfb_g450_altout = {
58962306a36Sopenharmony_ci	.name		= "Secondary output",
59062306a36Sopenharmony_ci	.compute	= matroxfb_g450_compute,
59162306a36Sopenharmony_ci	.program	= matroxfb_g450_program,
59262306a36Sopenharmony_ci	.verifymode	= matroxfb_g450_verify_mode,
59362306a36Sopenharmony_ci	.getqueryctrl	= g450_query_ctrl,
59462306a36Sopenharmony_ci	.getctrl	= g450_get_ctrl,
59562306a36Sopenharmony_ci	.setctrl	= g450_set_ctrl,
59662306a36Sopenharmony_ci};
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_cistatic struct matrox_altout matroxfb_g450_dvi = {
59962306a36Sopenharmony_ci	.name		= "DVI output",
60062306a36Sopenharmony_ci	.compute	= g450_dvi_compute,
60162306a36Sopenharmony_ci};
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_civoid matroxfb_g450_connect(struct matrox_fb_info *minfo)
60462306a36Sopenharmony_ci{
60562306a36Sopenharmony_ci	if (minfo->devflags.g450dac) {
60662306a36Sopenharmony_ci		down_write(&minfo->altout.lock);
60762306a36Sopenharmony_ci		tvo_fill_defaults(minfo);
60862306a36Sopenharmony_ci		minfo->outputs[1].src = minfo->outputs[1].default_src;
60962306a36Sopenharmony_ci		minfo->outputs[1].data = minfo;
61062306a36Sopenharmony_ci		minfo->outputs[1].output = &matroxfb_g450_altout;
61162306a36Sopenharmony_ci		minfo->outputs[1].mode = MATROXFB_OUTPUT_MODE_MONITOR;
61262306a36Sopenharmony_ci		minfo->outputs[2].src = minfo->outputs[2].default_src;
61362306a36Sopenharmony_ci		minfo->outputs[2].data = minfo;
61462306a36Sopenharmony_ci		minfo->outputs[2].output = &matroxfb_g450_dvi;
61562306a36Sopenharmony_ci		minfo->outputs[2].mode = MATROXFB_OUTPUT_MODE_MONITOR;
61662306a36Sopenharmony_ci		up_write(&minfo->altout.lock);
61762306a36Sopenharmony_ci	}
61862306a36Sopenharmony_ci}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_civoid matroxfb_g450_shutdown(struct matrox_fb_info *minfo)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci	if (minfo->devflags.g450dac) {
62362306a36Sopenharmony_ci		down_write(&minfo->altout.lock);
62462306a36Sopenharmony_ci		minfo->outputs[1].src = MATROXFB_SRC_NONE;
62562306a36Sopenharmony_ci		minfo->outputs[1].output = NULL;
62662306a36Sopenharmony_ci		minfo->outputs[1].data = NULL;
62762306a36Sopenharmony_ci		minfo->outputs[1].mode = MATROXFB_OUTPUT_MODE_MONITOR;
62862306a36Sopenharmony_ci		minfo->outputs[2].src = MATROXFB_SRC_NONE;
62962306a36Sopenharmony_ci		minfo->outputs[2].output = NULL;
63062306a36Sopenharmony_ci		minfo->outputs[2].data = NULL;
63162306a36Sopenharmony_ci		minfo->outputs[2].mode = MATROXFB_OUTPUT_MODE_MONITOR;
63262306a36Sopenharmony_ci		up_write(&minfo->altout.lock);
63362306a36Sopenharmony_ci	}
63462306a36Sopenharmony_ci}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ciEXPORT_SYMBOL(matroxfb_g450_connect);
63762306a36Sopenharmony_ciEXPORT_SYMBOL(matroxfb_g450_shutdown);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ciMODULE_AUTHOR("(c) 2000-2002 Petr Vandrovec <vandrove@vc.cvut.cz>");
64062306a36Sopenharmony_ciMODULE_DESCRIPTION("Matrox G450/G550 output driver");
64162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
642