xref: /linux/drivers/gpu/drm/mgag200/mgag200_g200eh3.c (revision 7a08cb9b4bb92fb86f5fe8a3aa0ac08a9b3d783b)
1 // SPDX-License-Identifier: GPL-2.0-only
2 
3 #include <linux/pci.h>
4 
5 #include <drm/drm_atomic.h>
6 #include <drm/drm_atomic_helper.h>
7 #include <drm/drm_drv.h>
8 #include <drm/drm_gem_atomic_helper.h>
9 #include <drm/drm_probe_helper.h>
10 #include <drm/drm_vblank.h>
11 
12 #include "mgag200_drv.h"
13 
14 /*
15  * PIXPLLC
16  */
17 
18 static int mgag200_g200eh3_pixpllc_atomic_check(struct drm_crtc *crtc,
19 						struct drm_atomic_state *new_state)
20 {
21 	static const unsigned int vcomax = 3000000;
22 	static const unsigned int vcomin = 1500000;
23 	static const unsigned int pllreffreq = 25000;
24 
25 	struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc);
26 	struct mgag200_crtc_state *new_mgag200_crtc_state = to_mgag200_crtc_state(new_crtc_state);
27 	long clock = new_crtc_state->mode.clock;
28 	struct mgag200_pll_values *pixpllc = &new_mgag200_crtc_state->pixpllc;
29 	unsigned int delta, tmpdelta;
30 	unsigned int testp, testm, testn;
31 	unsigned int p, m, n, s;
32 	unsigned int computed;
33 
34 	m = n = p = s = 0;
35 	delta = 0xffffffff;
36 	testp = 0;
37 
38 	for (testm = 150; testm >= 6; testm--) {
39 		if (clock * testm > vcomax)
40 			continue;
41 		if (clock * testm < vcomin)
42 			continue;
43 		for (testn = 120; testn >= 60; testn--) {
44 			computed = (pllreffreq * testn) / testm;
45 			if (computed > clock)
46 				tmpdelta = computed - clock;
47 			else
48 				tmpdelta = clock - computed;
49 			if (tmpdelta < delta) {
50 				delta = tmpdelta;
51 				n = testn + 1;
52 				m = testm + 1;
53 				p = testp + 1;
54 			}
55 			if (delta == 0)
56 				break;
57 		}
58 		if (delta == 0)
59 			break;
60 	}
61 
62 	pixpllc->m = m;
63 	pixpllc->n = n;
64 	pixpllc->p = p;
65 	pixpllc->s = s;
66 
67 	return 0;
68 }
69 
70 /*
71  * Mode-setting pipeline
72  */
73 
74 static const struct drm_plane_helper_funcs mgag200_g200eh3_primary_plane_helper_funcs = {
75 	MGAG200_PRIMARY_PLANE_HELPER_FUNCS,
76 };
77 
78 static const struct drm_plane_funcs mgag200_g200eh3_primary_plane_funcs = {
79 	MGAG200_PRIMARY_PLANE_FUNCS,
80 };
81 
82 static const struct drm_crtc_helper_funcs mgag200_g200eh3_crtc_helper_funcs = {
83 	MGAG200_CRTC_HELPER_FUNCS,
84 };
85 
86 static const struct drm_crtc_funcs mgag200_g200eh3_crtc_funcs = {
87 	MGAG200_CRTC_FUNCS,
88 };
89 
90 static int mgag200_g200eh3_pipeline_init(struct mga_device *mdev)
91 {
92 	struct drm_device *dev = &mdev->base;
93 	struct drm_plane *primary_plane = &mdev->primary_plane;
94 	struct drm_crtc *crtc = &mdev->crtc;
95 	int ret;
96 
97 	ret = drm_universal_plane_init(dev, primary_plane, 0,
98 				       &mgag200_g200eh3_primary_plane_funcs,
99 				       mgag200_primary_plane_formats,
100 				       mgag200_primary_plane_formats_size,
101 				       mgag200_primary_plane_fmtmods,
102 				       DRM_PLANE_TYPE_PRIMARY, NULL);
103 	if (ret) {
104 		drm_err(dev, "drm_universal_plane_init() failed: %d\n", ret);
105 		return ret;
106 	}
107 	drm_plane_helper_add(primary_plane, &mgag200_g200eh3_primary_plane_helper_funcs);
108 	drm_plane_enable_fb_damage_clips(primary_plane);
109 
110 	ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL,
111 					&mgag200_g200eh3_crtc_funcs, NULL);
112 	if (ret) {
113 		drm_err(dev, "drm_crtc_init_with_planes() failed: %d\n", ret);
114 		return ret;
115 	}
116 	drm_crtc_helper_add(crtc, &mgag200_g200eh3_crtc_helper_funcs);
117 
118 	/* FIXME: legacy gamma tables, but atomic gamma doesn't work without */
119 	drm_mode_crtc_set_gamma_size(crtc, MGAG200_LUT_SIZE);
120 	drm_crtc_enable_color_mgmt(crtc, 0, false, MGAG200_LUT_SIZE);
121 
122 	ret = mgag200_vga_bmc_output_init(mdev);
123 	if (ret)
124 		return ret;
125 
126 	return 0;
127 }
128 
129 /*
130  * DRM device
131  */
132 
133 static const struct mgag200_device_info mgag200_g200eh3_device_info =
134 	MGAG200_DEVICE_INFO_INIT(2048, 2048, 0, false, 1, 0, false);
135 
136 static const struct mgag200_device_funcs mgag200_g200eh3_device_funcs = {
137 	.pixpllc_atomic_check = mgag200_g200eh3_pixpllc_atomic_check,
138 	.pixpllc_atomic_update = mgag200_g200eh_pixpllc_atomic_update, // same as G200EH
139 };
140 
141 struct mga_device *mgag200_g200eh3_device_create(struct pci_dev *pdev,
142 						 const struct drm_driver *drv)
143 {
144 	struct mga_device *mdev;
145 	struct drm_device *dev;
146 	resource_size_t vram_available;
147 	int ret;
148 
149 	mdev = devm_drm_dev_alloc(&pdev->dev, drv, struct mga_device, base);
150 	if (IS_ERR(mdev))
151 		return mdev;
152 	dev = &mdev->base;
153 
154 	pci_set_drvdata(pdev, dev);
155 
156 	ret = mgag200_init_pci_options(pdev, 0x00000120, 0x0000b000);
157 	if (ret)
158 		return ERR_PTR(ret);
159 
160 	ret = mgag200_device_preinit(mdev);
161 	if (ret)
162 		return ERR_PTR(ret);
163 
164 	ret = mgag200_device_init(mdev, &mgag200_g200eh3_device_info,
165 				  &mgag200_g200eh3_device_funcs);
166 	if (ret)
167 		return ERR_PTR(ret);
168 
169 	mgag200_g200eh_init_registers(mdev); // same as G200EH
170 
171 	vram_available = mgag200_device_probe_vram(mdev);
172 
173 	ret = mgag200_mode_config_init(mdev, vram_available);
174 	if (ret)
175 		return ERR_PTR(ret);
176 
177 	ret = mgag200_g200eh3_pipeline_init(mdev);
178 	if (ret)
179 		return ERR_PTR(ret);
180 
181 	drm_mode_config_reset(dev);
182 	drm_kms_helper_poll_init(dev);
183 
184 	ret = drm_vblank_init(dev, 1);
185 	if (ret)
186 		return ERR_PTR(ret);
187 
188 	return mdev;
189 }
190