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 | void mgag200_g200wb_init_registers(struct mga_device *mdev) |
15 | { |
16 | static const u8 dacvalue[] = { |
17 | MGAG200_DAC_DEFAULT(0x07, 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 | ((i >= 0x44) && (i <= 0x4e))) |
29 | continue; |
30 | WREG_DAC(i, dacvalue[i]); |
31 | } |
32 | |
33 | mgag200_init_registers(mdev); |
34 | } |
35 | |
36 | /* |
37 | * PIXPLLC |
38 | */ |
39 | |
40 | static int mgag200_g200wb_pixpllc_atomic_check(struct drm_crtc *crtc, |
41 | struct drm_atomic_state *new_state) |
42 | { |
43 | static const unsigned int vcomax = 550000; |
44 | static const unsigned int vcomin = 150000; |
45 | static const unsigned int pllreffreq = 48000; |
46 | |
47 | struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(state: new_state, crtc); |
48 | struct mgag200_crtc_state *new_mgag200_crtc_state = to_mgag200_crtc_state(base: new_crtc_state); |
49 | long clock = new_crtc_state->mode.clock; |
50 | struct mgag200_pll_values *pixpllc = &new_mgag200_crtc_state->pixpllc; |
51 | unsigned int delta, tmpdelta; |
52 | unsigned int testp, testm, testn; |
53 | unsigned int p, m, n, s; |
54 | unsigned int computed; |
55 | |
56 | m = n = p = s = 0; |
57 | delta = 0xffffffff; |
58 | |
59 | for (testp = 1; testp < 9; testp++) { |
60 | if (clock * testp > vcomax) |
61 | continue; |
62 | if (clock * testp < vcomin) |
63 | continue; |
64 | |
65 | for (testm = 1; testm < 17; testm++) { |
66 | for (testn = 1; testn < 151; testn++) { |
67 | computed = (pllreffreq * testn) / (testm * testp); |
68 | if (computed > clock) |
69 | tmpdelta = computed - clock; |
70 | else |
71 | tmpdelta = clock - computed; |
72 | if (tmpdelta < delta) { |
73 | delta = tmpdelta; |
74 | n = testn; |
75 | m = testm; |
76 | p = testp; |
77 | s = 0; |
78 | } |
79 | } |
80 | } |
81 | } |
82 | |
83 | pixpllc->m = m; |
84 | pixpllc->n = n; |
85 | pixpllc->p = p; |
86 | pixpllc->s = s; |
87 | |
88 | return 0; |
89 | } |
90 | |
91 | void mgag200_g200wb_pixpllc_atomic_update(struct drm_crtc *crtc, |
92 | struct drm_atomic_state *old_state) |
93 | { |
94 | struct drm_device *dev = crtc->dev; |
95 | struct mga_device *mdev = to_mga_device(dev); |
96 | struct drm_crtc_state *crtc_state = crtc->state; |
97 | struct mgag200_crtc_state *mgag200_crtc_state = to_mgag200_crtc_state(base: crtc_state); |
98 | struct mgag200_pll_values *pixpllc = &mgag200_crtc_state->pixpllc; |
99 | bool pll_locked = false; |
100 | unsigned int pixpllcm, pixpllcn, pixpllcp, pixpllcs; |
101 | u8 xpixpllcm, xpixpllcn, xpixpllcp, tmp; |
102 | int i, j, tmpcount, vcount; |
103 | |
104 | pixpllcm = pixpllc->m - 1; |
105 | pixpllcn = pixpllc->n - 1; |
106 | pixpllcp = pixpllc->p - 1; |
107 | pixpllcs = pixpllc->s; |
108 | |
109 | xpixpllcm = ((pixpllcn & BIT(8)) >> 1) | pixpllcm; |
110 | xpixpllcn = pixpllcn; |
111 | xpixpllcp = ((pixpllcn & GENMASK(10, 9)) >> 3) | (pixpllcs << 3) | pixpllcp; |
112 | |
113 | WREG_MISC_MASKED(MGAREG_MISC_CLKSEL_MGA, MGAREG_MISC_CLKSEL_MASK); |
114 | |
115 | for (i = 0; i <= 32 && pll_locked == false; i++) { |
116 | if (i > 0) { |
117 | WREG8(MGAREG_CRTC_INDEX, 0x1e); |
118 | tmp = RREG8(MGAREG_CRTC_DATA); |
119 | if (tmp < 0xff) |
120 | WREG8(MGAREG_CRTC_DATA, tmp+1); |
121 | } |
122 | |
123 | /* set pixclkdis to 1 */ |
124 | WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL); |
125 | tmp = RREG8(DAC_DATA); |
126 | tmp |= MGA1064_PIX_CLK_CTL_CLK_DIS; |
127 | WREG8(DAC_DATA, tmp); |
128 | |
129 | WREG8(DAC_INDEX, MGA1064_REMHEADCTL); |
130 | tmp = RREG8(DAC_DATA); |
131 | tmp |= MGA1064_REMHEADCTL_CLKDIS; |
132 | WREG8(DAC_DATA, tmp); |
133 | |
134 | /* select PLL Set C */ |
135 | tmp = RREG8(MGAREG_MEM_MISC_READ); |
136 | tmp |= 0x3 << 2; |
137 | WREG8(MGAREG_MEM_MISC_WRITE, tmp); |
138 | |
139 | WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL); |
140 | tmp = RREG8(DAC_DATA); |
141 | tmp |= MGA1064_PIX_CLK_CTL_CLK_POW_DOWN | 0x80; |
142 | WREG8(DAC_DATA, tmp); |
143 | |
144 | udelay(500); |
145 | |
146 | /* reset the PLL */ |
147 | WREG8(DAC_INDEX, MGA1064_VREF_CTL); |
148 | tmp = RREG8(DAC_DATA); |
149 | tmp &= ~0x04; |
150 | WREG8(DAC_DATA, tmp); |
151 | |
152 | udelay(50); |
153 | |
154 | /* program pixel pll register */ |
155 | WREG_DAC(MGA1064_WB_PIX_PLLC_N, xpixpllcn); |
156 | WREG_DAC(MGA1064_WB_PIX_PLLC_M, xpixpllcm); |
157 | WREG_DAC(MGA1064_WB_PIX_PLLC_P, xpixpllcp); |
158 | |
159 | udelay(50); |
160 | |
161 | /* turn pll on */ |
162 | WREG8(DAC_INDEX, MGA1064_VREF_CTL); |
163 | tmp = RREG8(DAC_DATA); |
164 | tmp |= 0x04; |
165 | WREG_DAC(MGA1064_VREF_CTL, tmp); |
166 | |
167 | udelay(500); |
168 | |
169 | /* select the pixel pll */ |
170 | WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL); |
171 | tmp = RREG8(DAC_DATA); |
172 | tmp &= ~MGA1064_PIX_CLK_CTL_SEL_MSK; |
173 | tmp |= MGA1064_PIX_CLK_CTL_SEL_PLL; |
174 | WREG8(DAC_DATA, tmp); |
175 | |
176 | WREG8(DAC_INDEX, MGA1064_REMHEADCTL); |
177 | tmp = RREG8(DAC_DATA); |
178 | tmp &= ~MGA1064_REMHEADCTL_CLKSL_MSK; |
179 | tmp |= MGA1064_REMHEADCTL_CLKSL_PLL; |
180 | WREG8(DAC_DATA, tmp); |
181 | |
182 | /* reset dotclock rate bit */ |
183 | WREG8(MGAREG_SEQ_INDEX, 1); |
184 | tmp = RREG8(MGAREG_SEQ_DATA); |
185 | tmp &= ~0x8; |
186 | WREG8(MGAREG_SEQ_DATA, tmp); |
187 | |
188 | WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL); |
189 | tmp = RREG8(DAC_DATA); |
190 | tmp &= ~MGA1064_PIX_CLK_CTL_CLK_DIS; |
191 | WREG8(DAC_DATA, tmp); |
192 | |
193 | vcount = RREG8(MGAREG_VCOUNT); |
194 | |
195 | for (j = 0; j < 30 && pll_locked == false; j++) { |
196 | tmpcount = RREG8(MGAREG_VCOUNT); |
197 | if (tmpcount < vcount) |
198 | vcount = 0; |
199 | if ((tmpcount - vcount) > 2) |
200 | pll_locked = true; |
201 | else |
202 | udelay(5); |
203 | } |
204 | } |
205 | |
206 | WREG8(DAC_INDEX, MGA1064_REMHEADCTL); |
207 | tmp = RREG8(DAC_DATA); |
208 | tmp &= ~MGA1064_REMHEADCTL_CLKDIS; |
209 | WREG_DAC(MGA1064_REMHEADCTL, tmp); |
210 | } |
211 | |
212 | /* |
213 | * Mode-setting pipeline |
214 | */ |
215 | |
216 | static const struct drm_plane_helper_funcs mgag200_g200wb_primary_plane_helper_funcs = { |
217 | MGAG200_PRIMARY_PLANE_HELPER_FUNCS, |
218 | }; |
219 | |
220 | static const struct drm_plane_funcs mgag200_g200wb_primary_plane_funcs = { |
221 | MGAG200_PRIMARY_PLANE_FUNCS, |
222 | }; |
223 | |
224 | static const struct drm_crtc_helper_funcs mgag200_g200wb_crtc_helper_funcs = { |
225 | MGAG200_CRTC_HELPER_FUNCS, |
226 | }; |
227 | |
228 | static const struct drm_crtc_funcs mgag200_g200wb_crtc_funcs = { |
229 | MGAG200_CRTC_FUNCS, |
230 | }; |
231 | |
232 | static const struct drm_encoder_funcs mgag200_g200wb_dac_encoder_funcs = { |
233 | MGAG200_DAC_ENCODER_FUNCS, |
234 | }; |
235 | |
236 | static const struct drm_connector_helper_funcs mgag200_g200wb_vga_connector_helper_funcs = { |
237 | MGAG200_VGA_CONNECTOR_HELPER_FUNCS, |
238 | }; |
239 | |
240 | static const struct drm_connector_funcs mgag200_g200wb_vga_connector_funcs = { |
241 | MGAG200_VGA_CONNECTOR_FUNCS, |
242 | }; |
243 | |
244 | static int mgag200_g200wb_pipeline_init(struct mga_device *mdev) |
245 | { |
246 | struct drm_device *dev = &mdev->base; |
247 | struct drm_plane *primary_plane = &mdev->primary_plane; |
248 | struct drm_crtc *crtc = &mdev->crtc; |
249 | struct drm_encoder *encoder = &mdev->encoder; |
250 | struct mga_i2c_chan *i2c = &mdev->i2c; |
251 | struct drm_connector *connector = &mdev->connector; |
252 | int ret; |
253 | |
254 | ret = drm_universal_plane_init(dev, plane: primary_plane, possible_crtcs: 0, |
255 | funcs: &mgag200_g200wb_primary_plane_funcs, |
256 | formats: mgag200_primary_plane_formats, |
257 | format_count: mgag200_primary_plane_formats_size, |
258 | format_modifiers: mgag200_primary_plane_fmtmods, |
259 | type: DRM_PLANE_TYPE_PRIMARY, NULL); |
260 | if (ret) { |
261 | drm_err(dev, "drm_universal_plane_init() failed: %d\n" , ret); |
262 | return ret; |
263 | } |
264 | drm_plane_helper_add(plane: primary_plane, funcs: &mgag200_g200wb_primary_plane_helper_funcs); |
265 | drm_plane_enable_fb_damage_clips(plane: primary_plane); |
266 | |
267 | ret = drm_crtc_init_with_planes(dev, crtc, primary: primary_plane, NULL, |
268 | funcs: &mgag200_g200wb_crtc_funcs, NULL); |
269 | if (ret) { |
270 | drm_err(dev, "drm_crtc_init_with_planes() failed: %d\n" , ret); |
271 | return ret; |
272 | } |
273 | drm_crtc_helper_add(crtc, funcs: &mgag200_g200wb_crtc_helper_funcs); |
274 | |
275 | /* FIXME: legacy gamma tables, but atomic gamma doesn't work without */ |
276 | drm_mode_crtc_set_gamma_size(crtc, MGAG200_LUT_SIZE); |
277 | drm_crtc_enable_color_mgmt(crtc, degamma_lut_size: 0, has_ctm: false, MGAG200_LUT_SIZE); |
278 | |
279 | encoder->possible_crtcs = drm_crtc_mask(crtc); |
280 | ret = drm_encoder_init(dev, encoder, funcs: &mgag200_g200wb_dac_encoder_funcs, |
281 | DRM_MODE_ENCODER_DAC, NULL); |
282 | if (ret) { |
283 | drm_err(dev, "drm_encoder_init() failed: %d\n" , ret); |
284 | return ret; |
285 | } |
286 | |
287 | ret = mgag200_i2c_init(mdev, i2c); |
288 | if (ret) { |
289 | drm_err(dev, "failed to add DDC bus: %d\n" , ret); |
290 | return ret; |
291 | } |
292 | |
293 | ret = drm_connector_init_with_ddc(dev, connector, |
294 | funcs: &mgag200_g200wb_vga_connector_funcs, |
295 | DRM_MODE_CONNECTOR_VGA, |
296 | ddc: &i2c->adapter); |
297 | if (ret) { |
298 | drm_err(dev, "drm_connector_init_with_ddc() failed: %d\n" , ret); |
299 | return ret; |
300 | } |
301 | drm_connector_helper_add(connector, funcs: &mgag200_g200wb_vga_connector_helper_funcs); |
302 | |
303 | ret = drm_connector_attach_encoder(connector, encoder); |
304 | if (ret) { |
305 | drm_err(dev, "drm_connector_attach_encoder() failed: %d\n" , ret); |
306 | return ret; |
307 | } |
308 | |
309 | return 0; |
310 | } |
311 | |
312 | /* |
313 | * DRM device |
314 | */ |
315 | |
316 | static const struct mgag200_device_info mgag200_g200wb_device_info = |
317 | MGAG200_DEVICE_INFO_INIT(1280, 1024, 31877, true, 0, 1, false); |
318 | |
319 | static const struct mgag200_device_funcs mgag200_g200wb_device_funcs = { |
320 | .disable_vidrst = mgag200_bmc_disable_vidrst, |
321 | .enable_vidrst = mgag200_bmc_enable_vidrst, |
322 | .pixpllc_atomic_check = mgag200_g200wb_pixpllc_atomic_check, |
323 | .pixpllc_atomic_update = mgag200_g200wb_pixpllc_atomic_update, |
324 | }; |
325 | |
326 | struct mga_device *mgag200_g200wb_device_create(struct pci_dev *pdev, const struct drm_driver *drv) |
327 | { |
328 | struct mga_device *mdev; |
329 | struct drm_device *dev; |
330 | resource_size_t vram_available; |
331 | int ret; |
332 | |
333 | mdev = devm_drm_dev_alloc(&pdev->dev, drv, struct mga_device, base); |
334 | if (IS_ERR(ptr: mdev)) |
335 | return mdev; |
336 | dev = &mdev->base; |
337 | |
338 | pci_set_drvdata(pdev, data: dev); |
339 | |
340 | ret = mgag200_init_pci_options(pdev, option: 0x41049120, option2: 0x0000b000); |
341 | if (ret) |
342 | return ERR_PTR(error: ret); |
343 | |
344 | ret = mgag200_device_preinit(mdev); |
345 | if (ret) |
346 | return ERR_PTR(error: ret); |
347 | |
348 | ret = mgag200_device_init(mdev, info: &mgag200_g200wb_device_info, |
349 | funcs: &mgag200_g200wb_device_funcs); |
350 | if (ret) |
351 | return ERR_PTR(error: ret); |
352 | |
353 | mgag200_g200wb_init_registers(mdev); |
354 | |
355 | vram_available = mgag200_device_probe_vram(mdev); |
356 | |
357 | ret = mgag200_mode_config_init(mdev, vram_available); |
358 | if (ret) |
359 | return ERR_PTR(error: ret); |
360 | |
361 | ret = mgag200_g200wb_pipeline_init(mdev); |
362 | if (ret) |
363 | return ERR_PTR(error: ret); |
364 | |
365 | drm_mode_config_reset(dev); |
366 | |
367 | return mdev; |
368 | } |
369 | |