xref: /openbmc/linux/drivers/gpu/drm/gma500/fbdev.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1b8bbbea1SThomas Zimmermann // SPDX-License-Identifier: GPL-2.0-only
2b8bbbea1SThomas Zimmermann /**************************************************************************
3b8bbbea1SThomas Zimmermann  * Copyright (c) 2007-2011, Intel Corporation.
4b8bbbea1SThomas Zimmermann  * All Rights Reserved.
5b8bbbea1SThomas Zimmermann  *
6b8bbbea1SThomas Zimmermann  **************************************************************************/
7b8bbbea1SThomas Zimmermann 
87fca1dd9SThomas Zimmermann #include <linux/fb.h>
9b8bbbea1SThomas Zimmermann #include <linux/pfn_t.h>
10b8bbbea1SThomas Zimmermann 
11b8bbbea1SThomas Zimmermann #include <drm/drm_crtc_helper.h>
128f1aaccbSThomas Zimmermann #include <drm/drm_drv.h>
13b8bbbea1SThomas Zimmermann #include <drm/drm_fb_helper.h>
14b8bbbea1SThomas Zimmermann #include <drm/drm_framebuffer.h>
15b8bbbea1SThomas Zimmermann 
16b8bbbea1SThomas Zimmermann #include "gem.h"
17b8bbbea1SThomas Zimmermann #include "psb_drv.h"
18b8bbbea1SThomas Zimmermann 
19b8bbbea1SThomas Zimmermann /*
20b8bbbea1SThomas Zimmermann  * VM area struct
21b8bbbea1SThomas Zimmermann  */
22b8bbbea1SThomas Zimmermann 
psb_fbdev_vm_fault(struct vm_fault * vmf)238261dd97SThomas Zimmermann static vm_fault_t psb_fbdev_vm_fault(struct vm_fault *vmf)
24b8bbbea1SThomas Zimmermann {
25b8bbbea1SThomas Zimmermann 	struct vm_area_struct *vma = vmf->vma;
26b704eeaeSThomas Zimmermann 	struct fb_info *info = vma->vm_private_data;
27b704eeaeSThomas Zimmermann 	unsigned long address = vmf->address - (vmf->pgoff << PAGE_SHIFT);
28b704eeaeSThomas Zimmermann 	unsigned long pfn = info->fix.smem_start >> PAGE_SHIFT;
29b704eeaeSThomas Zimmermann 	vm_fault_t err = VM_FAULT_SIGBUS;
30b704eeaeSThomas Zimmermann 	unsigned long page_num = vma_pages(vma);
31b704eeaeSThomas Zimmermann 	unsigned long i;
32b8bbbea1SThomas Zimmermann 
33b8bbbea1SThomas Zimmermann 	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
34b8bbbea1SThomas Zimmermann 
35b704eeaeSThomas Zimmermann 	for (i = 0; i < page_num; ++i) {
36b704eeaeSThomas Zimmermann 		err = vmf_insert_mixed(vma, address, __pfn_to_pfn_t(pfn, PFN_DEV));
37b704eeaeSThomas Zimmermann 		if (unlikely(err & VM_FAULT_ERROR))
38b8bbbea1SThomas Zimmermann 			break;
39b8bbbea1SThomas Zimmermann 		address += PAGE_SIZE;
40b704eeaeSThomas Zimmermann 		++pfn;
41b8bbbea1SThomas Zimmermann 	}
42b704eeaeSThomas Zimmermann 
43b704eeaeSThomas Zimmermann 	return err;
44b8bbbea1SThomas Zimmermann }
45b8bbbea1SThomas Zimmermann 
468261dd97SThomas Zimmermann static const struct vm_operations_struct psb_fbdev_vm_ops = {
478261dd97SThomas Zimmermann 	.fault	= psb_fbdev_vm_fault,
48b8bbbea1SThomas Zimmermann };
49b8bbbea1SThomas Zimmermann 
50b8bbbea1SThomas Zimmermann /*
51b8bbbea1SThomas Zimmermann  * struct fb_ops
52b8bbbea1SThomas Zimmermann  */
53b8bbbea1SThomas Zimmermann 
54b8bbbea1SThomas Zimmermann #define CMAP_TOHW(_val, _width) ((((_val) << (_width)) + 0x7FFF - (_val)) >> 16)
55b8bbbea1SThomas Zimmermann 
psb_fbdev_fb_setcolreg(unsigned int regno,unsigned int red,unsigned int green,unsigned int blue,unsigned int transp,struct fb_info * info)56a89c957aSThomas Zimmermann static int psb_fbdev_fb_setcolreg(unsigned int regno,
57b8bbbea1SThomas Zimmermann 				  unsigned int red, unsigned int green,
58b8bbbea1SThomas Zimmermann 				  unsigned int blue, unsigned int transp,
59b8bbbea1SThomas Zimmermann 				  struct fb_info *info)
60b8bbbea1SThomas Zimmermann {
61b8bbbea1SThomas Zimmermann 	struct drm_fb_helper *fb_helper = info->par;
62b8bbbea1SThomas Zimmermann 	struct drm_framebuffer *fb = fb_helper->fb;
63b8bbbea1SThomas Zimmermann 	uint32_t v;
64b8bbbea1SThomas Zimmermann 
65b8bbbea1SThomas Zimmermann 	if (!fb)
66b8bbbea1SThomas Zimmermann 		return -ENOMEM;
67b8bbbea1SThomas Zimmermann 
68b8bbbea1SThomas Zimmermann 	if (regno > 255)
69b8bbbea1SThomas Zimmermann 		return 1;
70b8bbbea1SThomas Zimmermann 
71b8bbbea1SThomas Zimmermann 	red = CMAP_TOHW(red, info->var.red.length);
72b8bbbea1SThomas Zimmermann 	blue = CMAP_TOHW(blue, info->var.blue.length);
73b8bbbea1SThomas Zimmermann 	green = CMAP_TOHW(green, info->var.green.length);
74b8bbbea1SThomas Zimmermann 	transp = CMAP_TOHW(transp, info->var.transp.length);
75b8bbbea1SThomas Zimmermann 
76b8bbbea1SThomas Zimmermann 	v = (red << info->var.red.offset) |
77b8bbbea1SThomas Zimmermann 	    (green << info->var.green.offset) |
78b8bbbea1SThomas Zimmermann 	    (blue << info->var.blue.offset) |
79b8bbbea1SThomas Zimmermann 	    (transp << info->var.transp.offset);
80b8bbbea1SThomas Zimmermann 
81b8bbbea1SThomas Zimmermann 	if (regno < 16) {
82b8bbbea1SThomas Zimmermann 		switch (fb->format->cpp[0] * 8) {
83b8bbbea1SThomas Zimmermann 		case 16:
84b8bbbea1SThomas Zimmermann 			((uint32_t *) info->pseudo_palette)[regno] = v;
85b8bbbea1SThomas Zimmermann 			break;
86b8bbbea1SThomas Zimmermann 		case 24:
87b8bbbea1SThomas Zimmermann 		case 32:
88b8bbbea1SThomas Zimmermann 			((uint32_t *) info->pseudo_palette)[regno] = v;
89b8bbbea1SThomas Zimmermann 			break;
90b8bbbea1SThomas Zimmermann 		}
91b8bbbea1SThomas Zimmermann 	}
92b8bbbea1SThomas Zimmermann 
93b8bbbea1SThomas Zimmermann 	return 0;
94b8bbbea1SThomas Zimmermann }
95b8bbbea1SThomas Zimmermann 
psb_fbdev_fb_mmap(struct fb_info * info,struct vm_area_struct * vma)96a89c957aSThomas Zimmermann static int psb_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
97b8bbbea1SThomas Zimmermann {
98b8bbbea1SThomas Zimmermann 	if (vma->vm_pgoff != 0)
99b8bbbea1SThomas Zimmermann 		return -EINVAL;
100b8bbbea1SThomas Zimmermann 	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
101b8bbbea1SThomas Zimmermann 		return -EINVAL;
102b8bbbea1SThomas Zimmermann 
103b8bbbea1SThomas Zimmermann 	/*
104b8bbbea1SThomas Zimmermann 	 * If this is a GEM object then info->screen_base is the virtual
105b8bbbea1SThomas Zimmermann 	 * kernel remapping of the object. FIXME: Review if this is
106b8bbbea1SThomas Zimmermann 	 * suitable for our mmap work
107b8bbbea1SThomas Zimmermann 	 */
1088261dd97SThomas Zimmermann 	vma->vm_ops = &psb_fbdev_vm_ops;
109b704eeaeSThomas Zimmermann 	vma->vm_private_data = info;
110b8bbbea1SThomas Zimmermann 	vm_flags_set(vma, VM_IO | VM_MIXEDMAP | VM_DONTEXPAND | VM_DONTDUMP);
111b8bbbea1SThomas Zimmermann 
112b8bbbea1SThomas Zimmermann 	return 0;
113b8bbbea1SThomas Zimmermann }
114b8bbbea1SThomas Zimmermann 
psb_fbdev_fb_destroy(struct fb_info * info)1158f1aaccbSThomas Zimmermann static void psb_fbdev_fb_destroy(struct fb_info *info)
1168f1aaccbSThomas Zimmermann {
1178f1aaccbSThomas Zimmermann 	struct drm_fb_helper *fb_helper = info->par;
1188f1aaccbSThomas Zimmermann 	struct drm_framebuffer *fb = fb_helper->fb;
1198f1aaccbSThomas Zimmermann 	struct drm_gem_object *obj = fb->obj[0];
1208f1aaccbSThomas Zimmermann 
1218f1aaccbSThomas Zimmermann 	drm_fb_helper_fini(fb_helper);
1228f1aaccbSThomas Zimmermann 
1238f1aaccbSThomas Zimmermann 	drm_framebuffer_unregister_private(fb);
1248f1aaccbSThomas Zimmermann 	fb->obj[0] = NULL;
1258f1aaccbSThomas Zimmermann 	drm_framebuffer_cleanup(fb);
1268f1aaccbSThomas Zimmermann 	kfree(fb);
1278f1aaccbSThomas Zimmermann 
1288f1aaccbSThomas Zimmermann 	drm_gem_object_put(obj);
1298f1aaccbSThomas Zimmermann 
1308f1aaccbSThomas Zimmermann 	drm_client_release(&fb_helper->client);
1318f1aaccbSThomas Zimmermann 
1328f1aaccbSThomas Zimmermann 	drm_fb_helper_unprepare(fb_helper);
1338f1aaccbSThomas Zimmermann 	kfree(fb_helper);
1348f1aaccbSThomas Zimmermann }
1358f1aaccbSThomas Zimmermann 
136a89c957aSThomas Zimmermann static const struct fb_ops psb_fbdev_fb_ops = {
137b8bbbea1SThomas Zimmermann 	.owner = THIS_MODULE,
138*4520844bSThomas Zimmermann 	__FB_DEFAULT_IOMEM_OPS_RDWR,
139b8bbbea1SThomas Zimmermann 	DRM_FB_HELPER_DEFAULT_OPS,
140a89c957aSThomas Zimmermann 	.fb_setcolreg = psb_fbdev_fb_setcolreg,
141*4520844bSThomas Zimmermann 	__FB_DEFAULT_IOMEM_OPS_DRAW,
142a89c957aSThomas Zimmermann 	.fb_mmap = psb_fbdev_fb_mmap,
1438f1aaccbSThomas Zimmermann 	.fb_destroy = psb_fbdev_fb_destroy,
144b8bbbea1SThomas Zimmermann };
145b8bbbea1SThomas Zimmermann 
146b8bbbea1SThomas Zimmermann /*
147b8bbbea1SThomas Zimmermann  * struct drm_fb_helper_funcs
148b8bbbea1SThomas Zimmermann  */
149b8bbbea1SThomas Zimmermann 
psb_fbdev_fb_probe(struct drm_fb_helper * fb_helper,struct drm_fb_helper_surface_size * sizes)150d19ccb4cSThomas Zimmermann static int psb_fbdev_fb_probe(struct drm_fb_helper *fb_helper,
151b8bbbea1SThomas Zimmermann 			      struct drm_fb_helper_surface_size *sizes)
152b8bbbea1SThomas Zimmermann {
153b8bbbea1SThomas Zimmermann 	struct drm_device *dev = fb_helper->dev;
154b8bbbea1SThomas Zimmermann 	struct drm_psb_private *dev_priv = to_drm_psb_private(dev);
155b8bbbea1SThomas Zimmermann 	struct pci_dev *pdev = to_pci_dev(dev->dev);
156b8bbbea1SThomas Zimmermann 	struct fb_info *info;
157b8bbbea1SThomas Zimmermann 	struct drm_framebuffer *fb;
158d19ccb4cSThomas Zimmermann 	struct drm_mode_fb_cmd2 mode_cmd = { };
159b8bbbea1SThomas Zimmermann 	int size;
160b8bbbea1SThomas Zimmermann 	int ret;
161b8bbbea1SThomas Zimmermann 	struct psb_gem_object *backing;
162b8bbbea1SThomas Zimmermann 	struct drm_gem_object *obj;
163b8bbbea1SThomas Zimmermann 	u32 bpp, depth;
164b8bbbea1SThomas Zimmermann 
165d19ccb4cSThomas Zimmermann 	/* No 24-bit packed mode */
166d19ccb4cSThomas Zimmermann 	if (sizes->surface_bpp == 24) {
167d19ccb4cSThomas Zimmermann 		sizes->surface_bpp = 32;
168d19ccb4cSThomas Zimmermann 		sizes->surface_depth = 24;
169d19ccb4cSThomas Zimmermann 	}
170b8bbbea1SThomas Zimmermann 	bpp = sizes->surface_bpp;
171b8bbbea1SThomas Zimmermann 	depth = sizes->surface_depth;
172b8bbbea1SThomas Zimmermann 
173d19ccb4cSThomas Zimmermann 	/*
174d19ccb4cSThomas Zimmermann 	 * If the mode does not fit in 32 bit then switch to 16 bit to get
175d19ccb4cSThomas Zimmermann 	 * a console on full resolution. The X mode setting server will
176d19ccb4cSThomas Zimmermann 	 * allocate its own 32-bit GEM framebuffer.
177d19ccb4cSThomas Zimmermann 	 */
178d19ccb4cSThomas Zimmermann 	size = ALIGN(sizes->surface_width * DIV_ROUND_UP(bpp, 8), 64) *
179d19ccb4cSThomas Zimmermann 		     sizes->surface_height;
180d19ccb4cSThomas Zimmermann 	size = ALIGN(size, PAGE_SIZE);
181b8bbbea1SThomas Zimmermann 
182d19ccb4cSThomas Zimmermann 	if (size > dev_priv->vram_stolen_size) {
183d19ccb4cSThomas Zimmermann 		sizes->surface_bpp = 16;
184d19ccb4cSThomas Zimmermann 		sizes->surface_depth = 16;
185d19ccb4cSThomas Zimmermann 	}
186d19ccb4cSThomas Zimmermann 	bpp = sizes->surface_bpp;
187d19ccb4cSThomas Zimmermann 	depth = sizes->surface_depth;
188d19ccb4cSThomas Zimmermann 
189d19ccb4cSThomas Zimmermann 	mode_cmd.width = sizes->surface_width;
190d19ccb4cSThomas Zimmermann 	mode_cmd.height = sizes->surface_height;
191b8bbbea1SThomas Zimmermann 	mode_cmd.pitches[0] = ALIGN(mode_cmd.width * DIV_ROUND_UP(bpp, 8), 64);
192d19ccb4cSThomas Zimmermann 	mode_cmd.pixel_format = drm_mode_legacy_fb_format(bpp, depth);
193b8bbbea1SThomas Zimmermann 
194b8bbbea1SThomas Zimmermann 	size = mode_cmd.pitches[0] * mode_cmd.height;
195b8bbbea1SThomas Zimmermann 	size = ALIGN(size, PAGE_SIZE);
196b8bbbea1SThomas Zimmermann 
197b8bbbea1SThomas Zimmermann 	/* Allocate the framebuffer in the GTT with stolen page backing */
198b8bbbea1SThomas Zimmermann 	backing = psb_gem_create(dev, size, "fb", true, PAGE_SIZE);
199b8bbbea1SThomas Zimmermann 	if (IS_ERR(backing))
200b8bbbea1SThomas Zimmermann 		return PTR_ERR(backing);
201b8bbbea1SThomas Zimmermann 	obj = &backing->base;
202b8bbbea1SThomas Zimmermann 
203b8bbbea1SThomas Zimmermann 	fb = psb_framebuffer_create(dev, &mode_cmd, obj);
204b8bbbea1SThomas Zimmermann 	if (IS_ERR(fb)) {
205b8bbbea1SThomas Zimmermann 		ret = PTR_ERR(fb);
206b8bbbea1SThomas Zimmermann 		goto err_drm_gem_object_put;
207b8bbbea1SThomas Zimmermann 	}
208b8bbbea1SThomas Zimmermann 
209b8bbbea1SThomas Zimmermann 	fb_helper->fb = fb;
210b8bbbea1SThomas Zimmermann 
2118f1aaccbSThomas Zimmermann 	info = drm_fb_helper_alloc_info(fb_helper);
2128f1aaccbSThomas Zimmermann 	if (IS_ERR(info)) {
2138f1aaccbSThomas Zimmermann 		ret = PTR_ERR(info);
2148f1aaccbSThomas Zimmermann 		goto err_drm_framebuffer_unregister_private;
2158f1aaccbSThomas Zimmermann 	}
2168f1aaccbSThomas Zimmermann 
217a89c957aSThomas Zimmermann 	info->fbops = &psb_fbdev_fb_ops;
21840e324e0SThomas Zimmermann 
219b8bbbea1SThomas Zimmermann 	/* Accessed stolen memory directly */
220b8bbbea1SThomas Zimmermann 	info->screen_base = dev_priv->vram_addr + backing->offset;
221b8bbbea1SThomas Zimmermann 	info->screen_size = size;
222b8bbbea1SThomas Zimmermann 
223b8bbbea1SThomas Zimmermann 	drm_fb_helper_fill_info(info, fb_helper, sizes);
224b8bbbea1SThomas Zimmermann 
225b704eeaeSThomas Zimmermann 	info->fix.smem_start = dev_priv->stolen_base + backing->offset;
2268f1aaccbSThomas Zimmermann 	info->fix.smem_len = size;
2278f1aaccbSThomas Zimmermann 	info->fix.ywrapstep = 0;
2288f1aaccbSThomas Zimmermann 	info->fix.ypanstep = 0;
229b8bbbea1SThomas Zimmermann 	info->fix.mmio_start = pci_resource_start(pdev, 0);
230b8bbbea1SThomas Zimmermann 	info->fix.mmio_len = pci_resource_len(pdev, 0);
231b8bbbea1SThomas Zimmermann 
232aa25aaccSThomas Zimmermann 	fb_memset_io(info->screen_base, 0, info->screen_size);
2338f1aaccbSThomas Zimmermann 
234b8bbbea1SThomas Zimmermann 	/* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */
235b8bbbea1SThomas Zimmermann 
236b8bbbea1SThomas Zimmermann 	dev_dbg(dev->dev, "allocated %dx%d fb\n", fb->width, fb->height);
237b8bbbea1SThomas Zimmermann 
238b8bbbea1SThomas Zimmermann 	return 0;
239b8bbbea1SThomas Zimmermann 
2408f1aaccbSThomas Zimmermann err_drm_framebuffer_unregister_private:
2418f1aaccbSThomas Zimmermann 	drm_framebuffer_unregister_private(fb);
2428f1aaccbSThomas Zimmermann 	fb->obj[0] = NULL;
2438f1aaccbSThomas Zimmermann 	drm_framebuffer_cleanup(fb);
2448f1aaccbSThomas Zimmermann 	kfree(fb);
245b8bbbea1SThomas Zimmermann err_drm_gem_object_put:
246b8bbbea1SThomas Zimmermann 	drm_gem_object_put(obj);
247b8bbbea1SThomas Zimmermann 	return ret;
248b8bbbea1SThomas Zimmermann }
249b8bbbea1SThomas Zimmermann 
250d19ccb4cSThomas Zimmermann static const struct drm_fb_helper_funcs psb_fbdev_fb_helper_funcs = {
251d19ccb4cSThomas Zimmermann 	.fb_probe = psb_fbdev_fb_probe,
252b8bbbea1SThomas Zimmermann };
253b8bbbea1SThomas Zimmermann 
2548f1aaccbSThomas Zimmermann /*
2558f1aaccbSThomas Zimmermann  * struct drm_client_funcs and setup code
2568f1aaccbSThomas Zimmermann  */
2578f1aaccbSThomas Zimmermann 
psb_fbdev_client_unregister(struct drm_client_dev * client)2588f1aaccbSThomas Zimmermann static void psb_fbdev_client_unregister(struct drm_client_dev *client)
259b8bbbea1SThomas Zimmermann {
2608f1aaccbSThomas Zimmermann 	struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
261b8bbbea1SThomas Zimmermann 
2628f1aaccbSThomas Zimmermann 	if (fb_helper->info) {
263b8bbbea1SThomas Zimmermann 		drm_fb_helper_unregister_info(fb_helper);
2648f1aaccbSThomas Zimmermann 	} else {
2658f1aaccbSThomas Zimmermann 		drm_fb_helper_unprepare(fb_helper);
2668f1aaccbSThomas Zimmermann 		drm_client_release(&fb_helper->client);
2678f1aaccbSThomas Zimmermann 		kfree(fb_helper);
2688f1aaccbSThomas Zimmermann 	}
2698f1aaccbSThomas Zimmermann }
270b8bbbea1SThomas Zimmermann 
psb_fbdev_client_restore(struct drm_client_dev * client)2718f1aaccbSThomas Zimmermann static int psb_fbdev_client_restore(struct drm_client_dev *client)
2728f1aaccbSThomas Zimmermann {
2738f1aaccbSThomas Zimmermann 	drm_fb_helper_lastclose(client->dev);
274b8bbbea1SThomas Zimmermann 
275b8bbbea1SThomas Zimmermann 	return 0;
276b8bbbea1SThomas Zimmermann }
277b8bbbea1SThomas Zimmermann 
psb_fbdev_client_hotplug(struct drm_client_dev * client)2788f1aaccbSThomas Zimmermann static int psb_fbdev_client_hotplug(struct drm_client_dev *client)
279b8bbbea1SThomas Zimmermann {
2808f1aaccbSThomas Zimmermann 	struct drm_fb_helper *fb_helper = drm_fb_helper_from_client(client);
2818f1aaccbSThomas Zimmermann 	struct drm_device *dev = client->dev;
282b8bbbea1SThomas Zimmermann 	int ret;
283b8bbbea1SThomas Zimmermann 
2848f1aaccbSThomas Zimmermann 	if (dev->fb_helper)
2858f1aaccbSThomas Zimmermann 		return drm_fb_helper_hotplug_event(dev->fb_helper);
286b8bbbea1SThomas Zimmermann 
287b8bbbea1SThomas Zimmermann 	ret = drm_fb_helper_init(dev, fb_helper);
288b8bbbea1SThomas Zimmermann 	if (ret)
2898f1aaccbSThomas Zimmermann 		goto err_drm_err;
290b8bbbea1SThomas Zimmermann 
2918f1aaccbSThomas Zimmermann 	if (!drm_drv_uses_atomic_modeset(dev))
292b8bbbea1SThomas Zimmermann 		drm_helper_disable_unused_functions(dev);
293b8bbbea1SThomas Zimmermann 
294b8bbbea1SThomas Zimmermann 	ret = drm_fb_helper_initial_config(fb_helper);
295b8bbbea1SThomas Zimmermann 	if (ret)
2968f1aaccbSThomas Zimmermann 		goto err_drm_fb_helper_fini;
297b8bbbea1SThomas Zimmermann 
298b8bbbea1SThomas Zimmermann 	return 0;
299b8bbbea1SThomas Zimmermann 
3008f1aaccbSThomas Zimmermann err_drm_fb_helper_fini:
301b8bbbea1SThomas Zimmermann 	drm_fb_helper_fini(fb_helper);
3028f1aaccbSThomas Zimmermann err_drm_err:
3038f1aaccbSThomas Zimmermann 	drm_err(dev, "Failed to setup gma500 fbdev emulation (ret=%d)\n", ret);
304b8bbbea1SThomas Zimmermann 	return ret;
305b8bbbea1SThomas Zimmermann }
306b8bbbea1SThomas Zimmermann 
3078f1aaccbSThomas Zimmermann static const struct drm_client_funcs psb_fbdev_client_funcs = {
3088f1aaccbSThomas Zimmermann 	.owner		= THIS_MODULE,
3098f1aaccbSThomas Zimmermann 	.unregister	= psb_fbdev_client_unregister,
3108f1aaccbSThomas Zimmermann 	.restore	= psb_fbdev_client_restore,
3118f1aaccbSThomas Zimmermann 	.hotplug	= psb_fbdev_client_hotplug,
3128f1aaccbSThomas Zimmermann };
313b8bbbea1SThomas Zimmermann 
psb_fbdev_setup(struct drm_psb_private * dev_priv)3148f1aaccbSThomas Zimmermann void psb_fbdev_setup(struct drm_psb_private *dev_priv)
3158f1aaccbSThomas Zimmermann {
3168f1aaccbSThomas Zimmermann 	struct drm_device *dev = &dev_priv->dev;
3178f1aaccbSThomas Zimmermann 	struct drm_fb_helper *fb_helper;
3188f1aaccbSThomas Zimmermann 	int ret;
3198f1aaccbSThomas Zimmermann 
3208f1aaccbSThomas Zimmermann 	fb_helper = kzalloc(sizeof(*fb_helper), GFP_KERNEL);
3218f1aaccbSThomas Zimmermann 	if (!fb_helper)
3228f1aaccbSThomas Zimmermann 		return;
3238f1aaccbSThomas Zimmermann 	drm_fb_helper_prepare(dev, fb_helper, 32, &psb_fbdev_fb_helper_funcs);
3248f1aaccbSThomas Zimmermann 
3258f1aaccbSThomas Zimmermann 	ret = drm_client_init(dev, &fb_helper->client, "fbdev-gma500", &psb_fbdev_client_funcs);
3268f1aaccbSThomas Zimmermann 	if (ret) {
3278f1aaccbSThomas Zimmermann 		drm_err(dev, "Failed to register client: %d\n", ret);
3288f1aaccbSThomas Zimmermann 		goto err_drm_fb_helper_unprepare;
3298f1aaccbSThomas Zimmermann 	}
3308f1aaccbSThomas Zimmermann 
3318f1aaccbSThomas Zimmermann 	drm_client_register(&fb_helper->client);
3328f1aaccbSThomas Zimmermann 
333b8bbbea1SThomas Zimmermann 	return;
334b8bbbea1SThomas Zimmermann 
3358f1aaccbSThomas Zimmermann err_drm_fb_helper_unprepare:
3368f1aaccbSThomas Zimmermann 	drm_fb_helper_unprepare(fb_helper);
3378f1aaccbSThomas Zimmermann 	kfree(fb_helper);
338b8bbbea1SThomas Zimmermann }
339