Commit | Line | Data |
---|---|---|
2e3b3c42 LPC |
1 | /* |
2 | * drm kms/fb cma (contiguous memory allocator) helper functions | |
3 | * | |
4 | * Copyright (C) 2012 Analog Device Inc. | |
5 | * Author: Lars-Peter Clausen <lars@metafoo.de> | |
6 | * | |
7 | * Based on udl_fbdev.c | |
8 | * Copyright (C) 2012 Red Hat | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or | |
11 | * modify it under the terms of the GNU General Public License | |
12 | * as published by the Free Software Foundation; either version 2 | |
13 | * of the License, or (at your option) any later version. | |
14 | * This program is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | * GNU General Public License for more details. | |
18 | */ | |
19 | ||
20 | #include <drm/drmP.h> | |
894a677f | 21 | #include <drm/drm_client.h> |
2e3b3c42 | 22 | #include <drm/drm_fb_helper.h> |
5628648d | 23 | #include <drm/drm_framebuffer.h> |
2e3b3c42 | 24 | #include <drm/drm_gem_cma_helper.h> |
5628648d | 25 | #include <drm/drm_gem_framebuffer_helper.h> |
2e3b3c42 | 26 | #include <drm/drm_fb_cma_helper.h> |
41b676e0 | 27 | #include <drm/drm_print.h> |
2e3b3c42 LPC |
28 | #include <linux/module.h> |
29 | ||
2e3b3c42 LPC |
30 | struct drm_fbdev_cma { |
31 | struct drm_fb_helper fb_helper; | |
2e3b3c42 LPC |
32 | }; |
33 | ||
199c7717 NT |
34 | /** |
35 | * DOC: framebuffer cma helper functions | |
36 | * | |
37 | * Provides helper functions for creating a cma (contiguous memory allocator) | |
38 | * backed framebuffer. | |
39 | * | |
c0f095f7 | 40 | * drm_gem_fb_create() is used in the &drm_mode_config_funcs.fb_create |
02da16d0 | 41 | * callback function to create a cma backed framebuffer. |
199c7717 NT |
42 | * |
43 | * An fbdev framebuffer backed by cma is also available by calling | |
41b676e0 | 44 | * drm_fb_cma_fbdev_init(). drm_fb_cma_fbdev_fini() tears it down. |
199c7717 NT |
45 | */ |
46 | ||
2e3b3c42 LPC |
47 | static inline struct drm_fbdev_cma *to_fbdev_cma(struct drm_fb_helper *helper) |
48 | { | |
49 | return container_of(helper, struct drm_fbdev_cma, fb_helper); | |
50 | } | |
51 | ||
2e3b3c42 LPC |
52 | /** |
53 | * drm_fb_cma_get_gem_obj() - Get CMA GEM object for framebuffer | |
54 | * @fb: The framebuffer | |
55 | * @plane: Which plane | |
56 | * | |
57 | * Return the CMA GEM object for given framebuffer. | |
58 | * | |
59 | * This function will usually be called from the CRTC callback functions. | |
60 | */ | |
61 | struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb, | |
890358a6 | 62 | unsigned int plane) |
2e3b3c42 | 63 | { |
5628648d | 64 | struct drm_gem_object *gem; |
2e3b3c42 | 65 | |
5628648d NT |
66 | gem = drm_gem_fb_get_obj(fb, plane); |
67 | if (!gem) | |
2e3b3c42 LPC |
68 | return NULL; |
69 | ||
5628648d | 70 | return to_drm_gem_cma_obj(gem); |
2e3b3c42 LPC |
71 | } |
72 | EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_obj); | |
73 | ||
4636ce93 | 74 | /** |
042bf753 AG |
75 | * drm_fb_cma_get_gem_addr() - Get physical address for framebuffer, for pixel |
76 | * formats where values are grouped in blocks this will get you the beginning of | |
77 | * the block | |
4636ce93 YF |
78 | * @fb: The framebuffer |
79 | * @state: Which state of drm plane | |
80 | * @plane: Which plane | |
81 | * Return the CMA GEM address for given framebuffer. | |
82 | * | |
83 | * This function will usually be called from the PLANE callback functions. | |
84 | */ | |
85 | dma_addr_t drm_fb_cma_get_gem_addr(struct drm_framebuffer *fb, | |
86 | struct drm_plane_state *state, | |
87 | unsigned int plane) | |
88 | { | |
5628648d | 89 | struct drm_gem_cma_object *obj; |
4636ce93 | 90 | dma_addr_t paddr; |
c76abab5 | 91 | u8 h_div = 1, v_div = 1; |
042bf753 AG |
92 | u32 block_w = drm_format_info_block_width(fb->format, plane); |
93 | u32 block_h = drm_format_info_block_height(fb->format, plane); | |
94 | u32 block_size = fb->format->char_per_block[plane]; | |
95 | u32 sample_x; | |
96 | u32 sample_y; | |
97 | u32 block_start_y; | |
98 | u32 num_hblocks; | |
4636ce93 | 99 | |
5628648d NT |
100 | obj = drm_fb_cma_get_gem_obj(fb, plane); |
101 | if (!obj) | |
4636ce93 YF |
102 | return 0; |
103 | ||
5628648d | 104 | paddr = obj->paddr + fb->offsets[plane]; |
c76abab5 AKH |
105 | |
106 | if (plane > 0) { | |
107 | h_div = fb->format->hsub; | |
108 | v_div = fb->format->vsub; | |
109 | } | |
110 | ||
042bf753 AG |
111 | sample_x = (state->src_x >> 16) / h_div; |
112 | sample_y = (state->src_y >> 16) / v_div; | |
113 | block_start_y = (sample_y / block_h) * block_h; | |
114 | num_hblocks = sample_x / block_w; | |
115 | ||
116 | paddr += fb->pitches[plane] * block_start_y; | |
117 | paddr += block_size * num_hblocks; | |
4636ce93 YF |
118 | |
119 | return paddr; | |
120 | } | |
121 | EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_addr); | |
122 | ||
41b676e0 NT |
123 | /** |
124 | * drm_fb_cma_fbdev_init() - Allocate and initialize fbdev emulation | |
125 | * @dev: DRM device | |
126 | * @preferred_bpp: Preferred bits per pixel for the device. | |
127 | * @dev->mode_config.preferred_depth is used if this is zero. | |
128 | * @max_conn_count: Maximum number of connectors. | |
129 | * @dev->mode_config.num_connector is used if this is zero. | |
130 | * | |
131 | * Returns: | |
132 | * Zero on success or negative error code on failure. | |
133 | */ | |
134 | int drm_fb_cma_fbdev_init(struct drm_device *dev, unsigned int preferred_bpp, | |
135 | unsigned int max_conn_count) | |
136 | { | |
894a677f NT |
137 | struct drm_fbdev_cma *fbdev_cma; |
138 | ||
139 | /* dev->fb_helper will indirectly point to fbdev_cma after this call */ | |
140 | fbdev_cma = drm_fbdev_cma_init(dev, preferred_bpp, max_conn_count); | |
2a7be4b4 | 141 | return PTR_ERR_OR_ZERO(fbdev_cma); |
41b676e0 NT |
142 | } |
143 | EXPORT_SYMBOL_GPL(drm_fb_cma_fbdev_init); | |
144 | ||
145 | /** | |
146 | * drm_fb_cma_fbdev_fini() - Teardown fbdev emulation | |
147 | * @dev: DRM device | |
148 | */ | |
149 | void drm_fb_cma_fbdev_fini(struct drm_device *dev) | |
150 | { | |
894a677f NT |
151 | if (dev->fb_helper) |
152 | drm_fbdev_cma_fini(to_fbdev_cma(dev->fb_helper)); | |
41b676e0 NT |
153 | } |
154 | EXPORT_SYMBOL_GPL(drm_fb_cma_fbdev_fini); | |
155 | ||
894a677f NT |
156 | static const struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = { |
157 | .fb_probe = drm_fb_helper_generic_probe, | |
158 | }; | |
159 | ||
2e3b3c42 | 160 | /** |
894a677f | 161 | * drm_fbdev_cma_init() - Allocate and initializes a drm_fbdev_cma struct |
2e3b3c42 LPC |
162 | * @dev: DRM device |
163 | * @preferred_bpp: Preferred bits per pixel for the device | |
2e3b3c42 LPC |
164 | * @max_conn_count: Maximum number of connectors |
165 | * | |
166 | * Returns a newly allocated drm_fbdev_cma struct or a ERR_PTR. | |
167 | */ | |
894a677f NT |
168 | struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev, |
169 | unsigned int preferred_bpp, unsigned int max_conn_count) | |
2e3b3c42 LPC |
170 | { |
171 | struct drm_fbdev_cma *fbdev_cma; | |
894a677f | 172 | struct drm_fb_helper *fb_helper; |
2e3b3c42 LPC |
173 | int ret; |
174 | ||
175 | fbdev_cma = kzalloc(sizeof(*fbdev_cma), GFP_KERNEL); | |
894a677f | 176 | if (!fbdev_cma) |
2e3b3c42 | 177 | return ERR_PTR(-ENOMEM); |
2e3b3c42 | 178 | |
894a677f | 179 | fb_helper = &fbdev_cma->fb_helper; |
10a23102 | 180 | |
4d4c2d89 | 181 | ret = drm_client_init(dev, &fb_helper->client, "fbdev", NULL); |
894a677f | 182 | if (ret) |
2e3b3c42 | 183 | goto err_free; |
2e3b3c42 | 184 | |
894a677f NT |
185 | ret = drm_fb_helper_fbdev_setup(dev, fb_helper, &drm_fb_cma_helper_funcs, |
186 | preferred_bpp, max_conn_count); | |
187 | if (ret) | |
188 | goto err_client_put; | |
2e3b3c42 | 189 | |
4d4c2d89 NT |
190 | drm_client_add(&fb_helper->client); |
191 | ||
2e3b3c42 LPC |
192 | return fbdev_cma; |
193 | ||
894a677f NT |
194 | err_client_put: |
195 | drm_client_release(&fb_helper->client); | |
2e3b3c42 LPC |
196 | err_free: |
197 | kfree(fbdev_cma); | |
198 | ||
199 | return ERR_PTR(ret); | |
200 | } | |
201 | EXPORT_SYMBOL_GPL(drm_fbdev_cma_init); | |
202 | ||
203 | /** | |
204 | * drm_fbdev_cma_fini() - Free drm_fbdev_cma struct | |
205 | * @fbdev_cma: The drm_fbdev_cma struct | |
206 | */ | |
207 | void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma) | |
208 | { | |
85f2edf2 | 209 | drm_fb_helper_unregister_fbi(&fbdev_cma->fb_helper); |
894a677f | 210 | /* All resources have now been freed by drm_fbdev_fb_destroy() */ |
2e3b3c42 LPC |
211 | } |
212 | EXPORT_SYMBOL_GPL(drm_fbdev_cma_fini); | |
213 | ||
214 | /** | |
215 | * drm_fbdev_cma_restore_mode() - Restores initial framebuffer mode | |
216 | * @fbdev_cma: The drm_fbdev_cma struct, may be NULL | |
217 | * | |
421242aa | 218 | * This function is usually called from the &drm_driver.lastclose callback. |
2e3b3c42 LPC |
219 | */ |
220 | void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma) | |
221 | { | |
5ea1f752 RC |
222 | if (fbdev_cma) |
223 | drm_fb_helper_restore_fbdev_mode_unlocked(&fbdev_cma->fb_helper); | |
2e3b3c42 LPC |
224 | } |
225 | EXPORT_SYMBOL_GPL(drm_fbdev_cma_restore_mode); | |
226 | ||
227 | /** | |
228 | * drm_fbdev_cma_hotplug_event() - Poll for hotpulug events | |
229 | * @fbdev_cma: The drm_fbdev_cma struct, may be NULL | |
230 | * | |
421242aa | 231 | * This function is usually called from the &drm_mode_config.output_poll_changed |
2e3b3c42 LPC |
232 | * callback. |
233 | */ | |
234 | void drm_fbdev_cma_hotplug_event(struct drm_fbdev_cma *fbdev_cma) | |
235 | { | |
236 | if (fbdev_cma) | |
237 | drm_fb_helper_hotplug_event(&fbdev_cma->fb_helper); | |
238 | } | |
239 | EXPORT_SYMBOL_GPL(drm_fbdev_cma_hotplug_event); |