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