blob: 3b4e65d5122b69025a1fd863ff54f807dde4f2f9 [file] [log] [blame]
Ben Skeggs94580292012-07-06 12:14:00 +10001/*
2 * Copyright 2012 Red Hat Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 *
22 * Authors: Ben Skeggs
23 */
24
25#include <linux/module.h>
26#include <linux/pci.h>
27
28#include <core/device.h>
29#include <core/client.h>
30#include <core/class.h>
31
32#include <subdev/device.h>
33
34#include "nouveau_drm.h"
Ben Skeggscb75d972012-07-11 10:44:20 +100035#include "nouveau_agp.h"
Ben Skeggs94580292012-07-06 12:14:00 +100036
37int __devinit nouveau_pci_probe(struct pci_dev *, const struct pci_device_id *);
38void nouveau_pci_remove(struct pci_dev *);
39int nouveau_pci_suspend(struct pci_dev *, pm_message_t);
40int nouveau_pci_resume(struct pci_dev *);
41int __init nouveau_init(struct pci_driver *);
42void __exit nouveau_exit(struct pci_driver *);
43
44int nouveau_load(struct drm_device *, unsigned long);
45int nouveau_unload(struct drm_device *);
46void *nouveau_newpriv(struct drm_device *);
47
48MODULE_PARM_DESC(config, "option string to pass to driver core");
49static char *nouveau_config;
50module_param_named(config, nouveau_config, charp, 0400);
51
52MODULE_PARM_DESC(debug, "debug string to pass to driver core");
53static char *nouveau_debug;
54module_param_named(debug, nouveau_debug, charp, 0400);
55
56static u64
57nouveau_name(struct pci_dev *pdev)
58{
59 u64 name = (u64)pci_domain_nr(pdev->bus) << 32;
60 name |= pdev->bus->number << 16;
61 name |= PCI_SLOT(pdev->devfn) << 8;
62 return name | PCI_FUNC(pdev->devfn);
63}
64
65static int
66nouveau_cli_create(struct pci_dev *pdev, u32 name, int size, void **pcli)
67{
68 struct nouveau_cli *cli;
69 int ret;
70
71 ret = nouveau_client_create_(name, nouveau_name(pdev), nouveau_config,
72 nouveau_debug, size, pcli);
73 cli = *pcli;
74 if (ret)
75 return ret;
76
77 mutex_init(&cli->mutex);
78 return 0;
79}
80
81static void
82nouveau_cli_destroy(struct nouveau_cli *cli)
83{
84 struct nouveau_object *client = nv_object(cli);
85 nouveau_client_fini(&cli->base, false);
86 atomic_set(&client->refcount, 1);
87 nouveau_object_ref(NULL, &client);
88}
89
90static int __devinit
91nouveau_drm_probe(struct pci_dev *pdev, const struct pci_device_id *pent)
92{
93 struct nouveau_device *device;
94 int ret;
95
96 ret = nouveau_device_create(pdev, nouveau_name(pdev), pci_name(pdev),
97 nouveau_config, nouveau_debug, &device);
98 if (ret)
99 return ret;
100
101 pci_set_master(pdev);
102
103 ret = nouveau_pci_probe(pdev, pent);
104 if (ret) {
105 nouveau_device_destroy(&device);
106 return ret;
107 }
108
109 return 0;
110}
111
112int
113nouveau_drm_load(struct drm_device *dev, unsigned long flags)
114{
115 struct pci_dev *pdev = dev->pdev;
116 struct nouveau_drm *drm;
117 int ret;
118
119 ret = nouveau_cli_create(pdev, 0, sizeof(*drm), (void**)&drm);
120 dev->dev_private = drm;
121 if (ret)
122 return ret;
123
124 INIT_LIST_HEAD(&drm->clients);
125 drm->dev = dev;
126
Ben Skeggscb75d972012-07-11 10:44:20 +1000127 /* make sure AGP controller is in a consistent state before we
128 * (possibly) execute vbios init tables (see nouveau_agp.h)
129 */
130 if (drm_pci_device_is_agp(dev) && dev->agp) {
131 /* dummy device object, doesn't init anything, but allows
132 * agp code access to registers
133 */
134 ret = nouveau_object_new(nv_object(drm), NVDRM_CLIENT,
135 NVDRM_DEVICE, 0x0080,
136 &(struct nv_device_class) {
137 .device = ~0,
138 .disable =
139 ~(NV_DEVICE_DISABLE_MMIO |
140 NV_DEVICE_DISABLE_IDENTIFY),
141 .debug0 = ~0,
142 }, sizeof(struct nv_device_class),
143 &drm->device);
144 if (ret)
145 return ret;
146
147 nouveau_agp_reset(drm);
148 nouveau_object_del(nv_object(drm), NVDRM_CLIENT, NVDRM_DEVICE);
149 }
150
Ben Skeggs94580292012-07-06 12:14:00 +1000151 ret = nouveau_object_new(nv_object(drm), NVDRM_CLIENT, NVDRM_DEVICE,
152 0x0080, &(struct nv_device_class) {
153 .device = ~0,
154 .disable = 0,
155 .debug0 = 0,
156 }, sizeof(struct nv_device_class),
157 &drm->device);
158 if (ret)
159 goto fail_device;
160
Ben Skeggscb75d972012-07-11 10:44:20 +1000161 /* initialise AGP */
162 nouveau_agp_init(drm);
163
Ben Skeggs94580292012-07-06 12:14:00 +1000164 ret = nouveau_load(dev, flags);
165 if (ret)
166 goto fail_device;
167
168 return 0;
169
170fail_device:
171 nouveau_cli_destroy(&drm->client);
172 return ret;
173}
174
175int
176nouveau_drm_unload(struct drm_device *dev)
177{
178 struct nouveau_drm *drm = nouveau_newpriv(dev);
179 struct pci_dev *pdev = dev->pdev;
180 int ret;
181
182 ret = nouveau_unload(dev);
183 if (ret)
184 return ret;
185
Ben Skeggscb75d972012-07-11 10:44:20 +1000186 nouveau_agp_fini(drm);
187
Ben Skeggs94580292012-07-06 12:14:00 +1000188 pci_set_drvdata(pdev, drm->client.base.device);
189 nouveau_cli_destroy(&drm->client);
190 return 0;
191}
192
193static void
194nouveau_drm_remove(struct pci_dev *pdev)
195{
196 struct nouveau_device *device;
197 nouveau_pci_remove(pdev);
198 device = pci_get_drvdata(pdev);
199 nouveau_device_destroy(&device);
200}
201
202int
203nouveau_drm_suspend(struct pci_dev *pdev, pm_message_t pm_state)
204{
205 struct drm_device *dev = pci_get_drvdata(pdev);
206 struct nouveau_drm *drm = nouveau_newpriv(dev);
207 struct nouveau_cli *cli;
208 int ret;
209
210 if (dev->switch_power_state == DRM_SWITCH_POWER_OFF ||
211 pm_state.event == PM_EVENT_PRETHAW)
212 return 0;
213
214 ret = nouveau_pci_suspend(pdev, pm_state);
215 if (ret)
216 return ret;
217
218 list_for_each_entry(cli, &drm->clients, head) {
219 ret = nouveau_client_fini(&cli->base, true);
220 if (ret)
221 goto fail_client;
222 }
223
224 ret = nouveau_client_fini(&drm->client.base, true);
225 if (ret)
226 goto fail_client;
227
Ben Skeggscb75d972012-07-11 10:44:20 +1000228 nouveau_agp_fini(drm);
229
Ben Skeggs94580292012-07-06 12:14:00 +1000230 pci_save_state(pdev);
231 if (pm_state.event == PM_EVENT_SUSPEND) {
232 pci_disable_device(pdev);
233 pci_set_power_state(pdev, PCI_D3hot);
234 }
235
236 return 0;
237
238fail_client:
239 list_for_each_entry_continue_reverse(cli, &drm->clients, head) {
240 nouveau_client_init(&cli->base);
241 }
242
243 nouveau_pci_resume(pdev);
244 return ret;
245}
246
247int
248nouveau_drm_resume(struct pci_dev *pdev)
249{
250 struct drm_device *dev = pci_get_drvdata(pdev);
251 struct nouveau_drm *drm = nouveau_newpriv(dev);
252 struct nouveau_cli *cli;
253 int ret;
254
255 if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
256 return 0;
257
258 pci_set_power_state(pdev, PCI_D0);
259 pci_restore_state(pdev);
260 ret = pci_enable_device(pdev);
261 if (ret)
262 return ret;
263 pci_set_master(pdev);
264
Ben Skeggscb75d972012-07-11 10:44:20 +1000265 nouveau_agp_reset(drm);
266
Ben Skeggs94580292012-07-06 12:14:00 +1000267 nouveau_client_init(&drm->client.base);
268
269 list_for_each_entry(cli, &drm->clients, head) {
270 nouveau_client_init(&cli->base);
271 }
272
Ben Skeggscb75d972012-07-11 10:44:20 +1000273 nouveau_agp_init(drm);
274
Ben Skeggs94580292012-07-06 12:14:00 +1000275 return nouveau_pci_resume(pdev);
276}
277
278static struct pci_device_id
279nouveau_drm_pci_table[] = {
280 {
281 PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID),
282 .class = PCI_BASE_CLASS_DISPLAY << 16,
283 .class_mask = 0xff << 16,
284 },
285 {
286 PCI_DEVICE(PCI_VENDOR_ID_NVIDIA_SGS, PCI_ANY_ID),
287 .class = PCI_BASE_CLASS_DISPLAY << 16,
288 .class_mask = 0xff << 16,
289 },
290 {}
291};
292
293static struct pci_driver
294nouveau_drm_pci_driver = {
295 .name = "nouveau",
296 .id_table = nouveau_drm_pci_table,
297 .probe = nouveau_drm_probe,
298 .remove = nouveau_drm_remove,
299 .suspend = nouveau_drm_suspend,
300 .resume = nouveau_drm_resume,
301};
302
303static int __init
304nouveau_drm_init(void)
305{
306 return nouveau_init(&nouveau_drm_pci_driver);
307}
308
309static void __exit
310nouveau_drm_exit(void)
311{
312 nouveau_exit(&nouveau_drm_pci_driver);
313}
314
315module_init(nouveau_drm_init);
316module_exit(nouveau_drm_exit);
317
318MODULE_DEVICE_TABLE(pci, nouveau_drm_pci_table);
319MODULE_AUTHOR("Nouveau Project");
320MODULE_DESCRIPTION("nVidia Riva/TNT/GeForce/Quadro/Tesla");
321MODULE_LICENSE("GPL and additional rights");