xref: /dragonfly/sys/dev/drm/drm_dragonfly.c (revision 9348a738)
1 /*
2  * Copyright (c) 2015 Imre Vadász <imre@vdsz.com>
3  * Copyright (c) 2015 Rimvydas Jasinskas
4  *
5  * DRM Dragonfly-specific helper functions
6  *
7  * Permission to use, copy, modify, distribute, and sell this software and its
8  * documentation for any purpose is hereby granted without fee, provided that
9  * the above copyright notice appear in all copies and that both that copyright
10  * notice and this permission notice appear in supporting documentation, and
11  * that the name of the copyright holders not be used in advertising or
12  * publicity pertaining to distribution of the software without specific,
13  * written prior permission.  The copyright holders make no representations
14  * about the suitability of this software for any purpose.  It is provided "as
15  * is" without express or implied warranty.
16  *
17  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
18  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
19  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
20  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
22  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
23  * OF THIS SOFTWARE.
24  */
25 
26 #include <sys/libkern.h>
27 #include <sys/ctype.h>
28 #include <drm/drmP.h>
29 
30 /*
31  * An implementation of fb_get_options()
32  * This can be used to set the video mode used for the syscons fb console,
33  * a la "video=..." in linux.
34  */
35 int
36 fb_get_options(const char *connector_name, char **option)
37 {
38 	char buf[128], str[1024];
39 
40 	/*
41 	 * Where on linux one would use the command line option
42 	 * video=LVDS-1:<video-mode>, the corresponding tunable is
43 	 * drm.video.LVDS-1=<video-mode>.
44 	 * e.g. drm.video.LVDS-1=1024x768 sets the LVDS-1 connector to
45 	 * a 1024x768 video mode in the syscons framebuffer console.
46 	 * See https://wiki.archlinux.org/index.php/Kernel_mode_setting
47 	 * for an explanation of the video mode command line option.
48 	 */
49 	memset(str, 0, sizeof(str));
50 	ksnprintf(buf, sizeof(buf), "drm.video.%s", connector_name);
51 	if (kgetenv_string(buf, str, sizeof(str)-1)) {
52 		kprintf("found kenv %s=%s\n", buf, str);
53 		*option = kstrdup(str, M_DRM);
54 		return (0);
55 	} else {
56 		kprintf("tunable %s is not set\n", buf);
57 		return (1);
58 	}
59 }
60 
61 /*
62  * Implement simplified version of kvasnrprintf() for drm needs using
63  * M_DRM and kvsnprintf(). Since it is unclear what string size is
64  * optimal thus use of an actual length.
65  */
66 char *drm_vasprintf(int flags, const char *format, __va_list ap)
67 {
68 	char *str;
69 	size_t size;
70 	__va_list aq;
71 
72 	__va_copy(aq, ap);
73 	size = kvsnprintf(NULL, 0, format, aq);
74 	__va_end(aq);
75 
76 	str = kmalloc(size+1, M_DRM, flags);
77 	if (str == NULL)
78 		return NULL;
79 
80 	kvsnprintf(str, size+1, format, ap);
81 
82 	return str;
83 }
84 
85 /* mimic ksnrprintf(), return pointer to char* and match drm api */
86 char *drm_asprintf(int flags, const char *format, ...)
87 {
88 	char *str;
89 	__va_list ap;
90 
91 	__va_start(ap, format);
92 	str = drm_vasprintf(flags, format, ap);
93 	__va_end(ap);
94 
95 	return str;
96 }
97 
98 /*
99  * XXX pci glue logic helpers
100  * Should be done in drm_pci_init(), pending drm update.
101  * Assumes static runtime data.
102  * Only for usage in *_driver_[un]load()
103  */
104 
105 static void drm_fill_pdev(device_t dev, struct pci_dev *pdev)
106 {
107 	pdev->dev.bsddev = dev;
108 	pdev->vendor = pci_get_vendor(dev);
109 	pdev->device = pci_get_device(dev);
110 	pdev->subsystem_vendor = pci_get_subvendor(dev);
111 	pdev->subsystem_device = pci_get_subdevice(dev);
112 
113 	pdev->revision = pci_get_revid(dev) & 0xff;
114 }
115 
116 void drm_init_pdev(device_t dev, struct pci_dev **pdev)
117 {
118 	BUG_ON(*pdev != NULL);
119 
120 	*pdev = kzalloc(sizeof(struct pci_dev), GFP_KERNEL);
121 	drm_fill_pdev(dev, *pdev);
122 
123 	(*pdev)->bus = kzalloc(sizeof(struct pci_bus), GFP_KERNEL);
124 	(*pdev)->bus->self = kzalloc(sizeof(struct pci_dev), GFP_KERNEL);
125 
126 	drm_fill_pdev(device_get_parent(dev), (*pdev)->bus->self);
127 	(*pdev)->bus->number = pci_get_bus(dev);
128 }
129 
130 void drm_fini_pdev(struct pci_dev **pdev)
131 {
132 	kfree((*pdev)->bus->self);
133 	kfree((*pdev)->bus);
134 
135 	kfree(*pdev);
136 }
137 
138 void drm_print_pdev(struct pci_dev *pdev)
139 {
140 	if (pdev == NULL) {
141 		DRM_ERROR("pdev is null!\n");
142 		return;
143 	}
144 
145 	DRM_INFO("pdev:  vendor=0x%04x  device=0x%04x rev=0x%02x\n",
146 		 pdev->vendor, pdev->device, pdev->revision);
147 	DRM_INFO("      svendor=0x%04x sdevice=0x%04x irq=%u\n",
148 		 pdev->subsystem_vendor, pdev->subsystem_device, pdev->irq);
149 }
150 
151 /* Allocation of PCI memory resources (framebuffer, registers, etc.) for
152  * drm_get_resource_*.  Note that they are not RF_ACTIVE, so there's no virtual
153  * address for accessing them.  Cleaned up at unload.
154  */
155 static int drm_alloc_resource(struct drm_device *dev, int resource)
156 {
157 	struct resource *res;
158 	int rid;
159 
160 	DRM_LOCK_ASSERT(dev);
161 
162 	if (resource >= DRM_MAX_PCI_RESOURCE) {
163 		DRM_ERROR("Resource %d too large\n", resource);
164 		return 1;
165 	}
166 
167 	if (dev->pcir[resource] != NULL) {
168 		return 0;
169 	}
170 
171 	DRM_UNLOCK(dev);
172 	rid = PCIR_BAR(resource);
173 	res = bus_alloc_resource_any(dev->dev->bsddev, SYS_RES_MEMORY, &rid,
174 	    RF_SHAREABLE);
175 	DRM_LOCK(dev);
176 	if (res == NULL) {
177 		DRM_ERROR("Couldn't find resource 0x%x\n", resource);
178 		return 1;
179 	}
180 
181 	if (dev->pcir[resource] == NULL) {
182 		dev->pcirid[resource] = rid;
183 		dev->pcir[resource] = res;
184 	}
185 
186 	return 0;
187 }
188 
189 unsigned long drm_get_resource_start(struct drm_device *dev,
190 				     unsigned int resource)
191 {
192 	if (drm_alloc_resource(dev, resource) != 0)
193 		return 0;
194 
195 	return rman_get_start(dev->pcir[resource]);
196 }
197 
198 unsigned long drm_get_resource_len(struct drm_device *dev,
199 				   unsigned int resource)
200 {
201 	if (drm_alloc_resource(dev, resource) != 0)
202 		return 0;
203 
204 	return rman_get_size(dev->pcir[resource]);
205 }
206