nouveau_drm.c 6.7 KB

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