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