Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/gpu/drm/drm_client.c
26428 views
1
// SPDX-License-Identifier: GPL-2.0 or MIT
2
/*
3
* Copyright 2018 Noralf Trønnes
4
*/
5
6
#include <linux/export.h>
7
#include <linux/iosys-map.h>
8
#include <linux/list.h>
9
#include <linux/mutex.h>
10
#include <linux/seq_file.h>
11
#include <linux/slab.h>
12
13
#include <drm/drm_client.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
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
int ret;
82
83
if (!drm_core_check_feature(dev, DRIVER_MODESET) || !dev->driver->dumb_create)
84
return -EOPNOTSUPP;
85
86
client->dev = dev;
87
client->name = name;
88
client->funcs = funcs;
89
90
ret = drm_client_modeset_create(client);
91
if (ret)
92
return ret;
93
94
ret = drm_client_open(client);
95
if (ret)
96
goto err_free;
97
98
drm_dev_get(dev);
99
100
return 0;
101
102
err_free:
103
drm_client_modeset_free(client);
104
return ret;
105
}
106
EXPORT_SYMBOL(drm_client_init);
107
108
/**
109
* drm_client_register - Register client
110
* @client: DRM client
111
*
112
* Add the client to the &drm_device client list to activate its callbacks.
113
* @client must be initialized by a call to drm_client_init(). After
114
* drm_client_register() it is no longer permissible to call drm_client_release()
115
* directly (outside the unregister callback), instead cleanup will happen
116
* automatically on driver unload.
117
*
118
* Registering a client generates a hotplug event that allows the client
119
* to set up its display from pre-existing outputs. The client must have
120
* initialized its state to able to handle the hotplug event successfully.
121
*/
122
void drm_client_register(struct drm_client_dev *client)
123
{
124
struct drm_device *dev = client->dev;
125
int ret;
126
127
mutex_lock(&dev->clientlist_mutex);
128
list_add(&client->list, &dev->clientlist);
129
130
if (client->funcs && client->funcs->hotplug) {
131
/*
132
* Perform an initial hotplug event to pick up the
133
* display configuration for the client. This step
134
* has to be performed *after* registering the client
135
* in the list of clients, or a concurrent hotplug
136
* event might be lost; leaving the display off.
137
*
138
* Hold the clientlist_mutex as for a regular hotplug
139
* event.
140
*/
141
ret = client->funcs->hotplug(client);
142
if (ret)
143
drm_dbg_kms(dev, "client hotplug ret=%d\n", ret);
144
}
145
mutex_unlock(&dev->clientlist_mutex);
146
}
147
EXPORT_SYMBOL(drm_client_register);
148
149
/**
150
* drm_client_release - Release DRM client resources
151
* @client: DRM client
152
*
153
* Releases resources by closing the &drm_file that was opened by drm_client_init().
154
* It is called automatically if the &drm_client_funcs.unregister callback is _not_ set.
155
*
156
* This function should only be called from the unregister callback. An exception
157
* is fbdev which cannot free the buffer if userspace has open file descriptors.
158
*
159
* Note:
160
* Clients cannot initiate a release by themselves. This is done to keep the code simple.
161
* The driver has to be unloaded before the client can be unloaded.
162
*/
163
void drm_client_release(struct drm_client_dev *client)
164
{
165
struct drm_device *dev = client->dev;
166
167
drm_dbg_kms(dev, "%s\n", client->name);
168
169
drm_client_modeset_free(client);
170
drm_client_close(client);
171
drm_dev_put(dev);
172
}
173
EXPORT_SYMBOL(drm_client_release);
174
175
static void drm_client_buffer_delete(struct drm_client_buffer *buffer)
176
{
177
if (buffer->gem) {
178
drm_gem_vunmap(buffer->gem, &buffer->map);
179
drm_gem_object_put(buffer->gem);
180
}
181
182
kfree(buffer);
183
}
184
185
static struct drm_client_buffer *
186
drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height,
187
u32 format, u32 *handle)
188
{
189
const struct drm_format_info *info = drm_format_info(format);
190
struct drm_mode_create_dumb dumb_args = { };
191
struct drm_device *dev = client->dev;
192
struct drm_client_buffer *buffer;
193
struct drm_gem_object *obj;
194
int ret;
195
196
buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
197
if (!buffer)
198
return ERR_PTR(-ENOMEM);
199
200
buffer->client = client;
201
202
dumb_args.width = width;
203
dumb_args.height = height;
204
dumb_args.bpp = drm_format_info_bpp(info, 0);
205
ret = drm_mode_create_dumb(dev, &dumb_args, client->file);
206
if (ret)
207
goto err_delete;
208
209
obj = drm_gem_object_lookup(client->file, dumb_args.handle);
210
if (!obj) {
211
ret = -ENOENT;
212
goto err_delete;
213
}
214
215
buffer->pitch = dumb_args.pitch;
216
buffer->gem = obj;
217
*handle = dumb_args.handle;
218
219
return buffer;
220
221
err_delete:
222
drm_client_buffer_delete(buffer);
223
224
return ERR_PTR(ret);
225
}
226
227
/**
228
* drm_client_buffer_vmap_local - Map DRM client buffer into address space
229
* @buffer: DRM client buffer
230
* @map_copy: Returns the mapped memory's address
231
*
232
* This function maps a client buffer into kernel address space. If the
233
* buffer is already mapped, it returns the existing mapping's address.
234
*
235
* Client buffer mappings are not ref'counted. Each call to
236
* drm_client_buffer_vmap_local() should be closely followed by a call to
237
* drm_client_buffer_vunmap_local(). See drm_client_buffer_vmap() for
238
* long-term mappings.
239
*
240
* The returned address is a copy of the internal value. In contrast to
241
* other vmap interfaces, you don't need it for the client's vunmap
242
* function. So you can modify it at will during blit and draw operations.
243
*
244
* Returns:
245
* 0 on success, or a negative errno code otherwise.
246
*/
247
int drm_client_buffer_vmap_local(struct drm_client_buffer *buffer,
248
struct iosys_map *map_copy)
249
{
250
struct drm_gem_object *gem = buffer->gem;
251
struct iosys_map *map = &buffer->map;
252
int ret;
253
254
drm_gem_lock(gem);
255
256
ret = drm_gem_vmap_locked(gem, map);
257
if (ret)
258
goto err_drm_gem_vmap_unlocked;
259
*map_copy = *map;
260
261
return 0;
262
263
err_drm_gem_vmap_unlocked:
264
drm_gem_unlock(gem);
265
return ret;
266
}
267
EXPORT_SYMBOL(drm_client_buffer_vmap_local);
268
269
/**
270
* drm_client_buffer_vunmap_local - Unmap DRM client buffer
271
* @buffer: DRM client buffer
272
*
273
* This function removes a client buffer's memory mapping established
274
* with drm_client_buffer_vunmap_local(). Calling this function is only
275
* required by clients that manage their buffer mappings by themselves.
276
*/
277
void drm_client_buffer_vunmap_local(struct drm_client_buffer *buffer)
278
{
279
struct drm_gem_object *gem = buffer->gem;
280
struct iosys_map *map = &buffer->map;
281
282
drm_gem_vunmap_locked(gem, map);
283
drm_gem_unlock(gem);
284
}
285
EXPORT_SYMBOL(drm_client_buffer_vunmap_local);
286
287
/**
288
* drm_client_buffer_vmap - Map DRM client buffer into address space
289
* @buffer: DRM client buffer
290
* @map_copy: Returns the mapped memory's address
291
*
292
* This function maps a client buffer into kernel address space. If the
293
* buffer is already mapped, it returns the existing mapping's address.
294
*
295
* Client buffer mappings are not ref'counted. Each call to
296
* drm_client_buffer_vmap() should be followed by a call to
297
* drm_client_buffer_vunmap(); or the client buffer should be mapped
298
* throughout its lifetime.
299
*
300
* The returned address is a copy of the internal value. In contrast to
301
* other vmap interfaces, you don't need it for the client's vunmap
302
* function. So you can modify it at will during blit and draw operations.
303
*
304
* Returns:
305
* 0 on success, or a negative errno code otherwise.
306
*/
307
int drm_client_buffer_vmap(struct drm_client_buffer *buffer,
308
struct iosys_map *map_copy)
309
{
310
int ret;
311
312
ret = drm_gem_vmap(buffer->gem, &buffer->map);
313
if (ret)
314
return ret;
315
*map_copy = buffer->map;
316
317
return 0;
318
}
319
EXPORT_SYMBOL(drm_client_buffer_vmap);
320
321
/**
322
* drm_client_buffer_vunmap - Unmap DRM client buffer
323
* @buffer: DRM client buffer
324
*
325
* This function removes a client buffer's memory mapping. Calling this
326
* function is only required by clients that manage their buffer mappings
327
* by themselves.
328
*/
329
void drm_client_buffer_vunmap(struct drm_client_buffer *buffer)
330
{
331
drm_gem_vunmap(buffer->gem, &buffer->map);
332
}
333
EXPORT_SYMBOL(drm_client_buffer_vunmap);
334
335
static void drm_client_buffer_rmfb(struct drm_client_buffer *buffer)
336
{
337
int ret;
338
339
if (!buffer->fb)
340
return;
341
342
ret = drm_mode_rmfb(buffer->client->dev, buffer->fb->base.id, buffer->client->file);
343
if (ret)
344
drm_err(buffer->client->dev,
345
"Error removing FB:%u (%d)\n", buffer->fb->base.id, ret);
346
347
buffer->fb = NULL;
348
}
349
350
static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,
351
u32 width, u32 height, u32 format,
352
u32 handle)
353
{
354
struct drm_client_dev *client = buffer->client;
355
struct drm_mode_fb_cmd2 fb_req = { };
356
int ret;
357
358
fb_req.width = width;
359
fb_req.height = height;
360
fb_req.pixel_format = format;
361
fb_req.handles[0] = handle;
362
fb_req.pitches[0] = buffer->pitch;
363
364
ret = drm_mode_addfb2(client->dev, &fb_req, client->file);
365
if (ret)
366
return ret;
367
368
buffer->fb = drm_framebuffer_lookup(client->dev, buffer->client->file, fb_req.fb_id);
369
if (WARN_ON(!buffer->fb))
370
return -ENOENT;
371
372
/* drop the reference we picked up in framebuffer lookup */
373
drm_framebuffer_put(buffer->fb);
374
375
strscpy(buffer->fb->comm, client->name, TASK_COMM_LEN);
376
377
return 0;
378
}
379
380
/**
381
* drm_client_framebuffer_create - Create a client framebuffer
382
* @client: DRM client
383
* @width: Framebuffer width
384
* @height: Framebuffer height
385
* @format: Buffer format
386
*
387
* This function creates a &drm_client_buffer which consists of a
388
* &drm_framebuffer backed by a dumb buffer.
389
* Call drm_client_framebuffer_delete() to free the buffer.
390
*
391
* Returns:
392
* Pointer to a client buffer or an error pointer on failure.
393
*/
394
struct drm_client_buffer *
395
drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
396
{
397
struct drm_client_buffer *buffer;
398
u32 handle;
399
int ret;
400
401
buffer = drm_client_buffer_create(client, width, height, format,
402
&handle);
403
if (IS_ERR(buffer))
404
return buffer;
405
406
ret = drm_client_buffer_addfb(buffer, width, height, format, handle);
407
408
/*
409
* The handle is only needed for creating the framebuffer, destroy it
410
* again to solve a circular dependency should anybody export the GEM
411
* object as DMA-buf. The framebuffer and our buffer structure are still
412
* holding references to the GEM object to prevent its destruction.
413
*/
414
drm_mode_destroy_dumb(client->dev, handle, client->file);
415
416
if (ret) {
417
drm_client_buffer_delete(buffer);
418
return ERR_PTR(ret);
419
}
420
421
return buffer;
422
}
423
EXPORT_SYMBOL(drm_client_framebuffer_create);
424
425
/**
426
* drm_client_framebuffer_delete - Delete a client framebuffer
427
* @buffer: DRM client buffer (can be NULL)
428
*/
429
void drm_client_framebuffer_delete(struct drm_client_buffer *buffer)
430
{
431
if (!buffer)
432
return;
433
434
drm_client_buffer_rmfb(buffer);
435
drm_client_buffer_delete(buffer);
436
}
437
EXPORT_SYMBOL(drm_client_framebuffer_delete);
438
439
/**
440
* drm_client_framebuffer_flush - Manually flush client framebuffer
441
* @buffer: DRM client buffer (can be NULL)
442
* @rect: Damage rectangle (if NULL flushes all)
443
*
444
* This calls &drm_framebuffer_funcs->dirty (if present) to flush buffer changes
445
* for drivers that need it.
446
*
447
* Returns:
448
* Zero on success or negative error code on failure.
449
*/
450
int drm_client_framebuffer_flush(struct drm_client_buffer *buffer, struct drm_rect *rect)
451
{
452
if (!buffer || !buffer->fb || !buffer->fb->funcs->dirty)
453
return 0;
454
455
if (rect) {
456
struct drm_clip_rect clip = {
457
.x1 = rect->x1,
458
.y1 = rect->y1,
459
.x2 = rect->x2,
460
.y2 = rect->y2,
461
};
462
463
return buffer->fb->funcs->dirty(buffer->fb, buffer->client->file,
464
0, 0, &clip, 1);
465
}
466
467
return buffer->fb->funcs->dirty(buffer->fb, buffer->client->file,
468
0, 0, NULL, 0);
469
}
470
EXPORT_SYMBOL(drm_client_framebuffer_flush);
471
472