xref: /netbsd-src/sys/arch/arm/nvidia/tegra_drm.c (revision d909946ca08dceb44d7d0f22ec9488679695d976)
1 /* $NetBSD: tegra_drm.c,v 1.6 2016/01/30 00:00:56 riastradh Exp $ */
2 
3 /*-
4  * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: tegra_drm.c,v 1.6 2016/01/30 00:00:56 riastradh Exp $");
31 
32 #include <sys/param.h>
33 #include <sys/bus.h>
34 #include <sys/device.h>
35 #include <sys/intr.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/conf.h>
39 
40 #include <uvm/uvm_extern.h>
41 #include <uvm/uvm_device.h>
42 
43 #include <drm/drmP.h>
44 
45 #include <arm/nvidia/tegra_reg.h>
46 #include <arm/nvidia/tegra_var.h>
47 #include <arm/nvidia/tegra_drm.h>
48 
49 #include <dev/fdt/fdtvar.h>
50 
51 static int	tegra_drm_match(device_t, cfdata_t, void *);
52 static void	tegra_drm_attach(device_t, device_t, void *);
53 
54 static const char *tegra_drm_get_name(struct drm_device *);
55 static int	tegra_drm_set_busid(struct drm_device *, struct drm_master *);
56 
57 static int	tegra_drm_load(struct drm_device *, unsigned long);
58 static int	tegra_drm_unload(struct drm_device *);
59 
60 static int	tegra_drm_dumb_create(struct drm_file *, struct drm_device *,
61 		    struct drm_mode_create_dumb *);
62 static int	tegra_drm_dumb_map_offset(struct drm_file *,
63 		    struct drm_device *, uint32_t, uint64_t *);
64 
65 static const struct uvm_pagerops tegra_drm_gem_uvm_ops = {
66 	.pgo_reference = drm_gem_pager_reference,
67 	.pgo_detach = drm_gem_pager_detach,
68 	.pgo_fault = tegra_drm_gem_fault,
69 };
70 
71 static struct drm_driver tegra_drm_driver = {
72 	.driver_features = DRIVER_MODESET | DRIVER_GEM,
73 	.dev_priv_size = 0,
74 	.load = tegra_drm_load,
75 	.unload = tegra_drm_unload,
76 
77 	.gem_free_object = tegra_drm_gem_free_object,
78 	.mmap_object = drm_gem_or_legacy_mmap_object,
79 	.gem_uvm_ops = &tegra_drm_gem_uvm_ops,
80 
81 	.dumb_create = tegra_drm_dumb_create,
82 	.dumb_map_offset = tegra_drm_dumb_map_offset,
83 	.dumb_destroy = drm_gem_dumb_destroy,
84 
85 	.get_vblank_counter = tegra_drm_get_vblank_counter,
86 	.enable_vblank = tegra_drm_enable_vblank,
87 	.disable_vblank = tegra_drm_disable_vblank,
88 
89 	.name = DRIVER_NAME,
90 	.desc = DRIVER_DESC,
91 	.date = DRIVER_DATE,
92 	.major = DRIVER_MAJOR,
93 	.minor = DRIVER_MINOR,
94 	.patchlevel = DRIVER_PATCHLEVEL
95 };
96 
97 static const struct drm_bus tegra_drm_bus = {
98 	.bus_type = DRIVER_BUS_PLATFORM,
99 	.get_name = tegra_drm_get_name,
100 	.set_busid = tegra_drm_set_busid
101 };
102 
103 CFATTACH_DECL_NEW(tegra_drm, sizeof(struct tegra_drm_softc),
104 	tegra_drm_match, tegra_drm_attach, NULL, NULL);
105 
106 static int
107 tegra_drm_match(device_t parent, cfdata_t cf, void *aux)
108 {
109 	const char * compatible[] = { "nvidia,tegra124-host1x", NULL };
110 	struct fdt_attach_args * const faa = aux;
111 
112 	return of_match_compatible(faa->faa_phandle, compatible);
113 }
114 
115 static void
116 tegra_drm_attach(device_t parent, device_t self, void *aux)
117 {
118 	struct tegra_drm_softc * const sc = device_private(self);
119 	struct fdt_attach_args * const faa = aux;
120 	struct drm_driver * const driver = &tegra_drm_driver;
121 	prop_dictionary_t prop = device_properties(self);
122 	int error, node, hdmi_phandle, ddc_phandle;
123 	const char * const hdmi_compat[] = { "nvidia,tegra124-hdmi", NULL };
124 	const char * const dc_compat[] = { "nvidia,tegra124-dc", NULL };
125 	const char * const hdmi_supplies[] = {
126 		"hdmi-supply", "pll-supply", "vdd-supply"
127 	};
128 	struct fdtbus_regulator *reg;
129 	struct clk *pll_p_out0;
130 	u_int n, ndc;
131 
132 	sc->sc_dev = self;
133 	sc->sc_dmat = faa->faa_dmat;
134 	sc->sc_bst = faa->faa_bst;
135 	sc->sc_phandle = faa->faa_phandle;
136 
137 	aprint_naive("\n");
138 	aprint_normal("\n");
139 
140 	sc->sc_clk_host1x = fdtbus_clock_get_index(faa->faa_phandle, 0);
141 	if (sc->sc_clk_host1x == NULL) {
142 		aprint_error_dev(self, "couldn't get clock host1x\n");
143 		return;
144 	}
145 	sc->sc_rst_host1x = fdtbus_reset_get(faa->faa_phandle, "host1x");
146 	if (sc->sc_clk_host1x == NULL || sc->sc_rst_host1x == NULL) {
147 		aprint_error_dev(self, "couldn't get reset host1x\n");
148 		return;
149 	}
150 
151 	ndc = 0;
152 	hdmi_phandle = -1;
153 	for (node = OF_child(faa->faa_phandle); node; node = OF_peer(node)) {
154 		if (of_match_compatible(node, hdmi_compat)) {
155 			sc->sc_clk_hdmi = fdtbus_clock_get(node, "hdmi");
156 			sc->sc_clk_hdmi_parent = fdtbus_clock_get(node,
157 			    "parent");
158 			sc->sc_rst_hdmi = fdtbus_reset_get(node, "hdmi");
159 			hdmi_phandle = node;
160 		} else if (of_match_compatible(node, dc_compat) &&
161 			   ndc < __arraycount(sc->sc_clk_dc)) {
162 			sc->sc_clk_dc[ndc] = fdtbus_clock_get(node, "dc");
163 			sc->sc_clk_dc_parent[ndc] = fdtbus_clock_get(node,
164 			    "parent");
165 			sc->sc_rst_dc[ndc] = fdtbus_reset_get(node, "dc");
166 			++ndc;
167 		}
168 	}
169 	if (hdmi_phandle >= 0) {
170 		ddc_phandle = fdtbus_get_phandle(hdmi_phandle,
171 		    "nvidia,ddc-i2c-bus");
172 		if (ddc_phandle >= 0) {
173 			sc->sc_ddc = fdtbus_get_i2c_tag(ddc_phandle);
174 		}
175 
176 		sc->sc_pin_hpd = fdtbus_gpio_acquire(hdmi_phandle,
177 		    "nvidia,hpd-gpio", GPIO_PIN_INPUT);
178 
179 		for (n = 0; n < __arraycount(hdmi_supplies); n++) {
180 			const char *supply = hdmi_supplies[n];
181 			reg = fdtbus_regulator_acquire(hdmi_phandle, supply);
182 			if (reg == NULL) {
183 				aprint_error_dev(self, "couldn't acquire %s\n",
184 				    supply);
185 				continue;
186 			}
187 			if (fdtbus_regulator_enable(reg) != 0) {
188 				aprint_error_dev(self, "couldn't enable %s\n",
189 				    supply);
190 			}
191 			fdtbus_regulator_release(reg);
192 		}
193 	}
194 
195 	pll_p_out0 = clk_get("pll_p_out0");
196 	if (pll_p_out0 == NULL) {
197 		aprint_error_dev(self, "couldn't get clock pll_p_out0\n");
198 		return;
199 	}
200 	fdtbus_reset_assert(sc->sc_rst_host1x);
201 	error = clk_set_parent(sc->sc_clk_host1x, pll_p_out0);
202 	if (error) {
203 		aprint_error_dev(self, "couldn't set host1x clock parent: %d\n",
204 		    error);
205 		return;
206 	}
207 	error = clk_set_rate(sc->sc_clk_host1x, 408000000);
208 	if (error) {
209 		aprint_error_dev(self, "couldn't set host1x frequency: %d\n",
210 		    error);
211 		return;
212 	}
213 	error = clk_enable(sc->sc_clk_host1x);
214 	if (error) {
215 		aprint_error_dev(self, "couldn't enable clock host1x: %d\n",
216 		    error);
217 		return;
218 	}
219 	fdtbus_reset_deassert(sc->sc_rst_host1x);
220 	clk_put(pll_p_out0);
221 
222 	prop_dictionary_get_bool(prop, "force-dvi", &sc->sc_force_dvi);
223 
224 	driver->bus = &tegra_drm_bus;
225 
226 	sc->sc_ddev = drm_dev_alloc(driver, sc->sc_dev);
227 	if (sc->sc_ddev == NULL) {
228 		aprint_error_dev(self, "couldn't allocate DRM device\n");
229 		return;
230 	}
231 	sc->sc_ddev->dev_private = sc;
232 
233 	error = -drm_dev_register(sc->sc_ddev, 0);
234 	if (error) {
235 		drm_dev_unref(sc->sc_ddev);
236 		aprint_error_dev(self, "couldn't register DRM device: %d\n",
237 		    error);
238 		return;
239 	}
240 
241 	aprint_normal_dev(self, "initialized %s %d.%d.%d %s on minor %d\n",
242 	    driver->name, driver->major, driver->minor, driver->patchlevel,
243 	    driver->date, sc->sc_ddev->primary->index);
244 
245 	return;
246 }
247 
248 static const char *
249 tegra_drm_get_name(struct drm_device *ddev)
250 {
251 	return DRIVER_NAME;
252 }
253 
254 static int
255 tegra_drm_set_busid(struct drm_device *ddev, struct drm_master *master)
256 {
257 	const char *id = "platform:tegra:0";
258 
259 	master->unique = kzalloc(strlen(id) + 1, GFP_KERNEL);
260 	if (master->unique == NULL)
261 		return -ENOMEM;
262 	strcpy(master->unique, id);
263 	master->unique_len = strlen(master->unique);
264 
265 	return 0;
266 }
267 
268 
269 static int
270 tegra_drm_load(struct drm_device *ddev, unsigned long flags)
271 {
272 	char *devname;
273 	int error;
274 
275 	devname = kzalloc(strlen(DRIVER_NAME) + 1, GFP_KERNEL);
276 	if (devname == NULL) {
277 		return -ENOMEM;
278 	}
279 	strcpy(devname, DRIVER_NAME);
280 	ddev->devname = devname;
281 
282 	error = tegra_drm_mode_init(ddev);
283 	if (error)
284 		goto drmerr;
285 
286 	error = tegra_drm_fb_init(ddev);
287 	if (error)
288 		goto drmerr;
289 
290 	return 0;
291 
292 drmerr:
293 	drm_mode_config_cleanup(ddev);
294 
295 	return error;
296 }
297 
298 static int
299 tegra_drm_unload(struct drm_device *ddev)
300 {
301 	drm_mode_config_cleanup(ddev);
302 
303 	return 0;
304 }
305 
306 static int
307 tegra_drm_dumb_create(struct drm_file *file_priv, struct drm_device *ddev,
308     struct drm_mode_create_dumb *args)
309 {
310 	struct tegra_gem_object *obj;
311 	uint32_t handle;
312 	int error;
313 
314 	args->pitch = args->width * ((args->bpp + 7) / 8);
315 	args->size = args->pitch * args->height;
316 	args->size = roundup(args->size, PAGE_SIZE);
317 	args->handle = 0;
318 
319 	obj = tegra_drm_obj_alloc(ddev, args->size);
320 	if (obj == NULL)
321 		return -ENOMEM;
322 
323 	error = drm_gem_handle_create(file_priv, &obj->base, &handle);
324 	drm_gem_object_unreference_unlocked(&obj->base);
325 	if (error) {
326 		tegra_drm_obj_free(obj);
327 		return error;
328 	}
329 
330 	args->handle = handle;
331 
332 	return 0;
333 }
334 
335 static int
336 tegra_drm_dumb_map_offset(struct drm_file *file_priv,
337     struct drm_device *ddev, uint32_t handle, uint64_t *offset)
338 {
339 	struct drm_gem_object *gem_obj;
340 	struct tegra_gem_object *obj;
341 	int error;
342 
343 	gem_obj = drm_gem_object_lookup(ddev, file_priv, handle);
344 	if (gem_obj == NULL)
345 		return -ENOENT;
346 
347 	obj = to_tegra_gem_obj(gem_obj);
348 
349 	if (drm_vma_node_has_offset(&obj->base.vma_node) == 0) {
350 		error = drm_gem_create_mmap_offset(&obj->base);
351 		if (error)
352 			goto done;
353 	} else {
354 		error = 0;
355 	}
356 
357 	*offset = drm_vma_node_offset_addr(&obj->base.vma_node);
358 
359 done:
360 	drm_gem_object_unreference_unlocked(&obj->base);
361 
362 	return error;
363 }
364