drm_linux.c revision 1.8
1/*	$OpenBSD: drm_linux.c,v 1.8 2016/02/05 15:51:10 kettenis Exp $	*/
2/*
3 * Copyright (c) 2013 Jonathan Gray <jsg@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <dev/pci/drm/drmP.h>
19#include <dev/pci/ppbreg.h>
20
21struct timespec
22ns_to_timespec(const int64_t nsec)
23{
24	struct timespec ts;
25	int32_t rem;
26
27	if (nsec == 0) {
28		ts.tv_sec = 0;
29		ts.tv_nsec = 0;
30		return (ts);
31	}
32
33	ts.tv_sec = nsec / NSEC_PER_SEC;
34	rem = nsec % NSEC_PER_SEC;
35	if (rem < 0) {
36		ts.tv_sec--;
37		rem += NSEC_PER_SEC;
38	}
39	ts.tv_nsec = rem;
40	return (ts);
41}
42
43int64_t
44timeval_to_ns(const struct timeval *tv)
45{
46	return ((int64_t)tv->tv_sec * NSEC_PER_SEC) +
47		tv->tv_usec * NSEC_PER_USEC;
48}
49
50struct timeval
51ns_to_timeval(const int64_t nsec)
52{
53	struct timeval tv;
54	int32_t rem;
55
56	if (nsec == 0) {
57		tv.tv_sec = 0;
58		tv.tv_usec = 0;
59		return (tv);
60	}
61
62	tv.tv_sec = nsec / NSEC_PER_SEC;
63	rem = nsec % NSEC_PER_SEC;
64	if (rem < 0) {
65		tv.tv_sec--;
66		rem += NSEC_PER_SEC;
67	}
68	tv.tv_usec = rem / 1000;
69	return (tv);
70}
71
72extern char *hw_vendor, *hw_prod;
73
74static bool
75dmi_found(const struct dmi_system_id *dsi)
76{
77	int i, slot;
78
79	for (i = 0; i < nitems(dsi->matches); i++) {
80		slot = dsi->matches[i].slot;
81		switch (slot) {
82		case DMI_NONE:
83			break;
84		case DMI_SYS_VENDOR:
85		case DMI_BOARD_VENDOR:
86			if (hw_vendor != NULL &&
87			    !strcmp(hw_vendor, dsi->matches[i].substr))
88				break;
89			else
90				return false;
91		case DMI_PRODUCT_NAME:
92		case DMI_BOARD_NAME:
93			if (hw_prod != NULL &&
94			    !strcmp(hw_prod, dsi->matches[i].substr))
95				break;
96			else
97				return false;
98		default:
99			return false;
100		}
101	}
102
103	return true;
104}
105
106int
107dmi_check_system(const struct dmi_system_id *sysid)
108{
109	const struct dmi_system_id *dsi;
110	int num = 0;
111
112	for (dsi = sysid; dsi->matches[0].slot != 0 ; dsi++) {
113		if (dmi_found(dsi)) {
114			num++;
115			if (dsi->callback && dsi->callback(dsi))
116				break;
117		}
118	}
119	return (num);
120}
121
122struct vm_page *
123alloc_pages(unsigned int gfp_mask, unsigned int order)
124{
125	int flags = (gfp_mask & M_NOWAIT) ? UVM_PLA_NOWAIT : UVM_PLA_WAITOK;
126	struct pglist mlist;
127
128	if (gfp_mask & M_CANFAIL)
129		flags |= UVM_PLA_FAILOK;
130
131	TAILQ_INIT(&mlist);
132	if (uvm_pglistalloc(PAGE_SIZE << order, 0, -1, PAGE_SIZE, 0,
133	    &mlist, 1, flags))
134		return NULL;
135	return TAILQ_FIRST(&mlist);
136}
137
138void
139__free_pages(struct vm_page *page, unsigned int order)
140{
141	struct pglist mlist;
142	int i;
143
144	TAILQ_INIT(&mlist);
145	for (i = 0; i < (1 << order); i++)
146		TAILQ_INSERT_TAIL(&mlist, &page[i], pageq);
147	uvm_pglistfree(&mlist);
148}
149
150void *
151kmap(struct vm_page *pg)
152{
153	vaddr_t va;
154
155#if defined (__HAVE_PMAP_DIRECT)
156	va = pmap_map_direct(pg);
157#else
158	va = uvm_km_valloc_wait(phys_map, PAGE_SIZE);
159	pmap_kenter_pa(va, VM_PAGE_TO_PHYS(pg), PROT_READ | PROT_WRITE);
160	pmap_update(pmap_kernel());
161#endif
162	return (void *)va;
163}
164
165void
166kunmap(void *addr)
167{
168	vaddr_t va = (vaddr_t)addr;
169
170#if defined (__HAVE_PMAP_DIRECT)
171	pmap_unmap_direct(va);
172#else
173	pmap_kremove(va, PAGE_SIZE);
174	pmap_update(pmap_kernel());
175	uvm_km_free_wakeup(phys_map, va, PAGE_SIZE);
176#endif
177}
178
179void *
180vmap(struct vm_page **pages, unsigned int npages, unsigned long flags,
181     pgprot_t prot)
182{
183	vaddr_t va;
184	paddr_t pa;
185	int i;
186
187	va = uvm_km_valloc(kernel_map, PAGE_SIZE * npages);
188	if (va == 0)
189		return NULL;
190	for (i = 0; i < npages; i++) {
191		pa = VM_PAGE_TO_PHYS(pages[i]) | prot;
192		pmap_enter(pmap_kernel(), va + (i * PAGE_SIZE), pa,
193		    PROT_READ | PROT_WRITE,
194		    PROT_READ | PROT_WRITE | PMAP_WIRED);
195		pmap_update(pmap_kernel());
196	}
197
198	return (void *)va;
199}
200
201void
202vunmap(void *addr, size_t size)
203{
204	vaddr_t va = (vaddr_t)addr;
205
206	pmap_remove(pmap_kernel(), va, va + size);
207	pmap_update(pmap_kernel());
208	uvm_km_free(kernel_map, va, size);
209}
210
211#if defined(__amd64__) || defined(__i386__)
212
213/*
214 * This is a minimal implementation of the Linux vga_get/vga_put
215 * interface.  In all likelyhood, it will only work for inteldrm(4) as
216 * it assumes that if there is another active VGA device in the
217 * system, it is sitting behind a PCI bridge.
218 */
219
220extern int pci_enumerate_bus(struct pci_softc *,
221    int (*)(struct pci_attach_args *), struct pci_attach_args *);
222
223pcitag_t vga_bridge_tag;
224int vga_bridge_disabled;
225
226int
227vga_disable_bridge(struct pci_attach_args *pa)
228{
229	pcireg_t bhlc, bc;
230
231	if (pa->pa_domain != 0)
232		return 0;
233
234	bhlc = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG);
235	if (PCI_HDRTYPE_TYPE(bhlc) != 1)
236		return 0;
237
238	bc = pci_conf_read(pa->pa_pc, pa->pa_tag, PPB_REG_BRIDGECONTROL);
239	if ((bc & PPB_BC_VGA_ENABLE) == 0)
240		return 0;
241	bc &= ~PPB_BC_VGA_ENABLE;
242	pci_conf_write(pa->pa_pc, pa->pa_tag, PPB_REG_BRIDGECONTROL, bc);
243
244	vga_bridge_tag = pa->pa_tag;
245	vga_bridge_disabled = 1;
246
247	return 1;
248}
249
250void
251vga_get_uninterruptible(struct pci_dev *pdev, int rsrc)
252{
253	KASSERT(pdev->pci->sc_bridgetag == NULL);
254	pci_enumerate_bus(pdev->pci, vga_disable_bridge, NULL);
255}
256
257void
258vga_put(struct pci_dev *pdev, int rsrc)
259{
260	pcireg_t bc;
261
262	if (!vga_bridge_disabled)
263		return;
264
265	bc = pci_conf_read(pdev->pc, vga_bridge_tag, PPB_REG_BRIDGECONTROL);
266	bc |= PPB_BC_VGA_ENABLE;
267	pci_conf_write(pdev->pc, vga_bridge_tag, PPB_REG_BRIDGECONTROL, bc);
268
269	vga_bridge_disabled = 0;
270}
271
272#endif
273
274/*
275 * ACPI types and interfaces.
276 */
277
278#if defined(__amd64__) || defined(__i386__)
279#include "acpi.h"
280#endif
281
282#if NACPI > 0
283
284#include <dev/acpi/acpireg.h>
285#include <dev/acpi/acpivar.h>
286
287acpi_status
288acpi_get_table_with_size(const char *sig, int instance,
289    struct acpi_table_header **hdr, acpi_size *size)
290{
291	struct acpi_softc *sc = acpi_softc;
292	struct acpi_q *entry;
293
294	KASSERT(instance == 1);
295
296	SIMPLEQ_FOREACH(entry, &sc->sc_tables, q_next) {
297		if (memcmp(entry->q_table, sig, strlen(sig)) == 0) {
298			*hdr = entry->q_table;
299			*size = (*hdr)->length;
300			return 0;
301		}
302	}
303
304	return AE_NOT_FOUND;
305}
306
307#endif
308