1*a53f1117Sriastradh /* $NetBSD: vmwgfx_pci.c,v 1.2 2022/07/18 23:34:03 riastradh Exp $ */
2cae04ea7Sriastradh
3cae04ea7Sriastradh /*-
4cae04ea7Sriastradh * Copyright (c) 2022 The NetBSD Foundation, Inc.
5cae04ea7Sriastradh * All rights reserved.
6cae04ea7Sriastradh *
7cae04ea7Sriastradh * Redistribution and use in source and binary forms, with or without
8cae04ea7Sriastradh * modification, are permitted provided that the following conditions
9cae04ea7Sriastradh * are met:
10cae04ea7Sriastradh * 1. Redistributions of source code must retain the above copyright
11cae04ea7Sriastradh * notice, this list of conditions and the following disclaimer.
12cae04ea7Sriastradh * 2. Redistributions in binary form must reproduce the above copyright
13cae04ea7Sriastradh * notice, this list of conditions and the following disclaimer in the
14cae04ea7Sriastradh * documentation and/or other materials provided with the distribution.
15cae04ea7Sriastradh *
16cae04ea7Sriastradh * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17cae04ea7Sriastradh * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18cae04ea7Sriastradh * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19cae04ea7Sriastradh * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20cae04ea7Sriastradh * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21cae04ea7Sriastradh * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22cae04ea7Sriastradh * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23cae04ea7Sriastradh * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24cae04ea7Sriastradh * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25cae04ea7Sriastradh * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26cae04ea7Sriastradh * POSSIBILITY OF SUCH DAMAGE.
27cae04ea7Sriastradh */
28cae04ea7Sriastradh
29cae04ea7Sriastradh #include <sys/cdefs.h>
30*a53f1117Sriastradh __KERNEL_RCSID(0, "$NetBSD: vmwgfx_pci.c,v 1.2 2022/07/18 23:34:03 riastradh Exp $");
31cae04ea7Sriastradh
32cae04ea7Sriastradh #ifdef _KERNEL_OPT
33cae04ea7Sriastradh #include "vga.h"
34cae04ea7Sriastradh #endif
35cae04ea7Sriastradh
36cae04ea7Sriastradh #include <sys/types.h>
37cae04ea7Sriastradh #include <sys/atomic.h>
38cae04ea7Sriastradh #include <sys/queue.h>
39cae04ea7Sriastradh #include <sys/systm.h>
40cae04ea7Sriastradh #include <sys/workqueue.h>
41cae04ea7Sriastradh
42cae04ea7Sriastradh #include <dev/pci/pciio.h>
43cae04ea7Sriastradh #include <dev/pci/pcireg.h>
44cae04ea7Sriastradh #include <dev/pci/pcivar.h>
45cae04ea7Sriastradh
46cae04ea7Sriastradh #include <dev/pci/wsdisplay_pci.h>
47cae04ea7Sriastradh #include <dev/wsfb/genfbvar.h>
48cae04ea7Sriastradh
49cae04ea7Sriastradh #include <drm/drm_device.h>
50cae04ea7Sriastradh #include <drm/drm_drv.h>
51cae04ea7Sriastradh #include <drm/drm_fb_helper.h>
52cae04ea7Sriastradh #include <drm/drm_pci.h>
53cae04ea7Sriastradh
54cae04ea7Sriastradh #include "vmwgfx_drv.h"
55cae04ea7Sriastradh #include "vmwgfx_task.h"
56cae04ea7Sriastradh
57cae04ea7Sriastradh SIMPLEQ_HEAD(vmwgfx_task_head, vmwgfx_task);
58cae04ea7Sriastradh
59cae04ea7Sriastradh struct vmwgfx_softc {
60cae04ea7Sriastradh device_t sc_dev;
61cae04ea7Sriastradh struct pci_attach_args sc_pa;
62cae04ea7Sriastradh struct lwp *sc_task_thread;
63cae04ea7Sriastradh struct vmwgfx_task_head sc_tasks;
64cae04ea7Sriastradh struct workqueue *sc_task_wq;
65cae04ea7Sriastradh struct drm_device *sc_drm_dev;
66cae04ea7Sriastradh struct pci_dev sc_pci_dev;
67cae04ea7Sriastradh bool sc_pci_attached;
68cae04ea7Sriastradh bool sc_dev_registered;
69cae04ea7Sriastradh #if defined(__i386__)
70cae04ea7Sriastradh #define VMWGFX_PCI_UGLY_MAP_HACK
71cae04ea7Sriastradh /* XXX Used to claim the VGA device before attach_real */
72cae04ea7Sriastradh bus_space_handle_t sc_temp_memh;
73cae04ea7Sriastradh bool sc_temp_set;
74cae04ea7Sriastradh #endif
75cae04ea7Sriastradh };
76cae04ea7Sriastradh
77cae04ea7Sriastradh static bool vmwgfx_pci_lookup(const struct pci_attach_args *,
78cae04ea7Sriastradh unsigned long *);
79cae04ea7Sriastradh
80cae04ea7Sriastradh static int vmwgfx_match(device_t, cfdata_t, void *);
81cae04ea7Sriastradh static void vmwgfx_attach(device_t, device_t, void *);
82cae04ea7Sriastradh static void vmwgfx_attach_real(device_t);
83cae04ea7Sriastradh static int vmwgfx_detach(device_t, int);
84cae04ea7Sriastradh static bool vmwgfx_do_suspend(device_t, const pmf_qual_t *);
85cae04ea7Sriastradh static bool vmwgfx_do_resume(device_t, const pmf_qual_t *);
86cae04ea7Sriastradh
87cae04ea7Sriastradh static void vmwgfx_task_work(struct work *, void *);
88cae04ea7Sriastradh
89cae04ea7Sriastradh CFATTACH_DECL_NEW(vmwgfx, sizeof(struct vmwgfx_softc),
90cae04ea7Sriastradh vmwgfx_match, vmwgfx_attach, vmwgfx_detach, NULL);
91cae04ea7Sriastradh
92cae04ea7Sriastradh /* XXX Kludge to get these from vmwgfx_drv.c. */
93cae04ea7Sriastradh extern struct drm_driver *const vmwgfx_drm_driver;
94cae04ea7Sriastradh extern const struct pci_device_id *const vmwgfx_device_ids;
95cae04ea7Sriastradh extern const size_t vmwgfx_n_device_ids;
96cae04ea7Sriastradh
97cae04ea7Sriastradh static bool
vmwgfx_pci_lookup(const struct pci_attach_args * pa,unsigned long * flags)98cae04ea7Sriastradh vmwgfx_pci_lookup(const struct pci_attach_args *pa, unsigned long *flags)
99cae04ea7Sriastradh {
100cae04ea7Sriastradh size_t i;
101cae04ea7Sriastradh
102cae04ea7Sriastradh for (i = 0; i < vmwgfx_n_device_ids; i++) {
103cae04ea7Sriastradh if ((PCI_VENDOR(pa->pa_id) == vmwgfx_device_ids[i].vendor) &&
104cae04ea7Sriastradh (PCI_PRODUCT(pa->pa_id) == vmwgfx_device_ids[i].device))
105cae04ea7Sriastradh break;
106cae04ea7Sriastradh }
107cae04ea7Sriastradh
108cae04ea7Sriastradh /* Did we find it? */
109cae04ea7Sriastradh if (i == vmwgfx_n_device_ids)
110cae04ea7Sriastradh return false;
111cae04ea7Sriastradh
112cae04ea7Sriastradh if (flags)
113cae04ea7Sriastradh *flags = vmwgfx_device_ids[i].driver_data;
114cae04ea7Sriastradh return true;
115cae04ea7Sriastradh }
116cae04ea7Sriastradh
117cae04ea7Sriastradh static int
vmwgfx_match(device_t parent,cfdata_t match,void * aux)118cae04ea7Sriastradh vmwgfx_match(device_t parent, cfdata_t match, void *aux)
119cae04ea7Sriastradh {
120cae04ea7Sriastradh extern int vmwgfx_guarantee_initialized(void);
121cae04ea7Sriastradh const struct pci_attach_args *const pa = aux;
122cae04ea7Sriastradh int error;
123cae04ea7Sriastradh
124cae04ea7Sriastradh error = vmwgfx_guarantee_initialized();
125cae04ea7Sriastradh if (error) {
126cae04ea7Sriastradh aprint_error("vmwgfx: failed to initialize: %d\n", error);
127cae04ea7Sriastradh return 0;
128cae04ea7Sriastradh }
129cae04ea7Sriastradh
130cae04ea7Sriastradh if (!vmwgfx_pci_lookup(pa, NULL))
131cae04ea7Sriastradh return 0;
132cae04ea7Sriastradh
133cae04ea7Sriastradh return 6; /* XXX Beat genfb_pci... */
134cae04ea7Sriastradh }
135cae04ea7Sriastradh
136cae04ea7Sriastradh static void
vmwgfx_attach(device_t parent,device_t self,void * aux)137cae04ea7Sriastradh vmwgfx_attach(device_t parent, device_t self, void *aux)
138cae04ea7Sriastradh {
139cae04ea7Sriastradh struct vmwgfx_softc *const sc = device_private(self);
140cae04ea7Sriastradh const struct pci_attach_args *const pa = aux;
141cae04ea7Sriastradh int error;
142cae04ea7Sriastradh
143cae04ea7Sriastradh pci_aprint_devinfo(pa, NULL);
144cae04ea7Sriastradh
145cae04ea7Sriastradh /* Initialize the Linux PCI device descriptor. */
146cae04ea7Sriastradh linux_pci_dev_init(&sc->sc_pci_dev, self, device_parent(self), pa, 0);
147cae04ea7Sriastradh
148cae04ea7Sriastradh sc->sc_dev = self;
149cae04ea7Sriastradh sc->sc_pa = *pa;
150cae04ea7Sriastradh sc->sc_task_thread = NULL;
151cae04ea7Sriastradh SIMPLEQ_INIT(&sc->sc_tasks);
152cae04ea7Sriastradh error = workqueue_create(&sc->sc_task_wq, "vmwgfxfb",
153cae04ea7Sriastradh &vmwgfx_task_work, NULL, PRI_NONE, IPL_NONE, WQ_MPSAFE);
154cae04ea7Sriastradh if (error) {
155cae04ea7Sriastradh aprint_error_dev(self, "unable to create workqueue: %d\n",
156cae04ea7Sriastradh error);
157cae04ea7Sriastradh sc->sc_task_wq = NULL;
158cae04ea7Sriastradh return;
159cae04ea7Sriastradh }
160cae04ea7Sriastradh
161cae04ea7Sriastradh #ifdef VMWGFX_PCI_UGLY_MAP_HACK
162cae04ea7Sriastradh /*
163cae04ea7Sriastradh * XXX
164cae04ea7Sriastradh * We try to map the VGA registers, in case we can prevent vga@isa or
165cae04ea7Sriastradh * pcdisplay@isa attaching, and stealing wsdisplay0. This only works
166cae04ea7Sriastradh * with serial console, as actual VGA console has already mapped them.
167cae04ea7Sriastradh * The only way to handle that is for vga@isa to not attach.
168cae04ea7Sriastradh */
169cae04ea7Sriastradh int rv = bus_space_map(pa->pa_memt, 0xb0000, 0x10000, 0,
170cae04ea7Sriastradh &sc->sc_temp_memh);
171cae04ea7Sriastradh sc->sc_temp_set = rv == 0;
172cae04ea7Sriastradh if (rv != 0)
173cae04ea7Sriastradh aprint_error_dev(self, "unable to reserve VGA registers for "
174cae04ea7Sriastradh "i386 vmwgfxdrmkms hack\n");
175cae04ea7Sriastradh #endif
176cae04ea7Sriastradh
177cae04ea7Sriastradh /*
178cae04ea7Sriastradh * Defer the remainder of initialization until we have mounted
179cae04ea7Sriastradh * the root file system and can load firmware images.
180cae04ea7Sriastradh */
181cae04ea7Sriastradh config_mountroot(self, &vmwgfx_attach_real);
182cae04ea7Sriastradh }
183cae04ea7Sriastradh
184cae04ea7Sriastradh static void
vmwgfx_attach_real(device_t self)185cae04ea7Sriastradh vmwgfx_attach_real(device_t self)
186cae04ea7Sriastradh {
187cae04ea7Sriastradh struct vmwgfx_softc *const sc = device_private(self);
188cae04ea7Sriastradh const struct pci_attach_args *const pa = &sc->sc_pa;
189cae04ea7Sriastradh bool ok __diagused;
190cae04ea7Sriastradh unsigned long flags = 0 /*XXXGCC*/;
191cae04ea7Sriastradh int error;
192cae04ea7Sriastradh
193cae04ea7Sriastradh ok = vmwgfx_pci_lookup(pa, &flags);
194cae04ea7Sriastradh KASSERT(ok);
195cae04ea7Sriastradh
196cae04ea7Sriastradh #ifdef VMWGFX_PCI_UGLY_MAP_HACK
197cae04ea7Sriastradh /*
198cae04ea7Sriastradh * XXX
199cae04ea7Sriastradh * Unmap the VGA registers.
200cae04ea7Sriastradh */
201cae04ea7Sriastradh if (sc->sc_temp_set)
202cae04ea7Sriastradh bus_space_unmap(pa->pa_memt, sc->sc_temp_memh, 0x10000);
203cae04ea7Sriastradh #endif
204cae04ea7Sriastradh
205cae04ea7Sriastradh /*
206cae04ea7Sriastradh * Cause any tasks issued synchronously during attach to be
207cae04ea7Sriastradh * processed at the end of this function.
208cae04ea7Sriastradh */
209cae04ea7Sriastradh sc->sc_task_thread = curlwp;
210cae04ea7Sriastradh
211cae04ea7Sriastradh sc->sc_drm_dev = drm_dev_alloc(vmwgfx_drm_driver, self);
212cae04ea7Sriastradh if (IS_ERR(sc->sc_drm_dev)) {
213cae04ea7Sriastradh aprint_error_dev(self, "unable to create drm device: %ld\n",
214cae04ea7Sriastradh PTR_ERR(sc->sc_drm_dev));
215cae04ea7Sriastradh sc->sc_drm_dev = NULL;
216cae04ea7Sriastradh goto out;
217cae04ea7Sriastradh }
218cae04ea7Sriastradh
219cae04ea7Sriastradh /* XXX errno Linux->NetBSD */
220cae04ea7Sriastradh error = -drm_pci_attach(sc->sc_drm_dev, &sc->sc_pci_dev);
221cae04ea7Sriastradh if (error) {
222cae04ea7Sriastradh aprint_error_dev(self, "unable to attach drm: %d\n", error);
223cae04ea7Sriastradh goto out;
224cae04ea7Sriastradh }
225cae04ea7Sriastradh sc->sc_pci_attached = true;
226cae04ea7Sriastradh
227cae04ea7Sriastradh /* XXX errno Linux->NetBSD */
228cae04ea7Sriastradh error = -drm_dev_register(sc->sc_drm_dev, flags);
229cae04ea7Sriastradh if (error) {
230cae04ea7Sriastradh aprint_error_dev(self, "unable to register drm: %d\n", error);
231cae04ea7Sriastradh goto out;
232cae04ea7Sriastradh }
233cae04ea7Sriastradh sc->sc_dev_registered = true;
234cae04ea7Sriastradh
235cae04ea7Sriastradh if (!pmf_device_register(self, &vmwgfx_do_suspend, &vmwgfx_do_resume))
236cae04ea7Sriastradh aprint_error_dev(self, "unable to establish power handler\n");
237cae04ea7Sriastradh
238cae04ea7Sriastradh /*
239cae04ea7Sriastradh * Process asynchronous tasks queued synchronously during
240cae04ea7Sriastradh * attach. This will be for display detection to attach a
241cae04ea7Sriastradh * framebuffer, so we have the opportunity for a console device
242cae04ea7Sriastradh * to attach before autoconf has completed, in time for init(8)
243cae04ea7Sriastradh * to find that console without panicking.
244cae04ea7Sriastradh */
245cae04ea7Sriastradh while (!SIMPLEQ_EMPTY(&sc->sc_tasks)) {
246cae04ea7Sriastradh struct vmwgfx_task *const task = SIMPLEQ_FIRST(&sc->sc_tasks);
247cae04ea7Sriastradh
248cae04ea7Sriastradh SIMPLEQ_REMOVE_HEAD(&sc->sc_tasks, vt_u.queue);
249cae04ea7Sriastradh (*task->vt_fn)(task);
250cae04ea7Sriastradh }
251cae04ea7Sriastradh
252cae04ea7Sriastradh out: /* Cause any subesquent tasks to be processed by the workqueue. */
253cae04ea7Sriastradh atomic_store_relaxed(&sc->sc_task_thread, NULL);
254cae04ea7Sriastradh }
255cae04ea7Sriastradh
256cae04ea7Sriastradh static int
vmwgfx_detach(device_t self,int flags)257cae04ea7Sriastradh vmwgfx_detach(device_t self, int flags)
258cae04ea7Sriastradh {
259cae04ea7Sriastradh struct vmwgfx_softc *const sc = device_private(self);
260cae04ea7Sriastradh int error;
261cae04ea7Sriastradh
262cae04ea7Sriastradh /* XXX Check for in-use before tearing it all down... */
263cae04ea7Sriastradh error = config_detach_children(self, flags);
264cae04ea7Sriastradh if (error)
265cae04ea7Sriastradh return error;
266cae04ea7Sriastradh
267cae04ea7Sriastradh KASSERT(sc->sc_task_thread == NULL);
268cae04ea7Sriastradh KASSERT(SIMPLEQ_EMPTY(&sc->sc_tasks));
269cae04ea7Sriastradh
270cae04ea7Sriastradh pmf_device_deregister(self);
271cae04ea7Sriastradh if (sc->sc_dev_registered)
272cae04ea7Sriastradh drm_dev_unregister(sc->sc_drm_dev);
273cae04ea7Sriastradh if (sc->sc_pci_attached)
274cae04ea7Sriastradh drm_pci_detach(sc->sc_drm_dev);
275cae04ea7Sriastradh if (sc->sc_drm_dev) {
276cae04ea7Sriastradh drm_dev_put(sc->sc_drm_dev);
277cae04ea7Sriastradh sc->sc_drm_dev = NULL;
278cae04ea7Sriastradh }
279cae04ea7Sriastradh if (sc->sc_task_wq) {
280cae04ea7Sriastradh workqueue_destroy(sc->sc_task_wq);
281cae04ea7Sriastradh sc->sc_task_wq = NULL;
282cae04ea7Sriastradh }
283cae04ea7Sriastradh linux_pci_dev_destroy(&sc->sc_pci_dev);
284cae04ea7Sriastradh
285cae04ea7Sriastradh return 0;
286cae04ea7Sriastradh }
287cae04ea7Sriastradh
288cae04ea7Sriastradh static bool
vmwgfx_do_suspend(device_t self,const pmf_qual_t * qual)289cae04ea7Sriastradh vmwgfx_do_suspend(device_t self, const pmf_qual_t *qual)
290cae04ea7Sriastradh {
291cae04ea7Sriastradh struct vmwgfx_softc *const sc = device_private(self);
292cae04ea7Sriastradh struct drm_device *const dev = sc->sc_drm_dev;
293cae04ea7Sriastradh int ret;
294cae04ea7Sriastradh
295cae04ea7Sriastradh ret = vmw_kms_suspend(dev);
296cae04ea7Sriastradh if (ret)
297cae04ea7Sriastradh return false;
298cae04ea7Sriastradh
299cae04ea7Sriastradh return true;
300cae04ea7Sriastradh }
301cae04ea7Sriastradh
302cae04ea7Sriastradh static bool
vmwgfx_do_resume(device_t self,const pmf_qual_t * qual)303cae04ea7Sriastradh vmwgfx_do_resume(device_t self, const pmf_qual_t *qual)
304cae04ea7Sriastradh {
305cae04ea7Sriastradh struct vmwgfx_softc *const sc = device_private(self);
306cae04ea7Sriastradh struct drm_device *const dev = sc->sc_drm_dev;
307cae04ea7Sriastradh int ret;
308cae04ea7Sriastradh
309cae04ea7Sriastradh ret = vmw_kms_resume(dev);
310cae04ea7Sriastradh if (ret)
311cae04ea7Sriastradh return false;
312cae04ea7Sriastradh
313cae04ea7Sriastradh return true;
314cae04ea7Sriastradh }
315cae04ea7Sriastradh
316cae04ea7Sriastradh static void
vmwgfx_task_work(struct work * work,void * cookie __unused)317cae04ea7Sriastradh vmwgfx_task_work(struct work *work, void *cookie __unused)
318cae04ea7Sriastradh {
319cae04ea7Sriastradh struct vmwgfx_task *const task = container_of(work, struct vmwgfx_task,
320cae04ea7Sriastradh vt_u.work);
321cae04ea7Sriastradh
322cae04ea7Sriastradh (*task->vt_fn)(task);
323cae04ea7Sriastradh }
324cae04ea7Sriastradh
325*a53f1117Sriastradh void
vmwgfx_task_schedule(device_t self,struct vmwgfx_task * task)326cae04ea7Sriastradh vmwgfx_task_schedule(device_t self, struct vmwgfx_task *task)
327cae04ea7Sriastradh {
328cae04ea7Sriastradh struct vmwgfx_softc *const sc = device_private(self);
329cae04ea7Sriastradh
330cae04ea7Sriastradh if (atomic_load_relaxed(&sc->sc_task_thread) == curlwp)
331cae04ea7Sriastradh SIMPLEQ_INSERT_TAIL(&sc->sc_tasks, task, vt_u.queue);
332cae04ea7Sriastradh else
333cae04ea7Sriastradh workqueue_enqueue(sc->sc_task_wq, &task->vt_u.work, NULL);
334cae04ea7Sriastradh }
335