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
mgag200_g200ew3_init_registers(struct mga_device * mdev)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
mgag200_g200ew3_pixpllc_atomic_check(struct drm_crtc * crtc,struct drm_atomic_state * new_state)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(new_state, crtc);
32 struct mgag200_crtc_state *new_mgag200_crtc_state = to_mgag200_crtc_state(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
mgag200_g200ew3_pipeline_init(struct mga_device * mdev)98 static int mgag200_g200ew3_pipeline_init(struct mga_device *mdev)
99 {
100 struct drm_device *dev = &mdev->base;
101 struct drm_plane *primary_plane = &mdev->primary_plane;
102 struct drm_crtc *crtc = &mdev->crtc;
103 int ret;
104
105 ret = drm_universal_plane_init(dev, primary_plane, 0,
106 &mgag200_g200ew3_primary_plane_funcs,
107 mgag200_primary_plane_formats,
108 mgag200_primary_plane_formats_size,
109 mgag200_primary_plane_fmtmods,
110 DRM_PLANE_TYPE_PRIMARY, NULL);
111 if (ret) {
112 drm_err(dev, "drm_universal_plane_init() failed: %d\n", ret);
113 return ret;
114 }
115 drm_plane_helper_add(primary_plane, &mgag200_g200ew3_primary_plane_helper_funcs);
116 drm_plane_enable_fb_damage_clips(primary_plane);
117
118 ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL,
119 &mgag200_g200ew3_crtc_funcs, NULL);
120 if (ret) {
121 drm_err(dev, "drm_crtc_init_with_planes() failed: %d\n", ret);
122 return ret;
123 }
124 drm_crtc_helper_add(crtc, &mgag200_g200ew3_crtc_helper_funcs);
125
126 /* FIXME: legacy gamma tables, but atomic gamma doesn't work without */
127 drm_mode_crtc_set_gamma_size(crtc, MGAG200_LUT_SIZE);
128 drm_crtc_enable_color_mgmt(crtc, 0, false, MGAG200_LUT_SIZE);
129
130 ret = mgag200_vga_bmc_output_init(mdev);
131 if (ret)
132 return ret;
133
134 return 0;
135 }
136
137 /*
138 * DRM device
139 */
140
141 static const struct mgag200_device_info mgag200_g200ew3_device_info =
142 MGAG200_DEVICE_INFO_INIT(2048, 2048, 0, true, 0, 1, false);
143
144 static const struct mgag200_device_funcs mgag200_g200ew3_device_funcs = {
145 .pixpllc_atomic_check = mgag200_g200ew3_pixpllc_atomic_check,
146 .pixpllc_atomic_update = mgag200_g200wb_pixpllc_atomic_update, // same as G200WB
147 };
148
mgag200_g200ew3_device_probe_vram(struct mga_device * mdev)149 static resource_size_t mgag200_g200ew3_device_probe_vram(struct mga_device *mdev)
150 {
151 resource_size_t vram_size = resource_size(mdev->vram_res);
152
153 if (vram_size >= 0x1000000)
154 vram_size = vram_size - 0x400000;
155 return mgag200_probe_vram(mdev->vram, vram_size);
156 }
157
mgag200_g200ew3_device_create(struct pci_dev * pdev,const struct drm_driver * drv)158 struct mga_device *mgag200_g200ew3_device_create(struct pci_dev *pdev,
159 const struct drm_driver *drv)
160 {
161 struct mga_device *mdev;
162 struct drm_device *dev;
163 resource_size_t vram_available;
164 int ret;
165
166 mdev = devm_drm_dev_alloc(&pdev->dev, drv, struct mga_device, base);
167 if (IS_ERR(mdev))
168 return mdev;
169 dev = &mdev->base;
170
171 pci_set_drvdata(pdev, dev);
172
173 ret = mgag200_init_pci_options(pdev, 0x41049120, 0x0000b000);
174 if (ret)
175 return ERR_PTR(ret);
176
177 ret = mgag200_device_preinit(mdev);
178 if (ret)
179 return ERR_PTR(ret);
180
181 ret = mgag200_device_init(mdev, &mgag200_g200ew3_device_info,
182 &mgag200_g200ew3_device_funcs);
183 if (ret)
184 return ERR_PTR(ret);
185
186 mgag200_g200ew3_init_registers(mdev);
187
188 vram_available = mgag200_g200ew3_device_probe_vram(mdev);
189
190 ret = mgag200_mode_config_init(mdev, vram_available);
191 if (ret)
192 return ERR_PTR(ret);
193
194 ret = mgag200_g200ew3_pipeline_init(mdev);
195 if (ret)
196 return ERR_PTR(ret);
197
198 drm_mode_config_reset(dev);
199 drm_kms_helper_poll_init(dev);
200
201 return mdev;
202 }
203