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