xref: /linux/drivers/gpu/drm/drm_client.c (revision c76f0f7cb546b661b5e0ac769850a5c854927f65)
1*c76f0f7cSNoralf Trønnes // SPDX-License-Identifier: GPL-2.0
2*c76f0f7cSNoralf Trønnes /*
3*c76f0f7cSNoralf Trønnes  * Copyright 2018 Noralf Trønnes
4*c76f0f7cSNoralf Trønnes  */
5*c76f0f7cSNoralf Trønnes 
6*c76f0f7cSNoralf Trønnes #include <linux/list.h>
7*c76f0f7cSNoralf Trønnes #include <linux/module.h>
8*c76f0f7cSNoralf Trønnes #include <linux/mutex.h>
9*c76f0f7cSNoralf Trønnes #include <linux/seq_file.h>
10*c76f0f7cSNoralf Trønnes #include <linux/slab.h>
11*c76f0f7cSNoralf Trønnes 
12*c76f0f7cSNoralf Trønnes #include <drm/drm_client.h>
13*c76f0f7cSNoralf Trønnes #include <drm/drm_debugfs.h>
14*c76f0f7cSNoralf Trønnes #include <drm/drm_device.h>
15*c76f0f7cSNoralf Trønnes #include <drm/drm_drv.h>
16*c76f0f7cSNoralf Trønnes #include <drm/drm_file.h>
17*c76f0f7cSNoralf Trønnes #include <drm/drm_fourcc.h>
18*c76f0f7cSNoralf Trønnes #include <drm/drm_gem.h>
19*c76f0f7cSNoralf Trønnes #include <drm/drm_mode.h>
20*c76f0f7cSNoralf Trønnes #include <drm/drm_print.h>
21*c76f0f7cSNoralf Trønnes #include <drm/drmP.h>
22*c76f0f7cSNoralf Trønnes 
23*c76f0f7cSNoralf Trønnes #include "drm_crtc_internal.h"
24*c76f0f7cSNoralf Trønnes #include "drm_internal.h"
25*c76f0f7cSNoralf Trønnes 
26*c76f0f7cSNoralf Trønnes /**
27*c76f0f7cSNoralf Trønnes  * DOC: overview
28*c76f0f7cSNoralf Trønnes  *
29*c76f0f7cSNoralf Trønnes  * This library provides support for clients running in the kernel like fbdev and bootsplash.
30*c76f0f7cSNoralf Trønnes  * Currently it's only partially implemented, just enough to support fbdev.
31*c76f0f7cSNoralf Trønnes  *
32*c76f0f7cSNoralf Trønnes  * GEM drivers which provide a GEM based dumb buffer with a virtual address are supported.
33*c76f0f7cSNoralf Trønnes  */
34*c76f0f7cSNoralf Trønnes 
35*c76f0f7cSNoralf Trønnes static int drm_client_open(struct drm_client_dev *client)
36*c76f0f7cSNoralf Trønnes {
37*c76f0f7cSNoralf Trønnes 	struct drm_device *dev = client->dev;
38*c76f0f7cSNoralf Trønnes 	struct drm_file *file;
39*c76f0f7cSNoralf Trønnes 
40*c76f0f7cSNoralf Trønnes 	file = drm_file_alloc(dev->primary);
41*c76f0f7cSNoralf Trønnes 	if (IS_ERR(file))
42*c76f0f7cSNoralf Trønnes 		return PTR_ERR(file);
43*c76f0f7cSNoralf Trønnes 
44*c76f0f7cSNoralf Trønnes 	mutex_lock(&dev->filelist_mutex);
45*c76f0f7cSNoralf Trønnes 	list_add(&file->lhead, &dev->filelist_internal);
46*c76f0f7cSNoralf Trønnes 	mutex_unlock(&dev->filelist_mutex);
47*c76f0f7cSNoralf Trønnes 
48*c76f0f7cSNoralf Trønnes 	client->file = file;
49*c76f0f7cSNoralf Trønnes 
50*c76f0f7cSNoralf Trønnes 	return 0;
51*c76f0f7cSNoralf Trønnes }
52*c76f0f7cSNoralf Trønnes 
53*c76f0f7cSNoralf Trønnes static void drm_client_close(struct drm_client_dev *client)
54*c76f0f7cSNoralf Trønnes {
55*c76f0f7cSNoralf Trønnes 	struct drm_device *dev = client->dev;
56*c76f0f7cSNoralf Trønnes 
57*c76f0f7cSNoralf Trønnes 	mutex_lock(&dev->filelist_mutex);
58*c76f0f7cSNoralf Trønnes 	list_del(&client->file->lhead);
59*c76f0f7cSNoralf Trønnes 	mutex_unlock(&dev->filelist_mutex);
60*c76f0f7cSNoralf Trønnes 
61*c76f0f7cSNoralf Trønnes 	drm_file_free(client->file);
62*c76f0f7cSNoralf Trønnes }
63*c76f0f7cSNoralf Trønnes EXPORT_SYMBOL(drm_client_close);
64*c76f0f7cSNoralf Trønnes 
65*c76f0f7cSNoralf Trønnes /**
66*c76f0f7cSNoralf Trønnes  * drm_client_new - Create a DRM client
67*c76f0f7cSNoralf Trønnes  * @dev: DRM device
68*c76f0f7cSNoralf Trønnes  * @client: DRM client
69*c76f0f7cSNoralf Trønnes  * @name: Client name
70*c76f0f7cSNoralf Trønnes  * @funcs: DRM client functions (optional)
71*c76f0f7cSNoralf Trønnes  *
72*c76f0f7cSNoralf Trønnes  * The caller needs to hold a reference on @dev before calling this function.
73*c76f0f7cSNoralf Trønnes  * The client is freed when the &drm_device is unregistered. See drm_client_release().
74*c76f0f7cSNoralf Trønnes  *
75*c76f0f7cSNoralf Trønnes  * Returns:
76*c76f0f7cSNoralf Trønnes  * Zero on success or negative error code on failure.
77*c76f0f7cSNoralf Trønnes  */
78*c76f0f7cSNoralf Trønnes int drm_client_new(struct drm_device *dev, struct drm_client_dev *client,
79*c76f0f7cSNoralf Trønnes 		   const char *name, const struct drm_client_funcs *funcs)
80*c76f0f7cSNoralf Trønnes {
81*c76f0f7cSNoralf Trønnes 	bool registered;
82*c76f0f7cSNoralf Trønnes 	int ret;
83*c76f0f7cSNoralf Trønnes 
84*c76f0f7cSNoralf Trønnes 	if (!drm_core_check_feature(dev, DRIVER_MODESET) ||
85*c76f0f7cSNoralf Trønnes 	    !dev->driver->dumb_create || !dev->driver->gem_prime_vmap)
86*c76f0f7cSNoralf Trønnes 		return -ENOTSUPP;
87*c76f0f7cSNoralf Trønnes 
88*c76f0f7cSNoralf Trønnes 	if (funcs && !try_module_get(funcs->owner))
89*c76f0f7cSNoralf Trønnes 		return -ENODEV;
90*c76f0f7cSNoralf Trønnes 
91*c76f0f7cSNoralf Trønnes 	client->dev = dev;
92*c76f0f7cSNoralf Trønnes 	client->name = name;
93*c76f0f7cSNoralf Trønnes 	client->funcs = funcs;
94*c76f0f7cSNoralf Trønnes 
95*c76f0f7cSNoralf Trønnes 	ret = drm_client_open(client);
96*c76f0f7cSNoralf Trønnes 	if (ret)
97*c76f0f7cSNoralf Trønnes 		goto err_put_module;
98*c76f0f7cSNoralf Trønnes 
99*c76f0f7cSNoralf Trønnes 	mutex_lock(&dev->clientlist_mutex);
100*c76f0f7cSNoralf Trønnes 	registered = dev->registered;
101*c76f0f7cSNoralf Trønnes 	if (registered)
102*c76f0f7cSNoralf Trønnes 		list_add(&client->list, &dev->clientlist);
103*c76f0f7cSNoralf Trønnes 	mutex_unlock(&dev->clientlist_mutex);
104*c76f0f7cSNoralf Trønnes 	if (!registered) {
105*c76f0f7cSNoralf Trønnes 		ret = -ENODEV;
106*c76f0f7cSNoralf Trønnes 		goto err_close;
107*c76f0f7cSNoralf Trønnes 	}
108*c76f0f7cSNoralf Trønnes 
109*c76f0f7cSNoralf Trønnes 	drm_dev_get(dev);
110*c76f0f7cSNoralf Trønnes 
111*c76f0f7cSNoralf Trønnes 	return 0;
112*c76f0f7cSNoralf Trønnes 
113*c76f0f7cSNoralf Trønnes err_close:
114*c76f0f7cSNoralf Trønnes 	drm_client_close(client);
115*c76f0f7cSNoralf Trønnes err_put_module:
116*c76f0f7cSNoralf Trønnes 	if (funcs)
117*c76f0f7cSNoralf Trønnes 		module_put(funcs->owner);
118*c76f0f7cSNoralf Trønnes 
119*c76f0f7cSNoralf Trønnes 	return ret;
120*c76f0f7cSNoralf Trønnes }
121*c76f0f7cSNoralf Trønnes EXPORT_SYMBOL(drm_client_new);
122*c76f0f7cSNoralf Trønnes 
123*c76f0f7cSNoralf Trønnes /**
124*c76f0f7cSNoralf Trønnes  * drm_client_release - Release DRM client resources
125*c76f0f7cSNoralf Trønnes  * @client: DRM client
126*c76f0f7cSNoralf Trønnes  *
127*c76f0f7cSNoralf Trønnes  * Releases resources by closing the &drm_file that was opened by drm_client_new().
128*c76f0f7cSNoralf Trønnes  * It is called automatically if the &drm_client_funcs.unregister callback is _not_ set.
129*c76f0f7cSNoralf Trønnes  *
130*c76f0f7cSNoralf Trønnes  * This function should only be called from the unregister callback. An exception
131*c76f0f7cSNoralf Trønnes  * is fbdev which cannot free the buffer if userspace has open file descriptors.
132*c76f0f7cSNoralf Trønnes  *
133*c76f0f7cSNoralf Trønnes  * Note:
134*c76f0f7cSNoralf Trønnes  * Clients cannot initiate a release by themselves. This is done to keep the code simple.
135*c76f0f7cSNoralf Trønnes  * The driver has to be unloaded before the client can be unloaded.
136*c76f0f7cSNoralf Trønnes  */
137*c76f0f7cSNoralf Trønnes void drm_client_release(struct drm_client_dev *client)
138*c76f0f7cSNoralf Trønnes {
139*c76f0f7cSNoralf Trønnes 	struct drm_device *dev = client->dev;
140*c76f0f7cSNoralf Trønnes 
141*c76f0f7cSNoralf Trønnes 	DRM_DEV_DEBUG_KMS(dev->dev, "%s\n", client->name);
142*c76f0f7cSNoralf Trønnes 
143*c76f0f7cSNoralf Trønnes 	drm_client_close(client);
144*c76f0f7cSNoralf Trønnes 	drm_dev_put(dev);
145*c76f0f7cSNoralf Trønnes 	if (client->funcs)
146*c76f0f7cSNoralf Trønnes 		module_put(client->funcs->owner);
147*c76f0f7cSNoralf Trønnes }
148*c76f0f7cSNoralf Trønnes EXPORT_SYMBOL(drm_client_release);
149*c76f0f7cSNoralf Trønnes 
150*c76f0f7cSNoralf Trønnes void drm_client_dev_unregister(struct drm_device *dev)
151*c76f0f7cSNoralf Trønnes {
152*c76f0f7cSNoralf Trønnes 	struct drm_client_dev *client, *tmp;
153*c76f0f7cSNoralf Trønnes 
154*c76f0f7cSNoralf Trønnes 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
155*c76f0f7cSNoralf Trønnes 		return;
156*c76f0f7cSNoralf Trønnes 
157*c76f0f7cSNoralf Trønnes 	mutex_lock(&dev->clientlist_mutex);
158*c76f0f7cSNoralf Trønnes 	list_for_each_entry_safe(client, tmp, &dev->clientlist, list) {
159*c76f0f7cSNoralf Trønnes 		list_del(&client->list);
160*c76f0f7cSNoralf Trønnes 		if (client->funcs && client->funcs->unregister) {
161*c76f0f7cSNoralf Trønnes 			client->funcs->unregister(client);
162*c76f0f7cSNoralf Trønnes 		} else {
163*c76f0f7cSNoralf Trønnes 			drm_client_release(client);
164*c76f0f7cSNoralf Trønnes 			kfree(client);
165*c76f0f7cSNoralf Trønnes 		}
166*c76f0f7cSNoralf Trønnes 	}
167*c76f0f7cSNoralf Trønnes 	mutex_unlock(&dev->clientlist_mutex);
168*c76f0f7cSNoralf Trønnes }
169*c76f0f7cSNoralf Trønnes 
170*c76f0f7cSNoralf Trønnes /**
171*c76f0f7cSNoralf Trønnes  * drm_client_dev_hotplug - Send hotplug event to clients
172*c76f0f7cSNoralf Trønnes  * @dev: DRM device
173*c76f0f7cSNoralf Trønnes  *
174*c76f0f7cSNoralf Trønnes  * This function calls the &drm_client_funcs.hotplug callback on the attached clients.
175*c76f0f7cSNoralf Trønnes  *
176*c76f0f7cSNoralf Trønnes  * drm_kms_helper_hotplug_event() calls this function, so drivers that use it
177*c76f0f7cSNoralf Trønnes  * don't need to call this function themselves.
178*c76f0f7cSNoralf Trønnes  */
179*c76f0f7cSNoralf Trønnes void drm_client_dev_hotplug(struct drm_device *dev)
180*c76f0f7cSNoralf Trønnes {
181*c76f0f7cSNoralf Trønnes 	struct drm_client_dev *client;
182*c76f0f7cSNoralf Trønnes 	int ret;
183*c76f0f7cSNoralf Trønnes 
184*c76f0f7cSNoralf Trønnes 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
185*c76f0f7cSNoralf Trønnes 		return;
186*c76f0f7cSNoralf Trønnes 
187*c76f0f7cSNoralf Trønnes 	mutex_lock(&dev->clientlist_mutex);
188*c76f0f7cSNoralf Trønnes 	list_for_each_entry(client, &dev->clientlist, list) {
189*c76f0f7cSNoralf Trønnes 		if (!client->funcs || !client->funcs->hotplug)
190*c76f0f7cSNoralf Trønnes 			continue;
191*c76f0f7cSNoralf Trønnes 
192*c76f0f7cSNoralf Trønnes 		ret = client->funcs->hotplug(client);
193*c76f0f7cSNoralf Trønnes 		DRM_DEV_DEBUG_KMS(dev->dev, "%s: ret=%d\n", client->name, ret);
194*c76f0f7cSNoralf Trønnes 	}
195*c76f0f7cSNoralf Trønnes 	mutex_unlock(&dev->clientlist_mutex);
196*c76f0f7cSNoralf Trønnes }
197*c76f0f7cSNoralf Trønnes EXPORT_SYMBOL(drm_client_dev_hotplug);
198*c76f0f7cSNoralf Trønnes 
199*c76f0f7cSNoralf Trønnes void drm_client_dev_restore(struct drm_device *dev)
200*c76f0f7cSNoralf Trønnes {
201*c76f0f7cSNoralf Trønnes 	struct drm_client_dev *client;
202*c76f0f7cSNoralf Trønnes 	int ret;
203*c76f0f7cSNoralf Trønnes 
204*c76f0f7cSNoralf Trønnes 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
205*c76f0f7cSNoralf Trønnes 		return;
206*c76f0f7cSNoralf Trønnes 
207*c76f0f7cSNoralf Trønnes 	mutex_lock(&dev->clientlist_mutex);
208*c76f0f7cSNoralf Trønnes 	list_for_each_entry(client, &dev->clientlist, list) {
209*c76f0f7cSNoralf Trønnes 		if (!client->funcs || !client->funcs->restore)
210*c76f0f7cSNoralf Trønnes 			continue;
211*c76f0f7cSNoralf Trønnes 
212*c76f0f7cSNoralf Trønnes 		ret = client->funcs->restore(client);
213*c76f0f7cSNoralf Trønnes 		DRM_DEV_DEBUG_KMS(dev->dev, "%s: ret=%d\n", client->name, ret);
214*c76f0f7cSNoralf Trønnes 		if (!ret) /* The first one to return zero gets the privilege to restore */
215*c76f0f7cSNoralf Trønnes 			break;
216*c76f0f7cSNoralf Trønnes 	}
217*c76f0f7cSNoralf Trønnes 	mutex_unlock(&dev->clientlist_mutex);
218*c76f0f7cSNoralf Trønnes }
219*c76f0f7cSNoralf Trønnes 
220*c76f0f7cSNoralf Trønnes static void drm_client_buffer_delete(struct drm_client_buffer *buffer)
221*c76f0f7cSNoralf Trønnes {
222*c76f0f7cSNoralf Trønnes 	struct drm_device *dev = buffer->client->dev;
223*c76f0f7cSNoralf Trønnes 
224*c76f0f7cSNoralf Trønnes 	if (buffer->vaddr && dev->driver->gem_prime_vunmap)
225*c76f0f7cSNoralf Trønnes 		dev->driver->gem_prime_vunmap(buffer->gem, buffer->vaddr);
226*c76f0f7cSNoralf Trønnes 
227*c76f0f7cSNoralf Trønnes 	if (buffer->gem)
228*c76f0f7cSNoralf Trønnes 		drm_gem_object_put_unlocked(buffer->gem);
229*c76f0f7cSNoralf Trønnes 
230*c76f0f7cSNoralf Trønnes 	drm_mode_destroy_dumb(dev, buffer->handle, buffer->client->file);
231*c76f0f7cSNoralf Trønnes 	kfree(buffer);
232*c76f0f7cSNoralf Trønnes }
233*c76f0f7cSNoralf Trønnes 
234*c76f0f7cSNoralf Trønnes static struct drm_client_buffer *
235*c76f0f7cSNoralf Trønnes drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
236*c76f0f7cSNoralf Trønnes {
237*c76f0f7cSNoralf Trønnes 	struct drm_mode_create_dumb dumb_args = { };
238*c76f0f7cSNoralf Trønnes 	struct drm_device *dev = client->dev;
239*c76f0f7cSNoralf Trønnes 	struct drm_client_buffer *buffer;
240*c76f0f7cSNoralf Trønnes 	struct drm_gem_object *obj;
241*c76f0f7cSNoralf Trønnes 	void *vaddr;
242*c76f0f7cSNoralf Trønnes 	int ret;
243*c76f0f7cSNoralf Trønnes 
244*c76f0f7cSNoralf Trønnes 	buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
245*c76f0f7cSNoralf Trønnes 	if (!buffer)
246*c76f0f7cSNoralf Trønnes 		return ERR_PTR(-ENOMEM);
247*c76f0f7cSNoralf Trønnes 
248*c76f0f7cSNoralf Trønnes 	buffer->client = client;
249*c76f0f7cSNoralf Trønnes 
250*c76f0f7cSNoralf Trønnes 	dumb_args.width = width;
251*c76f0f7cSNoralf Trønnes 	dumb_args.height = height;
252*c76f0f7cSNoralf Trønnes 	dumb_args.bpp = drm_format_plane_cpp(format, 0) * 8;
253*c76f0f7cSNoralf Trønnes 	ret = drm_mode_create_dumb(dev, &dumb_args, client->file);
254*c76f0f7cSNoralf Trønnes 	if (ret)
255*c76f0f7cSNoralf Trønnes 		goto err_free;
256*c76f0f7cSNoralf Trønnes 
257*c76f0f7cSNoralf Trønnes 	buffer->handle = dumb_args.handle;
258*c76f0f7cSNoralf Trønnes 	buffer->pitch = dumb_args.pitch;
259*c76f0f7cSNoralf Trønnes 
260*c76f0f7cSNoralf Trønnes 	obj = drm_gem_object_lookup(client->file, dumb_args.handle);
261*c76f0f7cSNoralf Trønnes 	if (!obj)  {
262*c76f0f7cSNoralf Trønnes 		ret = -ENOENT;
263*c76f0f7cSNoralf Trønnes 		goto err_delete;
264*c76f0f7cSNoralf Trønnes 	}
265*c76f0f7cSNoralf Trønnes 
266*c76f0f7cSNoralf Trønnes 	buffer->gem = obj;
267*c76f0f7cSNoralf Trønnes 
268*c76f0f7cSNoralf Trønnes 	/*
269*c76f0f7cSNoralf Trønnes 	 * FIXME: The dependency on GEM here isn't required, we could
270*c76f0f7cSNoralf Trønnes 	 * convert the driver handle to a dma-buf instead and use the
271*c76f0f7cSNoralf Trønnes 	 * backend-agnostic dma-buf vmap support instead. This would
272*c76f0f7cSNoralf Trønnes 	 * require that the handle2fd prime ioctl is reworked to pull the
273*c76f0f7cSNoralf Trønnes 	 * fd_install step out of the driver backend hooks, to make that
274*c76f0f7cSNoralf Trønnes 	 * final step optional for internal users.
275*c76f0f7cSNoralf Trønnes 	 */
276*c76f0f7cSNoralf Trønnes 	vaddr = dev->driver->gem_prime_vmap(obj);
277*c76f0f7cSNoralf Trønnes 	if (!vaddr) {
278*c76f0f7cSNoralf Trønnes 		ret = -ENOMEM;
279*c76f0f7cSNoralf Trønnes 		goto err_delete;
280*c76f0f7cSNoralf Trønnes 	}
281*c76f0f7cSNoralf Trønnes 
282*c76f0f7cSNoralf Trønnes 	buffer->vaddr = vaddr;
283*c76f0f7cSNoralf Trønnes 
284*c76f0f7cSNoralf Trønnes 	return buffer;
285*c76f0f7cSNoralf Trønnes 
286*c76f0f7cSNoralf Trønnes err_delete:
287*c76f0f7cSNoralf Trønnes 	drm_client_buffer_delete(buffer);
288*c76f0f7cSNoralf Trønnes err_free:
289*c76f0f7cSNoralf Trønnes 	kfree(buffer);
290*c76f0f7cSNoralf Trønnes 
291*c76f0f7cSNoralf Trønnes 	return ERR_PTR(ret);
292*c76f0f7cSNoralf Trønnes }
293*c76f0f7cSNoralf Trønnes 
294*c76f0f7cSNoralf Trønnes static void drm_client_buffer_rmfb(struct drm_client_buffer *buffer)
295*c76f0f7cSNoralf Trønnes {
296*c76f0f7cSNoralf Trønnes 	int ret;
297*c76f0f7cSNoralf Trønnes 
298*c76f0f7cSNoralf Trønnes 	if (!buffer->fb)
299*c76f0f7cSNoralf Trønnes 		return;
300*c76f0f7cSNoralf Trønnes 
301*c76f0f7cSNoralf Trønnes 	ret = drm_mode_rmfb(buffer->client->dev, buffer->fb->base.id, buffer->client->file);
302*c76f0f7cSNoralf Trønnes 	if (ret)
303*c76f0f7cSNoralf Trønnes 		DRM_DEV_ERROR(buffer->client->dev->dev,
304*c76f0f7cSNoralf Trønnes 			      "Error removing FB:%u (%d)\n", buffer->fb->base.id, ret);
305*c76f0f7cSNoralf Trønnes 
306*c76f0f7cSNoralf Trønnes 	buffer->fb = NULL;
307*c76f0f7cSNoralf Trønnes }
308*c76f0f7cSNoralf Trønnes 
309*c76f0f7cSNoralf Trønnes static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,
310*c76f0f7cSNoralf Trønnes 				   u32 width, u32 height, u32 format)
311*c76f0f7cSNoralf Trønnes {
312*c76f0f7cSNoralf Trønnes 	struct drm_client_dev *client = buffer->client;
313*c76f0f7cSNoralf Trønnes 	struct drm_mode_fb_cmd fb_req = { };
314*c76f0f7cSNoralf Trønnes 	const struct drm_format_info *info;
315*c76f0f7cSNoralf Trønnes 	int ret;
316*c76f0f7cSNoralf Trønnes 
317*c76f0f7cSNoralf Trønnes 	info = drm_format_info(format);
318*c76f0f7cSNoralf Trønnes 	fb_req.bpp = info->cpp[0] * 8;
319*c76f0f7cSNoralf Trønnes 	fb_req.depth = info->depth;
320*c76f0f7cSNoralf Trønnes 	fb_req.width = width;
321*c76f0f7cSNoralf Trønnes 	fb_req.height = height;
322*c76f0f7cSNoralf Trønnes 	fb_req.handle = buffer->handle;
323*c76f0f7cSNoralf Trønnes 	fb_req.pitch = buffer->pitch;
324*c76f0f7cSNoralf Trønnes 
325*c76f0f7cSNoralf Trønnes 	ret = drm_mode_addfb(client->dev, &fb_req, client->file);
326*c76f0f7cSNoralf Trønnes 	if (ret)
327*c76f0f7cSNoralf Trønnes 		return ret;
328*c76f0f7cSNoralf Trønnes 
329*c76f0f7cSNoralf Trønnes 	buffer->fb = drm_framebuffer_lookup(client->dev, buffer->client->file, fb_req.fb_id);
330*c76f0f7cSNoralf Trønnes 	if (WARN_ON(!buffer->fb))
331*c76f0f7cSNoralf Trønnes 		return -ENOENT;
332*c76f0f7cSNoralf Trønnes 
333*c76f0f7cSNoralf Trønnes 	/* drop the reference we picked up in framebuffer lookup */
334*c76f0f7cSNoralf Trønnes 	drm_framebuffer_put(buffer->fb);
335*c76f0f7cSNoralf Trønnes 
336*c76f0f7cSNoralf Trønnes 	strscpy(buffer->fb->comm, client->name, TASK_COMM_LEN);
337*c76f0f7cSNoralf Trønnes 
338*c76f0f7cSNoralf Trønnes 	return 0;
339*c76f0f7cSNoralf Trønnes }
340*c76f0f7cSNoralf Trønnes 
341*c76f0f7cSNoralf Trønnes /**
342*c76f0f7cSNoralf Trønnes  * drm_client_framebuffer_create - Create a client framebuffer
343*c76f0f7cSNoralf Trønnes  * @client: DRM client
344*c76f0f7cSNoralf Trønnes  * @width: Framebuffer width
345*c76f0f7cSNoralf Trønnes  * @height: Framebuffer height
346*c76f0f7cSNoralf Trønnes  * @format: Buffer format
347*c76f0f7cSNoralf Trønnes  *
348*c76f0f7cSNoralf Trønnes  * This function creates a &drm_client_buffer which consists of a
349*c76f0f7cSNoralf Trønnes  * &drm_framebuffer backed by a dumb buffer.
350*c76f0f7cSNoralf Trønnes  * Call drm_client_framebuffer_delete() to free the buffer.
351*c76f0f7cSNoralf Trønnes  *
352*c76f0f7cSNoralf Trønnes  * Returns:
353*c76f0f7cSNoralf Trønnes  * Pointer to a client buffer or an error pointer on failure.
354*c76f0f7cSNoralf Trønnes  */
355*c76f0f7cSNoralf Trønnes struct drm_client_buffer *
356*c76f0f7cSNoralf Trønnes drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
357*c76f0f7cSNoralf Trønnes {
358*c76f0f7cSNoralf Trønnes 	struct drm_client_buffer *buffer;
359*c76f0f7cSNoralf Trønnes 	int ret;
360*c76f0f7cSNoralf Trønnes 
361*c76f0f7cSNoralf Trønnes 	buffer = drm_client_buffer_create(client, width, height, format);
362*c76f0f7cSNoralf Trønnes 	if (IS_ERR(buffer))
363*c76f0f7cSNoralf Trønnes 		return buffer;
364*c76f0f7cSNoralf Trønnes 
365*c76f0f7cSNoralf Trønnes 	ret = drm_client_buffer_addfb(buffer, width, height, format);
366*c76f0f7cSNoralf Trønnes 	if (ret) {
367*c76f0f7cSNoralf Trønnes 		drm_client_buffer_delete(buffer);
368*c76f0f7cSNoralf Trønnes 		return ERR_PTR(ret);
369*c76f0f7cSNoralf Trønnes 	}
370*c76f0f7cSNoralf Trønnes 
371*c76f0f7cSNoralf Trønnes 	return buffer;
372*c76f0f7cSNoralf Trønnes }
373*c76f0f7cSNoralf Trønnes EXPORT_SYMBOL(drm_client_framebuffer_create);
374*c76f0f7cSNoralf Trønnes 
375*c76f0f7cSNoralf Trønnes /**
376*c76f0f7cSNoralf Trønnes  * drm_client_framebuffer_delete - Delete a client framebuffer
377*c76f0f7cSNoralf Trønnes  * @buffer: DRM client buffer (can be NULL)
378*c76f0f7cSNoralf Trønnes  */
379*c76f0f7cSNoralf Trønnes void drm_client_framebuffer_delete(struct drm_client_buffer *buffer)
380*c76f0f7cSNoralf Trønnes {
381*c76f0f7cSNoralf Trønnes 	if (!buffer)
382*c76f0f7cSNoralf Trønnes 		return;
383*c76f0f7cSNoralf Trønnes 
384*c76f0f7cSNoralf Trønnes 	drm_client_buffer_rmfb(buffer);
385*c76f0f7cSNoralf Trønnes 	drm_client_buffer_delete(buffer);
386*c76f0f7cSNoralf Trønnes }
387*c76f0f7cSNoralf Trønnes EXPORT_SYMBOL(drm_client_framebuffer_delete);
388