Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0
2 : : /*
3 : : * Copyright 2018 Noralf Trønnes
4 : : */
5 : :
6 : : #include <linux/list.h>
7 : : #include <linux/module.h>
8 : : #include <linux/mutex.h>
9 : : #include <linux/seq_file.h>
10 : : #include <linux/slab.h>
11 : :
12 : : #include <drm/drm_client.h>
13 : : #include <drm/drm_debugfs.h>
14 : : #include <drm/drm_device.h>
15 : : #include <drm/drm_drv.h>
16 : : #include <drm/drm_file.h>
17 : : #include <drm/drm_fourcc.h>
18 : : #include <drm/drm_framebuffer.h>
19 : : #include <drm/drm_gem.h>
20 : : #include <drm/drm_mode.h>
21 : : #include <drm/drm_print.h>
22 : :
23 : : #include "drm_crtc_internal.h"
24 : : #include "drm_internal.h"
25 : :
26 : : /**
27 : : * DOC: overview
28 : : *
29 : : * This library provides support for clients running in the kernel like fbdev and bootsplash.
30 : : *
31 : : * GEM drivers which provide a GEM based dumb buffer with a virtual address are supported.
32 : : */
33 : :
34 : : static int drm_client_open(struct drm_client_dev *client)
35 : : {
36 : : struct drm_device *dev = client->dev;
37 : : struct drm_file *file;
38 : :
39 : : file = drm_file_alloc(dev->primary);
40 : : if (IS_ERR(file))
41 : : return PTR_ERR(file);
42 : :
43 : : mutex_lock(&dev->filelist_mutex);
44 : : list_add(&file->lhead, &dev->filelist_internal);
45 : : mutex_unlock(&dev->filelist_mutex);
46 : :
47 : : client->file = file;
48 : :
49 : : return 0;
50 : : }
51 : :
52 : : static void drm_client_close(struct drm_client_dev *client)
53 : : {
54 : : struct drm_device *dev = client->dev;
55 : :
56 : : mutex_lock(&dev->filelist_mutex);
57 : : list_del(&client->file->lhead);
58 : : mutex_unlock(&dev->filelist_mutex);
59 : :
60 : : drm_file_free(client->file);
61 : : }
62 : :
63 : : /**
64 : : * drm_client_init - Initialise a DRM client
65 : : * @dev: DRM device
66 : : * @client: DRM client
67 : : * @name: Client name
68 : : * @funcs: DRM client functions (optional)
69 : : *
70 : : * This initialises the client and opens a &drm_file.
71 : : * Use drm_client_register() to complete the process.
72 : : * The caller needs to hold a reference on @dev before calling this function.
73 : : * The client is freed when the &drm_device is unregistered. See drm_client_release().
74 : : *
75 : : * Returns:
76 : : * Zero on success or negative error code on failure.
77 : : */
78 : 0 : int drm_client_init(struct drm_device *dev, struct drm_client_dev *client,
79 : : const char *name, const struct drm_client_funcs *funcs)
80 : : {
81 : 0 : int ret;
82 : :
83 [ # # # # ]: 0 : if (!drm_core_check_feature(dev, DRIVER_MODESET) || !dev->driver->dumb_create)
84 : : return -EOPNOTSUPP;
85 : :
86 [ # # # # ]: 0 : if (funcs && !try_module_get(funcs->owner))
87 : : return -ENODEV;
88 : :
89 : 0 : client->dev = dev;
90 : 0 : client->name = name;
91 : 0 : client->funcs = funcs;
92 : :
93 : 0 : ret = drm_client_modeset_create(client);
94 [ # # ]: 0 : if (ret)
95 : 0 : goto err_put_module;
96 : :
97 : 0 : ret = drm_client_open(client);
98 [ # # ]: 0 : if (ret)
99 : 0 : goto err_free;
100 : :
101 : 0 : drm_dev_get(dev);
102 : :
103 : 0 : return 0;
104 : :
105 : : err_free:
106 : 0 : drm_client_modeset_free(client);
107 : 0 : err_put_module:
108 [ # # ]: 0 : if (funcs)
109 : 0 : module_put(funcs->owner);
110 : :
111 : : return ret;
112 : : }
113 : : EXPORT_SYMBOL(drm_client_init);
114 : :
115 : : /**
116 : : * drm_client_register - Register client
117 : : * @client: DRM client
118 : : *
119 : : * Add the client to the &drm_device client list to activate its callbacks.
120 : : * @client must be initialized by a call to drm_client_init(). After
121 : : * drm_client_register() it is no longer permissible to call drm_client_release()
122 : : * directly (outside the unregister callback), instead cleanup will happen
123 : : * automatically on driver unload.
124 : : */
125 : 0 : void drm_client_register(struct drm_client_dev *client)
126 : : {
127 : 0 : struct drm_device *dev = client->dev;
128 : :
129 : 0 : mutex_lock(&dev->clientlist_mutex);
130 : 0 : list_add(&client->list, &dev->clientlist);
131 : 0 : mutex_unlock(&dev->clientlist_mutex);
132 : 0 : }
133 : : EXPORT_SYMBOL(drm_client_register);
134 : :
135 : : /**
136 : : * drm_client_release - Release DRM client resources
137 : : * @client: DRM client
138 : : *
139 : : * Releases resources by closing the &drm_file that was opened by drm_client_init().
140 : : * It is called automatically if the &drm_client_funcs.unregister callback is _not_ set.
141 : : *
142 : : * This function should only be called from the unregister callback. An exception
143 : : * is fbdev which cannot free the buffer if userspace has open file descriptors.
144 : : *
145 : : * Note:
146 : : * Clients cannot initiate a release by themselves. This is done to keep the code simple.
147 : : * The driver has to be unloaded before the client can be unloaded.
148 : : */
149 : 0 : void drm_client_release(struct drm_client_dev *client)
150 : : {
151 : 0 : struct drm_device *dev = client->dev;
152 : :
153 : 0 : drm_dbg_kms(dev, "%s\n", client->name);
154 : :
155 : 0 : drm_client_modeset_free(client);
156 : 0 : drm_client_close(client);
157 : 0 : drm_dev_put(dev);
158 [ # # ]: 0 : if (client->funcs)
159 : 0 : module_put(client->funcs->owner);
160 : 0 : }
161 : : EXPORT_SYMBOL(drm_client_release);
162 : :
163 : 0 : void drm_client_dev_unregister(struct drm_device *dev)
164 : : {
165 : 0 : struct drm_client_dev *client, *tmp;
166 : :
167 [ # # ]: 0 : if (!drm_core_check_feature(dev, DRIVER_MODESET))
168 : : return;
169 : :
170 : 0 : mutex_lock(&dev->clientlist_mutex);
171 [ # # ]: 0 : list_for_each_entry_safe(client, tmp, &dev->clientlist, list) {
172 [ # # ]: 0 : list_del(&client->list);
173 [ # # # # ]: 0 : if (client->funcs && client->funcs->unregister) {
174 : 0 : client->funcs->unregister(client);
175 : : } else {
176 : 0 : drm_client_release(client);
177 : 0 : kfree(client);
178 : : }
179 : : }
180 : 0 : mutex_unlock(&dev->clientlist_mutex);
181 : : }
182 : :
183 : : /**
184 : : * drm_client_dev_hotplug - Send hotplug event to clients
185 : : * @dev: DRM device
186 : : *
187 : : * This function calls the &drm_client_funcs.hotplug callback on the attached clients.
188 : : *
189 : : * drm_kms_helper_hotplug_event() calls this function, so drivers that use it
190 : : * don't need to call this function themselves.
191 : : */
192 : 0 : void drm_client_dev_hotplug(struct drm_device *dev)
193 : : {
194 : 0 : struct drm_client_dev *client;
195 : 0 : int ret;
196 : :
197 [ # # ]: 0 : if (!drm_core_check_feature(dev, DRIVER_MODESET))
198 : : return;
199 : :
200 : 0 : mutex_lock(&dev->clientlist_mutex);
201 [ # # ]: 0 : list_for_each_entry(client, &dev->clientlist, list) {
202 [ # # # # ]: 0 : if (!client->funcs || !client->funcs->hotplug)
203 : 0 : continue;
204 : :
205 : 0 : ret = client->funcs->hotplug(client);
206 : 0 : drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret);
207 : : }
208 : 0 : mutex_unlock(&dev->clientlist_mutex);
209 : : }
210 : : EXPORT_SYMBOL(drm_client_dev_hotplug);
211 : :
212 : 0 : void drm_client_dev_restore(struct drm_device *dev)
213 : : {
214 : 0 : struct drm_client_dev *client;
215 : 0 : int ret;
216 : :
217 [ # # ]: 0 : if (!drm_core_check_feature(dev, DRIVER_MODESET))
218 : : return;
219 : :
220 : 0 : mutex_lock(&dev->clientlist_mutex);
221 [ # # ]: 0 : list_for_each_entry(client, &dev->clientlist, list) {
222 [ # # # # ]: 0 : if (!client->funcs || !client->funcs->restore)
223 : 0 : continue;
224 : :
225 : 0 : ret = client->funcs->restore(client);
226 : 0 : drm_dbg_kms(dev, "%s: ret=%d\n", client->name, ret);
227 [ # # ]: 0 : if (!ret) /* The first one to return zero gets the privilege to restore */
228 : : break;
229 : : }
230 : 0 : mutex_unlock(&dev->clientlist_mutex);
231 : : }
232 : :
233 : 0 : static void drm_client_buffer_delete(struct drm_client_buffer *buffer)
234 : : {
235 : 0 : struct drm_device *dev = buffer->client->dev;
236 : :
237 : 0 : drm_gem_vunmap(buffer->gem, buffer->vaddr);
238 : :
239 [ # # ]: 0 : if (buffer->gem)
240 : 0 : drm_gem_object_put_unlocked(buffer->gem);
241 : :
242 [ # # ]: 0 : if (buffer->handle)
243 : 0 : drm_mode_destroy_dumb(dev, buffer->handle, buffer->client->file);
244 : :
245 : 0 : kfree(buffer);
246 : 0 : }
247 : :
248 : : static struct drm_client_buffer *
249 : 0 : drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
250 : : {
251 : 0 : const struct drm_format_info *info = drm_format_info(format);
252 : 0 : struct drm_mode_create_dumb dumb_args = { };
253 : 0 : struct drm_device *dev = client->dev;
254 : 0 : struct drm_client_buffer *buffer;
255 : 0 : struct drm_gem_object *obj;
256 : 0 : int ret;
257 : :
258 : 0 : buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
259 [ # # ]: 0 : if (!buffer)
260 : : return ERR_PTR(-ENOMEM);
261 : :
262 : 0 : buffer->client = client;
263 : :
264 : 0 : dumb_args.width = width;
265 : 0 : dumb_args.height = height;
266 : 0 : dumb_args.bpp = info->cpp[0] * 8;
267 : 0 : ret = drm_mode_create_dumb(dev, &dumb_args, client->file);
268 [ # # ]: 0 : if (ret)
269 : 0 : goto err_delete;
270 : :
271 : 0 : buffer->handle = dumb_args.handle;
272 : 0 : buffer->pitch = dumb_args.pitch;
273 : :
274 : 0 : obj = drm_gem_object_lookup(client->file, dumb_args.handle);
275 [ # # ]: 0 : if (!obj) {
276 : 0 : ret = -ENOENT;
277 : 0 : goto err_delete;
278 : : }
279 : :
280 : 0 : buffer->gem = obj;
281 : :
282 : 0 : return buffer;
283 : :
284 : 0 : err_delete:
285 : 0 : drm_client_buffer_delete(buffer);
286 : :
287 : 0 : return ERR_PTR(ret);
288 : : }
289 : :
290 : : /**
291 : : * drm_client_buffer_vmap - Map DRM client buffer into address space
292 : : * @buffer: DRM client buffer
293 : : *
294 : : * This function maps a client buffer into kernel address space. If the
295 : : * buffer is already mapped, it returns the mapping's address.
296 : : *
297 : : * Client buffer mappings are not ref'counted. Each call to
298 : : * drm_client_buffer_vmap() should be followed by a call to
299 : : * drm_client_buffer_vunmap(); or the client buffer should be mapped
300 : : * throughout its lifetime.
301 : : *
302 : : * Returns:
303 : : * The mapped memory's address
304 : : */
305 : 0 : void *drm_client_buffer_vmap(struct drm_client_buffer *buffer)
306 : : {
307 : 0 : void *vaddr;
308 : :
309 [ # # ]: 0 : if (buffer->vaddr)
310 : : return buffer->vaddr;
311 : :
312 : : /*
313 : : * FIXME: The dependency on GEM here isn't required, we could
314 : : * convert the driver handle to a dma-buf instead and use the
315 : : * backend-agnostic dma-buf vmap support instead. This would
316 : : * require that the handle2fd prime ioctl is reworked to pull the
317 : : * fd_install step out of the driver backend hooks, to make that
318 : : * final step optional for internal users.
319 : : */
320 : 0 : vaddr = drm_gem_vmap(buffer->gem);
321 [ # # ]: 0 : if (IS_ERR(vaddr))
322 : : return vaddr;
323 : :
324 : 0 : buffer->vaddr = vaddr;
325 : :
326 : 0 : return vaddr;
327 : : }
328 : : EXPORT_SYMBOL(drm_client_buffer_vmap);
329 : :
330 : : /**
331 : : * drm_client_buffer_vunmap - Unmap DRM client buffer
332 : : * @buffer: DRM client buffer
333 : : *
334 : : * This function removes a client buffer's memory mapping. Calling this
335 : : * function is only required by clients that manage their buffer mappings
336 : : * by themselves.
337 : : */
338 : 0 : void drm_client_buffer_vunmap(struct drm_client_buffer *buffer)
339 : : {
340 : 0 : drm_gem_vunmap(buffer->gem, buffer->vaddr);
341 : 0 : buffer->vaddr = NULL;
342 : 0 : }
343 : : EXPORT_SYMBOL(drm_client_buffer_vunmap);
344 : :
345 : : static void drm_client_buffer_rmfb(struct drm_client_buffer *buffer)
346 : : {
347 : : int ret;
348 : :
349 : : if (!buffer->fb)
350 : : return;
351 : :
352 : : ret = drm_mode_rmfb(buffer->client->dev, buffer->fb->base.id, buffer->client->file);
353 : : if (ret)
354 : : drm_err(buffer->client->dev,
355 : : "Error removing FB:%u (%d)\n", buffer->fb->base.id, ret);
356 : :
357 : : buffer->fb = NULL;
358 : : }
359 : :
360 : 0 : static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,
361 : : u32 width, u32 height, u32 format)
362 : : {
363 : 0 : struct drm_client_dev *client = buffer->client;
364 : 0 : struct drm_mode_fb_cmd fb_req = { };
365 : 0 : const struct drm_format_info *info;
366 : 0 : int ret;
367 : :
368 : 0 : info = drm_format_info(format);
369 : 0 : fb_req.bpp = info->cpp[0] * 8;
370 : 0 : fb_req.depth = info->depth;
371 : 0 : fb_req.width = width;
372 : 0 : fb_req.height = height;
373 : 0 : fb_req.handle = buffer->handle;
374 : 0 : fb_req.pitch = buffer->pitch;
375 : :
376 : 0 : ret = drm_mode_addfb(client->dev, &fb_req, client->file);
377 [ # # ]: 0 : if (ret)
378 : : return ret;
379 : :
380 : 0 : buffer->fb = drm_framebuffer_lookup(client->dev, buffer->client->file, fb_req.fb_id);
381 [ # # # # ]: 0 : if (WARN_ON(!buffer->fb))
382 : : return -ENOENT;
383 : :
384 : : /* drop the reference we picked up in framebuffer lookup */
385 : 0 : drm_framebuffer_put(buffer->fb);
386 : :
387 : 0 : strscpy(buffer->fb->comm, client->name, TASK_COMM_LEN);
388 : :
389 : 0 : return 0;
390 : : }
391 : :
392 : : /**
393 : : * drm_client_framebuffer_create - Create a client framebuffer
394 : : * @client: DRM client
395 : : * @width: Framebuffer width
396 : : * @height: Framebuffer height
397 : : * @format: Buffer format
398 : : *
399 : : * This function creates a &drm_client_buffer which consists of a
400 : : * &drm_framebuffer backed by a dumb buffer.
401 : : * Call drm_client_framebuffer_delete() to free the buffer.
402 : : *
403 : : * Returns:
404 : : * Pointer to a client buffer or an error pointer on failure.
405 : : */
406 : : struct drm_client_buffer *
407 : 0 : drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
408 : : {
409 : 0 : struct drm_client_buffer *buffer;
410 : 0 : int ret;
411 : :
412 : 0 : buffer = drm_client_buffer_create(client, width, height, format);
413 [ # # ]: 0 : if (IS_ERR(buffer))
414 : : return buffer;
415 : :
416 : 0 : ret = drm_client_buffer_addfb(buffer, width, height, format);
417 [ # # ]: 0 : if (ret) {
418 : 0 : drm_client_buffer_delete(buffer);
419 : 0 : return ERR_PTR(ret);
420 : : }
421 : :
422 : : return buffer;
423 : : }
424 : : EXPORT_SYMBOL(drm_client_framebuffer_create);
425 : :
426 : : /**
427 : : * drm_client_framebuffer_delete - Delete a client framebuffer
428 : : * @buffer: DRM client buffer (can be NULL)
429 : : */
430 : 0 : void drm_client_framebuffer_delete(struct drm_client_buffer *buffer)
431 : : {
432 [ # # ]: 0 : if (!buffer)
433 : : return;
434 : :
435 : 0 : drm_client_buffer_rmfb(buffer);
436 : 0 : drm_client_buffer_delete(buffer);
437 : : }
438 : : EXPORT_SYMBOL(drm_client_framebuffer_delete);
439 : :
440 : : #ifdef CONFIG_DEBUG_FS
441 : 0 : static int drm_client_debugfs_internal_clients(struct seq_file *m, void *data)
442 : : {
443 : 0 : struct drm_info_node *node = m->private;
444 : 0 : struct drm_device *dev = node->minor->dev;
445 : 0 : struct drm_printer p = drm_seq_file_printer(m);
446 : 0 : struct drm_client_dev *client;
447 : :
448 : 0 : mutex_lock(&dev->clientlist_mutex);
449 [ # # ]: 0 : list_for_each_entry(client, &dev->clientlist, list)
450 : 0 : drm_printf(&p, "%s\n", client->name);
451 : 0 : mutex_unlock(&dev->clientlist_mutex);
452 : :
453 : 0 : return 0;
454 : : }
455 : :
456 : : static const struct drm_info_list drm_client_debugfs_list[] = {
457 : : { "internal_clients", drm_client_debugfs_internal_clients, 0 },
458 : : };
459 : :
460 : 0 : int drm_client_debugfs_init(struct drm_minor *minor)
461 : : {
462 : 0 : return drm_debugfs_create_files(drm_client_debugfs_list,
463 : : ARRAY_SIZE(drm_client_debugfs_list),
464 : : minor->debugfs_root, minor);
465 : : }
466 : : #endif
|