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, | |
22 | .fb_check_var = drm_fb_helper_check_var, | |
23 | .fb_set_par = drm_fb_helper_set_par, | |
e8b70e4d AT |
24 | .fb_fillrect = drm_fb_helper_cfb_fillrect, |
25 | .fb_copyarea = drm_fb_helper_cfb_copyarea, | |
26 | .fb_imageblit = drm_fb_helper_cfb_imageblit, | |
96f60e37 RK |
27 | .fb_pan_display = drm_fb_helper_pan_display, |
28 | .fb_blank = drm_fb_helper_blank, | |
29 | .fb_setcmap = drm_fb_helper_setcmap, | |
30 | .fb_debug_enter = drm_fb_helper_debug_enter, | |
31 | .fb_debug_leave = drm_fb_helper_debug_leave, | |
32 | }; | |
33 | ||
34 | static int armada_fb_create(struct drm_fb_helper *fbh, | |
35 | struct drm_fb_helper_surface_size *sizes) | |
36 | { | |
37 | struct drm_device *dev = fbh->dev; | |
38 | struct drm_mode_fb_cmd2 mode; | |
39 | struct armada_framebuffer *dfb; | |
40 | struct armada_gem_object *obj; | |
41 | struct fb_info *info; | |
42 | int size, ret; | |
43 | void *ptr; | |
44 | ||
45 | memset(&mode, 0, sizeof(mode)); | |
46 | mode.width = sizes->surface_width; | |
47 | mode.height = sizes->surface_height; | |
48 | mode.pitches[0] = armada_pitch(mode.width, sizes->surface_bpp); | |
49 | mode.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, | |
50 | sizes->surface_depth); | |
51 | ||
52 | size = mode.pitches[0] * mode.height; | |
53 | obj = armada_gem_alloc_private_object(dev, size); | |
54 | if (!obj) { | |
55 | DRM_ERROR("failed to allocate fb memory\n"); | |
56 | return -ENOMEM; | |
57 | } | |
58 | ||
59 | ret = armada_gem_linear_back(dev, obj); | |
60 | if (ret) { | |
61 | drm_gem_object_unreference_unlocked(&obj->obj); | |
62 | return ret; | |
63 | } | |
64 | ||
65 | ptr = armada_gem_map_object(dev, obj); | |
66 | if (!ptr) { | |
67 | drm_gem_object_unreference_unlocked(&obj->obj); | |
68 | return -ENOMEM; | |
69 | } | |
70 | ||
71 | dfb = armada_framebuffer_create(dev, &mode, obj); | |
72 | ||
73 | /* | |
74 | * A reference is now held by the framebuffer object if | |
75 | * successful, otherwise this drops the ref for the error path. | |
76 | */ | |
77 | drm_gem_object_unreference_unlocked(&obj->obj); | |
78 | ||
79 | if (IS_ERR(dfb)) | |
80 | return PTR_ERR(dfb); | |
81 | ||
e8b70e4d AT |
82 | info = drm_fb_helper_alloc_fbi(fbh); |
83 | if (IS_ERR(info)) { | |
84 | ret = PTR_ERR(info); | |
96f60e37 RK |
85 | goto err_fballoc; |
86 | } | |
87 | ||
96f60e37 RK |
88 | strlcpy(info->fix.id, "armada-drmfb", sizeof(info->fix.id)); |
89 | info->par = fbh; | |
90 | info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT; | |
91 | info->fbops = &armada_fb_ops; | |
92 | info->fix.smem_start = obj->phys_addr; | |
93 | info->fix.smem_len = obj->obj.size; | |
94 | info->screen_size = obj->obj.size; | |
95 | info->screen_base = ptr; | |
96 | fbh->fb = &dfb->fb; | |
e8b70e4d | 97 | |
96f60e37 RK |
98 | drm_fb_helper_fill_fix(info, dfb->fb.pitches[0], dfb->fb.depth); |
99 | drm_fb_helper_fill_var(info, fbh, sizes->fb_width, sizes->fb_height); | |
100 | ||
7513e095 RK |
101 | DRM_DEBUG_KMS("allocated %dx%d %dbpp fb: 0x%08llx\n", |
102 | dfb->fb.width, dfb->fb.height, dfb->fb.bits_per_pixel, | |
103 | (unsigned long long)obj->phys_addr); | |
96f60e37 RK |
104 | |
105 | return 0; | |
106 | ||
96f60e37 RK |
107 | err_fballoc: |
108 | dfb->fb.funcs->destroy(&dfb->fb); | |
109 | return ret; | |
110 | } | |
111 | ||
112 | static int armada_fb_probe(struct drm_fb_helper *fbh, | |
113 | struct drm_fb_helper_surface_size *sizes) | |
114 | { | |
115 | int ret = 0; | |
116 | ||
117 | if (!fbh->fb) { | |
118 | ret = armada_fb_create(fbh, sizes); | |
119 | if (ret == 0) | |
120 | ret = 1; | |
121 | } | |
122 | return ret; | |
123 | } | |
124 | ||
3a493879 | 125 | static const struct drm_fb_helper_funcs armada_fb_helper_funcs = { |
96f60e37 RK |
126 | .gamma_set = armada_drm_crtc_gamma_set, |
127 | .gamma_get = armada_drm_crtc_gamma_get, | |
128 | .fb_probe = armada_fb_probe, | |
129 | }; | |
130 | ||
131 | int armada_fbdev_init(struct drm_device *dev) | |
132 | { | |
133 | struct armada_private *priv = dev->dev_private; | |
134 | struct drm_fb_helper *fbh; | |
135 | int ret; | |
136 | ||
137 | fbh = devm_kzalloc(dev->dev, sizeof(*fbh), GFP_KERNEL); | |
138 | if (!fbh) | |
139 | return -ENOMEM; | |
140 | ||
141 | priv->fbdev = fbh; | |
142 | ||
10a23102 | 143 | drm_fb_helper_prepare(dev, fbh, &armada_fb_helper_funcs); |
96f60e37 RK |
144 | |
145 | ret = drm_fb_helper_init(dev, fbh, 1, 1); | |
146 | if (ret) { | |
147 | DRM_ERROR("failed to initialize drm fb helper\n"); | |
148 | goto err_fb_helper; | |
149 | } | |
150 | ||
151 | ret = drm_fb_helper_single_add_all_connectors(fbh); | |
152 | if (ret) { | |
153 | DRM_ERROR("failed to add fb connectors\n"); | |
154 | goto err_fb_setup; | |
155 | } | |
156 | ||
157 | ret = drm_fb_helper_initial_config(fbh, 32); | |
158 | if (ret) { | |
159 | DRM_ERROR("failed to set initial config\n"); | |
160 | goto err_fb_setup; | |
161 | } | |
162 | ||
163 | return 0; | |
164 | err_fb_setup: | |
e8b70e4d | 165 | drm_fb_helper_release_fbi(fbh); |
96f60e37 RK |
166 | drm_fb_helper_fini(fbh); |
167 | err_fb_helper: | |
168 | priv->fbdev = NULL; | |
169 | return ret; | |
170 | } | |
171 | ||
2f5ae490 RK |
172 | void armada_fbdev_lastclose(struct drm_device *dev) |
173 | { | |
174 | struct armada_private *priv = dev->dev_private; | |
175 | ||
2f5ae490 | 176 | if (priv->fbdev) |
5ea1f752 | 177 | drm_fb_helper_restore_fbdev_mode_unlocked(priv->fbdev); |
2f5ae490 RK |
178 | } |
179 | ||
96f60e37 RK |
180 | void armada_fbdev_fini(struct drm_device *dev) |
181 | { | |
182 | struct armada_private *priv = dev->dev_private; | |
183 | struct drm_fb_helper *fbh = priv->fbdev; | |
184 | ||
185 | if (fbh) { | |
e8b70e4d AT |
186 | drm_fb_helper_unregister_fbi(fbh); |
187 | drm_fb_helper_release_fbi(fbh); | |
96f60e37 | 188 | |
077acbab RK |
189 | drm_fb_helper_fini(fbh); |
190 | ||
96f60e37 RK |
191 | if (fbh->fb) |
192 | fbh->fb->funcs->destroy(fbh->fb); | |
193 | ||
96f60e37 RK |
194 | priv->fbdev = NULL; |
195 | } | |
196 | } |