blob: c72d42636c89db15337bb011971b0098d2cae8ac [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"
35
36int __devinit nouveau_pci_probe(struct pci_dev *, const struct pci_device_id *);
37void nouveau_pci_remove(struct pci_dev *);
38int nouveau_pci_suspend(struct pci_dev *, pm_message_t);
39int nouveau_pci_resume(struct pci_dev *);
40int __init nouveau_init(struct pci_driver *);
41void __exit nouveau_exit(struct pci_driver *);
42
43int nouveau_load(struct drm_device *, unsigned long);
44int nouveau_unload(struct drm_device *);
45void *nouveau_newpriv(struct drm_device *);
46
47MODULE_PARM_DESC(config, "option string to pass to driver core");
48static char *nouveau_config;
49module_param_named(config, nouveau_config, charp, 0400);
50
51MODULE_PARM_DESC(debug, "debug string to pass to driver core");
52static char *nouveau_debug;
53module_param_named(debug, nouveau_debug, charp, 0400);
54
55static u64
56nouveau_name(struct pci_dev *pdev)
57{
58 u64 name = (u64)pci_domain_nr(pdev->bus) << 32;
59 name |= pdev->bus->number << 16;
60 name |= PCI_SLOT(pdev->devfn) << 8;
61 return name | PCI_FUNC(pdev->devfn);
62}
63
64static int
65nouveau_cli_create(struct pci_dev *pdev, u32 name, int size, void **pcli)
66{
67 struct nouveau_cli *cli;
68 int ret;
69
70 ret = nouveau_client_create_(name, nouveau_name(pdev), nouveau_config,
71 nouveau_debug, size, pcli);
72 cli = *pcli;
73 if (ret)
74 return ret;
75
76 mutex_init(&cli->mutex);
77 return 0;
78}
79
80static void
81nouveau_cli_destroy(struct nouveau_cli *cli)
82{
83 struct nouveau_object *client = nv_object(cli);
84 nouveau_client_fini(&cli->base, false);
85 atomic_set(&client->refcount, 1);
86 nouveau_object_ref(NULL, &client);
87}
88
89static int __devinit
90nouveau_drm_probe(struct pci_dev *pdev, const struct pci_device_id *pent)
91{
92 struct nouveau_device *device;
93 int ret;
94
95 ret = nouveau_device_create(pdev, nouveau_name(pdev), pci_name(pdev),
96 nouveau_config, nouveau_debug, &device);
97 if (ret)
98 return ret;
99
100 pci_set_master(pdev);
101
102 ret = nouveau_pci_probe(pdev, pent);
103 if (ret) {
104 nouveau_device_destroy(&device);
105 return ret;
106 }
107
108 return 0;
109}
110
111int
112nouveau_drm_load(struct drm_device *dev, unsigned long flags)
113{
114 struct pci_dev *pdev = dev->pdev;
115 struct nouveau_drm *drm;
116 int ret;
117
118 ret = nouveau_cli_create(pdev, 0, sizeof(*drm), (void**)&drm);
119 dev->dev_private = drm;
120 if (ret)
121 return ret;
122
123 INIT_LIST_HEAD(&drm->clients);
124 drm->dev = dev;
125
126 ret = nouveau_object_new(nv_object(drm), NVDRM_CLIENT, NVDRM_DEVICE,
127 0x0080, &(struct nv_device_class) {
128 .device = ~0,
129 .disable = 0,
130 .debug0 = 0,
131 }, sizeof(struct nv_device_class),
132 &drm->device);
133 if (ret)
134 goto fail_device;
135
136 ret = nouveau_load(dev, flags);
137 if (ret)
138 goto fail_device;
139
140 return 0;
141
142fail_device:
143 nouveau_cli_destroy(&drm->client);
144 return ret;
145}
146
147int
148nouveau_drm_unload(struct drm_device *dev)
149{
150 struct nouveau_drm *drm = nouveau_newpriv(dev);
151 struct pci_dev *pdev = dev->pdev;
152 int ret;
153
154 ret = nouveau_unload(dev);
155 if (ret)
156 return ret;
157
158 pci_set_drvdata(pdev, drm->client.base.device);
159 nouveau_cli_destroy(&drm->client);
160 return 0;
161}
162
163static void
164nouveau_drm_remove(struct pci_dev *pdev)
165{
166 struct nouveau_device *device;
167 nouveau_pci_remove(pdev);
168 device = pci_get_drvdata(pdev);
169 nouveau_device_destroy(&device);
170}
171
172int
173nouveau_drm_suspend(struct pci_dev *pdev, pm_message_t pm_state)
174{
175 struct drm_device *dev = pci_get_drvdata(pdev);
176 struct nouveau_drm *drm = nouveau_newpriv(dev);
177 struct nouveau_cli *cli;
178 int ret;
179
180 if (dev->switch_power_state == DRM_SWITCH_POWER_OFF ||
181 pm_state.event == PM_EVENT_PRETHAW)
182 return 0;
183
184 ret = nouveau_pci_suspend(pdev, pm_state);
185 if (ret)
186 return ret;
187
188 list_for_each_entry(cli, &drm->clients, head) {
189 ret = nouveau_client_fini(&cli->base, true);
190 if (ret)
191 goto fail_client;
192 }
193
194 ret = nouveau_client_fini(&drm->client.base, true);
195 if (ret)
196 goto fail_client;
197
198 pci_save_state(pdev);
199 if (pm_state.event == PM_EVENT_SUSPEND) {
200 pci_disable_device(pdev);
201 pci_set_power_state(pdev, PCI_D3hot);
202 }
203
204 return 0;
205
206fail_client:
207 list_for_each_entry_continue_reverse(cli, &drm->clients, head) {
208 nouveau_client_init(&cli->base);
209 }
210
211 nouveau_pci_resume(pdev);
212 return ret;
213}
214
215int
216nouveau_drm_resume(struct pci_dev *pdev)
217{
218 struct drm_device *dev = pci_get_drvdata(pdev);
219 struct nouveau_drm *drm = nouveau_newpriv(dev);
220 struct nouveau_cli *cli;
221 int ret;
222
223 if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
224 return 0;
225
226 pci_set_power_state(pdev, PCI_D0);
227 pci_restore_state(pdev);
228 ret = pci_enable_device(pdev);
229 if (ret)
230 return ret;
231 pci_set_master(pdev);
232
233 nouveau_client_init(&drm->client.base);
234
235 list_for_each_entry(cli, &drm->clients, head) {
236 nouveau_client_init(&cli->base);
237 }
238
239 return nouveau_pci_resume(pdev);
240}
241
242static struct pci_device_id
243nouveau_drm_pci_table[] = {
244 {
245 PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID),
246 .class = PCI_BASE_CLASS_DISPLAY << 16,
247 .class_mask = 0xff << 16,
248 },
249 {
250 PCI_DEVICE(PCI_VENDOR_ID_NVIDIA_SGS, PCI_ANY_ID),
251 .class = PCI_BASE_CLASS_DISPLAY << 16,
252 .class_mask = 0xff << 16,
253 },
254 {}
255};
256
257static struct pci_driver
258nouveau_drm_pci_driver = {
259 .name = "nouveau",
260 .id_table = nouveau_drm_pci_table,
261 .probe = nouveau_drm_probe,
262 .remove = nouveau_drm_remove,
263 .suspend = nouveau_drm_suspend,
264 .resume = nouveau_drm_resume,
265};
266
267static int __init
268nouveau_drm_init(void)
269{
270 return nouveau_init(&nouveau_drm_pci_driver);
271}
272
273static void __exit
274nouveau_drm_exit(void)
275{
276 nouveau_exit(&nouveau_drm_pci_driver);
277}
278
279module_init(nouveau_drm_init);
280module_exit(nouveau_drm_exit);
281
282MODULE_DEVICE_TABLE(pci, nouveau_drm_pci_table);
283MODULE_AUTHOR("Nouveau Project");
284MODULE_DESCRIPTION("nVidia Riva/TNT/GeForce/Quadro/Tesla");
285MODULE_LICENSE("GPL and additional rights");