]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blame - drivers/gpu/host1x/drm/drm.c
gpu: host1x: drm: Add memory manager and fb
[mirror_ubuntu-bionic-kernel.git] / drivers / gpu / host1x / drm / drm.c
CommitLineData
d8f4a9ed
TR
1/*
2 * Copyright (C) 2012 Avionic Design GmbH
3 * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 */
9
10#include <linux/module.h>
11#include <linux/of_address.h>
12#include <linux/of_platform.h>
13
d8f4a9ed
TR
14#include <linux/dma-mapping.h>
15#include <asm/dma-iommu.h>
16
692e6d7b 17#include "host1x_client.h"
de2ba664 18#include "dev.h"
d8f4a9ed 19#include "drm.h"
de2ba664
AM
20#include "gem.h"
21#include "syncpt.h"
d8f4a9ed
TR
22
23#define DRIVER_NAME "tegra"
24#define DRIVER_DESC "NVIDIA Tegra graphics"
25#define DRIVER_DATE "20120330"
26#define DRIVER_MAJOR 0
27#define DRIVER_MINOR 0
28#define DRIVER_PATCHLEVEL 0
29
692e6d7b
TB
30struct host1x_drm_client {
31 struct host1x_client *client;
32 struct device_node *np;
33 struct list_head list;
34};
35
36static int host1x_add_drm_client(struct host1x_drm *host1x,
37 struct device_node *np)
38{
39 struct host1x_drm_client *client;
40
41 client = kzalloc(sizeof(*client), GFP_KERNEL);
42 if (!client)
43 return -ENOMEM;
44
45 INIT_LIST_HEAD(&client->list);
46 client->np = of_node_get(np);
47
48 list_add_tail(&client->list, &host1x->drm_clients);
49
50 return 0;
51}
52
53static int host1x_activate_drm_client(struct host1x_drm *host1x,
54 struct host1x_drm_client *drm,
55 struct host1x_client *client)
56{
57 mutex_lock(&host1x->drm_clients_lock);
58 list_del_init(&drm->list);
59 list_add_tail(&drm->list, &host1x->drm_active);
60 drm->client = client;
61 mutex_unlock(&host1x->drm_clients_lock);
62
63 return 0;
64}
65
66static int host1x_remove_drm_client(struct host1x_drm *host1x,
67 struct host1x_drm_client *client)
68{
69 mutex_lock(&host1x->drm_clients_lock);
70 list_del_init(&client->list);
71 mutex_unlock(&host1x->drm_clients_lock);
72
73 of_node_put(client->np);
74 kfree(client);
75
76 return 0;
77}
78
79static int host1x_parse_dt(struct host1x_drm *host1x)
80{
81 static const char * const compat[] = {
82 "nvidia,tegra20-dc",
83 "nvidia,tegra20-hdmi",
84 "nvidia,tegra30-dc",
85 "nvidia,tegra30-hdmi",
86 };
87 unsigned int i;
88 int err;
89
90 for (i = 0; i < ARRAY_SIZE(compat); i++) {
91 struct device_node *np;
92
93 for_each_child_of_node(host1x->dev->of_node, np) {
94 if (of_device_is_compatible(np, compat[i]) &&
95 of_device_is_available(np)) {
96 err = host1x_add_drm_client(host1x, np);
97 if (err < 0)
98 return err;
99 }
100 }
101 }
102
103 return 0;
104}
105
106int host1x_drm_alloc(struct platform_device *pdev)
107{
108 struct host1x_drm *host1x;
109 int err;
110
111 host1x = devm_kzalloc(&pdev->dev, sizeof(*host1x), GFP_KERNEL);
112 if (!host1x)
113 return -ENOMEM;
114
115 mutex_init(&host1x->drm_clients_lock);
116 INIT_LIST_HEAD(&host1x->drm_clients);
117 INIT_LIST_HEAD(&host1x->drm_active);
118 mutex_init(&host1x->clients_lock);
119 INIT_LIST_HEAD(&host1x->clients);
120 host1x->dev = &pdev->dev;
121
122 err = host1x_parse_dt(host1x);
123 if (err < 0) {
124 dev_err(&pdev->dev, "failed to parse DT: %d\n", err);
125 return err;
126 }
127
128 host1x_set_drm_data(&pdev->dev, host1x);
129
130 return 0;
131}
132
133int host1x_drm_init(struct host1x_drm *host1x, struct drm_device *drm)
134{
135 struct host1x_client *client;
136
137 mutex_lock(&host1x->clients_lock);
138
139 list_for_each_entry(client, &host1x->clients, list) {
140 if (client->ops && client->ops->drm_init) {
141 int err = client->ops->drm_init(client, drm);
142 if (err < 0) {
143 dev_err(host1x->dev,
144 "DRM setup failed for %s: %d\n",
145 dev_name(client->dev), err);
146 return err;
147 }
148 }
149 }
150
151 mutex_unlock(&host1x->clients_lock);
152
153 return 0;
154}
155
156int host1x_drm_exit(struct host1x_drm *host1x)
157{
158 struct platform_device *pdev = to_platform_device(host1x->dev);
159 struct host1x_client *client;
160
161 if (!host1x->drm)
162 return 0;
163
164 mutex_lock(&host1x->clients_lock);
165
166 list_for_each_entry_reverse(client, &host1x->clients, list) {
167 if (client->ops && client->ops->drm_exit) {
168 int err = client->ops->drm_exit(client);
169 if (err < 0) {
170 dev_err(host1x->dev,
171 "DRM cleanup failed for %s: %d\n",
172 dev_name(client->dev), err);
173 return err;
174 }
175 }
176 }
177
178 mutex_unlock(&host1x->clients_lock);
179
180 drm_platform_exit(&tegra_drm_driver, pdev);
181 host1x->drm = NULL;
182
183 return 0;
184}
185
186int host1x_register_client(struct host1x_drm *host1x,
187 struct host1x_client *client)
188{
189 struct host1x_drm_client *drm, *tmp;
190 int err;
191
192 mutex_lock(&host1x->clients_lock);
193 list_add_tail(&client->list, &host1x->clients);
194 mutex_unlock(&host1x->clients_lock);
195
196 list_for_each_entry_safe(drm, tmp, &host1x->drm_clients, list)
197 if (drm->np == client->dev->of_node)
198 host1x_activate_drm_client(host1x, drm, client);
199
200 if (list_empty(&host1x->drm_clients)) {
201 struct platform_device *pdev = to_platform_device(host1x->dev);
202
203 err = drm_platform_init(&tegra_drm_driver, pdev);
204 if (err < 0) {
205 dev_err(host1x->dev, "drm_platform_init(): %d\n", err);
206 return err;
207 }
208 }
209
210 return 0;
211}
212
213int host1x_unregister_client(struct host1x_drm *host1x,
214 struct host1x_client *client)
215{
216 struct host1x_drm_client *drm, *tmp;
217 int err;
218
219 list_for_each_entry_safe(drm, tmp, &host1x->drm_active, list) {
220 if (drm->client == client) {
221 err = host1x_drm_exit(host1x);
222 if (err < 0) {
223 dev_err(host1x->dev, "host1x_drm_exit(): %d\n",
224 err);
225 return err;
226 }
227
228 host1x_remove_drm_client(host1x, drm);
229 break;
230 }
231 }
232
233 mutex_lock(&host1x->clients_lock);
234 list_del_init(&client->list);
235 mutex_unlock(&host1x->clients_lock);
236
237 return 0;
238}
239
d8f4a9ed
TR
240static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
241{
c89c0ea6 242 struct host1x_drm *host1x;
d8f4a9ed
TR
243 int err;
244
692e6d7b 245 host1x = host1x_get_drm_data(drm->dev);
d8f4a9ed
TR
246 drm->dev_private = host1x;
247 host1x->drm = drm;
248
249 drm_mode_config_init(drm);
250
251 err = host1x_drm_init(host1x, drm);
252 if (err < 0)
253 return err;
254
6e5ff998
TR
255 err = drm_vblank_init(drm, drm->mode_config.num_crtc);
256 if (err < 0)
257 return err;
258
d8f4a9ed
TR
259 err = tegra_drm_fb_init(drm);
260 if (err < 0)
261 return err;
262
263 drm_kms_helper_poll_init(drm);
264
265 return 0;
266}
267
268static int tegra_drm_unload(struct drm_device *drm)
269{
270 drm_kms_helper_poll_fini(drm);
271 tegra_drm_fb_exit(drm);
272
273 drm_mode_config_cleanup(drm);
274
275 return 0;
276}
277
278static int tegra_drm_open(struct drm_device *drm, struct drm_file *filp)
279{
280 return 0;
281}
282
283static void tegra_drm_lastclose(struct drm_device *drm)
284{
c89c0ea6 285 struct host1x_drm *host1x = drm->dev_private;
d8f4a9ed 286
de2ba664 287 tegra_fbdev_restore_mode(host1x->fbdev);
d8f4a9ed
TR
288}
289
290static struct drm_ioctl_desc tegra_drm_ioctls[] = {
291};
292
293static const struct file_operations tegra_drm_fops = {
294 .owner = THIS_MODULE,
295 .open = drm_open,
296 .release = drm_release,
297 .unlocked_ioctl = drm_ioctl,
de2ba664 298 .mmap = tegra_drm_mmap,
d8f4a9ed
TR
299 .poll = drm_poll,
300 .fasync = drm_fasync,
301 .read = drm_read,
302#ifdef CONFIG_COMPAT
303 .compat_ioctl = drm_compat_ioctl,
304#endif
305 .llseek = noop_llseek,
306};
307
6e5ff998
TR
308static struct drm_crtc *tegra_crtc_from_pipe(struct drm_device *drm, int pipe)
309{
310 struct drm_crtc *crtc;
311
312 list_for_each_entry(crtc, &drm->mode_config.crtc_list, head) {
313 struct tegra_dc *dc = to_tegra_dc(crtc);
314
315 if (dc->pipe == pipe)
316 return crtc;
317 }
318
319 return NULL;
320}
321
322static u32 tegra_drm_get_vblank_counter(struct drm_device *dev, int crtc)
323{
324 /* TODO: implement real hardware counter using syncpoints */
325 return drm_vblank_count(dev, crtc);
326}
327
328static int tegra_drm_enable_vblank(struct drm_device *drm, int pipe)
329{
330 struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe);
331 struct tegra_dc *dc = to_tegra_dc(crtc);
332
333 if (!crtc)
334 return -ENODEV;
335
336 tegra_dc_enable_vblank(dc);
337
338 return 0;
339}
340
341static void tegra_drm_disable_vblank(struct drm_device *drm, int pipe)
342{
343 struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe);
344 struct tegra_dc *dc = to_tegra_dc(crtc);
345
346 if (crtc)
347 tegra_dc_disable_vblank(dc);
348}
349
3c03c46a
TR
350static void tegra_drm_preclose(struct drm_device *drm, struct drm_file *file)
351{
352 struct drm_crtc *crtc;
353
354 list_for_each_entry(crtc, &drm->mode_config.crtc_list, head)
355 tegra_dc_cancel_page_flip(crtc, file);
356}
357
e450fcc6
TR
358#ifdef CONFIG_DEBUG_FS
359static int tegra_debugfs_framebuffers(struct seq_file *s, void *data)
360{
361 struct drm_info_node *node = (struct drm_info_node *)s->private;
362 struct drm_device *drm = node->minor->dev;
363 struct drm_framebuffer *fb;
364
365 mutex_lock(&drm->mode_config.fb_lock);
366
367 list_for_each_entry(fb, &drm->mode_config.fb_list, head) {
368 seq_printf(s, "%3d: user size: %d x %d, depth %d, %d bpp, refcount %d\n",
369 fb->base.id, fb->width, fb->height, fb->depth,
370 fb->bits_per_pixel,
371 atomic_read(&fb->refcount.refcount));
372 }
373
374 mutex_unlock(&drm->mode_config.fb_lock);
375
376 return 0;
377}
378
379static struct drm_info_list tegra_debugfs_list[] = {
380 { "framebuffers", tegra_debugfs_framebuffers, 0 },
381};
382
383static int tegra_debugfs_init(struct drm_minor *minor)
384{
385 return drm_debugfs_create_files(tegra_debugfs_list,
386 ARRAY_SIZE(tegra_debugfs_list),
387 minor->debugfs_root, minor);
388}
389
390static void tegra_debugfs_cleanup(struct drm_minor *minor)
391{
392 drm_debugfs_remove_files(tegra_debugfs_list,
393 ARRAY_SIZE(tegra_debugfs_list), minor);
394}
395#endif
396
d8f4a9ed
TR
397struct drm_driver tegra_drm_driver = {
398 .driver_features = DRIVER_BUS_PLATFORM | DRIVER_MODESET | DRIVER_GEM,
399 .load = tegra_drm_load,
400 .unload = tegra_drm_unload,
401 .open = tegra_drm_open,
3c03c46a 402 .preclose = tegra_drm_preclose,
d8f4a9ed
TR
403 .lastclose = tegra_drm_lastclose,
404
6e5ff998
TR
405 .get_vblank_counter = tegra_drm_get_vblank_counter,
406 .enable_vblank = tegra_drm_enable_vblank,
407 .disable_vblank = tegra_drm_disable_vblank,
408
e450fcc6
TR
409#if defined(CONFIG_DEBUG_FS)
410 .debugfs_init = tegra_debugfs_init,
411 .debugfs_cleanup = tegra_debugfs_cleanup,
412#endif
413
de2ba664
AM
414 .gem_free_object = tegra_bo_free_object,
415 .gem_vm_ops = &tegra_bo_vm_ops,
416 .dumb_create = tegra_bo_dumb_create,
417 .dumb_map_offset = tegra_bo_dumb_map_offset,
418 .dumb_destroy = tegra_bo_dumb_destroy,
d8f4a9ed
TR
419
420 .ioctls = tegra_drm_ioctls,
421 .num_ioctls = ARRAY_SIZE(tegra_drm_ioctls),
422 .fops = &tegra_drm_fops,
423
424 .name = DRIVER_NAME,
425 .desc = DRIVER_DESC,
426 .date = DRIVER_DATE,
427 .major = DRIVER_MAJOR,
428 .minor = DRIVER_MINOR,
429 .patchlevel = DRIVER_PATCHLEVEL,
430};