1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | |
3 | #include <linux/delay.h> |
4 | #include <linux/pci.h> |
5 | |
6 | #include <drm/drm_atomic.h> |
7 | #include <drm/drm_atomic_helper.h> |
8 | #include <drm/drm_drv.h> |
9 | #include <drm/drm_gem_atomic_helper.h> |
10 | #include <drm/drm_probe_helper.h> |
11 | |
12 | #include "mgag200_drv.h" |
13 | |
14 | static void mgag200_g200er_init_registers(struct mga_device *mdev) |
15 | { |
16 | static const u8 dacvalue[] = { |
17 | MGAG200_DAC_DEFAULT(0x00, 0xc9, 0x1f, 0x00, 0x00, 0x00) |
18 | }; |
19 | |
20 | size_t i; |
21 | |
22 | for (i = 0; i < ARRAY_SIZE(dacvalue); i++) { |
23 | if ((i <= 0x17) || |
24 | (i == 0x1b) || |
25 | (i == 0x1c) || |
26 | ((i >= 0x1f) && (i <= 0x29)) || |
27 | ((i >= 0x30) && (i <= 0x37))) |
28 | continue; |
29 | WREG_DAC(i, dacvalue[i]); |
30 | } |
31 | |
32 | WREG_DAC(0x90, 0); /* G200ER specific */ |
33 | |
34 | mgag200_init_registers(mdev); |
35 | |
36 | WREG_ECRT(0x24, 0x5); /* G200ER specific */ |
37 | } |
38 | |
39 | static void mgag200_g200er_reset_tagfifo(struct mga_device *mdev) |
40 | { |
41 | static const uint32_t RESET_FLAG = 0x00200000; /* undocumented magic value */ |
42 | u32 memctl; |
43 | |
44 | memctl = RREG32(MGAREG_MEMCTL); |
45 | |
46 | memctl |= RESET_FLAG; |
47 | WREG32(MGAREG_MEMCTL, memctl); |
48 | |
49 | udelay(1000); |
50 | |
51 | memctl &= ~RESET_FLAG; |
52 | WREG32(MGAREG_MEMCTL, memctl); |
53 | } |
54 | |
55 | /* |
56 | * PIXPLLC |
57 | */ |
58 | |
59 | static int mgag200_g200er_pixpllc_atomic_check(struct drm_crtc *crtc, |
60 | struct drm_atomic_state *new_state) |
61 | { |
62 | static const unsigned int vcomax = 1488000; |
63 | static const unsigned int vcomin = 1056000; |
64 | static const unsigned int pllreffreq = 48000; |
65 | static const unsigned int m_div_val[] = { 1, 2, 4, 8 }; |
66 | |
67 | struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(state: new_state, crtc); |
68 | struct mgag200_crtc_state *new_mgag200_crtc_state = to_mgag200_crtc_state(base: new_crtc_state); |
69 | long clock = new_crtc_state->mode.clock; |
70 | struct mgag200_pll_values *pixpllc = &new_mgag200_crtc_state->pixpllc; |
71 | unsigned int delta, tmpdelta; |
72 | int testr, testn, testm, testo; |
73 | unsigned int p, m, n, s; |
74 | unsigned int computed, vco; |
75 | |
76 | m = n = p = s = 0; |
77 | delta = 0xffffffff; |
78 | |
79 | for (testr = 0; testr < 4; testr++) { |
80 | if (delta == 0) |
81 | break; |
82 | for (testn = 5; testn < 129; testn++) { |
83 | if (delta == 0) |
84 | break; |
85 | for (testm = 3; testm >= 0; testm--) { |
86 | if (delta == 0) |
87 | break; |
88 | for (testo = 5; testo < 33; testo++) { |
89 | vco = pllreffreq * (testn + 1) / |
90 | (testr + 1); |
91 | if (vco < vcomin) |
92 | continue; |
93 | if (vco > vcomax) |
94 | continue; |
95 | computed = vco / (m_div_val[testm] * (testo + 1)); |
96 | if (computed > clock) |
97 | tmpdelta = computed - clock; |
98 | else |
99 | tmpdelta = clock - computed; |
100 | if (tmpdelta < delta) { |
101 | delta = tmpdelta; |
102 | m = (testm | (testo << 3)) + 1; |
103 | n = testn + 1; |
104 | p = testr + 1; |
105 | s = testr; |
106 | } |
107 | } |
108 | } |
109 | } |
110 | } |
111 | |
112 | pixpllc->m = m; |
113 | pixpllc->n = n; |
114 | pixpllc->p = p; |
115 | pixpllc->s = s; |
116 | |
117 | return 0; |
118 | } |
119 | |
120 | static void mgag200_g200er_pixpllc_atomic_update(struct drm_crtc *crtc, |
121 | struct drm_atomic_state *old_state) |
122 | { |
123 | struct drm_device *dev = crtc->dev; |
124 | struct mga_device *mdev = to_mga_device(dev); |
125 | struct drm_crtc_state *crtc_state = crtc->state; |
126 | struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(base: crtc_state); |
127 | struct mgag200_pll_values *pixpllc = &mgag200_crtc_state->pixpllc; |
128 | unsigned int pixpllcm, pixpllcn, pixpllcp, pixpllcs; |
129 | u8 xpixpllcm, xpixpllcn, xpixpllcp, tmp; |
130 | |
131 | pixpllcm = pixpllc->m - 1; |
132 | pixpllcn = pixpllc->n - 1; |
133 | pixpllcp = pixpllc->p - 1; |
134 | pixpllcs = pixpllc->s; |
135 | |
136 | xpixpllcm = pixpllcm; |
137 | xpixpllcn = pixpllcn; |
138 | xpixpllcp = (pixpllcs << 3) | pixpllcp; |
139 | |
140 | WREG_MISC_MASKED(MGAREG_MISC_CLKSEL_MGA, MGAREG_MISC_CLKSEL_MASK); |
141 | |
142 | WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL); |
143 | tmp = RREG8(DAC_DATA); |
144 | tmp |= MGA1064_PIX_CLK_CTL_CLK_DIS; |
145 | WREG8(DAC_DATA, tmp); |
146 | |
147 | WREG8(DAC_INDEX, MGA1064_REMHEADCTL); |
148 | tmp = RREG8(DAC_DATA); |
149 | tmp |= MGA1064_REMHEADCTL_CLKDIS; |
150 | WREG8(DAC_DATA, tmp); |
151 | |
152 | tmp = RREG8(MGAREG_MEM_MISC_READ); |
153 | tmp |= (0x3<<2) | 0xc0; |
154 | WREG8(MGAREG_MEM_MISC_WRITE, tmp); |
155 | |
156 | WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL); |
157 | tmp = RREG8(DAC_DATA); |
158 | tmp &= ~MGA1064_PIX_CLK_CTL_CLK_DIS; |
159 | tmp |= MGA1064_PIX_CLK_CTL_CLK_POW_DOWN; |
160 | WREG8(DAC_DATA, tmp); |
161 | |
162 | udelay(500); |
163 | |
164 | WREG_DAC(MGA1064_ER_PIX_PLLC_N, xpixpllcn); |
165 | WREG_DAC(MGA1064_ER_PIX_PLLC_M, xpixpllcm); |
166 | WREG_DAC(MGA1064_ER_PIX_PLLC_P, xpixpllcp); |
167 | |
168 | udelay(50); |
169 | } |
170 | |
171 | /* |
172 | * Mode-setting pipeline |
173 | */ |
174 | |
175 | static const struct drm_plane_helper_funcs mgag200_g200er_primary_plane_helper_funcs = { |
176 | MGAG200_PRIMARY_PLANE_HELPER_FUNCS, |
177 | }; |
178 | |
179 | static const struct drm_plane_funcs mgag200_g200er_primary_plane_funcs = { |
180 | MGAG200_PRIMARY_PLANE_FUNCS, |
181 | }; |
182 | |
183 | static void mgag200_g200er_crtc_helper_atomic_enable(struct drm_crtc *crtc, |
184 | struct drm_atomic_state *old_state) |
185 | { |
186 | struct drm_device *dev = crtc->dev; |
187 | struct mga_device *mdev = to_mga_device(dev); |
188 | const struct mgag200_device_funcs *funcs = mdev->funcs; |
189 | struct drm_crtc_state *crtc_state = crtc->state; |
190 | struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode; |
191 | struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(base: crtc_state); |
192 | const struct drm_format_info *format = mgag200_crtc_state->format; |
193 | |
194 | if (funcs->disable_vidrst) |
195 | funcs->disable_vidrst(mdev); |
196 | |
197 | mgag200_set_format_regs(mdev, format); |
198 | mgag200_set_mode_regs(mdev, mode: adjusted_mode); |
199 | |
200 | if (funcs->pixpllc_atomic_update) |
201 | funcs->pixpllc_atomic_update(crtc, old_state); |
202 | |
203 | mgag200_g200er_reset_tagfifo(mdev); |
204 | |
205 | if (crtc_state->gamma_lut) |
206 | mgag200_crtc_set_gamma(mdev, format, lut: crtc_state->gamma_lut->data); |
207 | else |
208 | mgag200_crtc_set_gamma_linear(mdev, format); |
209 | |
210 | mgag200_enable_display(mdev); |
211 | |
212 | if (funcs->enable_vidrst) |
213 | funcs->enable_vidrst(mdev); |
214 | } |
215 | |
216 | static const struct drm_crtc_helper_funcs mgag200_g200er_crtc_helper_funcs = { |
217 | .mode_valid = mgag200_crtc_helper_mode_valid, |
218 | .atomic_check = mgag200_crtc_helper_atomic_check, |
219 | .atomic_flush = mgag200_crtc_helper_atomic_flush, |
220 | .atomic_enable = mgag200_g200er_crtc_helper_atomic_enable, |
221 | .atomic_disable = mgag200_crtc_helper_atomic_disable |
222 | }; |
223 | |
224 | static const struct drm_crtc_funcs mgag200_g200er_crtc_funcs = { |
225 | MGAG200_CRTC_FUNCS, |
226 | }; |
227 | |
228 | static const struct drm_encoder_funcs mgag200_g200er_dac_encoder_funcs = { |
229 | MGAG200_DAC_ENCODER_FUNCS, |
230 | }; |
231 | |
232 | static const struct drm_connector_helper_funcs mgag200_g200er_vga_connector_helper_funcs = { |
233 | MGAG200_VGA_CONNECTOR_HELPER_FUNCS, |
234 | }; |
235 | |
236 | static const struct drm_connector_funcs mgag200_g200er_vga_connector_funcs = { |
237 | MGAG200_VGA_CONNECTOR_FUNCS, |
238 | }; |
239 | |
240 | static int mgag200_g200er_pipeline_init(struct mga_device *mdev) |
241 | { |
242 | struct drm_device *dev = &mdev->base; |
243 | struct drm_plane *primary_plane = &mdev->primary_plane; |
244 | struct drm_crtc *crtc = &mdev->crtc; |
245 | struct drm_encoder *encoder = &mdev->encoder; |
246 | struct mga_i2c_chan *i2c = &mdev->i2c; |
247 | struct drm_connector *connector = &mdev->connector; |
248 | int ret; |
249 | |
250 | ret = drm_universal_plane_init(dev, plane: primary_plane, possible_crtcs: 0, |
251 | funcs: &mgag200_g200er_primary_plane_funcs, |
252 | formats: mgag200_primary_plane_formats, |
253 | format_count: mgag200_primary_plane_formats_size, |
254 | format_modifiers: mgag200_primary_plane_fmtmods, |
255 | type: DRM_PLANE_TYPE_PRIMARY, NULL); |
256 | if (ret) { |
257 | drm_err(dev, "drm_universal_plane_init() failed: %d\n" , ret); |
258 | return ret; |
259 | } |
260 | drm_plane_helper_add(plane: primary_plane, funcs: &mgag200_g200er_primary_plane_helper_funcs); |
261 | drm_plane_enable_fb_damage_clips(plane: primary_plane); |
262 | |
263 | ret = drm_crtc_init_with_planes(dev, crtc, primary: primary_plane, NULL, |
264 | funcs: &mgag200_g200er_crtc_funcs, NULL); |
265 | if (ret) { |
266 | drm_err(dev, "drm_crtc_init_with_planes() failed: %d\n" , ret); |
267 | return ret; |
268 | } |
269 | drm_crtc_helper_add(crtc, funcs: &mgag200_g200er_crtc_helper_funcs); |
270 | |
271 | /* FIXME: legacy gamma tables, but atomic gamma doesn't work without */ |
272 | drm_mode_crtc_set_gamma_size(crtc, MGAG200_LUT_SIZE); |
273 | drm_crtc_enable_color_mgmt(crtc, degamma_lut_size: 0, has_ctm: false, MGAG200_LUT_SIZE); |
274 | |
275 | encoder->possible_crtcs = drm_crtc_mask(crtc); |
276 | ret = drm_encoder_init(dev, encoder, funcs: &mgag200_g200er_dac_encoder_funcs, |
277 | DRM_MODE_ENCODER_DAC, NULL); |
278 | if (ret) { |
279 | drm_err(dev, "drm_encoder_init() failed: %d\n" , ret); |
280 | return ret; |
281 | } |
282 | |
283 | ret = mgag200_i2c_init(mdev, i2c); |
284 | if (ret) { |
285 | drm_err(dev, "failed to add DDC bus: %d\n" , ret); |
286 | return ret; |
287 | } |
288 | |
289 | ret = drm_connector_init_with_ddc(dev, connector, |
290 | funcs: &mgag200_g200er_vga_connector_funcs, |
291 | DRM_MODE_CONNECTOR_VGA, |
292 | ddc: &i2c->adapter); |
293 | if (ret) { |
294 | drm_err(dev, "drm_connector_init_with_ddc() failed: %d\n" , ret); |
295 | return ret; |
296 | } |
297 | drm_connector_helper_add(connector, funcs: &mgag200_g200er_vga_connector_helper_funcs); |
298 | |
299 | ret = drm_connector_attach_encoder(connector, encoder); |
300 | if (ret) { |
301 | drm_err(dev, "drm_connector_attach_encoder() failed: %d\n" , ret); |
302 | return ret; |
303 | } |
304 | |
305 | return 0; |
306 | } |
307 | |
308 | /* |
309 | * DRM device |
310 | */ |
311 | |
312 | static const struct mgag200_device_info mgag200_g200er_device_info = |
313 | MGAG200_DEVICE_INFO_INIT(2048, 2048, 55000, false, 1, 0, false); |
314 | |
315 | static const struct mgag200_device_funcs mgag200_g200er_device_funcs = { |
316 | .pixpllc_atomic_check = mgag200_g200er_pixpllc_atomic_check, |
317 | .pixpllc_atomic_update = mgag200_g200er_pixpllc_atomic_update, |
318 | }; |
319 | |
320 | struct mga_device *mgag200_g200er_device_create(struct pci_dev *pdev, const struct drm_driver *drv) |
321 | { |
322 | struct mga_device *mdev; |
323 | struct drm_device *dev; |
324 | resource_size_t vram_available; |
325 | int ret; |
326 | |
327 | mdev = devm_drm_dev_alloc(&pdev->dev, drv, struct mga_device, base); |
328 | if (IS_ERR(ptr: mdev)) |
329 | return mdev; |
330 | dev = &mdev->base; |
331 | |
332 | pci_set_drvdata(pdev, data: dev); |
333 | |
334 | ret = mgag200_device_preinit(mdev); |
335 | if (ret) |
336 | return ERR_PTR(error: ret); |
337 | |
338 | ret = mgag200_device_init(mdev, info: &mgag200_g200er_device_info, |
339 | funcs: &mgag200_g200er_device_funcs); |
340 | if (ret) |
341 | return ERR_PTR(error: ret); |
342 | |
343 | mgag200_g200er_init_registers(mdev); |
344 | |
345 | vram_available = mgag200_device_probe_vram(mdev); |
346 | |
347 | ret = mgag200_mode_config_init(mdev, vram_available); |
348 | if (ret) |
349 | return ERR_PTR(error: ret); |
350 | |
351 | ret = mgag200_g200er_pipeline_init(mdev); |
352 | if (ret) |
353 | return ERR_PTR(error: ret); |
354 | |
355 | drm_mode_config_reset(dev); |
356 | |
357 | return mdev; |
358 | } |
359 | |