1254885Sdumbbell/**
2254885Sdumbbell * \file ati_pcigart.c
3254885Sdumbbell * ATI PCI GART support
4254885Sdumbbell *
5254885Sdumbbell * \author Gareth Hughes <gareth@valinux.com>
6254885Sdumbbell */
7254885Sdumbbell
8254885Sdumbbell/*
9254885Sdumbbell * Created: Wed Dec 13 21:52:19 2000 by gareth@valinux.com
10254885Sdumbbell *
11254885Sdumbbell * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
12254885Sdumbbell * All Rights Reserved.
13254885Sdumbbell *
14254885Sdumbbell * Permission is hereby granted, free of charge, to any person obtaining a
15254885Sdumbbell * copy of this software and associated documentation files (the "Software"),
16254885Sdumbbell * to deal in the Software without restriction, including without limitation
17254885Sdumbbell * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18254885Sdumbbell * and/or sell copies of the Software, and to permit persons to whom the
19254885Sdumbbell * Software is furnished to do so, subject to the following conditions:
20254885Sdumbbell *
21254885Sdumbbell * The above copyright notice and this permission notice (including the next
22254885Sdumbbell * paragraph) shall be included in all copies or substantial portions of the
23254885Sdumbbell * Software.
24254885Sdumbbell *
25254885Sdumbbell * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26254885Sdumbbell * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27254885Sdumbbell * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
28254885Sdumbbell * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
29254885Sdumbbell * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
30254885Sdumbbell * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
31254885Sdumbbell * DEALINGS IN THE SOFTWARE.
32254885Sdumbbell */
33254885Sdumbbell
34254885Sdumbbell#include <sys/cdefs.h>
35254885Sdumbbell__FBSDID("$FreeBSD$");
36254885Sdumbbell
37254885Sdumbbell#include <dev/drm2/drmP.h>
38254885Sdumbbell
39254885Sdumbbell# define ATI_PCIGART_PAGE_SIZE		4096	/**< PCI GART page size */
40254885Sdumbbell
41254885Sdumbbellstatic int drm_ati_alloc_pcigart_table(struct drm_device *dev,
42254885Sdumbbell				       struct drm_ati_pcigart_info *gart_info)
43254885Sdumbbell{
44254885Sdumbbell	gart_info->table_handle = drm_pci_alloc(dev, gart_info->table_size,
45254885Sdumbbell						PAGE_SIZE, 0xFFFFFFFFUL);
46254885Sdumbbell	if (gart_info->table_handle == NULL)
47254885Sdumbbell		return -ENOMEM;
48254885Sdumbbell
49254885Sdumbbell	return 0;
50254885Sdumbbell}
51254885Sdumbbell
52254885Sdumbbellstatic void drm_ati_free_pcigart_table(struct drm_device *dev,
53254885Sdumbbell				       struct drm_ati_pcigart_info *gart_info)
54254885Sdumbbell{
55254885Sdumbbell	drm_pci_free(dev, gart_info->table_handle);
56254885Sdumbbell	gart_info->table_handle = NULL;
57254885Sdumbbell}
58254885Sdumbbell
59254885Sdumbbellint drm_ati_pcigart_cleanup(struct drm_device *dev, struct drm_ati_pcigart_info *gart_info)
60254885Sdumbbell{
61254885Sdumbbell	struct drm_sg_mem *entry = dev->sg;
62254885Sdumbbell#ifdef __linux__
63254885Sdumbbell	unsigned long pages;
64254885Sdumbbell	int i;
65254885Sdumbbell	int max_pages;
66254885Sdumbbell#endif
67254885Sdumbbell
68254885Sdumbbell	/* we need to support large memory configurations */
69254885Sdumbbell	if (!entry) {
70254885Sdumbbell		DRM_ERROR("no scatter/gather memory!\n");
71254885Sdumbbell		return 0;
72254885Sdumbbell	}
73254885Sdumbbell
74254885Sdumbbell	if (gart_info->bus_addr) {
75254885Sdumbbell#ifdef __linux__
76254885Sdumbbell
77254885Sdumbbell		max_pages = (gart_info->table_size / sizeof(u32));
78254885Sdumbbell		pages = (entry->pages <= max_pages)
79254885Sdumbbell		  ? entry->pages : max_pages;
80254885Sdumbbell
81254885Sdumbbell		for (i = 0; i < pages; i++) {
82254885Sdumbbell			if (!entry->busaddr[i])
83254885Sdumbbell				break;
84254885Sdumbbell			pci_unmap_page(dev->pdev, entry->busaddr[i],
85254885Sdumbbell					 PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
86254885Sdumbbell		}
87254885Sdumbbell#endif
88254885Sdumbbell
89254885Sdumbbell		if (gart_info->gart_table_location == DRM_ATI_GART_MAIN)
90254885Sdumbbell			gart_info->bus_addr = 0;
91254885Sdumbbell	}
92254885Sdumbbell
93254885Sdumbbell	if (gart_info->gart_table_location == DRM_ATI_GART_MAIN &&
94254885Sdumbbell	    gart_info->table_handle) {
95254885Sdumbbell		drm_ati_free_pcigart_table(dev, gart_info);
96254885Sdumbbell	}
97254885Sdumbbell
98254885Sdumbbell	return 1;
99254885Sdumbbell}
100254885Sdumbbell
101254885Sdumbbellint drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *gart_info)
102254885Sdumbbell{
103254885Sdumbbell	struct drm_local_map *map = &gart_info->mapping;
104254885Sdumbbell	struct drm_sg_mem *entry = dev->sg;
105254885Sdumbbell	void *address = NULL;
106254885Sdumbbell	unsigned long pages;
107254885Sdumbbell	u32 *pci_gart = NULL, page_base, gart_idx;
108254885Sdumbbell	dma_addr_t bus_address = 0;
109254885Sdumbbell	int i, j, ret = 0;
110254885Sdumbbell	int max_ati_pages, max_real_pages;
111254885Sdumbbell
112254885Sdumbbell	if (!entry) {
113254885Sdumbbell		DRM_ERROR("no scatter/gather memory!\n");
114254885Sdumbbell		goto done;
115254885Sdumbbell	}
116254885Sdumbbell
117254885Sdumbbell	if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) {
118254885Sdumbbell		DRM_DEBUG("PCI: no table in VRAM: using normal RAM\n");
119254885Sdumbbell
120254885Sdumbbell#ifdef __linux__
121254885Sdumbbell		if (pci_set_dma_mask(dev->pdev, gart_info->table_mask)) {
122254885Sdumbbell			DRM_ERROR("fail to set dma mask to 0x%Lx\n",
123254885Sdumbbell				  (unsigned long long)gart_info->table_mask);
124254885Sdumbbell			ret = 1;
125254885Sdumbbell			goto done;
126254885Sdumbbell		}
127254885Sdumbbell#endif
128254885Sdumbbell
129254885Sdumbbell		ret = drm_ati_alloc_pcigart_table(dev, gart_info);
130254885Sdumbbell		if (ret) {
131254885Sdumbbell			DRM_ERROR("cannot allocate PCI GART page!\n");
132254885Sdumbbell			goto done;
133254885Sdumbbell		}
134254885Sdumbbell
135254885Sdumbbell		pci_gart = gart_info->table_handle->vaddr;
136254885Sdumbbell		address = gart_info->table_handle->vaddr;
137254885Sdumbbell		bus_address = gart_info->table_handle->busaddr;
138254885Sdumbbell	} else {
139254885Sdumbbell		address = gart_info->addr;
140254885Sdumbbell		bus_address = gart_info->bus_addr;
141254885Sdumbbell		DRM_DEBUG("PCI: Gart Table: VRAM %08LX mapped at %08lX\n",
142254885Sdumbbell			  (unsigned long long)bus_address,
143254885Sdumbbell			  (unsigned long)address);
144254885Sdumbbell	}
145254885Sdumbbell
146254885Sdumbbell
147254885Sdumbbell	max_ati_pages = (gart_info->table_size / sizeof(u32));
148254885Sdumbbell	max_real_pages = max_ati_pages / (PAGE_SIZE / ATI_PCIGART_PAGE_SIZE);
149254885Sdumbbell	pages = (entry->pages <= max_real_pages)
150254885Sdumbbell	    ? entry->pages : max_real_pages;
151254885Sdumbbell
152254885Sdumbbell	if (gart_info->gart_table_location == DRM_ATI_GART_MAIN) {
153254885Sdumbbell		memset(pci_gart, 0, max_ati_pages * sizeof(u32));
154254885Sdumbbell	} else {
155254885Sdumbbell		memset_io((void __iomem *)map->handle, 0, max_ati_pages * sizeof(u32));
156254885Sdumbbell	}
157254885Sdumbbell
158254885Sdumbbell	gart_idx = 0;
159254885Sdumbbell	for (i = 0; i < pages; i++) {
160254885Sdumbbell#ifdef __linux__
161254885Sdumbbell		/* we need to support large memory configurations */
162254885Sdumbbell		entry->busaddr[i] = pci_map_page(dev->pdev, entry->pagelist[i],
163254885Sdumbbell						 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
164254885Sdumbbell		if (pci_dma_mapping_error(dev->pdev, entry->busaddr[i])) {
165254885Sdumbbell			DRM_ERROR("unable to map PCIGART pages!\n");
166254885Sdumbbell			drm_ati_pcigart_cleanup(dev, gart_info);
167254885Sdumbbell			address = NULL;
168254885Sdumbbell			bus_address = 0;
169254885Sdumbbell			goto done;
170254885Sdumbbell		}
171254885Sdumbbell#endif
172254885Sdumbbell		page_base = (u32) entry->busaddr[i];
173254885Sdumbbell
174254885Sdumbbell		for (j = 0; j < (PAGE_SIZE / ATI_PCIGART_PAGE_SIZE); j++) {
175254885Sdumbbell			u32 val;
176254885Sdumbbell
177254885Sdumbbell			switch(gart_info->gart_reg_if) {
178254885Sdumbbell			case DRM_ATI_GART_IGP:
179254885Sdumbbell				val = page_base | 0xc;
180254885Sdumbbell				break;
181254885Sdumbbell			case DRM_ATI_GART_PCIE:
182254885Sdumbbell				val = (page_base >> 8) | 0xc;
183254885Sdumbbell				break;
184254885Sdumbbell			default:
185254885Sdumbbell			case DRM_ATI_GART_PCI:
186254885Sdumbbell				val = page_base;
187254885Sdumbbell				break;
188254885Sdumbbell			}
189254885Sdumbbell			if (gart_info->gart_table_location ==
190254885Sdumbbell			    DRM_ATI_GART_MAIN)
191254885Sdumbbell				pci_gart[gart_idx] = cpu_to_le32(val);
192254885Sdumbbell			else
193254885Sdumbbell				DRM_WRITE32(map, gart_idx * sizeof(u32), val);
194254885Sdumbbell			gart_idx++;
195254885Sdumbbell			page_base += ATI_PCIGART_PAGE_SIZE;
196254885Sdumbbell		}
197254885Sdumbbell	}
198254885Sdumbbell	ret = 1;
199254885Sdumbbell
200254885Sdumbbell#if defined(__i386) || defined(__amd64)
201254885Sdumbbell	wbinvd();
202254885Sdumbbell#else
203254885Sdumbbell	mb();
204254885Sdumbbell#endif
205254885Sdumbbell
206254885Sdumbbell      done:
207254885Sdumbbell	gart_info->addr = address;
208254885Sdumbbell	gart_info->bus_addr = bus_address;
209254885Sdumbbell	return ret;
210254885Sdumbbell}
211