xref: /netbsd-src/sys/external/bsd/drm2/i915drm/intelfb.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: intelfb.c,v 1.14 2016/12/12 19:45:56 maya Exp $	*/
2 
3 /*-
4  * Copyright (c) 2014 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Taylor R. Campbell.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: intelfb.c,v 1.14 2016/12/12 19:45:56 maya Exp $");
34 
35 #include <sys/types.h>
36 #include <sys/bus.h>
37 #include <sys/device.h>
38 
39 #include <drm/drmP.h>
40 #include <drm/drmfb.h>
41 #include <drm/drmfb_pci.h>
42 
43 #include "i915_drv.h"
44 #include "i915_pci.h"
45 #include "intelfb.h"
46 
47 static int	intelfb_match(device_t, cfdata_t, void *);
48 static void	intelfb_attach(device_t, device_t, void *);
49 static int	intelfb_detach(device_t, int);
50 
51 static void	intelfb_attach_task(struct i915drmkms_task *);
52 
53 static bool	intelfb_shutdown(device_t, int);
54 
55 static paddr_t	intelfb_drmfb_mmapfb(struct drmfb_softc *, off_t, int);
56 
57 struct intelfb_softc {
58 	struct drmfb_softc		sc_drmfb; /* XXX Must be first.  */
59 	device_t			sc_dev;
60 	struct intelfb_attach_args	sc_ifa;
61 	bus_space_handle_t		sc_fb_bsh;
62 	struct i915drmkms_task		sc_attach_task;
63 	bool				sc_mapped:1;
64 	bool				sc_scheduled:1;
65 	bool				sc_attached:1;
66 };
67 
68 static const struct drmfb_params intelfb_drmfb_params = {
69 	.dp_mmapfb = intelfb_drmfb_mmapfb,
70 	.dp_mmap = drmfb_pci_mmap,
71 	.dp_ioctl = drmfb_pci_ioctl,
72 	.dp_is_vga_console = drmfb_pci_is_vga_console,
73 	.dp_disable_vga = i915_disable_vga,
74 };
75 
76 CFATTACH_DECL_NEW(intelfb, sizeof(struct intelfb_softc),
77     intelfb_match, intelfb_attach, intelfb_detach, NULL);
78 
79 static int
80 intelfb_match(device_t parent, cfdata_t match, void *aux)
81 {
82 
83 	return 1;
84 }
85 
86 static void
87 intelfb_attach(device_t parent, device_t self, void *aux)
88 {
89 	struct intelfb_softc *const sc = device_private(self);
90 	const struct intelfb_attach_args *const ifa = aux;
91 	int error;
92 
93 	sc->sc_dev = self;
94 	sc->sc_ifa = *ifa;
95 	sc->sc_mapped = false;
96 	sc->sc_scheduled = false;
97 	sc->sc_attached = false;
98 
99 	aprint_naive("\n");
100 	aprint_normal("\n");
101 
102 	/* XXX Defer this too?  */
103 	error = bus_space_map(ifa->ifa_fb_bst, ifa->ifa_fb_addr,
104 	    ifa->ifa_fb_size,
105 	    BUS_SPACE_MAP_LINEAR|BUS_SPACE_MAP_PREFETCHABLE,
106 	    &sc->sc_fb_bsh);
107 	if (error) {
108 		aprint_error_dev(self, "unable to map framebuffer: %d\n",
109 		    error);
110 		goto fail0;
111 	}
112 	sc->sc_mapped = true;
113 
114 	i915drmkms_task_init(&sc->sc_attach_task, &intelfb_attach_task);
115 	error = i915drmkms_task_schedule(parent, &sc->sc_attach_task);
116 	if (error) {
117 		aprint_error_dev(self, "failed to schedule mode set: %d\n",
118 		    error);
119 		goto fail1;
120 	}
121 	sc->sc_scheduled = true;
122 
123 	/* Success!  */
124 	return;
125 
126 fail1:	bus_space_unmap(ifa->ifa_fb_bst, sc->sc_fb_bsh, ifa->ifa_fb_size);
127 	sc->sc_mapped = false;
128 fail0:	return;
129 }
130 
131 static int
132 intelfb_detach(device_t self, int flags)
133 {
134 	struct intelfb_softc *const sc = device_private(self);
135 	int error;
136 
137 	if (sc->sc_scheduled)
138 		return EBUSY;
139 
140 	if (sc->sc_attached) {
141 		pmf_device_deregister(self);
142 		error = drmfb_detach(&sc->sc_drmfb, flags);
143 		if (error) {
144 			/* XXX Ugh.  */
145 			(void)pmf_device_register1(self, NULL, NULL,
146 			    &intelfb_shutdown);
147 			return error;
148 		}
149 		sc->sc_attached = false;
150 	}
151 
152 	if (sc->sc_mapped) {
153 		bus_space_unmap(sc->sc_ifa.ifa_fb_bst, sc->sc_fb_bsh,
154 		    sc->sc_ifa.ifa_fb_size);
155 		sc->sc_mapped = false;
156 	}
157 
158 	return 0;
159 }
160 
161 static void
162 intelfb_attach_task(struct i915drmkms_task *task)
163 {
164 	struct intelfb_softc *const sc = container_of(task,
165 	    struct intelfb_softc, sc_attach_task);
166 	const struct intelfb_attach_args *const ifa = &sc->sc_ifa;
167 	const struct drm_fb_helper_surface_size *const sizes = &ifa->ifa_fb_sizes;
168 	const struct drmfb_attach_args da = {
169 		.da_dev = sc->sc_dev,
170 		.da_fb_helper = ifa->ifa_fb_helper,
171 		.da_fb_sizes = &ifa->ifa_fb_sizes,
172 		.da_fb_vaddr = bus_space_vaddr(ifa->ifa_fb_bst, sc->sc_fb_bsh),
173 		.da_fb_linebytes = roundup2((sizes->surface_width *
174 		    howmany(sizes->surface_bpp, 8)), 64),
175 		.da_params = &intelfb_drmfb_params,
176 	};
177 	int error;
178 
179 	error = drmfb_attach(&sc->sc_drmfb, &da);
180 	if (error) {
181 		aprint_error_dev(sc->sc_dev, "failed to attach drmfb: %d\n",
182 		    error);
183 		return;
184 	}
185 
186 	if (!pmf_device_register1(sc->sc_dev, NULL, NULL, &intelfb_shutdown))
187 		aprint_error_dev(sc->sc_dev,
188 		    "failed to register shutdown handler\n");
189 
190 	sc->sc_attached = true;
191 }
192 
193 static bool
194 intelfb_shutdown(device_t self, int flags)
195 {
196 	struct intelfb_softc *const sc = device_private(self);
197 
198 	return drmfb_shutdown(&sc->sc_drmfb, flags);
199 }
200 
201 static paddr_t
202 intelfb_drmfb_mmapfb(struct drmfb_softc *drmfb, off_t offset, int prot)
203 {
204 	struct intelfb_softc *const sc = container_of(drmfb,
205 	    struct intelfb_softc, sc_drmfb);
206 	struct drm_fb_helper *const helper = sc->sc_ifa.ifa_fb_helper;
207 	struct intel_fbdev *const fbdev = container_of(helper,
208 	    struct intel_fbdev, helper);
209 	struct drm_device *const dev = helper->dev;
210 	struct drm_i915_private *const dev_priv = dev->dev_private;
211 
212 	KASSERT(0 <= offset);
213 	KASSERT(offset < fbdev->fb->obj->base.size);
214 
215 	return bus_space_mmap(dev->bst, dev_priv->gtt.mappable_base,
216 	    i915_gem_obj_ggtt_offset(fbdev->fb->obj) + offset,
217 	    prot, BUS_SPACE_MAP_PREFETCHABLE);
218 }
219