1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | |
3 | #include <linux/pci.h> |
4 | #include <linux/vmalloc.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 int mgag200_g200_init_pci_options(struct pci_dev *pdev) |
15 | { |
16 | struct device *dev = &pdev->dev; |
17 | bool has_sgram; |
18 | u32 option; |
19 | int err; |
20 | |
21 | err = pci_read_config_dword(dev: pdev, PCI_MGA_OPTION, val: &option); |
22 | if (err != PCIBIOS_SUCCESSFUL) { |
23 | dev_err(dev, "pci_read_config_dword(PCI_MGA_OPTION) failed: %d\n" , err); |
24 | return pcibios_err_to_errno(err); |
25 | } |
26 | |
27 | has_sgram = !!(option & PCI_MGA_OPTION_HARDPWMSK); |
28 | |
29 | if (has_sgram) |
30 | option = 0x4049cd21; |
31 | else |
32 | option = 0x40499121; |
33 | |
34 | return mgag200_init_pci_options(pdev, option, option2: 0x00008000); |
35 | } |
36 | |
37 | static void mgag200_g200_init_registers(struct mgag200_g200_device *g200) |
38 | { |
39 | static const u8 dacvalue[] = { |
40 | MGAG200_DAC_DEFAULT(0x00, 0xc9, 0x1f, |
41 | 0x04, 0x2d, 0x19) |
42 | }; |
43 | |
44 | struct mga_device *mdev = &g200->base; |
45 | size_t i; |
46 | |
47 | for (i = 0; i < ARRAY_SIZE(dacvalue); ++i) { |
48 | if ((i <= 0x17) || |
49 | (i == 0x1b) || |
50 | (i == 0x1c) || |
51 | ((i >= 0x1f) && (i <= 0x29)) || |
52 | ((i >= 0x30) && (i <= 0x37))) |
53 | continue; |
54 | WREG_DAC(i, dacvalue[i]); |
55 | } |
56 | |
57 | mgag200_init_registers(mdev); |
58 | } |
59 | |
60 | /* |
61 | * PIXPLLC |
62 | */ |
63 | |
64 | static int mgag200_g200_pixpllc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *new_state) |
65 | { |
66 | static const int post_div_max = 7; |
67 | static const int in_div_min = 1; |
68 | static const int in_div_max = 6; |
69 | static const int feed_div_min = 7; |
70 | static const int feed_div_max = 127; |
71 | |
72 | struct drm_device *dev = crtc->dev; |
73 | struct mgag200_g200_device *g200 = to_mgag200_g200_device(dev); |
74 | struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(state: new_state, crtc); |
75 | struct mgag200_crtc_state *new_mgag200_crtc_state = to_mgag200_crtc_state(base: new_crtc_state); |
76 | long clock = new_crtc_state->mode.clock; |
77 | struct mgag200_pll_values *pixpllc = &new_mgag200_crtc_state->pixpllc; |
78 | u8 testp, testm, testn; |
79 | u8 n = 0, m = 0, p, s; |
80 | long f_vco; |
81 | long computed; |
82 | long delta, tmp_delta; |
83 | long ref_clk = g200->ref_clk; |
84 | long p_clk_min = g200->pclk_min; |
85 | long p_clk_max = g200->pclk_max; |
86 | |
87 | if (clock > p_clk_max) { |
88 | drm_err(dev, "Pixel Clock %ld too high\n" , clock); |
89 | return -EINVAL; |
90 | } |
91 | |
92 | if (clock < p_clk_min >> 3) |
93 | clock = p_clk_min >> 3; |
94 | |
95 | f_vco = clock; |
96 | for (testp = 0; |
97 | testp <= post_div_max && f_vco < p_clk_min; |
98 | testp = (testp << 1) + 1, f_vco <<= 1) |
99 | ; |
100 | p = testp + 1; |
101 | |
102 | delta = clock; |
103 | |
104 | for (testm = in_div_min; testm <= in_div_max; testm++) { |
105 | for (testn = feed_div_min; testn <= feed_div_max; testn++) { |
106 | computed = ref_clk * (testn + 1) / (testm + 1); |
107 | if (computed < f_vco) |
108 | tmp_delta = f_vco - computed; |
109 | else |
110 | tmp_delta = computed - f_vco; |
111 | if (tmp_delta < delta) { |
112 | delta = tmp_delta; |
113 | m = testm + 1; |
114 | n = testn + 1; |
115 | } |
116 | } |
117 | } |
118 | f_vco = ref_clk * n / m; |
119 | if (f_vco < 100000) |
120 | s = 0; |
121 | else if (f_vco < 140000) |
122 | s = 1; |
123 | else if (f_vco < 180000) |
124 | s = 2; |
125 | else |
126 | s = 3; |
127 | |
128 | drm_dbg_kms(dev, "clock: %ld vco: %ld m: %d n: %d p: %d s: %d\n" , |
129 | clock, f_vco, m, n, p, s); |
130 | |
131 | pixpllc->m = m; |
132 | pixpllc->n = n; |
133 | pixpllc->p = p; |
134 | pixpllc->s = s; |
135 | |
136 | return 0; |
137 | } |
138 | |
139 | static void mgag200_g200_pixpllc_atomic_update(struct drm_crtc *crtc, |
140 | struct drm_atomic_state *old_state) |
141 | { |
142 | struct drm_device *dev = crtc->dev; |
143 | struct mga_device *mdev = to_mga_device(dev); |
144 | struct drm_crtc_state *crtc_state = crtc->state; |
145 | struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(base: crtc_state); |
146 | struct mgag200_pll_values *pixpllc = &mgag200_crtc_state->pixpllc; |
147 | unsigned int pixpllcm, pixpllcn, pixpllcp, pixpllcs; |
148 | u8 xpixpllcm, xpixpllcn, xpixpllcp; |
149 | |
150 | pixpllcm = pixpllc->m - 1; |
151 | pixpllcn = pixpllc->n - 1; |
152 | pixpllcp = pixpllc->p - 1; |
153 | pixpllcs = pixpllc->s; |
154 | |
155 | xpixpllcm = pixpllcm; |
156 | xpixpllcn = pixpllcn; |
157 | xpixpllcp = (pixpllcs << 3) | pixpllcp; |
158 | |
159 | WREG_MISC_MASKED(MGAREG_MISC_CLKSEL_MGA, MGAREG_MISC_CLKSEL_MASK); |
160 | |
161 | WREG_DAC(MGA1064_PIX_PLLC_M, xpixpllcm); |
162 | WREG_DAC(MGA1064_PIX_PLLC_N, xpixpllcn); |
163 | WREG_DAC(MGA1064_PIX_PLLC_P, xpixpllcp); |
164 | } |
165 | |
166 | /* |
167 | * Mode-setting pipeline |
168 | */ |
169 | |
170 | static const struct drm_plane_helper_funcs mgag200_g200_primary_plane_helper_funcs = { |
171 | MGAG200_PRIMARY_PLANE_HELPER_FUNCS, |
172 | }; |
173 | |
174 | static const struct drm_plane_funcs mgag200_g200_primary_plane_funcs = { |
175 | MGAG200_PRIMARY_PLANE_FUNCS, |
176 | }; |
177 | |
178 | static const struct drm_crtc_helper_funcs mgag200_g200_crtc_helper_funcs = { |
179 | MGAG200_CRTC_HELPER_FUNCS, |
180 | }; |
181 | |
182 | static const struct drm_crtc_funcs mgag200_g200_crtc_funcs = { |
183 | MGAG200_CRTC_FUNCS, |
184 | }; |
185 | |
186 | static const struct drm_encoder_funcs mgag200_g200_dac_encoder_funcs = { |
187 | MGAG200_DAC_ENCODER_FUNCS, |
188 | }; |
189 | |
190 | static const struct drm_connector_helper_funcs mgag200_g200_vga_connector_helper_funcs = { |
191 | MGAG200_VGA_CONNECTOR_HELPER_FUNCS, |
192 | }; |
193 | |
194 | static const struct drm_connector_funcs mgag200_g200_vga_connector_funcs = { |
195 | MGAG200_VGA_CONNECTOR_FUNCS, |
196 | }; |
197 | |
198 | static int mgag200_g200_pipeline_init(struct mga_device *mdev) |
199 | { |
200 | struct drm_device *dev = &mdev->base; |
201 | struct drm_plane *primary_plane = &mdev->primary_plane; |
202 | struct drm_crtc *crtc = &mdev->crtc; |
203 | struct drm_encoder *encoder = &mdev->encoder; |
204 | struct mga_i2c_chan *i2c = &mdev->i2c; |
205 | struct drm_connector *connector = &mdev->connector; |
206 | int ret; |
207 | |
208 | ret = drm_universal_plane_init(dev, plane: primary_plane, possible_crtcs: 0, |
209 | funcs: &mgag200_g200_primary_plane_funcs, |
210 | formats: mgag200_primary_plane_formats, |
211 | format_count: mgag200_primary_plane_formats_size, |
212 | format_modifiers: mgag200_primary_plane_fmtmods, |
213 | type: DRM_PLANE_TYPE_PRIMARY, NULL); |
214 | if (ret) { |
215 | drm_err(dev, "drm_universal_plane_init() failed: %d\n" , ret); |
216 | return ret; |
217 | } |
218 | drm_plane_helper_add(plane: primary_plane, funcs: &mgag200_g200_primary_plane_helper_funcs); |
219 | drm_plane_enable_fb_damage_clips(plane: primary_plane); |
220 | |
221 | ret = drm_crtc_init_with_planes(dev, crtc, primary: primary_plane, NULL, |
222 | funcs: &mgag200_g200_crtc_funcs, NULL); |
223 | if (ret) { |
224 | drm_err(dev, "drm_crtc_init_with_planes() failed: %d\n" , ret); |
225 | return ret; |
226 | } |
227 | drm_crtc_helper_add(crtc, funcs: &mgag200_g200_crtc_helper_funcs); |
228 | |
229 | /* FIXME: legacy gamma tables, but atomic gamma doesn't work without */ |
230 | drm_mode_crtc_set_gamma_size(crtc, MGAG200_LUT_SIZE); |
231 | drm_crtc_enable_color_mgmt(crtc, degamma_lut_size: 0, has_ctm: false, MGAG200_LUT_SIZE); |
232 | |
233 | encoder->possible_crtcs = drm_crtc_mask(crtc); |
234 | ret = drm_encoder_init(dev, encoder, funcs: &mgag200_g200_dac_encoder_funcs, |
235 | DRM_MODE_ENCODER_DAC, NULL); |
236 | if (ret) { |
237 | drm_err(dev, "drm_encoder_init() failed: %d\n" , ret); |
238 | return ret; |
239 | } |
240 | |
241 | ret = mgag200_i2c_init(mdev, i2c); |
242 | if (ret) { |
243 | drm_err(dev, "failed to add DDC bus: %d\n" , ret); |
244 | return ret; |
245 | } |
246 | |
247 | ret = drm_connector_init_with_ddc(dev, connector, |
248 | funcs: &mgag200_g200_vga_connector_funcs, |
249 | DRM_MODE_CONNECTOR_VGA, |
250 | ddc: &i2c->adapter); |
251 | if (ret) { |
252 | drm_err(dev, "drm_connector_init_with_ddc() failed: %d\n" , ret); |
253 | return ret; |
254 | } |
255 | drm_connector_helper_add(connector, funcs: &mgag200_g200_vga_connector_helper_funcs); |
256 | |
257 | ret = drm_connector_attach_encoder(connector, encoder); |
258 | if (ret) { |
259 | drm_err(dev, "drm_connector_attach_encoder() failed: %d\n" , ret); |
260 | return ret; |
261 | } |
262 | |
263 | return 0; |
264 | } |
265 | |
266 | /* |
267 | * DRM Device |
268 | */ |
269 | |
270 | static const struct mgag200_device_info mgag200_g200_device_info = |
271 | MGAG200_DEVICE_INFO_INIT(2048, 2048, 0, false, 1, 3, false); |
272 | |
273 | static void mgag200_g200_interpret_bios(struct mgag200_g200_device *g200, |
274 | const unsigned char *bios, size_t size) |
275 | { |
276 | static const char matrox[] = {'M', 'A', 'T', 'R', 'O', 'X'}; |
277 | static const unsigned int expected_length[6] = { |
278 | 0, 64, 64, 64, 128, 128 |
279 | }; |
280 | struct mga_device *mdev = &g200->base; |
281 | struct drm_device *dev = &mdev->base; |
282 | const unsigned char *pins; |
283 | unsigned int pins_len, version; |
284 | int offset; |
285 | int tmp; |
286 | |
287 | /* Test for MATROX string. */ |
288 | if (size < 45 + sizeof(matrox)) |
289 | return; |
290 | if (memcmp(p: &bios[45], q: matrox, size: sizeof(matrox)) != 0) |
291 | return; |
292 | |
293 | /* Get the PInS offset. */ |
294 | if (size < MGA_BIOS_OFFSET + 2) |
295 | return; |
296 | offset = (bios[MGA_BIOS_OFFSET + 1] << 8) | bios[MGA_BIOS_OFFSET]; |
297 | |
298 | /* Get PInS data structure. */ |
299 | |
300 | if (size < offset + 6) |
301 | return; |
302 | pins = bios + offset; |
303 | if (pins[0] == 0x2e && pins[1] == 0x41) { |
304 | version = pins[5]; |
305 | pins_len = pins[2]; |
306 | } else { |
307 | version = 1; |
308 | pins_len = pins[0] + (pins[1] << 8); |
309 | } |
310 | |
311 | if (version < 1 || version > 5) { |
312 | drm_warn(dev, "Unknown BIOS PInS version: %d\n" , version); |
313 | return; |
314 | } |
315 | if (pins_len != expected_length[version]) { |
316 | drm_warn(dev, "Unexpected BIOS PInS size: %d expected: %d\n" , |
317 | pins_len, expected_length[version]); |
318 | return; |
319 | } |
320 | if (size < offset + pins_len) |
321 | return; |
322 | |
323 | drm_dbg_kms(dev, "MATROX BIOS PInS version %d size: %d found\n" , version, pins_len); |
324 | |
325 | /* Extract the clock values */ |
326 | |
327 | switch (version) { |
328 | case 1: |
329 | tmp = pins[24] + (pins[25] << 8); |
330 | if (tmp) |
331 | g200->pclk_max = tmp * 10; |
332 | break; |
333 | case 2: |
334 | if (pins[41] != 0xff) |
335 | g200->pclk_max = (pins[41] + 100) * 1000; |
336 | break; |
337 | case 3: |
338 | if (pins[36] != 0xff) |
339 | g200->pclk_max = (pins[36] + 100) * 1000; |
340 | if (pins[52] & 0x20) |
341 | g200->ref_clk = 14318; |
342 | break; |
343 | case 4: |
344 | if (pins[39] != 0xff) |
345 | g200->pclk_max = pins[39] * 4 * 1000; |
346 | if (pins[92] & 0x01) |
347 | g200->ref_clk = 14318; |
348 | break; |
349 | case 5: |
350 | tmp = pins[4] ? 8000 : 6000; |
351 | if (pins[123] != 0xff) |
352 | g200->pclk_min = pins[123] * tmp; |
353 | if (pins[38] != 0xff) |
354 | g200->pclk_max = pins[38] * tmp; |
355 | if (pins[110] & 0x01) |
356 | g200->ref_clk = 14318; |
357 | break; |
358 | default: |
359 | break; |
360 | } |
361 | } |
362 | |
363 | static void mgag200_g200_init_refclk(struct mgag200_g200_device *g200) |
364 | { |
365 | struct mga_device *mdev = &g200->base; |
366 | struct drm_device *dev = &mdev->base; |
367 | struct pci_dev *pdev = to_pci_dev(dev->dev); |
368 | unsigned char __iomem *rom; |
369 | unsigned char *bios; |
370 | size_t size; |
371 | |
372 | g200->pclk_min = 50000; |
373 | g200->pclk_max = 230000; |
374 | g200->ref_clk = 27050; |
375 | |
376 | rom = pci_map_rom(pdev, size: &size); |
377 | if (!rom) |
378 | return; |
379 | |
380 | bios = vmalloc(size); |
381 | if (!bios) |
382 | goto out; |
383 | memcpy_fromio(bios, rom, size); |
384 | |
385 | if (size != 0 && bios[0] == 0x55 && bios[1] == 0xaa) |
386 | mgag200_g200_interpret_bios(g200, bios, size); |
387 | |
388 | drm_dbg_kms(dev, "pclk_min: %ld pclk_max: %ld ref_clk: %ld\n" , |
389 | g200->pclk_min, g200->pclk_max, g200->ref_clk); |
390 | |
391 | vfree(addr: bios); |
392 | out: |
393 | pci_unmap_rom(pdev, rom); |
394 | } |
395 | |
396 | static const struct mgag200_device_funcs mgag200_g200_device_funcs = { |
397 | .pixpllc_atomic_check = mgag200_g200_pixpllc_atomic_check, |
398 | .pixpllc_atomic_update = mgag200_g200_pixpllc_atomic_update, |
399 | }; |
400 | |
401 | struct mga_device *mgag200_g200_device_create(struct pci_dev *pdev, const struct drm_driver *drv) |
402 | { |
403 | struct mgag200_g200_device *g200; |
404 | struct mga_device *mdev; |
405 | struct drm_device *dev; |
406 | resource_size_t vram_available; |
407 | int ret; |
408 | |
409 | g200 = devm_drm_dev_alloc(&pdev->dev, drv, struct mgag200_g200_device, base.base); |
410 | if (IS_ERR(ptr: g200)) |
411 | return ERR_CAST(ptr: g200); |
412 | mdev = &g200->base; |
413 | dev = &mdev->base; |
414 | |
415 | pci_set_drvdata(pdev, data: dev); |
416 | |
417 | ret = mgag200_g200_init_pci_options(pdev); |
418 | if (ret) |
419 | return ERR_PTR(error: ret); |
420 | |
421 | ret = mgag200_device_preinit(mdev); |
422 | if (ret) |
423 | return ERR_PTR(error: ret); |
424 | |
425 | mgag200_g200_init_refclk(g200); |
426 | |
427 | ret = mgag200_device_init(mdev, info: &mgag200_g200_device_info, |
428 | funcs: &mgag200_g200_device_funcs); |
429 | if (ret) |
430 | return ERR_PTR(error: ret); |
431 | |
432 | mgag200_g200_init_registers(g200); |
433 | |
434 | vram_available = mgag200_device_probe_vram(mdev); |
435 | |
436 | ret = mgag200_mode_config_init(mdev, vram_available); |
437 | if (ret) |
438 | return ERR_PTR(error: ret); |
439 | |
440 | ret = mgag200_g200_pipeline_init(mdev); |
441 | if (ret) |
442 | return ERR_PTR(error: ret); |
443 | |
444 | drm_mode_config_reset(dev); |
445 | |
446 | return mdev; |
447 | } |
448 | |