agp_intel_gtt.c revision 1.1
1/*
2 * Copyright (c) 2008-2009 Owain G. Ainsworth <oga@openbsd.org>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include "../i915_drv.h"
18
19#include <drm/drm_agpsupport.h>
20
21/* MCH IFP BARs */
22#define I915_IFPADDR	0x60
23#define I965_IFPADDR	0x70
24
25extern struct cfdriver inteldrm_cd;
26
27#ifdef __amd64__
28#define membar_producer_wc()	__asm __volatile("sfence":::"memory")
29#else
30#define membar_producer_wc()	__asm __volatile(\
31				"lock; addl $0,0(%%esp)":::"memory")
32#endif
33
34/*
35 * We're intel IGD, bus 0 function 0 dev 0 should be the GMCH, so it should
36 * be Intel
37 */
38int
39inteldrm_gmch_match(struct pci_attach_args *pa)
40{
41	if (pa->pa_bus == 0 && pa->pa_device == 0 && pa->pa_function == 0 &&
42	    PCI_VENDOR(pa->pa_id) == PCI_VENDOR_INTEL &&
43	    PCI_CLASS(pa->pa_class) == PCI_CLASS_BRIDGE &&
44	    PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_BRIDGE_HOST)
45		return (1);
46	return (0);
47}
48
49void
50i915_alloc_ifp(struct inteldrm_softc *dev_priv, struct pci_attach_args *bpa)
51{
52	bus_addr_t	addr;
53	u_int32_t	reg;
54
55	dev_priv->ifp.i9xx.bst = bpa->pa_memt;
56
57	reg = pci_conf_read(bpa->pa_pc, bpa->pa_tag, I915_IFPADDR);
58	if (reg & 0x1) {
59		addr = (bus_addr_t)reg;
60		addr &= ~0x1;
61		/* XXX extents ... need data on whether bioses alloc or not. */
62		if (bus_space_map(bpa->pa_memt, addr, PAGE_SIZE, 0,
63		    &dev_priv->ifp.i9xx.bsh) != 0)
64			goto nope;
65		return;
66	} else if (bpa->pa_memex == NULL ||
67	    extent_alloc_subregion(bpa->pa_memex, 0x100000, 0xffffffff,
68	    PAGE_SIZE, PAGE_SIZE, 0, 0, 0, &addr) ||
69	    bus_space_map(bpa->pa_memt, addr, PAGE_SIZE, 0,
70	    &dev_priv->ifp.i9xx.bsh))
71		goto nope;
72
73	pci_conf_write(bpa->pa_pc, bpa->pa_tag, I915_IFPADDR, addr | 0x1);
74
75	return;
76
77nope:
78	dev_priv->ifp.i9xx.bsh = 0;
79	printf("%s: no ifp\n", dev_priv->sc_dev.dv_xname);
80}
81
82void
83i965_alloc_ifp(struct inteldrm_softc *dev_priv, struct pci_attach_args *bpa)
84{
85	bus_addr_t	addr;
86	u_int32_t	lo, hi;
87
88	dev_priv->ifp.i9xx.bst = bpa->pa_memt;
89
90	hi = pci_conf_read(bpa->pa_pc, bpa->pa_tag, I965_IFPADDR + 4);
91	lo = pci_conf_read(bpa->pa_pc, bpa->pa_tag, I965_IFPADDR);
92	if (lo & 0x1) {
93		addr = (((u_int64_t)hi << 32) | lo);
94		addr &= ~0x1;
95		/* XXX extents ... need data on whether bioses alloc or not. */
96		if (bus_space_map(bpa->pa_memt, addr, PAGE_SIZE, 0,
97		    &dev_priv->ifp.i9xx.bsh) != 0)
98			goto nope;
99		return;
100	} else if (bpa->pa_memex == NULL ||
101	    extent_alloc_subregion(bpa->pa_memex, 0x100000, 0xffffffff,
102	    PAGE_SIZE, PAGE_SIZE, 0, 0, 0, &addr) ||
103	    bus_space_map(bpa->pa_memt, addr, PAGE_SIZE, 0,
104	    &dev_priv->ifp.i9xx.bsh))
105		goto nope;
106
107	pci_conf_write(bpa->pa_pc, bpa->pa_tag, I965_IFPADDR + 4,
108	    upper_32_bits(addr));
109	pci_conf_write(bpa->pa_pc, bpa->pa_tag, I965_IFPADDR,
110	    (addr & 0xffffffff) | 0x1);
111
112	return;
113
114nope:
115	dev_priv->ifp.i9xx.bsh = 0;
116	printf("%s: no ifp\n", dev_priv->sc_dev.dv_xname);
117}
118
119void
120intel_gtt_chipset_setup(struct drm_device *dev)
121{
122	struct inteldrm_softc *dev_priv = dev->dev_private;
123	struct pci_attach_args bpa;
124
125	if (INTEL_GEN(dev_priv) >= 6)
126		return;
127
128	if (pci_find_device(&bpa, inteldrm_gmch_match) == 0) {
129		printf("%s: can't find GMCH\n",
130		    dev_priv->sc_dev.dv_xname);
131		return;
132	}
133
134	/* Set up the IFP for chipset flushing */
135	if (INTEL_GEN(dev_priv) >= 4 || IS_G33(dev_priv)) {
136		i965_alloc_ifp(dev_priv, &bpa);
137	} else if (INTEL_GEN(dev_priv) == 3) {
138		i915_alloc_ifp(dev_priv, &bpa);
139	} else {
140		int nsegs;
141		/*
142		 * I8XX has no flush page mechanism, we fake it by writing until
143		 * the cache is empty. allocate a page to scribble on
144		 */
145		dev_priv->ifp.i8xx.kva = NULL;
146		if (bus_dmamem_alloc(dev_priv->dmat, PAGE_SIZE, 0, 0,
147		    &dev_priv->ifp.i8xx.seg, 1, &nsegs, BUS_DMA_WAITOK) == 0) {
148			if (bus_dmamem_map(dev_priv->dmat, &dev_priv->ifp.i8xx.seg,
149			    1, PAGE_SIZE, &dev_priv->ifp.i8xx.kva, 0) != 0) {
150				bus_dmamem_free(dev_priv->dmat,
151				    &dev_priv->ifp.i8xx.seg, nsegs);
152				dev_priv->ifp.i8xx.kva = NULL;
153			}
154		}
155	}
156}
157
158int
159intel_enable_gtt()
160{
161	struct inteldrm_softc *dev_priv = (void *)inteldrm_cd.cd_devs[0];
162
163	intel_gtt_chipset_setup(&dev_priv->drm);
164	return 1;
165}
166
167int
168intel_gmch_probe(struct pci_dev *bridge_dev, struct pci_dev *gpu_pdev,
169    void *bridge)
170{
171	return 1;
172}
173
174void
175intel_gtt_get(u64 *gtt_total,
176    phys_addr_t *mappable_base, resource_size_t *mappable_end)
177{
178	struct inteldrm_softc *dev_priv = (void *)inteldrm_cd.cd_devs[0];
179	struct agp_info *ai = &dev_priv->drm.agp->info;
180
181	*gtt_total = ai->ai_aperture_size;
182	*mappable_base = ai->ai_aperture_base;
183	*mappable_end = ai->ai_aperture_size;
184}
185
186void
187intel_gtt_chipset_flush(void)
188{
189	struct inteldrm_softc *dev_priv = (void *)inteldrm_cd.cd_devs[0];
190
191	/*
192	 * Write to this flush page flushes the chipset write cache.
193	 * The write will return when it is done.
194	 */
195	if (INTEL_GEN(dev_priv) >= 3) {
196	    if (dev_priv->ifp.i9xx.bsh != 0)
197		bus_space_write_4(dev_priv->ifp.i9xx.bst,
198		    dev_priv->ifp.i9xx.bsh, 0, 1);
199	} else {
200		int i;
201#define I830_HIC        0x70
202		i915_reg_t hic = _MMIO(I830_HIC);
203
204		wbinvd();
205
206		I915_WRITE(hic, (I915_READ(hic) | (1<<31)));
207		for (i = 1000; i; i--) {
208			if (!(I915_READ(hic) & (1<<31)))
209				break;
210			delay(100);
211		}
212
213	}
214}
215
216void
217intel_gmch_remove(void)
218{
219}
220
221void
222intel_gtt_insert_sg_entries(struct sg_table *pages, unsigned int pg_start,
223    unsigned int flags)
224{
225	struct inteldrm_softc *dev_priv = (void *)inteldrm_cd.cd_devs[0];
226	struct agp_softc *sc = dev_priv->drm.agp->agpdev;
227	bus_addr_t addr = sc->sc_apaddr + pg_start * PAGE_SIZE;
228	struct sg_page_iter sg_iter;
229
230	for_each_sg_page(pages->sgl, &sg_iter, pages->nents, 0) {
231		sc->sc_methods->bind_page(sc->sc_chipc, addr,
232		    sg_page_iter_dma_address(&sg_iter), flags);
233		addr += PAGE_SIZE;
234	}
235	membar_producer_wc();
236	intel_gtt_chipset_flush();
237}
238
239void
240intel_gtt_insert_page(dma_addr_t addr, unsigned int pg,
241    unsigned int flags)
242{
243	struct inteldrm_softc *dev_priv = (void *)inteldrm_cd.cd_devs[0];
244	struct agp_softc *sc = dev_priv->drm.agp->agpdev;
245	bus_addr_t apaddr = sc->sc_apaddr + (pg * PAGE_SIZE);
246	sc->sc_methods->bind_page(sc->sc_chipc, apaddr, addr, flags);
247	intel_gtt_chipset_flush();
248}
249
250void
251intel_gtt_clear_range(unsigned int first_entry, unsigned int num_entries)
252{
253	struct inteldrm_softc *dev_priv = (void *)inteldrm_cd.cd_devs[0];
254	struct agp_softc *sc = dev_priv->drm.agp->agpdev;
255	bus_addr_t addr = sc->sc_apaddr + first_entry * PAGE_SIZE;
256	int i;
257
258	for (i = 0; i < num_entries; i++) {
259		sc->sc_methods->unbind_page(sc->sc_chipc, addr);
260		addr += PAGE_SIZE;
261	}
262	membar_producer_wc();
263}
264