162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/delay.h>
462306a36Sopenharmony_ci#include <linux/pci.h>
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <drm/drm_atomic.h>
762306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h>
862306a36Sopenharmony_ci#include <drm/drm_drv.h>
962306a36Sopenharmony_ci#include <drm/drm_gem_atomic_helper.h>
1062306a36Sopenharmony_ci#include <drm/drm_probe_helper.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "mgag200_drv.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistatic int mgag200_g200se_init_pci_options(struct pci_dev *pdev)
1562306a36Sopenharmony_ci{
1662306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
1762306a36Sopenharmony_ci	bool has_sgram;
1862306a36Sopenharmony_ci	u32 option;
1962306a36Sopenharmony_ci	int err;
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	err = pci_read_config_dword(pdev, PCI_MGA_OPTION, &option);
2262306a36Sopenharmony_ci	if (err != PCIBIOS_SUCCESSFUL) {
2362306a36Sopenharmony_ci		dev_err(dev, "pci_read_config_dword(PCI_MGA_OPTION) failed: %d\n", err);
2462306a36Sopenharmony_ci		return pcibios_err_to_errno(err);
2562306a36Sopenharmony_ci	}
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	has_sgram = !!(option & PCI_MGA_OPTION_HARDPWMSK);
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	option = 0x40049120;
3062306a36Sopenharmony_ci	if (has_sgram)
3162306a36Sopenharmony_ci		option |= PCI_MGA_OPTION_HARDPWMSK;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	return mgag200_init_pci_options(pdev, option, 0x00008000);
3462306a36Sopenharmony_ci}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic void mgag200_g200se_init_registers(struct mgag200_g200se_device *g200se)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	static const u8 dacvalue[] = {
3962306a36Sopenharmony_ci		MGAG200_DAC_DEFAULT(0x03,
4062306a36Sopenharmony_ci				    MGA1064_PIX_CLK_CTL_SEL_PLL,
4162306a36Sopenharmony_ci				    MGA1064_MISC_CTL_DAC_EN |
4262306a36Sopenharmony_ci				    MGA1064_MISC_CTL_VGA8 |
4362306a36Sopenharmony_ci				    MGA1064_MISC_CTL_DAC_RAM_CS,
4462306a36Sopenharmony_ci				    0x00, 0x00, 0x00)
4562306a36Sopenharmony_ci	};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	struct mga_device *mdev = &g200se->base;
4862306a36Sopenharmony_ci	size_t i;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(dacvalue); i++) {
5162306a36Sopenharmony_ci		if ((i <= 0x17) ||
5262306a36Sopenharmony_ci		    (i == 0x1b) ||
5362306a36Sopenharmony_ci		    (i == 0x1c) ||
5462306a36Sopenharmony_ci		    ((i >= 0x1f) && (i <= 0x29)) ||
5562306a36Sopenharmony_ci		    ((i == 0x2c) || (i == 0x2d) || (i == 0x2e)) ||
5662306a36Sopenharmony_ci		    ((i >= 0x30) && (i <= 0x37)))
5762306a36Sopenharmony_ci			continue;
5862306a36Sopenharmony_ci		WREG_DAC(i, dacvalue[i]);
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	mgag200_init_registers(mdev);
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic void mgag200_g200se_set_hiprilvl(struct mga_device *mdev,
6562306a36Sopenharmony_ci					const struct drm_display_mode *mode,
6662306a36Sopenharmony_ci					const struct drm_format_info *format)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	struct mgag200_g200se_device *g200se = to_mgag200_g200se_device(&mdev->base);
6962306a36Sopenharmony_ci	unsigned int hiprilvl;
7062306a36Sopenharmony_ci	u8 crtcext6;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	if  (g200se->unique_rev_id >= 0x04) {
7362306a36Sopenharmony_ci		hiprilvl = 0;
7462306a36Sopenharmony_ci	} else if (g200se->unique_rev_id >= 0x02) {
7562306a36Sopenharmony_ci		unsigned int bpp;
7662306a36Sopenharmony_ci		unsigned long mb;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci		if (format->cpp[0] * 8 > 16)
7962306a36Sopenharmony_ci			bpp = 32;
8062306a36Sopenharmony_ci		else if (format->cpp[0] * 8 > 8)
8162306a36Sopenharmony_ci			bpp = 16;
8262306a36Sopenharmony_ci		else
8362306a36Sopenharmony_ci			bpp = 8;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci		mb = (mode->clock * bpp) / 1000;
8662306a36Sopenharmony_ci		if (mb > 3100)
8762306a36Sopenharmony_ci			hiprilvl = 0;
8862306a36Sopenharmony_ci		else if (mb > 2600)
8962306a36Sopenharmony_ci			hiprilvl = 1;
9062306a36Sopenharmony_ci		else if (mb > 1900)
9162306a36Sopenharmony_ci			hiprilvl = 2;
9262306a36Sopenharmony_ci		else if (mb > 1160)
9362306a36Sopenharmony_ci			hiprilvl = 3;
9462306a36Sopenharmony_ci		else if (mb > 440)
9562306a36Sopenharmony_ci			hiprilvl = 4;
9662306a36Sopenharmony_ci		else
9762306a36Sopenharmony_ci			hiprilvl = 5;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	} else if (g200se->unique_rev_id >= 0x01) {
10062306a36Sopenharmony_ci		hiprilvl = 3;
10162306a36Sopenharmony_ci	} else {
10262306a36Sopenharmony_ci		hiprilvl = 4;
10362306a36Sopenharmony_ci	}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	crtcext6 = hiprilvl; /* implicitly sets maxhipri to 0 */
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	WREG_ECRT(0x06, crtcext6);
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci/*
11162306a36Sopenharmony_ci * PIXPLLC
11262306a36Sopenharmony_ci */
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistatic int mgag200_g200se_00_pixpllc_atomic_check(struct drm_crtc *crtc,
11562306a36Sopenharmony_ci						  struct drm_atomic_state *new_state)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	static const unsigned int vcomax = 320000;
11862306a36Sopenharmony_ci	static const unsigned int vcomin = 160000;
11962306a36Sopenharmony_ci	static const unsigned int pllreffreq = 25000;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc);
12262306a36Sopenharmony_ci	struct mgag200_crtc_state *new_mgag200_crtc_state = to_mgag200_crtc_state(new_crtc_state);
12362306a36Sopenharmony_ci	long clock = new_crtc_state->mode.clock;
12462306a36Sopenharmony_ci	struct mgag200_pll_values *pixpllc = &new_mgag200_crtc_state->pixpllc;
12562306a36Sopenharmony_ci	unsigned int delta, tmpdelta, permitteddelta;
12662306a36Sopenharmony_ci	unsigned int testp, testm, testn;
12762306a36Sopenharmony_ci	unsigned int p, m, n, s;
12862306a36Sopenharmony_ci	unsigned int computed;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	m = n = p = s = 0;
13162306a36Sopenharmony_ci	delta = 0xffffffff;
13262306a36Sopenharmony_ci	permitteddelta = clock * 5 / 1000;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	for (testp = 8; testp > 0; testp /= 2) {
13562306a36Sopenharmony_ci		if (clock * testp > vcomax)
13662306a36Sopenharmony_ci			continue;
13762306a36Sopenharmony_ci		if (clock * testp < vcomin)
13862306a36Sopenharmony_ci			continue;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci		for (testn = 17; testn < 256; testn++) {
14162306a36Sopenharmony_ci			for (testm = 1; testm < 32; testm++) {
14262306a36Sopenharmony_ci				computed = (pllreffreq * testn) / (testm * testp);
14362306a36Sopenharmony_ci				if (computed > clock)
14462306a36Sopenharmony_ci					tmpdelta = computed - clock;
14562306a36Sopenharmony_ci				else
14662306a36Sopenharmony_ci					tmpdelta = clock - computed;
14762306a36Sopenharmony_ci				if (tmpdelta < delta) {
14862306a36Sopenharmony_ci					delta = tmpdelta;
14962306a36Sopenharmony_ci					m = testm;
15062306a36Sopenharmony_ci					n = testn;
15162306a36Sopenharmony_ci					p = testp;
15262306a36Sopenharmony_ci				}
15362306a36Sopenharmony_ci			}
15462306a36Sopenharmony_ci		}
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	if (delta > permitteddelta) {
15862306a36Sopenharmony_ci		pr_warn("PLL delta too large\n");
15962306a36Sopenharmony_ci		return -EINVAL;
16062306a36Sopenharmony_ci	}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	pixpllc->m = m;
16362306a36Sopenharmony_ci	pixpllc->n = n;
16462306a36Sopenharmony_ci	pixpllc->p = p;
16562306a36Sopenharmony_ci	pixpllc->s = s;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	return 0;
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic void mgag200_g200se_00_pixpllc_atomic_update(struct drm_crtc *crtc,
17162306a36Sopenharmony_ci						    struct drm_atomic_state *old_state)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	struct drm_device *dev = crtc->dev;
17462306a36Sopenharmony_ci	struct mga_device *mdev = to_mga_device(dev);
17562306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state = crtc->state;
17662306a36Sopenharmony_ci	struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state);
17762306a36Sopenharmony_ci	struct mgag200_pll_values *pixpllc = &mgag200_crtc_state->pixpllc;
17862306a36Sopenharmony_ci	unsigned int pixpllcm, pixpllcn, pixpllcp, pixpllcs;
17962306a36Sopenharmony_ci	u8 xpixpllcm, xpixpllcn, xpixpllcp;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	pixpllcm = pixpllc->m - 1;
18262306a36Sopenharmony_ci	pixpllcn = pixpllc->n - 1;
18362306a36Sopenharmony_ci	pixpllcp = pixpllc->p - 1;
18462306a36Sopenharmony_ci	pixpllcs = pixpllc->s;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	xpixpllcm = pixpllcm | ((pixpllcn & BIT(8)) >> 1);
18762306a36Sopenharmony_ci	xpixpllcn = pixpllcn;
18862306a36Sopenharmony_ci	xpixpllcp = (pixpllcs << 3) | pixpllcp;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	WREG_MISC_MASKED(MGAREG_MISC_CLKSEL_MGA, MGAREG_MISC_CLKSEL_MASK);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	WREG_DAC(MGA1064_PIX_PLLC_M, xpixpllcm);
19362306a36Sopenharmony_ci	WREG_DAC(MGA1064_PIX_PLLC_N, xpixpllcn);
19462306a36Sopenharmony_ci	WREG_DAC(MGA1064_PIX_PLLC_P, xpixpllcp);
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic int mgag200_g200se_04_pixpllc_atomic_check(struct drm_crtc *crtc,
19862306a36Sopenharmony_ci						  struct drm_atomic_state *new_state)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	static const unsigned int vcomax = 1600000;
20162306a36Sopenharmony_ci	static const unsigned int vcomin = 800000;
20262306a36Sopenharmony_ci	static const unsigned int pllreffreq = 25000;
20362306a36Sopenharmony_ci	static const unsigned int pvalues_e4[] = {16, 14, 12, 10, 8, 6, 4, 2, 1};
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc);
20662306a36Sopenharmony_ci	struct mgag200_crtc_state *new_mgag200_crtc_state = to_mgag200_crtc_state(new_crtc_state);
20762306a36Sopenharmony_ci	long clock = new_crtc_state->mode.clock;
20862306a36Sopenharmony_ci	struct mgag200_pll_values *pixpllc = &new_mgag200_crtc_state->pixpllc;
20962306a36Sopenharmony_ci	unsigned int delta, tmpdelta, permitteddelta;
21062306a36Sopenharmony_ci	unsigned int testp, testm, testn;
21162306a36Sopenharmony_ci	unsigned int p, m, n, s;
21262306a36Sopenharmony_ci	unsigned int computed;
21362306a36Sopenharmony_ci	unsigned int fvv;
21462306a36Sopenharmony_ci	unsigned int i;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	m = n = p = s = 0;
21762306a36Sopenharmony_ci	delta = 0xffffffff;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	if (clock < 25000)
22062306a36Sopenharmony_ci		clock = 25000;
22162306a36Sopenharmony_ci	clock = clock * 2;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	/* Permited delta is 0.5% as VESA Specification */
22462306a36Sopenharmony_ci	permitteddelta = clock * 5 / 1000;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	for (i = 0 ; i < ARRAY_SIZE(pvalues_e4); i++) {
22762306a36Sopenharmony_ci		testp = pvalues_e4[i];
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci		if ((clock * testp) > vcomax)
23062306a36Sopenharmony_ci			continue;
23162306a36Sopenharmony_ci		if ((clock * testp) < vcomin)
23262306a36Sopenharmony_ci			continue;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci		for (testn = 50; testn <= 256; testn++) {
23562306a36Sopenharmony_ci			for (testm = 1; testm <= 32; testm++) {
23662306a36Sopenharmony_ci				computed = (pllreffreq * testn) / (testm * testp);
23762306a36Sopenharmony_ci				if (computed > clock)
23862306a36Sopenharmony_ci					tmpdelta = computed - clock;
23962306a36Sopenharmony_ci				else
24062306a36Sopenharmony_ci					tmpdelta = clock - computed;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci				if (tmpdelta < delta) {
24362306a36Sopenharmony_ci					delta = tmpdelta;
24462306a36Sopenharmony_ci					m = testm;
24562306a36Sopenharmony_ci					n = testn;
24662306a36Sopenharmony_ci					p = testp;
24762306a36Sopenharmony_ci				}
24862306a36Sopenharmony_ci			}
24962306a36Sopenharmony_ci		}
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	fvv = pllreffreq * n / m;
25362306a36Sopenharmony_ci	fvv = (fvv - 800000) / 50000;
25462306a36Sopenharmony_ci	if (fvv > 15)
25562306a36Sopenharmony_ci		fvv = 15;
25662306a36Sopenharmony_ci	s = fvv << 1;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	if (delta > permitteddelta) {
25962306a36Sopenharmony_ci		pr_warn("PLL delta too large\n");
26062306a36Sopenharmony_ci		return -EINVAL;
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	pixpllc->m = m;
26462306a36Sopenharmony_ci	pixpllc->n = n;
26562306a36Sopenharmony_ci	pixpllc->p = p;
26662306a36Sopenharmony_ci	pixpllc->s = s;
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	return 0;
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic void mgag200_g200se_04_pixpllc_atomic_update(struct drm_crtc *crtc,
27262306a36Sopenharmony_ci						    struct drm_atomic_state *old_state)
27362306a36Sopenharmony_ci{
27462306a36Sopenharmony_ci	struct drm_device *dev = crtc->dev;
27562306a36Sopenharmony_ci	struct mga_device *mdev = to_mga_device(dev);
27662306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state = crtc->state;
27762306a36Sopenharmony_ci	struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state);
27862306a36Sopenharmony_ci	struct mgag200_pll_values *pixpllc = &mgag200_crtc_state->pixpllc;
27962306a36Sopenharmony_ci	unsigned int pixpllcm, pixpllcn, pixpllcp, pixpllcs;
28062306a36Sopenharmony_ci	u8 xpixpllcm, xpixpllcn, xpixpllcp;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	pixpllcm = pixpllc->m - 1;
28362306a36Sopenharmony_ci	pixpllcn = pixpllc->n - 1;
28462306a36Sopenharmony_ci	pixpllcp = pixpllc->p - 1;
28562306a36Sopenharmony_ci	pixpllcs = pixpllc->s;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	// For G200SE A, BIT(7) should be set unconditionally.
28862306a36Sopenharmony_ci	xpixpllcm = BIT(7) | pixpllcm;
28962306a36Sopenharmony_ci	xpixpllcn = pixpllcn;
29062306a36Sopenharmony_ci	xpixpllcp = (pixpllcs << 3) | pixpllcp;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	WREG_MISC_MASKED(MGAREG_MISC_CLKSEL_MGA, MGAREG_MISC_CLKSEL_MASK);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	WREG_DAC(MGA1064_PIX_PLLC_M, xpixpllcm);
29562306a36Sopenharmony_ci	WREG_DAC(MGA1064_PIX_PLLC_N, xpixpllcn);
29662306a36Sopenharmony_ci	WREG_DAC(MGA1064_PIX_PLLC_P, xpixpllcp);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	WREG_DAC(0x1a, 0x09);
29962306a36Sopenharmony_ci	msleep(20);
30062306a36Sopenharmony_ci	WREG_DAC(0x1a, 0x01);
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci/*
30462306a36Sopenharmony_ci * Mode-setting pipeline
30562306a36Sopenharmony_ci */
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs mgag200_g200se_primary_plane_helper_funcs = {
30862306a36Sopenharmony_ci	MGAG200_PRIMARY_PLANE_HELPER_FUNCS,
30962306a36Sopenharmony_ci};
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic const struct drm_plane_funcs mgag200_g200se_primary_plane_funcs = {
31262306a36Sopenharmony_ci	MGAG200_PRIMARY_PLANE_FUNCS,
31362306a36Sopenharmony_ci};
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic void mgag200_g200se_crtc_helper_atomic_enable(struct drm_crtc *crtc,
31662306a36Sopenharmony_ci						     struct drm_atomic_state *old_state)
31762306a36Sopenharmony_ci{
31862306a36Sopenharmony_ci	struct drm_device *dev = crtc->dev;
31962306a36Sopenharmony_ci	struct mga_device *mdev = to_mga_device(dev);
32062306a36Sopenharmony_ci	const struct mgag200_device_funcs *funcs = mdev->funcs;
32162306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state = crtc->state;
32262306a36Sopenharmony_ci	struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
32362306a36Sopenharmony_ci	struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(crtc_state);
32462306a36Sopenharmony_ci	const struct drm_format_info *format = mgag200_crtc_state->format;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	if (funcs->disable_vidrst)
32762306a36Sopenharmony_ci		funcs->disable_vidrst(mdev);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	mgag200_set_format_regs(mdev, format);
33062306a36Sopenharmony_ci	mgag200_set_mode_regs(mdev, adjusted_mode);
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	if (funcs->pixpllc_atomic_update)
33362306a36Sopenharmony_ci		funcs->pixpllc_atomic_update(crtc, old_state);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	mgag200_g200se_set_hiprilvl(mdev, adjusted_mode, format);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	if (crtc_state->gamma_lut)
33862306a36Sopenharmony_ci		mgag200_crtc_set_gamma(mdev, format, crtc_state->gamma_lut->data);
33962306a36Sopenharmony_ci	else
34062306a36Sopenharmony_ci		mgag200_crtc_set_gamma_linear(mdev, format);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	mgag200_enable_display(mdev);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	if (funcs->enable_vidrst)
34562306a36Sopenharmony_ci		funcs->enable_vidrst(mdev);
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic const struct drm_crtc_helper_funcs mgag200_g200se_crtc_helper_funcs = {
34962306a36Sopenharmony_ci	.mode_valid = mgag200_crtc_helper_mode_valid,
35062306a36Sopenharmony_ci	.atomic_check = mgag200_crtc_helper_atomic_check,
35162306a36Sopenharmony_ci	.atomic_flush = mgag200_crtc_helper_atomic_flush,
35262306a36Sopenharmony_ci	.atomic_enable = mgag200_g200se_crtc_helper_atomic_enable,
35362306a36Sopenharmony_ci	.atomic_disable = mgag200_crtc_helper_atomic_disable
35462306a36Sopenharmony_ci};
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic const struct drm_crtc_funcs mgag200_g200se_crtc_funcs = {
35762306a36Sopenharmony_ci	MGAG200_CRTC_FUNCS,
35862306a36Sopenharmony_ci};
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_cistatic const struct drm_encoder_funcs mgag200_g200se_dac_encoder_funcs = {
36162306a36Sopenharmony_ci	MGAG200_DAC_ENCODER_FUNCS,
36262306a36Sopenharmony_ci};
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cistatic const struct drm_connector_helper_funcs mgag200_g200se_vga_connector_helper_funcs = {
36562306a36Sopenharmony_ci	MGAG200_VGA_CONNECTOR_HELPER_FUNCS,
36662306a36Sopenharmony_ci};
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_cistatic const struct drm_connector_funcs mgag200_g200se_vga_connector_funcs = {
36962306a36Sopenharmony_ci	MGAG200_VGA_CONNECTOR_FUNCS,
37062306a36Sopenharmony_ci};
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic int mgag200_g200se_pipeline_init(struct mga_device *mdev)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	struct drm_device *dev = &mdev->base;
37562306a36Sopenharmony_ci	struct drm_plane *primary_plane = &mdev->primary_plane;
37662306a36Sopenharmony_ci	struct drm_crtc *crtc = &mdev->crtc;
37762306a36Sopenharmony_ci	struct drm_encoder *encoder = &mdev->encoder;
37862306a36Sopenharmony_ci	struct mga_i2c_chan *i2c = &mdev->i2c;
37962306a36Sopenharmony_ci	struct drm_connector *connector = &mdev->connector;
38062306a36Sopenharmony_ci	int ret;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	ret = drm_universal_plane_init(dev, primary_plane, 0,
38362306a36Sopenharmony_ci				       &mgag200_g200se_primary_plane_funcs,
38462306a36Sopenharmony_ci				       mgag200_primary_plane_formats,
38562306a36Sopenharmony_ci				       mgag200_primary_plane_formats_size,
38662306a36Sopenharmony_ci				       mgag200_primary_plane_fmtmods,
38762306a36Sopenharmony_ci				       DRM_PLANE_TYPE_PRIMARY, NULL);
38862306a36Sopenharmony_ci	if (ret) {
38962306a36Sopenharmony_ci		drm_err(dev, "drm_universal_plane_init() failed: %d\n", ret);
39062306a36Sopenharmony_ci		return ret;
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci	drm_plane_helper_add(primary_plane, &mgag200_g200se_primary_plane_helper_funcs);
39362306a36Sopenharmony_ci	drm_plane_enable_fb_damage_clips(primary_plane);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL,
39662306a36Sopenharmony_ci					&mgag200_g200se_crtc_funcs, NULL);
39762306a36Sopenharmony_ci	if (ret) {
39862306a36Sopenharmony_ci		drm_err(dev, "drm_crtc_init_with_planes() failed: %d\n", ret);
39962306a36Sopenharmony_ci		return ret;
40062306a36Sopenharmony_ci	}
40162306a36Sopenharmony_ci	drm_crtc_helper_add(crtc, &mgag200_g200se_crtc_helper_funcs);
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	/* FIXME: legacy gamma tables, but atomic gamma doesn't work without */
40462306a36Sopenharmony_ci	drm_mode_crtc_set_gamma_size(crtc, MGAG200_LUT_SIZE);
40562306a36Sopenharmony_ci	drm_crtc_enable_color_mgmt(crtc, 0, false, MGAG200_LUT_SIZE);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	encoder->possible_crtcs = drm_crtc_mask(crtc);
40862306a36Sopenharmony_ci	ret = drm_encoder_init(dev, encoder, &mgag200_g200se_dac_encoder_funcs,
40962306a36Sopenharmony_ci			       DRM_MODE_ENCODER_DAC, NULL);
41062306a36Sopenharmony_ci	if (ret) {
41162306a36Sopenharmony_ci		drm_err(dev, "drm_encoder_init() failed: %d\n", ret);
41262306a36Sopenharmony_ci		return ret;
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	ret = mgag200_i2c_init(mdev, i2c);
41662306a36Sopenharmony_ci	if (ret) {
41762306a36Sopenharmony_ci		drm_err(dev, "failed to add DDC bus: %d\n", ret);
41862306a36Sopenharmony_ci		return ret;
41962306a36Sopenharmony_ci	}
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	ret = drm_connector_init_with_ddc(dev, connector,
42262306a36Sopenharmony_ci					  &mgag200_g200se_vga_connector_funcs,
42362306a36Sopenharmony_ci					  DRM_MODE_CONNECTOR_VGA,
42462306a36Sopenharmony_ci					  &i2c->adapter);
42562306a36Sopenharmony_ci	if (ret) {
42662306a36Sopenharmony_ci		drm_err(dev, "drm_connector_init_with_ddc() failed: %d\n", ret);
42762306a36Sopenharmony_ci		return ret;
42862306a36Sopenharmony_ci	}
42962306a36Sopenharmony_ci	drm_connector_helper_add(connector, &mgag200_g200se_vga_connector_helper_funcs);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	ret = drm_connector_attach_encoder(connector, encoder);
43262306a36Sopenharmony_ci	if (ret) {
43362306a36Sopenharmony_ci		drm_err(dev, "drm_connector_attach_encoder() failed: %d\n", ret);
43462306a36Sopenharmony_ci		return ret;
43562306a36Sopenharmony_ci	}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	return 0;
43862306a36Sopenharmony_ci}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci/*
44162306a36Sopenharmony_ci * DRM device
44262306a36Sopenharmony_ci */
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cistatic const struct mgag200_device_info mgag200_g200se_a_01_device_info =
44562306a36Sopenharmony_ci	MGAG200_DEVICE_INFO_INIT(1600, 1200, 24400, false, 0, 1, true);
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic const struct mgag200_device_info mgag200_g200se_a_02_device_info =
44862306a36Sopenharmony_ci	MGAG200_DEVICE_INFO_INIT(1920, 1200, 30100, false, 0, 1, true);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_cistatic const struct mgag200_device_info mgag200_g200se_a_03_device_info =
45162306a36Sopenharmony_ci	MGAG200_DEVICE_INFO_INIT(2048, 2048, 55000, false, 0, 1, false);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_cistatic const struct mgag200_device_info mgag200_g200se_b_01_device_info =
45462306a36Sopenharmony_ci	MGAG200_DEVICE_INFO_INIT(1600, 1200, 24400, false, 0, 1, false);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_cistatic const struct mgag200_device_info mgag200_g200se_b_02_device_info =
45762306a36Sopenharmony_ci	MGAG200_DEVICE_INFO_INIT(1920, 1200, 30100, false, 0, 1, false);
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_cistatic const struct mgag200_device_info mgag200_g200se_b_03_device_info =
46062306a36Sopenharmony_ci	MGAG200_DEVICE_INFO_INIT(2048, 2048, 55000, false, 0, 1, false);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic int mgag200_g200se_init_unique_rev_id(struct mgag200_g200se_device *g200se)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	struct mga_device *mdev = &g200se->base;
46562306a36Sopenharmony_ci	struct drm_device *dev = &mdev->base;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	/* stash G200 SE model number for later use */
46862306a36Sopenharmony_ci	g200se->unique_rev_id = RREG32(0x1e24);
46962306a36Sopenharmony_ci	if (!g200se->unique_rev_id)
47062306a36Sopenharmony_ci		return -ENODEV;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	drm_dbg(dev, "G200 SE unique revision id is 0x%x\n", g200se->unique_rev_id);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	return 0;
47562306a36Sopenharmony_ci}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_cistatic const struct mgag200_device_funcs mgag200_g200se_00_device_funcs = {
47862306a36Sopenharmony_ci	.pixpllc_atomic_check = mgag200_g200se_00_pixpllc_atomic_check,
47962306a36Sopenharmony_ci	.pixpllc_atomic_update = mgag200_g200se_00_pixpllc_atomic_update,
48062306a36Sopenharmony_ci};
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_cistatic const struct mgag200_device_funcs mgag200_g200se_04_device_funcs = {
48362306a36Sopenharmony_ci	.pixpllc_atomic_check = mgag200_g200se_04_pixpllc_atomic_check,
48462306a36Sopenharmony_ci	.pixpllc_atomic_update = mgag200_g200se_04_pixpllc_atomic_update,
48562306a36Sopenharmony_ci};
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_cistruct mga_device *mgag200_g200se_device_create(struct pci_dev *pdev, const struct drm_driver *drv,
48862306a36Sopenharmony_ci						enum mga_type type)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	struct mgag200_g200se_device *g200se;
49162306a36Sopenharmony_ci	const struct mgag200_device_info *info;
49262306a36Sopenharmony_ci	const struct mgag200_device_funcs *funcs;
49362306a36Sopenharmony_ci	struct mga_device *mdev;
49462306a36Sopenharmony_ci	struct drm_device *dev;
49562306a36Sopenharmony_ci	resource_size_t vram_available;
49662306a36Sopenharmony_ci	int ret;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	g200se = devm_drm_dev_alloc(&pdev->dev, drv, struct mgag200_g200se_device, base.base);
49962306a36Sopenharmony_ci	if (IS_ERR(g200se))
50062306a36Sopenharmony_ci		return ERR_CAST(g200se);
50162306a36Sopenharmony_ci	mdev = &g200se->base;
50262306a36Sopenharmony_ci	dev = &mdev->base;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	pci_set_drvdata(pdev, dev);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	ret = mgag200_g200se_init_pci_options(pdev);
50762306a36Sopenharmony_ci	if (ret)
50862306a36Sopenharmony_ci		return ERR_PTR(ret);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	ret = mgag200_device_preinit(mdev);
51162306a36Sopenharmony_ci	if (ret)
51262306a36Sopenharmony_ci		return ERR_PTR(ret);
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_ci	ret = mgag200_g200se_init_unique_rev_id(g200se);
51562306a36Sopenharmony_ci	if (ret)
51662306a36Sopenharmony_ci		return ERR_PTR(ret);
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	switch (type) {
51962306a36Sopenharmony_ci	case G200_SE_A:
52062306a36Sopenharmony_ci		if (g200se->unique_rev_id >= 0x03)
52162306a36Sopenharmony_ci			info = &mgag200_g200se_a_03_device_info;
52262306a36Sopenharmony_ci		else if (g200se->unique_rev_id >= 0x02)
52362306a36Sopenharmony_ci			info = &mgag200_g200se_a_02_device_info;
52462306a36Sopenharmony_ci		else
52562306a36Sopenharmony_ci			info = &mgag200_g200se_a_01_device_info;
52662306a36Sopenharmony_ci		break;
52762306a36Sopenharmony_ci	case G200_SE_B:
52862306a36Sopenharmony_ci		if (g200se->unique_rev_id >= 0x03)
52962306a36Sopenharmony_ci			info = &mgag200_g200se_b_03_device_info;
53062306a36Sopenharmony_ci		else if (g200se->unique_rev_id >= 0x02)
53162306a36Sopenharmony_ci			info = &mgag200_g200se_b_02_device_info;
53262306a36Sopenharmony_ci		else
53362306a36Sopenharmony_ci			info = &mgag200_g200se_b_01_device_info;
53462306a36Sopenharmony_ci		break;
53562306a36Sopenharmony_ci	default:
53662306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
53762306a36Sopenharmony_ci	}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	if (g200se->unique_rev_id >= 0x04)
54062306a36Sopenharmony_ci		funcs = &mgag200_g200se_04_device_funcs;
54162306a36Sopenharmony_ci	else
54262306a36Sopenharmony_ci		funcs = &mgag200_g200se_00_device_funcs;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	ret = mgag200_device_init(mdev, info, funcs);
54562306a36Sopenharmony_ci	if (ret)
54662306a36Sopenharmony_ci		return ERR_PTR(ret);
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	mgag200_g200se_init_registers(g200se);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	vram_available = mgag200_device_probe_vram(mdev);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	ret = mgag200_mode_config_init(mdev, vram_available);
55362306a36Sopenharmony_ci	if (ret)
55462306a36Sopenharmony_ci		return ERR_PTR(ret);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	ret = mgag200_g200se_pipeline_init(mdev);
55762306a36Sopenharmony_ci	if (ret)
55862306a36Sopenharmony_ci		return ERR_PTR(ret);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	drm_mode_config_reset(dev);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	return mdev;
56362306a36Sopenharmony_ci}
564