]>
Commit | Line | Data |
---|---|---|
96f60e37 RK |
1 | /* |
2 | * Copyright (C) 2012 Russell King | |
3 | * Written from the i915 driver. | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License version 2 as | |
7 | * published by the Free Software Foundation. | |
8 | */ | |
9 | #include <linux/errno.h> | |
96f60e37 RK |
10 | #include <linux/kernel.h> |
11 | #include <linux/module.h> | |
12 | ||
13 | #include <drm/drmP.h> | |
14 | #include <drm/drm_fb_helper.h> | |
15 | #include "armada_crtc.h" | |
16 | #include "armada_drm.h" | |
17 | #include "armada_fb.h" | |
18 | #include "armada_gem.h" | |
19 | ||
20 | static /*const*/ struct fb_ops armada_fb_ops = { | |
21 | .owner = THIS_MODULE, | |
1caae277 | 22 | DRM_FB_HELPER_DEFAULT_OPS, |
e8b70e4d AT |
23 | .fb_fillrect = drm_fb_helper_cfb_fillrect, |
24 | .fb_copyarea = drm_fb_helper_cfb_copyarea, | |
25 | .fb_imageblit = drm_fb_helper_cfb_imageblit, | |
96f60e37 RK |
26 | }; |
27 | ||
28 | static int armada_fb_create(struct drm_fb_helper *fbh, | |
29 | struct drm_fb_helper_surface_size *sizes) | |
30 | { | |
31 | struct drm_device *dev = fbh->dev; | |
32 | struct drm_mode_fb_cmd2 mode; | |
33 | struct armada_framebuffer *dfb; | |
34 | struct armada_gem_object *obj; | |
35 | struct fb_info *info; | |
36 | int size, ret; | |
37 | void *ptr; | |
38 | ||
39 | memset(&mode, 0, sizeof(mode)); | |
40 | mode.width = sizes->surface_width; | |
41 | mode.height = sizes->surface_height; | |
42 | mode.pitches[0] = armada_pitch(mode.width, sizes->surface_bpp); | |
43 | mode.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, | |
44 | sizes->surface_depth); | |
45 | ||
46 | size = mode.pitches[0] * mode.height; | |
47 | obj = armada_gem_alloc_private_object(dev, size); | |
48 | if (!obj) { | |
49 | DRM_ERROR("failed to allocate fb memory\n"); | |
50 | return -ENOMEM; | |
51 | } | |
52 | ||
53 | ret = armada_gem_linear_back(dev, obj); | |
54 | if (ret) { | |
55 | drm_gem_object_unreference_unlocked(&obj->obj); | |
56 | return ret; | |
57 | } | |
58 | ||
59 | ptr = armada_gem_map_object(dev, obj); | |
60 | if (!ptr) { | |
61 | drm_gem_object_unreference_unlocked(&obj->obj); | |
62 | return -ENOMEM; | |
63 | } | |
64 | ||
65 | dfb = armada_framebuffer_create(dev, &mode, obj); | |
66 | ||
67 | /* | |
68 | * A reference is now held by the framebuffer object if | |
69 | * successful, otherwise this drops the ref for the error path. | |
70 | */ | |
71 | drm_gem_object_unreference_unlocked(&obj->obj); | |
72 | ||
73 | if (IS_ERR(dfb)) | |
74 | return PTR_ERR(dfb); | |
75 | ||
e8b70e4d AT |
76 | info = drm_fb_helper_alloc_fbi(fbh); |
77 | if (IS_ERR(info)) { | |
78 | ret = PTR_ERR(info); | |
96f60e37 RK |
79 | goto err_fballoc; |
80 | } | |
81 | ||
96f60e37 RK |
82 | strlcpy(info->fix.id, "armada-drmfb", sizeof(info->fix.id)); |
83 | info->par = fbh; | |
84 | info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT; | |
85 | info->fbops = &armada_fb_ops; | |
86 | info->fix.smem_start = obj->phys_addr; | |
87 | info->fix.smem_len = obj->obj.size; | |
88 | info->screen_size = obj->obj.size; | |
89 | info->screen_base = ptr; | |
90 | fbh->fb = &dfb->fb; | |
e8b70e4d | 91 | |
b00c600e VS |
92 | drm_fb_helper_fill_fix(info, dfb->fb.pitches[0], |
93 | dfb->fb.format->depth); | |
96f60e37 RK |
94 | drm_fb_helper_fill_var(info, fbh, sizes->fb_width, sizes->fb_height); |
95 | ||
7513e095 | 96 | DRM_DEBUG_KMS("allocated %dx%d %dbpp fb: 0x%08llx\n", |
272725c7 | 97 | dfb->fb.width, dfb->fb.height, dfb->fb.format->cpp[0] * 8, |
7513e095 | 98 | (unsigned long long)obj->phys_addr); |
96f60e37 RK |
99 | |
100 | return 0; | |
101 | ||
96f60e37 RK |
102 | err_fballoc: |
103 | dfb->fb.funcs->destroy(&dfb->fb); | |
104 | return ret; | |
105 | } | |
106 | ||
107 | static int armada_fb_probe(struct drm_fb_helper *fbh, | |
108 | struct drm_fb_helper_surface_size *sizes) | |
109 | { | |
110 | int ret = 0; | |
111 | ||
112 | if (!fbh->fb) { | |
113 | ret = armada_fb_create(fbh, sizes); | |
114 | if (ret == 0) | |
115 | ret = 1; | |
116 | } | |
117 | return ret; | |
118 | } | |
119 | ||
3a493879 | 120 | static const struct drm_fb_helper_funcs armada_fb_helper_funcs = { |
96f60e37 RK |
121 | .gamma_set = armada_drm_crtc_gamma_set, |
122 | .gamma_get = armada_drm_crtc_gamma_get, | |
123 | .fb_probe = armada_fb_probe, | |
124 | }; | |
125 | ||
126 | int armada_fbdev_init(struct drm_device *dev) | |
127 | { | |
128 | struct armada_private *priv = dev->dev_private; | |
129 | struct drm_fb_helper *fbh; | |
130 | int ret; | |
131 | ||
132 | fbh = devm_kzalloc(dev->dev, sizeof(*fbh), GFP_KERNEL); | |
133 | if (!fbh) | |
134 | return -ENOMEM; | |
135 | ||
136 | priv->fbdev = fbh; | |
137 | ||
10a23102 | 138 | drm_fb_helper_prepare(dev, fbh, &armada_fb_helper_funcs); |
96f60e37 | 139 | |
e4563f6b | 140 | ret = drm_fb_helper_init(dev, fbh, 1); |
96f60e37 RK |
141 | if (ret) { |
142 | DRM_ERROR("failed to initialize drm fb helper\n"); | |
143 | goto err_fb_helper; | |
144 | } | |
145 | ||
146 | ret = drm_fb_helper_single_add_all_connectors(fbh); | |
147 | if (ret) { | |
148 | DRM_ERROR("failed to add fb connectors\n"); | |
149 | goto err_fb_setup; | |
150 | } | |
151 | ||
152 | ret = drm_fb_helper_initial_config(fbh, 32); | |
153 | if (ret) { | |
154 | DRM_ERROR("failed to set initial config\n"); | |
155 | goto err_fb_setup; | |
156 | } | |
157 | ||
158 | return 0; | |
159 | err_fb_setup: | |
160 | drm_fb_helper_fini(fbh); | |
161 | err_fb_helper: | |
162 | priv->fbdev = NULL; | |
163 | return ret; | |
164 | } | |
165 | ||
2f5ae490 RK |
166 | void armada_fbdev_lastclose(struct drm_device *dev) |
167 | { | |
168 | struct armada_private *priv = dev->dev_private; | |
169 | ||
2f5ae490 | 170 | if (priv->fbdev) |
5ea1f752 | 171 | drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev); |
2f5ae490 RK |
172 | } |
173 | ||
96f60e37 RK |
174 | void armada_fbdev_fini(struct drm_device *dev) |
175 | { | |
176 | struct armada_private *priv = dev->dev_private; | |
177 | struct drm_fb_helper *fbh = priv->fbdev; | |
178 | ||
179 | if (fbh) { | |
e8b70e4d | 180 | drm_fb_helper_unregister_fbi(fbh); |
96f60e37 | 181 | |
077acbab RK |
182 | drm_fb_helper_fini(fbh); |
183 | ||
96f60e37 RK |
184 | if (fbh->fb) |
185 | fbh->fb->funcs->destroy(fbh->fb); | |
186 | ||
96f60e37 RK |
187 | priv->fbdev = NULL; |
188 | } | |
189 | } |