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 | |
11 | #include "mgag200_drv.h" |
12 | |
13 | static void mgag200_g200ew3_init_registers(struct mga_device *mdev) |
14 | { |
15 | mgag200_g200wb_init_registers(mdev); // same as G200WB |
16 | |
17 | WREG_ECRT(0x34, 0x5); // G200EW3 specific |
18 | } |
19 | |
20 | /* |
21 | * PIXPLLC |
22 | */ |
23 | |
24 | static int mgag200_g200ew3_pixpllc_atomic_check(struct drm_crtc *crtc, |
25 | struct drm_atomic_state *new_state) |
26 | { |
27 | static const unsigned int vcomax = 800000; |
28 | static const unsigned int vcomin = 400000; |
29 | static const unsigned int pllreffreq = 25000; |
30 | |
31 | struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(state: new_state, crtc); |
32 | struct mgag200_crtc_state *new_mgag200_crtc_state = to_mgag200_crtc_state(base: new_crtc_state); |
33 | long clock = new_crtc_state->mode.clock; |
34 | struct mgag200_pll_values *pixpllc = &new_mgag200_crtc_state->pixpllc; |
35 | unsigned int delta, tmpdelta; |
36 | unsigned int testp, testm, testn, testp2; |
37 | unsigned int p, m, n, s; |
38 | unsigned int computed; |
39 | |
40 | m = n = p = s = 0; |
41 | delta = 0xffffffff; |
42 | |
43 | for (testp = 1; testp < 8; testp++) { |
44 | for (testp2 = 1; testp2 < 8; testp2++) { |
45 | if (testp < testp2) |
46 | continue; |
47 | if ((clock * testp * testp2) > vcomax) |
48 | continue; |
49 | if ((clock * testp * testp2) < vcomin) |
50 | continue; |
51 | for (testm = 1; testm < 26; testm++) { |
52 | for (testn = 32; testn < 2048 ; testn++) { |
53 | computed = (pllreffreq * testn) / (testm * testp * testp2); |
54 | if (computed > clock) |
55 | tmpdelta = computed - clock; |
56 | else |
57 | tmpdelta = clock - computed; |
58 | if (tmpdelta < delta) { |
59 | delta = tmpdelta; |
60 | m = testm + 1; |
61 | n = testn + 1; |
62 | p = testp + 1; |
63 | s = testp2; |
64 | } |
65 | } |
66 | } |
67 | } |
68 | } |
69 | |
70 | pixpllc->m = m; |
71 | pixpllc->n = n; |
72 | pixpllc->p = p; |
73 | pixpllc->s = s; |
74 | |
75 | return 0; |
76 | } |
77 | |
78 | /* |
79 | * Mode-setting pipeline |
80 | */ |
81 | |
82 | static const struct drm_plane_helper_funcs mgag200_g200ew3_primary_plane_helper_funcs = { |
83 | MGAG200_PRIMARY_PLANE_HELPER_FUNCS, |
84 | }; |
85 | |
86 | static const struct drm_plane_funcs mgag200_g200ew3_primary_plane_funcs = { |
87 | MGAG200_PRIMARY_PLANE_FUNCS, |
88 | }; |
89 | |
90 | static const struct drm_crtc_helper_funcs mgag200_g200ew3_crtc_helper_funcs = { |
91 | MGAG200_CRTC_HELPER_FUNCS, |
92 | }; |
93 | |
94 | static const struct drm_crtc_funcs mgag200_g200ew3_crtc_funcs = { |
95 | MGAG200_CRTC_FUNCS, |
96 | }; |
97 | |
98 | static const struct drm_encoder_funcs mgag200_g200ew3_dac_encoder_funcs = { |
99 | MGAG200_DAC_ENCODER_FUNCS, |
100 | }; |
101 | |
102 | static const struct drm_connector_helper_funcs mgag200_g200ew3_vga_connector_helper_funcs = { |
103 | MGAG200_VGA_CONNECTOR_HELPER_FUNCS, |
104 | }; |
105 | |
106 | static const struct drm_connector_funcs mgag200_g200ew3_vga_connector_funcs = { |
107 | MGAG200_VGA_CONNECTOR_FUNCS, |
108 | }; |
109 | |
110 | static int mgag200_g200ew3_pipeline_init(struct mga_device *mdev) |
111 | { |
112 | struct drm_device *dev = &mdev->base; |
113 | struct drm_plane *primary_plane = &mdev->primary_plane; |
114 | struct drm_crtc *crtc = &mdev->crtc; |
115 | struct drm_encoder *encoder = &mdev->encoder; |
116 | struct mga_i2c_chan *i2c = &mdev->i2c; |
117 | struct drm_connector *connector = &mdev->connector; |
118 | int ret; |
119 | |
120 | ret = drm_universal_plane_init(dev, plane: primary_plane, possible_crtcs: 0, |
121 | funcs: &mgag200_g200ew3_primary_plane_funcs, |
122 | formats: mgag200_primary_plane_formats, |
123 | format_count: mgag200_primary_plane_formats_size, |
124 | format_modifiers: mgag200_primary_plane_fmtmods, |
125 | type: DRM_PLANE_TYPE_PRIMARY, NULL); |
126 | if (ret) { |
127 | drm_err(dev, "drm_universal_plane_init() failed: %d\n" , ret); |
128 | return ret; |
129 | } |
130 | drm_plane_helper_add(plane: primary_plane, funcs: &mgag200_g200ew3_primary_plane_helper_funcs); |
131 | drm_plane_enable_fb_damage_clips(plane: primary_plane); |
132 | |
133 | ret = drm_crtc_init_with_planes(dev, crtc, primary: primary_plane, NULL, |
134 | funcs: &mgag200_g200ew3_crtc_funcs, NULL); |
135 | if (ret) { |
136 | drm_err(dev, "drm_crtc_init_with_planes() failed: %d\n" , ret); |
137 | return ret; |
138 | } |
139 | drm_crtc_helper_add(crtc, funcs: &mgag200_g200ew3_crtc_helper_funcs); |
140 | |
141 | /* FIXME: legacy gamma tables, but atomic gamma doesn't work without */ |
142 | drm_mode_crtc_set_gamma_size(crtc, MGAG200_LUT_SIZE); |
143 | drm_crtc_enable_color_mgmt(crtc, degamma_lut_size: 0, has_ctm: false, MGAG200_LUT_SIZE); |
144 | |
145 | encoder->possible_crtcs = drm_crtc_mask(crtc); |
146 | ret = drm_encoder_init(dev, encoder, funcs: &mgag200_g200ew3_dac_encoder_funcs, |
147 | DRM_MODE_ENCODER_DAC, NULL); |
148 | if (ret) { |
149 | drm_err(dev, "drm_encoder_init() failed: %d\n" , ret); |
150 | return ret; |
151 | } |
152 | |
153 | ret = mgag200_i2c_init(mdev, i2c); |
154 | if (ret) { |
155 | drm_err(dev, "failed to add DDC bus: %d\n" , ret); |
156 | return ret; |
157 | } |
158 | |
159 | ret = drm_connector_init_with_ddc(dev, connector, |
160 | funcs: &mgag200_g200ew3_vga_connector_funcs, |
161 | DRM_MODE_CONNECTOR_VGA, |
162 | ddc: &i2c->adapter); |
163 | if (ret) { |
164 | drm_err(dev, "drm_connector_init_with_ddc() failed: %d\n" , ret); |
165 | return ret; |
166 | } |
167 | drm_connector_helper_add(connector, funcs: &mgag200_g200ew3_vga_connector_helper_funcs); |
168 | |
169 | ret = drm_connector_attach_encoder(connector, encoder); |
170 | if (ret) { |
171 | drm_err(dev, "drm_connector_attach_encoder() failed: %d\n" , ret); |
172 | return ret; |
173 | } |
174 | |
175 | return 0; |
176 | } |
177 | |
178 | /* |
179 | * DRM device |
180 | */ |
181 | |
182 | static const struct mgag200_device_info mgag200_g200ew3_device_info = |
183 | MGAG200_DEVICE_INFO_INIT(2048, 2048, 0, true, 0, 1, false); |
184 | |
185 | static const struct mgag200_device_funcs mgag200_g200ew3_device_funcs = { |
186 | .disable_vidrst = mgag200_bmc_disable_vidrst, |
187 | .enable_vidrst = mgag200_bmc_enable_vidrst, |
188 | .pixpllc_atomic_check = mgag200_g200ew3_pixpllc_atomic_check, |
189 | .pixpllc_atomic_update = mgag200_g200wb_pixpllc_atomic_update, // same as G200WB |
190 | }; |
191 | |
192 | static resource_size_t mgag200_g200ew3_device_probe_vram(struct mga_device *mdev) |
193 | { |
194 | resource_size_t vram_size = resource_size(res: mdev->vram_res); |
195 | |
196 | if (vram_size >= 0x1000000) |
197 | vram_size = vram_size - 0x400000; |
198 | return mgag200_probe_vram(mem: mdev->vram, size: vram_size); |
199 | } |
200 | |
201 | struct mga_device *mgag200_g200ew3_device_create(struct pci_dev *pdev, |
202 | const struct drm_driver *drv) |
203 | { |
204 | struct mga_device *mdev; |
205 | struct drm_device *dev; |
206 | resource_size_t vram_available; |
207 | int ret; |
208 | |
209 | mdev = devm_drm_dev_alloc(&pdev->dev, drv, struct mga_device, base); |
210 | if (IS_ERR(ptr: mdev)) |
211 | return mdev; |
212 | dev = &mdev->base; |
213 | |
214 | pci_set_drvdata(pdev, data: dev); |
215 | |
216 | ret = mgag200_init_pci_options(pdev, option: 0x41049120, option2: 0x0000b000); |
217 | if (ret) |
218 | return ERR_PTR(error: ret); |
219 | |
220 | ret = mgag200_device_preinit(mdev); |
221 | if (ret) |
222 | return ERR_PTR(error: ret); |
223 | |
224 | ret = mgag200_device_init(mdev, info: &mgag200_g200ew3_device_info, |
225 | funcs: &mgag200_g200ew3_device_funcs); |
226 | if (ret) |
227 | return ERR_PTR(error: ret); |
228 | |
229 | mgag200_g200ew3_init_registers(mdev); |
230 | |
231 | vram_available = mgag200_g200ew3_device_probe_vram(mdev); |
232 | |
233 | ret = mgag200_mode_config_init(mdev, vram_available); |
234 | if (ret) |
235 | return ERR_PTR(error: ret); |
236 | |
237 | ret = mgag200_g200ew3_pipeline_init(mdev); |
238 | if (ret) |
239 | return ERR_PTR(error: ret); |
240 | |
241 | drm_mode_config_reset(dev); |
242 | |
243 | return mdev; |
244 | } |
245 | |