]>
Commit | Line | Data |
---|---|---|
d2912cb1 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
96f60e37 RK |
2 | /* |
3 | * Copyright (C) 2012 Russell King | |
4 | * Written from the i915 driver. | |
96f60e37 | 5 | */ |
25e28ef2 | 6 | |
96f60e37 | 7 | #include <linux/errno.h> |
96f60e37 RK |
8 | #include <linux/kernel.h> |
9 | #include <linux/module.h> | |
10 | ||
96f60e37 | 11 | #include <drm/drm_fb_helper.h> |
25e28ef2 SR |
12 | #include <drm/drm_fourcc.h> |
13 | ||
96f60e37 RK |
14 | #include "armada_crtc.h" |
15 | #include "armada_drm.h" | |
16 | #include "armada_fb.h" | |
17 | #include "armada_gem.h" | |
18 | ||
b6ff753a | 19 | static const struct fb_ops armada_fb_ops = { |
96f60e37 | 20 | .owner = THIS_MODULE, |
1caae277 | 21 | DRM_FB_HELPER_DEFAULT_OPS, |
e8b70e4d AT |
22 | .fb_fillrect = drm_fb_helper_cfb_fillrect, |
23 | .fb_copyarea = drm_fb_helper_cfb_copyarea, | |
24 | .fb_imageblit = drm_fb_helper_cfb_imageblit, | |
96f60e37 RK |
25 | }; |
26 | ||
3382a6b9 | 27 | static int armada_fbdev_create(struct drm_fb_helper *fbh, |
96f60e37 RK |
28 | struct drm_fb_helper_surface_size *sizes) |
29 | { | |
30 | struct drm_device *dev = fbh->dev; | |
31 | struct drm_mode_fb_cmd2 mode; | |
32 | struct armada_framebuffer *dfb; | |
33 | struct armada_gem_object *obj; | |
34 | struct fb_info *info; | |
35 | int size, ret; | |
36 | void *ptr; | |
37 | ||
38 | memset(&mode, 0, sizeof(mode)); | |
39 | mode.width = sizes->surface_width; | |
40 | mode.height = sizes->surface_height; | |
41 | mode.pitches[0] = armada_pitch(mode.width, sizes->surface_bpp); | |
42 | mode.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, | |
43 | sizes->surface_depth); | |
44 | ||
45 | size = mode.pitches[0] * mode.height; | |
46 | obj = armada_gem_alloc_private_object(dev, size); | |
47 | if (!obj) { | |
48 | DRM_ERROR("failed to allocate fb memory\n"); | |
49 | return -ENOMEM; | |
50 | } | |
51 | ||
52 | ret = armada_gem_linear_back(dev, obj); | |
53 | if (ret) { | |
dda156cf | 54 | drm_gem_object_put(&obj->obj); |
96f60e37 RK |
55 | return ret; |
56 | } | |
57 | ||
58 | ptr = armada_gem_map_object(dev, obj); | |
59 | if (!ptr) { | |
dda156cf | 60 | drm_gem_object_put(&obj->obj); |
96f60e37 RK |
61 | return -ENOMEM; |
62 | } | |
63 | ||
64 | dfb = armada_framebuffer_create(dev, &mode, obj); | |
65 | ||
66 | /* | |
67 | * A reference is now held by the framebuffer object if | |
68 | * successful, otherwise this drops the ref for the error path. | |
69 | */ | |
dda156cf | 70 | drm_gem_object_put(&obj->obj); |
96f60e37 RK |
71 | |
72 | if (IS_ERR(dfb)) | |
73 | return PTR_ERR(dfb); | |
74 | ||
e8b70e4d AT |
75 | info = drm_fb_helper_alloc_fbi(fbh); |
76 | if (IS_ERR(info)) { | |
77 | ret = PTR_ERR(info); | |
96f60e37 RK |
78 | goto err_fballoc; |
79 | } | |
80 | ||
96f60e37 RK |
81 | info->fbops = &armada_fb_ops; |
82 | info->fix.smem_start = obj->phys_addr; | |
83 | info->fix.smem_len = obj->obj.size; | |
84 | info->screen_size = obj->obj.size; | |
85 | info->screen_base = ptr; | |
86 | fbh->fb = &dfb->fb; | |
e8b70e4d | 87 | |
f21b6e47 | 88 | drm_fb_helper_fill_info(info, fbh, sizes); |
96f60e37 | 89 | |
7513e095 | 90 | DRM_DEBUG_KMS("allocated %dx%d %dbpp fb: 0x%08llx\n", |
272725c7 | 91 | dfb->fb.width, dfb->fb.height, dfb->fb.format->cpp[0] * 8, |
7513e095 | 92 | (unsigned long long)obj->phys_addr); |
96f60e37 RK |
93 | |
94 | return 0; | |
95 | ||
96f60e37 RK |
96 | err_fballoc: |
97 | dfb->fb.funcs->destroy(&dfb->fb); | |
98 | return ret; | |
99 | } | |
100 | ||
101 | static int armada_fb_probe(struct drm_fb_helper *fbh, | |
102 | struct drm_fb_helper_surface_size *sizes) | |
103 | { | |
104 | int ret = 0; | |
105 | ||
106 | if (!fbh->fb) { | |
3382a6b9 | 107 | ret = armada_fbdev_create(fbh, sizes); |
96f60e37 RK |
108 | if (ret == 0) |
109 | ret = 1; | |
110 | } | |
111 | return ret; | |
112 | } | |
113 | ||
3a493879 | 114 | static const struct drm_fb_helper_funcs armada_fb_helper_funcs = { |
96f60e37 RK |
115 | .fb_probe = armada_fb_probe, |
116 | }; | |
117 | ||
118 | int armada_fbdev_init(struct drm_device *dev) | |
119 | { | |
dad75a52 | 120 | struct armada_private *priv = drm_to_armada_dev(dev); |
96f60e37 RK |
121 | struct drm_fb_helper *fbh; |
122 | int ret; | |
123 | ||
124 | fbh = devm_kzalloc(dev->dev, sizeof(*fbh), GFP_KERNEL); | |
125 | if (!fbh) | |
126 | return -ENOMEM; | |
127 | ||
128 | priv->fbdev = fbh; | |
129 | ||
10a23102 | 130 | drm_fb_helper_prepare(dev, fbh, &armada_fb_helper_funcs); |
96f60e37 | 131 | |
2dea2d11 | 132 | ret = drm_fb_helper_init(dev, fbh); |
96f60e37 RK |
133 | if (ret) { |
134 | DRM_ERROR("failed to initialize drm fb helper\n"); | |
135 | goto err_fb_helper; | |
136 | } | |
137 | ||
96f60e37 RK |
138 | ret = drm_fb_helper_initial_config(fbh, 32); |
139 | if (ret) { | |
140 | DRM_ERROR("failed to set initial config\n"); | |
141 | goto err_fb_setup; | |
142 | } | |
143 | ||
144 | return 0; | |
145 | err_fb_setup: | |
146 | drm_fb_helper_fini(fbh); | |
147 | err_fb_helper: | |
148 | priv->fbdev = NULL; | |
149 | return ret; | |
150 | } | |
151 | ||
152 | void armada_fbdev_fini(struct drm_device *dev) | |
153 | { | |
dad75a52 | 154 | struct armada_private *priv = drm_to_armada_dev(dev); |
96f60e37 RK |
155 | struct drm_fb_helper *fbh = priv->fbdev; |
156 | ||
157 | if (fbh) { | |
e8b70e4d | 158 | drm_fb_helper_unregister_fbi(fbh); |
96f60e37 | 159 | |
077acbab RK |
160 | drm_fb_helper_fini(fbh); |
161 | ||
96f60e37 RK |
162 | if (fbh->fb) |
163 | fbh->fb->funcs->destroy(fbh->fb); | |
164 | ||
96f60e37 RK |
165 | priv->fbdev = NULL; |
166 | } | |
167 | } |