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 | |
96f60e37 RK |
92 | drm_fb_helper_fill_fix(info, dfb->fb.pitches[0], dfb->fb.depth); |
93 | drm_fb_helper_fill_var(info, fbh, sizes->fb_width, sizes->fb_height); | |
94 | ||
7513e095 RK |
95 | DRM_DEBUG_KMS("allocated %dx%d %dbpp fb: 0x%08llx\n", |
96 | dfb->fb.width, dfb->fb.height, dfb->fb.bits_per_pixel, | |
97 | (unsigned long long)obj->phys_addr); | |
96f60e37 RK |
98 | |
99 | return 0; | |
100 | ||
96f60e37 RK |
101 | err_fballoc: |
102 | dfb->fb.funcs->destroy(&dfb->fb); | |
103 | return ret; | |
104 | } | |
105 | ||
106 | static int armada_fb_probe(struct drm_fb_helper *fbh, | |
107 | struct drm_fb_helper_surface_size *sizes) | |
108 | { | |
109 | int ret = 0; | |
110 | ||
111 | if (!fbh->fb) { | |
112 | ret = armada_fb_create(fbh, sizes); | |
113 | if (ret == 0) | |
114 | ret = 1; | |
115 | } | |
116 | return ret; | |
117 | } | |
118 | ||
3a493879 | 119 | static const struct drm_fb_helper_funcs armada_fb_helper_funcs = { |
96f60e37 RK |
120 | .gamma_set = armada_drm_crtc_gamma_set, |
121 | .gamma_get = armada_drm_crtc_gamma_get, | |
122 | .fb_probe = armada_fb_probe, | |
123 | }; | |
124 | ||
125 | int armada_fbdev_init(struct drm_device *dev) | |
126 | { | |
127 | struct armada_private *priv = dev->dev_private; | |
128 | struct drm_fb_helper *fbh; | |
129 | int ret; | |
130 | ||
131 | fbh = devm_kzalloc(dev->dev, sizeof(*fbh), GFP_KERNEL); | |
132 | if (!fbh) | |
133 | return -ENOMEM; | |
134 | ||
135 | priv->fbdev = fbh; | |
136 | ||
10a23102 | 137 | drm_fb_helper_prepare(dev, fbh, &armada_fb_helper_funcs); |
96f60e37 RK |
138 | |
139 | ret = drm_fb_helper_init(dev, fbh, 1, 1); | |
140 | if (ret) { | |
141 | DRM_ERROR("failed to initialize drm fb helper\n"); | |
142 | goto err_fb_helper; | |
143 | } | |
144 | ||
145 | ret = drm_fb_helper_single_add_all_connectors(fbh); | |
146 | if (ret) { | |
147 | DRM_ERROR("failed to add fb connectors\n"); | |
148 | goto err_fb_setup; | |
149 | } | |
150 | ||
151 | ret = drm_fb_helper_initial_config(fbh, 32); | |
152 | if (ret) { | |
153 | DRM_ERROR("failed to set initial config\n"); | |
154 | goto err_fb_setup; | |
155 | } | |
156 | ||
157 | return 0; | |
158 | err_fb_setup: | |
e8b70e4d | 159 | drm_fb_helper_release_fbi(fbh); |
96f60e37 RK |
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 AT |
180 | drm_fb_helper_unregister_fbi(fbh); |
181 | drm_fb_helper_release_fbi(fbh); | |
96f60e37 | 182 | |
077acbab RK |
183 | drm_fb_helper_fini(fbh); |
184 | ||
96f60e37 RK |
185 | if (fbh->fb) |
186 | fbh->fb->funcs->destroy(fbh->fb); | |
187 | ||
96f60e37 RK |
188 | priv->fbdev = NULL; |
189 | } | |
190 | } |