Rev 1246 | Rev 1313 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 1246 | Rev 1268 | ||
---|---|---|---|
1 | /* |
1 | /* |
2 | * Copyright (c) 2006-2009 Red Hat Inc. |
2 | * Copyright (c) 2006-2009 Red Hat Inc. |
3 | * Copyright (c) 2006-2008 Intel Corporation |
3 | * Copyright (c) 2006-2008 Intel Corporation |
4 | * Copyright (c) 2007 Dave Airlie |
4 | * Copyright (c) 2007 Dave Airlie |
5 | * |
5 | * |
6 | * DRM framebuffer helper functions |
6 | * DRM framebuffer helper functions |
7 | * |
7 | * |
8 | * Permission to use, copy, modify, distribute, and sell this software and its |
8 | * Permission to use, copy, modify, distribute, and sell this software and its |
9 | * documentation for any purpose is hereby granted without fee, provided that |
9 | * documentation for any purpose is hereby granted without fee, provided that |
10 | * the above copyright notice appear in all copies and that both that copyright |
10 | * the above copyright notice appear in all copies and that both that copyright |
11 | * notice and this permission notice appear in supporting documentation, and |
11 | * notice and this permission notice appear in supporting documentation, and |
12 | * that the name of the copyright holders not be used in advertising or |
12 | * that the name of the copyright holders not be used in advertising or |
13 | * publicity pertaining to distribution of the software without specific, |
13 | * publicity pertaining to distribution of the software without specific, |
14 | * written prior permission. The copyright holders make no representations |
14 | * written prior permission. The copyright holders make no representations |
15 | * about the suitability of this software for any purpose. It is provided "as |
15 | * about the suitability of this software for any purpose. It is provided "as |
16 | * is" without express or implied warranty. |
16 | * is" without express or implied warranty. |
17 | * |
17 | * |
18 | * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, |
18 | * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, |
19 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO |
19 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO |
20 | * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR |
20 | * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR |
21 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, |
21 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, |
22 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
22 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
23 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE |
23 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE |
24 | * OF THIS SOFTWARE. |
24 | * OF THIS SOFTWARE. |
25 | * |
25 | * |
26 | * Authors: |
26 | * Authors: |
27 | * Dave Airlie |
27 | * Dave Airlie |
28 | * Jesse Barnes |
28 | * Jesse Barnes |
29 | */ |
29 | */ |
30 | //#include |
30 | //#include |
31 | #include |
31 | #include |
32 | #include "drmP.h" |
32 | #include "drmP.h" |
33 | #include "drm_crtc.h" |
33 | #include "drm_crtc.h" |
34 | #include "drm_fb_helper.h" |
34 | #include "drm_fb_helper.h" |
35 | #include "drm_crtc_helper.h" |
35 | #include "drm_crtc_helper.h" |
36 | 36 | ||
37 | //MODULE_AUTHOR("David Airlie, Jesse Barnes"); |
37 | //MODULE_AUTHOR("David Airlie, Jesse Barnes"); |
38 | //MODULE_DESCRIPTION("DRM KMS helper"); |
38 | //MODULE_DESCRIPTION("DRM KMS helper"); |
39 | //MODULE_LICENSE("GPL and additional rights"); |
39 | //MODULE_LICENSE("GPL and additional rights"); |
40 | 40 | ||
41 | static LIST_HEAD(kernel_fb_helper_list); |
41 | static LIST_HEAD(kernel_fb_helper_list); |
42 | 42 | ||
43 | int drm_fb_helper_add_connector(struct drm_connector *connector) |
43 | int drm_fb_helper_add_connector(struct drm_connector *connector) |
44 | { |
44 | { |
45 | connector->fb_helper_private = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL); |
45 | connector->fb_helper_private = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL); |
46 | if (!connector->fb_helper_private) |
46 | if (!connector->fb_helper_private) |
47 | return -ENOMEM; |
47 | return -ENOMEM; |
48 | 48 | ||
49 | return 0; |
49 | return 0; |
50 | } |
50 | } |
51 | EXPORT_SYMBOL(drm_fb_helper_add_connector); |
51 | EXPORT_SYMBOL(drm_fb_helper_add_connector); |
52 | 52 | ||
53 | bool drm_fb_helper_force_kernel_mode(void) |
53 | bool drm_fb_helper_force_kernel_mode(void) |
54 | { |
54 | { |
55 | int i = 0; |
55 | int i = 0; |
56 | bool ret, error = false; |
56 | bool ret, error = false; |
57 | struct drm_fb_helper *helper; |
57 | struct drm_fb_helper *helper; |
58 | 58 | ||
59 | if (list_empty(&kernel_fb_helper_list)) |
59 | if (list_empty(&kernel_fb_helper_list)) |
60 | return false; |
60 | return false; |
61 | 61 | ||
62 | list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) { |
62 | list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) { |
63 | for (i = 0; i < helper->crtc_count; i++) { |
63 | for (i = 0; i < helper->crtc_count; i++) { |
64 | struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set; |
64 | struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set; |
65 | ret = drm_crtc_helper_set_config(mode_set); |
65 | ret = drm_crtc_helper_set_config(mode_set); |
66 | if (ret) |
66 | if (ret) |
67 | error = true; |
67 | error = true; |
68 | } |
68 | } |
69 | } |
69 | } |
70 | return error; |
70 | return error; |
71 | } |
71 | } |
72 | 72 | ||
73 | /** |
73 | /** |
74 | * drm_fb_helper_restore - restore the framebuffer console (kernel) config |
74 | * drm_fb_helper_restore - restore the framebuffer console (kernel) config |
75 | * |
75 | * |
76 | * Restore's the kernel's fbcon mode, used for lastclose & panic paths. |
76 | * Restore's the kernel's fbcon mode, used for lastclose & panic paths. |
77 | */ |
77 | */ |
78 | void drm_fb_helper_restore(void) |
78 | void drm_fb_helper_restore(void) |
79 | { |
79 | { |
80 | bool ret; |
80 | bool ret; |
81 | ret = drm_fb_helper_force_kernel_mode(); |
81 | ret = drm_fb_helper_force_kernel_mode(); |
82 | if (ret == true) |
82 | if (ret == true) |
83 | DRM_ERROR("Failed to restore crtc configuration\n"); |
83 | DRM_ERROR("Failed to restore crtc configuration\n"); |
84 | } |
84 | } |
85 | EXPORT_SYMBOL(drm_fb_helper_restore); |
85 | EXPORT_SYMBOL(drm_fb_helper_restore); |
86 | 86 | ||
87 | 87 | ||
88 | static void drm_fb_helper_on(struct fb_info *info) |
88 | static void drm_fb_helper_on(struct fb_info *info) |
89 | { |
89 | { |
90 | struct drm_fb_helper *fb_helper = info->par; |
90 | struct drm_fb_helper *fb_helper = info->par; |
91 | struct drm_device *dev = fb_helper->dev; |
91 | struct drm_device *dev = fb_helper->dev; |
92 | struct drm_crtc *crtc; |
92 | struct drm_crtc *crtc; |
93 | struct drm_encoder *encoder; |
93 | struct drm_encoder *encoder; |
94 | int i; |
94 | int i; |
95 | 95 | ||
96 | /* |
96 | /* |
97 | * For each CRTC in this fb, turn the crtc on then, |
97 | * For each CRTC in this fb, turn the crtc on then, |
98 | * find all associated encoders and turn them on. |
98 | * find all associated encoders and turn them on. |
99 | */ |
99 | */ |
100 | for (i = 0; i < fb_helper->crtc_count; i++) { |
100 | for (i = 0; i < fb_helper->crtc_count; i++) { |
101 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
101 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
102 | struct drm_crtc_helper_funcs *crtc_funcs = |
102 | struct drm_crtc_helper_funcs *crtc_funcs = |
103 | crtc->helper_private; |
103 | crtc->helper_private; |
104 | 104 | ||
105 | /* Only mess with CRTCs in this fb */ |
105 | /* Only mess with CRTCs in this fb */ |
106 | if (crtc->base.id != fb_helper->crtc_info[i].crtc_id || |
106 | if (crtc->base.id != fb_helper->crtc_info[i].crtc_id || |
107 | !crtc->enabled) |
107 | !crtc->enabled) |
108 | continue; |
108 | continue; |
109 | 109 | ||
110 | mutex_lock(&dev->mode_config.mutex); |
110 | mutex_lock(&dev->mode_config.mutex); |
111 | crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); |
111 | crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); |
112 | mutex_unlock(&dev->mode_config.mutex); |
112 | mutex_unlock(&dev->mode_config.mutex); |
113 | 113 | ||
114 | /* Found a CRTC on this fb, now find encoders */ |
114 | /* Found a CRTC on this fb, now find encoders */ |
115 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
115 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
116 | if (encoder->crtc == crtc) { |
116 | if (encoder->crtc == crtc) { |
117 | struct drm_encoder_helper_funcs *encoder_funcs; |
117 | struct drm_encoder_helper_funcs *encoder_funcs; |
118 | 118 | ||
119 | encoder_funcs = encoder->helper_private; |
119 | encoder_funcs = encoder->helper_private; |
120 | mutex_lock(&dev->mode_config.mutex); |
120 | mutex_lock(&dev->mode_config.mutex); |
121 | encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); |
121 | encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); |
122 | mutex_unlock(&dev->mode_config.mutex); |
122 | mutex_unlock(&dev->mode_config.mutex); |
123 | } |
123 | } |
124 | } |
124 | } |
125 | } |
125 | } |
126 | } |
126 | } |
127 | } |
127 | } |
128 | 128 | ||
129 | static void drm_fb_helper_off(struct fb_info *info, int dpms_mode) |
129 | static void drm_fb_helper_off(struct fb_info *info, int dpms_mode) |
130 | { |
130 | { |
131 | struct drm_fb_helper *fb_helper = info->par; |
131 | struct drm_fb_helper *fb_helper = info->par; |
132 | struct drm_device *dev = fb_helper->dev; |
132 | struct drm_device *dev = fb_helper->dev; |
133 | struct drm_crtc *crtc; |
133 | struct drm_crtc *crtc; |
134 | struct drm_encoder *encoder; |
134 | struct drm_encoder *encoder; |
135 | int i; |
135 | int i; |
136 | 136 | ||
137 | /* |
137 | /* |
138 | * For each CRTC in this fb, find all associated encoders |
138 | * For each CRTC in this fb, find all associated encoders |
139 | * and turn them off, then turn off the CRTC. |
139 | * and turn them off, then turn off the CRTC. |
140 | */ |
140 | */ |
141 | for (i = 0; i < fb_helper->crtc_count; i++) { |
141 | for (i = 0; i < fb_helper->crtc_count; i++) { |
142 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
142 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
143 | struct drm_crtc_helper_funcs *crtc_funcs = |
143 | struct drm_crtc_helper_funcs *crtc_funcs = |
144 | crtc->helper_private; |
144 | crtc->helper_private; |
145 | 145 | ||
146 | /* Only mess with CRTCs in this fb */ |
146 | /* Only mess with CRTCs in this fb */ |
147 | if (crtc->base.id != fb_helper->crtc_info[i].crtc_id || |
147 | if (crtc->base.id != fb_helper->crtc_info[i].crtc_id || |
148 | !crtc->enabled) |
148 | !crtc->enabled) |
149 | continue; |
149 | continue; |
150 | 150 | ||
151 | /* Found a CRTC on this fb, now find encoders */ |
151 | /* Found a CRTC on this fb, now find encoders */ |
152 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
152 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { |
153 | if (encoder->crtc == crtc) { |
153 | if (encoder->crtc == crtc) { |
154 | struct drm_encoder_helper_funcs *encoder_funcs; |
154 | struct drm_encoder_helper_funcs *encoder_funcs; |
155 | 155 | ||
156 | encoder_funcs = encoder->helper_private; |
156 | encoder_funcs = encoder->helper_private; |
157 | mutex_lock(&dev->mode_config.mutex); |
157 | mutex_lock(&dev->mode_config.mutex); |
158 | encoder_funcs->dpms(encoder, dpms_mode); |
158 | encoder_funcs->dpms(encoder, dpms_mode); |
159 | mutex_unlock(&dev->mode_config.mutex); |
159 | mutex_unlock(&dev->mode_config.mutex); |
160 | } |
160 | } |
161 | } |
161 | } |
162 | if (dpms_mode == DRM_MODE_DPMS_OFF) { |
162 | if (dpms_mode == DRM_MODE_DPMS_OFF) { |
163 | mutex_lock(&dev->mode_config.mutex); |
163 | mutex_lock(&dev->mode_config.mutex); |
164 | crtc_funcs->dpms(crtc, dpms_mode); |
164 | crtc_funcs->dpms(crtc, dpms_mode); |
165 | mutex_unlock(&dev->mode_config.mutex); |
165 | mutex_unlock(&dev->mode_config.mutex); |
166 | } |
166 | } |
167 | } |
167 | } |
168 | } |
168 | } |
169 | } |
169 | } |
170 | 170 | ||
171 | int drm_fb_helper_blank(int blank, struct fb_info *info) |
171 | int drm_fb_helper_blank(int blank, struct fb_info *info) |
172 | { |
172 | { |
173 | switch (blank) { |
173 | switch (blank) { |
174 | case FB_BLANK_UNBLANK: |
174 | case FB_BLANK_UNBLANK: |
175 | drm_fb_helper_on(info); |
175 | drm_fb_helper_on(info); |
176 | break; |
176 | break; |
177 | case FB_BLANK_NORMAL: |
177 | case FB_BLANK_NORMAL: |
178 | drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY); |
178 | drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY); |
179 | break; |
179 | break; |
180 | case FB_BLANK_HSYNC_SUSPEND: |
180 | case FB_BLANK_HSYNC_SUSPEND: |
181 | drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY); |
181 | drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY); |
182 | break; |
182 | break; |
183 | case FB_BLANK_VSYNC_SUSPEND: |
183 | case FB_BLANK_VSYNC_SUSPEND: |
184 | drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND); |
184 | drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND); |
185 | break; |
185 | break; |
186 | case FB_BLANK_POWERDOWN: |
186 | case FB_BLANK_POWERDOWN: |
187 | drm_fb_helper_off(info, DRM_MODE_DPMS_OFF); |
187 | drm_fb_helper_off(info, DRM_MODE_DPMS_OFF); |
188 | break; |
188 | break; |
189 | } |
189 | } |
190 | return 0; |
190 | return 0; |
191 | } |
191 | } |
192 | EXPORT_SYMBOL(drm_fb_helper_blank); |
192 | EXPORT_SYMBOL(drm_fb_helper_blank); |
193 | 193 | ||
194 | static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper) |
194 | static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper) |
195 | { |
195 | { |
196 | int i; |
196 | int i; |
197 | 197 | ||
198 | for (i = 0; i < helper->crtc_count; i++) |
198 | for (i = 0; i < helper->crtc_count; i++) |
199 | kfree(helper->crtc_info[i].mode_set.connectors); |
199 | kfree(helper->crtc_info[i].mode_set.connectors); |
200 | kfree(helper->crtc_info); |
200 | kfree(helper->crtc_info); |
201 | } |
201 | } |
202 | 202 | ||
203 | int drm_fb_helper_init_crtc_count(struct drm_fb_helper *helper, int crtc_count, int max_conn_count) |
203 | int drm_fb_helper_init_crtc_count(struct drm_fb_helper *helper, int crtc_count, int max_conn_count) |
204 | { |
204 | { |
205 | struct drm_device *dev = helper->dev; |
205 | struct drm_device *dev = helper->dev; |
206 | struct drm_crtc *crtc; |
206 | struct drm_crtc *crtc; |
207 | int ret = 0; |
207 | int ret = 0; |
208 | int i; |
208 | int i; |
209 | 209 | ||
210 | helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL); |
210 | helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL); |
211 | if (!helper->crtc_info) |
211 | if (!helper->crtc_info) |
212 | return -ENOMEM; |
212 | return -ENOMEM; |
213 | 213 | ||
214 | helper->crtc_count = crtc_count; |
214 | helper->crtc_count = crtc_count; |
215 | 215 | ||
216 | for (i = 0; i < crtc_count; i++) { |
216 | for (i = 0; i < crtc_count; i++) { |
217 | helper->crtc_info[i].mode_set.connectors = |
217 | helper->crtc_info[i].mode_set.connectors = |
218 | kcalloc(max_conn_count, |
218 | kcalloc(max_conn_count, |
219 | sizeof(struct drm_connector *), |
219 | sizeof(struct drm_connector *), |
220 | GFP_KERNEL); |
220 | GFP_KERNEL); |
221 | 221 | ||
222 | if (!helper->crtc_info[i].mode_set.connectors) { |
222 | if (!helper->crtc_info[i].mode_set.connectors) { |
223 | ret = -ENOMEM; |
223 | ret = -ENOMEM; |
224 | goto out_free; |
224 | goto out_free; |
225 | } |
225 | } |
226 | helper->crtc_info[i].mode_set.num_connectors = 0; |
226 | helper->crtc_info[i].mode_set.num_connectors = 0; |
227 | } |
227 | } |
228 | 228 | ||
229 | i = 0; |
229 | i = 0; |
230 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
230 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
231 | helper->crtc_info[i].crtc_id = crtc->base.id; |
231 | helper->crtc_info[i].crtc_id = crtc->base.id; |
232 | helper->crtc_info[i].mode_set.crtc = crtc; |
232 | helper->crtc_info[i].mode_set.crtc = crtc; |
233 | i++; |
233 | i++; |
234 | } |
234 | } |
235 | helper->conn_limit = max_conn_count; |
235 | helper->conn_limit = max_conn_count; |
236 | return 0; |
236 | return 0; |
237 | out_free: |
237 | out_free: |
238 | drm_fb_helper_crtc_free(helper); |
238 | drm_fb_helper_crtc_free(helper); |
239 | return -ENOMEM; |
239 | return -ENOMEM; |
240 | } |
240 | } |
241 | EXPORT_SYMBOL(drm_fb_helper_init_crtc_count); |
241 | EXPORT_SYMBOL(drm_fb_helper_init_crtc_count); |
242 | 242 | ||
243 | static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, |
243 | static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, |
244 | u16 blue, u16 regno, struct fb_info *info) |
244 | u16 blue, u16 regno, struct fb_info *info) |
245 | { |
245 | { |
246 | struct drm_fb_helper *fb_helper = info->par; |
246 | struct drm_fb_helper *fb_helper = info->par; |
247 | struct drm_framebuffer *fb = fb_helper->fb; |
247 | struct drm_framebuffer *fb = fb_helper->fb; |
248 | int pindex; |
248 | int pindex; |
249 | 249 | ||
250 | if (info->fix.visual == FB_VISUAL_TRUECOLOR) { |
250 | if (info->fix.visual == FB_VISUAL_TRUECOLOR) { |
251 | u32 *palette; |
251 | u32 *palette; |
252 | u32 value; |
252 | u32 value; |
253 | /* place color in psuedopalette */ |
253 | /* place color in psuedopalette */ |
254 | if (regno > 16) |
254 | if (regno > 16) |
255 | return -EINVAL; |
255 | return -EINVAL; |
256 | palette = (u32 *)info->pseudo_palette; |
256 | palette = (u32 *)info->pseudo_palette; |
257 | red >>= (16 - info->var.red.length); |
257 | red >>= (16 - info->var.red.length); |
258 | green >>= (16 - info->var.green.length); |
258 | green >>= (16 - info->var.green.length); |
259 | blue >>= (16 - info->var.blue.length); |
259 | blue >>= (16 - info->var.blue.length); |
260 | value = (red << info->var.red.offset) | |
260 | value = (red << info->var.red.offset) | |
261 | (green << info->var.green.offset) | |
261 | (green << info->var.green.offset) | |
262 | (blue << info->var.blue.offset); |
262 | (blue << info->var.blue.offset); |
263 | palette[regno] = value; |
263 | palette[regno] = value; |
264 | return 0; |
264 | return 0; |
265 | } |
265 | } |
266 | 266 | ||
267 | pindex = regno; |
267 | pindex = regno; |
268 | 268 | ||
269 | if (fb->bits_per_pixel == 16) { |
269 | if (fb->bits_per_pixel == 16) { |
270 | pindex = regno << 3; |
270 | pindex = regno << 3; |
271 | 271 | ||
272 | if (fb->depth == 16 && regno > 63) |
272 | if (fb->depth == 16 && regno > 63) |
273 | return -EINVAL; |
273 | return -EINVAL; |
274 | if (fb->depth == 15 && regno > 31) |
274 | if (fb->depth == 15 && regno > 31) |
275 | return -EINVAL; |
275 | return -EINVAL; |
276 | 276 | ||
277 | if (fb->depth == 16) { |
277 | if (fb->depth == 16) { |
278 | u16 r, g, b; |
278 | u16 r, g, b; |
279 | int i; |
279 | int i; |
280 | if (regno < 32) { |
280 | if (regno < 32) { |
281 | for (i = 0; i < 8; i++) |
281 | for (i = 0; i < 8; i++) |
282 | fb_helper->funcs->gamma_set(crtc, red, |
282 | fb_helper->funcs->gamma_set(crtc, red, |
283 | green, blue, pindex + i); |
283 | green, blue, pindex + i); |
284 | } |
284 | } |
285 | 285 | ||
286 | fb_helper->funcs->gamma_get(crtc, &r, |
286 | fb_helper->funcs->gamma_get(crtc, &r, |
287 | &g, &b, |
287 | &g, &b, |
288 | pindex >> 1); |
288 | pindex >> 1); |
289 | 289 | ||
290 | for (i = 0; i < 4; i++) |
290 | for (i = 0; i < 4; i++) |
291 | fb_helper->funcs->gamma_set(crtc, r, |
291 | fb_helper->funcs->gamma_set(crtc, r, |
292 | green, b, |
292 | green, b, |
293 | (pindex >> 1) + i); |
293 | (pindex >> 1) + i); |
294 | } |
294 | } |
295 | } |
295 | } |
296 | 296 | ||
297 | if (fb->depth != 16) |
297 | if (fb->depth != 16) |
298 | fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex); |
298 | fb_helper->funcs->gamma_set(crtc, red, green, blue, pindex); |
299 | return 0; |
299 | return 0; |
300 | } |
300 | } |
301 | 301 | ||
302 | int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) |
302 | int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) |
303 | { |
303 | { |
304 | struct drm_fb_helper *fb_helper = info->par; |
304 | struct drm_fb_helper *fb_helper = info->par; |
305 | struct drm_device *dev = fb_helper->dev; |
305 | struct drm_device *dev = fb_helper->dev; |
306 | u16 *red, *green, *blue, *transp; |
306 | u16 *red, *green, *blue, *transp; |
307 | struct drm_crtc *crtc; |
307 | struct drm_crtc *crtc; |
308 | int i, rc = 0; |
308 | int i, rc = 0; |
309 | int start; |
309 | int start; |
310 | 310 | ||
311 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
311 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
312 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
312 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
313 | for (i = 0; i < fb_helper->crtc_count; i++) { |
313 | for (i = 0; i < fb_helper->crtc_count; i++) { |
314 | if (crtc->base.id == fb_helper->crtc_info[i].crtc_id) |
314 | if (crtc->base.id == fb_helper->crtc_info[i].crtc_id) |
315 | break; |
315 | break; |
316 | } |
316 | } |
317 | if (i == fb_helper->crtc_count) |
317 | if (i == fb_helper->crtc_count) |
318 | continue; |
318 | continue; |
319 | 319 | ||
320 | red = cmap->red; |
320 | red = cmap->red; |
321 | green = cmap->green; |
321 | green = cmap->green; |
322 | blue = cmap->blue; |
322 | blue = cmap->blue; |
323 | transp = cmap->transp; |
323 | transp = cmap->transp; |
324 | start = cmap->start; |
324 | start = cmap->start; |
325 | 325 | ||
326 | for (i = 0; i < cmap->len; i++) { |
326 | for (i = 0; i < cmap->len; i++) { |
327 | u16 hred, hgreen, hblue, htransp = 0xffff; |
327 | u16 hred, hgreen, hblue, htransp = 0xffff; |
328 | 328 | ||
329 | hred = *red++; |
329 | hred = *red++; |
330 | hgreen = *green++; |
330 | hgreen = *green++; |
331 | hblue = *blue++; |
331 | hblue = *blue++; |
332 | 332 | ||
333 | if (transp) |
333 | if (transp) |
334 | htransp = *transp++; |
334 | htransp = *transp++; |
335 | 335 | ||
336 | rc = setcolreg(crtc, hred, hgreen, hblue, start++, info); |
336 | rc = setcolreg(crtc, hred, hgreen, hblue, start++, info); |
337 | if (rc) |
337 | if (rc) |
338 | return rc; |
338 | return rc; |
339 | } |
339 | } |
340 | crtc_funcs->load_lut(crtc); |
340 | crtc_funcs->load_lut(crtc); |
341 | } |
341 | } |
342 | return rc; |
342 | return rc; |
343 | } |
343 | } |
344 | EXPORT_SYMBOL(drm_fb_helper_setcmap); |
344 | EXPORT_SYMBOL(drm_fb_helper_setcmap); |
345 | 345 | ||
346 | int drm_fb_helper_setcolreg(unsigned regno, |
346 | int drm_fb_helper_setcolreg(unsigned regno, |
347 | unsigned red, |
347 | unsigned red, |
348 | unsigned green, |
348 | unsigned green, |
349 | unsigned blue, |
349 | unsigned blue, |
350 | unsigned transp, |
350 | unsigned transp, |
351 | struct fb_info *info) |
351 | struct fb_info *info) |
352 | { |
352 | { |
353 | struct drm_fb_helper *fb_helper = info->par; |
353 | struct drm_fb_helper *fb_helper = info->par; |
354 | struct drm_device *dev = fb_helper->dev; |
354 | struct drm_device *dev = fb_helper->dev; |
355 | struct drm_crtc *crtc; |
355 | struct drm_crtc *crtc; |
356 | int i; |
356 | int i; |
357 | int ret; |
357 | int ret; |
358 | 358 | ||
359 | if (regno > 255) |
359 | if (regno > 255) |
360 | return 1; |
360 | return 1; |
361 | 361 | ||
362 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
362 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
363 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
363 | struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
364 | for (i = 0; i < fb_helper->crtc_count; i++) { |
364 | for (i = 0; i < fb_helper->crtc_count; i++) { |
365 | if (crtc->base.id == fb_helper->crtc_info[i].crtc_id) |
365 | if (crtc->base.id == fb_helper->crtc_info[i].crtc_id) |
366 | break; |
366 | break; |
367 | } |
367 | } |
368 | if (i == fb_helper->crtc_count) |
368 | if (i == fb_helper->crtc_count) |
369 | continue; |
369 | continue; |
370 | 370 | ||
371 | ret = setcolreg(crtc, red, green, blue, regno, info); |
371 | ret = setcolreg(crtc, red, green, blue, regno, info); |
372 | if (ret) |
372 | if (ret) |
373 | return ret; |
373 | return ret; |
374 | 374 | ||
375 | crtc_funcs->load_lut(crtc); |
375 | crtc_funcs->load_lut(crtc); |
376 | } |
376 | } |
377 | return 0; |
377 | return 0; |
378 | } |
378 | } |
379 | EXPORT_SYMBOL(drm_fb_helper_setcolreg); |
379 | EXPORT_SYMBOL(drm_fb_helper_setcolreg); |
380 | 380 | ||
381 | int drm_fb_helper_check_var(struct fb_var_screeninfo *var, |
381 | int drm_fb_helper_check_var(struct fb_var_screeninfo *var, |
382 | struct fb_info *info) |
382 | struct fb_info *info) |
383 | { |
383 | { |
384 | struct drm_fb_helper *fb_helper = info->par; |
384 | struct drm_fb_helper *fb_helper = info->par; |
385 | struct drm_framebuffer *fb = fb_helper->fb; |
385 | struct drm_framebuffer *fb = fb_helper->fb; |
386 | int depth; |
386 | int depth; |
387 | 387 | ||
388 | if (var->pixclock == -1 || !var->pixclock) |
388 | if (var->pixclock == -1 || !var->pixclock) |
389 | return -EINVAL; |
389 | return -EINVAL; |
390 | 390 | ||
391 | /* Need to resize the fb object !!! */ |
391 | /* Need to resize the fb object !!! */ |
392 | if (var->xres > fb->width || var->yres > fb->height) { |
392 | if (var->xres > fb->width || var->yres > fb->height) { |
393 | DRM_ERROR("Requested width/height is greater than current fb " |
393 | DRM_ERROR("Requested width/height is greater than current fb " |
394 | "object %dx%d > %dx%d\n", var->xres, var->yres, |
394 | "object %dx%d > %dx%d\n", var->xres, var->yres, |
395 | fb->width, fb->height); |
395 | fb->width, fb->height); |
396 | DRM_ERROR("Need resizing code.\n"); |
396 | DRM_ERROR("Need resizing code.\n"); |
397 | return -EINVAL; |
397 | return -EINVAL; |
398 | } |
398 | } |
399 | 399 | ||
400 | switch (var->bits_per_pixel) { |
400 | switch (var->bits_per_pixel) { |
401 | case 16: |
401 | case 16: |
402 | depth = (var->green.length == 6) ? 16 : 15; |
402 | depth = (var->green.length == 6) ? 16 : 15; |
403 | break; |
403 | break; |
404 | case 32: |
404 | case 32: |
405 | depth = (var->transp.length > 0) ? 32 : 24; |
405 | depth = (var->transp.length > 0) ? 32 : 24; |
406 | break; |
406 | break; |
407 | default: |
407 | default: |
408 | depth = var->bits_per_pixel; |
408 | depth = var->bits_per_pixel; |
409 | break; |
409 | break; |
410 | } |
410 | } |
411 | 411 | ||
412 | switch (depth) { |
412 | switch (depth) { |
413 | case 8: |
413 | case 8: |
414 | var->red.offset = 0; |
414 | var->red.offset = 0; |
415 | var->green.offset = 0; |
415 | var->green.offset = 0; |
416 | var->blue.offset = 0; |
416 | var->blue.offset = 0; |
417 | var->red.length = 8; |
417 | var->red.length = 8; |
418 | var->green.length = 8; |
418 | var->green.length = 8; |
419 | var->blue.length = 8; |
419 | var->blue.length = 8; |
420 | var->transp.length = 0; |
420 | var->transp.length = 0; |
421 | var->transp.offset = 0; |
421 | var->transp.offset = 0; |
422 | break; |
422 | break; |
423 | case 15: |
423 | case 15: |
424 | var->red.offset = 10; |
424 | var->red.offset = 10; |
425 | var->green.offset = 5; |
425 | var->green.offset = 5; |
426 | var->blue.offset = 0; |
426 | var->blue.offset = 0; |
427 | var->red.length = 5; |
427 | var->red.length = 5; |
428 | var->green.length = 5; |
428 | var->green.length = 5; |
429 | var->blue.length = 5; |
429 | var->blue.length = 5; |
430 | var->transp.length = 1; |
430 | var->transp.length = 1; |
431 | var->transp.offset = 15; |
431 | var->transp.offset = 15; |
432 | break; |
432 | break; |
433 | case 16: |
433 | case 16: |
434 | var->red.offset = 11; |
434 | var->red.offset = 11; |
435 | var->green.offset = 5; |
435 | var->green.offset = 5; |
436 | var->blue.offset = 0; |
436 | var->blue.offset = 0; |
437 | var->red.length = 5; |
437 | var->red.length = 5; |
438 | var->green.length = 6; |
438 | var->green.length = 6; |
439 | var->blue.length = 5; |
439 | var->blue.length = 5; |
440 | var->transp.length = 0; |
440 | var->transp.length = 0; |
441 | var->transp.offset = 0; |
441 | var->transp.offset = 0; |
442 | break; |
442 | break; |
443 | case 24: |
443 | case 24: |
444 | var->red.offset = 16; |
444 | var->red.offset = 16; |
445 | var->green.offset = 8; |
445 | var->green.offset = 8; |
446 | var->blue.offset = 0; |
446 | var->blue.offset = 0; |
447 | var->red.length = 8; |
447 | var->red.length = 8; |
448 | var->green.length = 8; |
448 | var->green.length = 8; |
449 | var->blue.length = 8; |
449 | var->blue.length = 8; |
450 | var->transp.length = 0; |
450 | var->transp.length = 0; |
451 | var->transp.offset = 0; |
451 | var->transp.offset = 0; |
452 | break; |
452 | break; |
453 | case 32: |
453 | case 32: |
454 | var->red.offset = 16; |
454 | var->red.offset = 16; |
455 | var->green.offset = 8; |
455 | var->green.offset = 8; |
456 | var->blue.offset = 0; |
456 | var->blue.offset = 0; |
457 | var->red.length = 8; |
457 | var->red.length = 8; |
458 | var->green.length = 8; |
458 | var->green.length = 8; |
459 | var->blue.length = 8; |
459 | var->blue.length = 8; |
460 | var->transp.length = 8; |
460 | var->transp.length = 8; |
461 | var->transp.offset = 24; |
461 | var->transp.offset = 24; |
462 | break; |
462 | break; |
463 | default: |
463 | default: |
464 | return -EINVAL; |
464 | return -EINVAL; |
465 | } |
465 | } |
466 | return 0; |
466 | return 0; |
467 | } |
467 | } |
468 | EXPORT_SYMBOL(drm_fb_helper_check_var); |
468 | EXPORT_SYMBOL(drm_fb_helper_check_var); |
469 | 469 | ||
470 | /* this will let fbcon do the mode init */ |
470 | /* this will let fbcon do the mode init */ |
471 | int drm_fb_helper_set_par(struct fb_info *info) |
471 | int drm_fb_helper_set_par(struct fb_info *info) |
472 | { |
472 | { |
473 | struct drm_fb_helper *fb_helper = info->par; |
473 | struct drm_fb_helper *fb_helper = info->par; |
474 | struct drm_device *dev = fb_helper->dev; |
474 | struct drm_device *dev = fb_helper->dev; |
475 | struct fb_var_screeninfo *var = &info->var; |
475 | struct fb_var_screeninfo *var = &info->var; |
476 | struct drm_crtc *crtc; |
476 | struct drm_crtc *crtc; |
477 | int ret; |
477 | int ret; |
478 | int i; |
478 | int i; |
479 | 479 | ||
480 | if (var->pixclock != -1) { |
480 | if (var->pixclock != -1) { |
481 | DRM_ERROR("PIXEL CLCOK SET\n"); |
481 | DRM_ERROR("PIXEL CLCOK SET\n"); |
482 | return -EINVAL; |
482 | return -EINVAL; |
483 | } |
483 | } |
484 | 484 | ||
485 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
485 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
486 | 486 | ||
487 | for (i = 0; i < fb_helper->crtc_count; i++) { |
487 | for (i = 0; i < fb_helper->crtc_count; i++) { |
488 | if (crtc->base.id == fb_helper->crtc_info[i].crtc_id) |
488 | if (crtc->base.id == fb_helper->crtc_info[i].crtc_id) |
489 | break; |
489 | break; |
490 | } |
490 | } |
491 | if (i == fb_helper->crtc_count) |
491 | if (i == fb_helper->crtc_count) |
492 | continue; |
492 | continue; |
493 | 493 | ||
494 | if (crtc->fb == fb_helper->crtc_info[i].mode_set.fb) { |
494 | if (crtc->fb == fb_helper->crtc_info[i].mode_set.fb) { |
495 | mutex_lock(&dev->mode_config.mutex); |
495 | mutex_lock(&dev->mode_config.mutex); |
496 | ret = crtc->funcs->set_config(&fb_helper->crtc_info->mode_set); |
496 | ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set); |
497 | mutex_unlock(&dev->mode_config.mutex); |
497 | mutex_unlock(&dev->mode_config.mutex); |
498 | if (ret) |
498 | if (ret) |
499 | return ret; |
499 | return ret; |
500 | } |
500 | } |
501 | } |
501 | } |
502 | return 0; |
502 | return 0; |
503 | } |
503 | } |
504 | EXPORT_SYMBOL(drm_fb_helper_set_par); |
504 | EXPORT_SYMBOL(drm_fb_helper_set_par); |
505 | 505 | ||
506 | int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, |
506 | int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, |
507 | struct fb_info *info) |
507 | struct fb_info *info) |
508 | { |
508 | { |
509 | struct drm_fb_helper *fb_helper = info->par; |
509 | struct drm_fb_helper *fb_helper = info->par; |
510 | struct drm_device *dev = fb_helper->dev; |
510 | struct drm_device *dev = fb_helper->dev; |
511 | struct drm_mode_set *modeset; |
511 | struct drm_mode_set *modeset; |
512 | struct drm_crtc *crtc; |
512 | struct drm_crtc *crtc; |
513 | int ret = 0; |
513 | int ret = 0; |
514 | int i; |
514 | int i; |
515 | 515 | ||
516 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
516 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
517 | for (i = 0; i < fb_helper->crtc_count; i++) { |
517 | for (i = 0; i < fb_helper->crtc_count; i++) { |
518 | if (crtc->base.id == fb_helper->crtc_info[i].crtc_id) |
518 | if (crtc->base.id == fb_helper->crtc_info[i].crtc_id) |
519 | break; |
519 | break; |
520 | } |
520 | } |
521 | 521 | ||
522 | if (i == fb_helper->crtc_count) |
522 | if (i == fb_helper->crtc_count) |
523 | continue; |
523 | continue; |
524 | 524 | ||
525 | modeset = &fb_helper->crtc_info[i].mode_set; |
525 | modeset = &fb_helper->crtc_info[i].mode_set; |
526 | 526 | ||
527 | modeset->x = var->xoffset; |
527 | modeset->x = var->xoffset; |
528 | modeset->y = var->yoffset; |
528 | modeset->y = var->yoffset; |
529 | 529 | ||
530 | if (modeset->num_connectors) { |
530 | if (modeset->num_connectors) { |
531 | mutex_lock(&dev->mode_config.mutex); |
531 | mutex_lock(&dev->mode_config.mutex); |
532 | ret = crtc->funcs->set_config(modeset); |
532 | ret = crtc->funcs->set_config(modeset); |
533 | mutex_unlock(&dev->mode_config.mutex); |
533 | mutex_unlock(&dev->mode_config.mutex); |
534 | if (!ret) { |
534 | if (!ret) { |
535 | info->var.xoffset = var->xoffset; |
535 | info->var.xoffset = var->xoffset; |
536 | info->var.yoffset = var->yoffset; |
536 | info->var.yoffset = var->yoffset; |
537 | } |
537 | } |
538 | } |
538 | } |
539 | } |
539 | } |
540 | return ret; |
540 | return ret; |
541 | } |
541 | } |
542 | EXPORT_SYMBOL(drm_fb_helper_pan_display); |
542 | EXPORT_SYMBOL(drm_fb_helper_pan_display); |
543 | 543 | ||
544 | int drm_fb_helper_single_fb_probe(struct drm_device *dev, |
544 | int drm_fb_helper_single_fb_probe(struct drm_device *dev, |
545 | int preferred_bpp, |
545 | int preferred_bpp, |
546 | int (*fb_create)(struct drm_device *dev, |
546 | int (*fb_create)(struct drm_device *dev, |
547 | uint32_t fb_width, |
547 | uint32_t fb_width, |
548 | uint32_t fb_height, |
548 | uint32_t fb_height, |
549 | uint32_t surface_width, |
549 | uint32_t surface_width, |
550 | uint32_t surface_height, |
550 | uint32_t surface_height, |
551 | uint32_t surface_depth, |
551 | uint32_t surface_depth, |
552 | uint32_t surface_bpp, |
552 | uint32_t surface_bpp, |
553 | struct drm_framebuffer **fb_ptr)) |
553 | struct drm_framebuffer **fb_ptr)) |
554 | { |
554 | { |
555 | struct drm_crtc *crtc; |
555 | struct drm_crtc *crtc; |
556 | struct drm_connector *connector; |
556 | struct drm_connector *connector; |
557 | unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1; |
557 | unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1; |
558 | unsigned int surface_width = 0, surface_height = 0; |
558 | unsigned int surface_width = 0, surface_height = 0; |
559 | int new_fb = 0; |
559 | int new_fb = 0; |
560 | int crtc_count = 0; |
560 | int crtc_count = 0; |
561 | int ret, i, conn_count = 0; |
561 | int ret, i, conn_count = 0; |
562 | struct fb_info *info; |
562 | struct fb_info *info; |
563 | struct drm_framebuffer *fb; |
563 | struct drm_framebuffer *fb; |
564 | struct drm_mode_set *modeset = NULL; |
564 | struct drm_mode_set *modeset = NULL; |
565 | struct drm_fb_helper *fb_helper; |
565 | struct drm_fb_helper *fb_helper; |
566 | uint32_t surface_depth = 24, surface_bpp = 32; |
566 | uint32_t surface_depth = 24, surface_bpp = 32; |
567 | 567 | ||
568 | /* if driver picks 8 or 16 by default use that |
568 | /* if driver picks 8 or 16 by default use that |
569 | for both depth/bpp */ |
569 | for both depth/bpp */ |
570 | if (preferred_bpp != surface_bpp) { |
570 | if (preferred_bpp != surface_bpp) { |
571 | surface_depth = surface_bpp = preferred_bpp; |
571 | surface_depth = surface_bpp = preferred_bpp; |
572 | } |
572 | } |
573 | /* first up get a count of crtcs now in use and new min/maxes width/heights */ |
573 | /* first up get a count of crtcs now in use and new min/maxes width/heights */ |
574 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
574 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
575 | struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private; |
575 | struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private; |
576 | 576 | ||
577 | struct drm_fb_helper_cmdline_mode *cmdline_mode; |
577 | struct drm_fb_helper_cmdline_mode *cmdline_mode; |
578 | 578 | ||
579 | if (!fb_help_conn) |
579 | if (!fb_help_conn) |
580 | continue; |
580 | continue; |
581 | 581 | ||
582 | cmdline_mode = &fb_help_conn->cmdline_mode; |
582 | cmdline_mode = &fb_help_conn->cmdline_mode; |
583 | 583 | ||
584 | if (cmdline_mode->bpp_specified) { |
584 | if (cmdline_mode->bpp_specified) { |
585 | switch (cmdline_mode->bpp) { |
585 | switch (cmdline_mode->bpp) { |
586 | case 8: |
586 | case 8: |
587 | surface_depth = surface_bpp = 8; |
587 | surface_depth = surface_bpp = 8; |
588 | break; |
588 | break; |
589 | case 15: |
589 | case 15: |
590 | surface_depth = 15; |
590 | surface_depth = 15; |
591 | surface_bpp = 16; |
591 | surface_bpp = 16; |
592 | break; |
592 | break; |
593 | case 16: |
593 | case 16: |
594 | surface_depth = surface_bpp = 16; |
594 | surface_depth = surface_bpp = 16; |
595 | break; |
595 | break; |
596 | case 24: |
596 | case 24: |
597 | surface_depth = surface_bpp = 24; |
597 | surface_depth = surface_bpp = 24; |
598 | break; |
598 | break; |
599 | case 32: |
599 | case 32: |
600 | surface_depth = 24; |
600 | surface_depth = 24; |
601 | surface_bpp = 32; |
601 | surface_bpp = 32; |
602 | break; |
602 | break; |
603 | } |
603 | } |
604 | break; |
604 | break; |
605 | } |
605 | } |
606 | } |
606 | } |
607 | 607 | ||
608 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
608 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
609 | if (drm_helper_crtc_in_use(crtc)) { |
609 | if (drm_helper_crtc_in_use(crtc)) { |
610 | if (crtc->desired_mode) { |
610 | if (crtc->desired_mode) { |
611 | if (crtc->desired_mode->hdisplay < fb_width) |
611 | if (crtc->desired_mode->hdisplay < fb_width) |
612 | fb_width = crtc->desired_mode->hdisplay; |
612 | fb_width = crtc->desired_mode->hdisplay; |
613 | 613 | ||
614 | if (crtc->desired_mode->vdisplay < fb_height) |
614 | if (crtc->desired_mode->vdisplay < fb_height) |
615 | fb_height = crtc->desired_mode->vdisplay; |
615 | fb_height = crtc->desired_mode->vdisplay; |
616 | 616 | ||
617 | if (crtc->desired_mode->hdisplay > surface_width) |
617 | if (crtc->desired_mode->hdisplay > surface_width) |
618 | surface_width = crtc->desired_mode->hdisplay; |
618 | surface_width = crtc->desired_mode->hdisplay; |
619 | 619 | ||
620 | if (crtc->desired_mode->vdisplay > surface_height) |
620 | if (crtc->desired_mode->vdisplay > surface_height) |
621 | surface_height = crtc->desired_mode->vdisplay; |
621 | surface_height = crtc->desired_mode->vdisplay; |
622 | } |
622 | } |
623 | crtc_count++; |
623 | crtc_count++; |
624 | } |
624 | } |
625 | } |
625 | } |
626 | 626 | ||
627 | if (crtc_count == 0 || fb_width == -1 || fb_height == -1) { |
627 | if (crtc_count == 0 || fb_width == -1 || fb_height == -1) { |
628 | /* hmm everyone went away - assume VGA cable just fell out |
628 | /* hmm everyone went away - assume VGA cable just fell out |
629 | and will come back later. */ |
629 | and will come back later. */ |
630 | return 0; |
630 | return 0; |
631 | } |
631 | } |
632 | 632 | ||
633 | /* do we have an fb already? */ |
633 | /* do we have an fb already? */ |
634 | if (list_empty(&dev->mode_config.fb_kernel_list)) { |
634 | if (list_empty(&dev->mode_config.fb_kernel_list)) { |
635 | ret = (*fb_create)(dev, fb_width, fb_height, surface_width, |
635 | ret = (*fb_create)(dev, fb_width, fb_height, surface_width, |
636 | surface_height, surface_depth, surface_bpp, |
636 | surface_height, surface_depth, surface_bpp, |
637 | &fb); |
637 | &fb); |
638 | if (ret) |
638 | if (ret) |
639 | return -EINVAL; |
639 | return -EINVAL; |
640 | new_fb = 1; |
640 | new_fb = 1; |
641 | } else { |
641 | } else { |
642 | fb = list_first_entry(&dev->mode_config.fb_kernel_list, |
642 | fb = list_first_entry(&dev->mode_config.fb_kernel_list, |
643 | struct drm_framebuffer, filp_head); |
643 | struct drm_framebuffer, filp_head); |
644 | 644 | ||
645 | /* if someone hotplugs something bigger than we have already allocated, we are pwned. |
645 | /* if someone hotplugs something bigger than we have already allocated, we are pwned. |
646 | As really we can't resize an fbdev that is in the wild currently due to fbdev |
646 | As really we can't resize an fbdev that is in the wild currently due to fbdev |
647 | not really being designed for the lower layers moving stuff around under it. |
647 | not really being designed for the lower layers moving stuff around under it. |
648 | - so in the grand style of things - punt. */ |
648 | - so in the grand style of things - punt. */ |
649 | if ((fb->width < surface_width) || |
649 | if ((fb->width < surface_width) || |
650 | (fb->height < surface_height)) { |
650 | (fb->height < surface_height)) { |
651 | DRM_ERROR("Framebuffer not large enough to scale console onto.\n"); |
651 | DRM_ERROR("Framebuffer not large enough to scale console onto.\n"); |
652 | return -EINVAL; |
652 | return -EINVAL; |
653 | } |
653 | } |
654 | } |
654 | } |
655 | 655 | ||
656 | info = fb->fbdev; |
656 | info = fb->fbdev; |
657 | fb_helper = info->par; |
657 | fb_helper = info->par; |
658 | 658 | ||
659 | crtc_count = 0; |
659 | crtc_count = 0; |
660 | /* okay we need to setup new connector sets in the crtcs */ |
660 | /* okay we need to setup new connector sets in the crtcs */ |
661 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
661 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
662 | modeset = &fb_helper->crtc_info[crtc_count].mode_set; |
662 | modeset = &fb_helper->crtc_info[crtc_count].mode_set; |
663 | modeset->fb = fb; |
663 | modeset->fb = fb; |
664 | conn_count = 0; |
664 | conn_count = 0; |
665 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
665 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
666 | if (connector->encoder) |
666 | if (connector->encoder) |
667 | if (connector->encoder->crtc == modeset->crtc) { |
667 | if (connector->encoder->crtc == modeset->crtc) { |
668 | modeset->connectors[conn_count] = connector; |
668 | modeset->connectors[conn_count] = connector; |
669 | conn_count++; |
669 | conn_count++; |
670 | if (conn_count > fb_helper->conn_limit) |
670 | if (conn_count > fb_helper->conn_limit) |
671 | BUG(); |
671 | BUG(); |
672 | } |
672 | } |
673 | } |
673 | } |
674 | 674 | ||
675 | for (i = conn_count; i < fb_helper->conn_limit; i++) |
675 | for (i = conn_count; i < fb_helper->conn_limit; i++) |
676 | modeset->connectors[i] = NULL; |
676 | modeset->connectors[i] = NULL; |
677 | 677 | ||
678 | modeset->crtc = crtc; |
678 | modeset->crtc = crtc; |
679 | crtc_count++; |
679 | crtc_count++; |
680 | 680 | ||
681 | modeset->num_connectors = conn_count; |
681 | modeset->num_connectors = conn_count; |
682 | if (modeset->crtc->desired_mode) { |
682 | if (modeset->crtc->desired_mode) { |
683 | if (modeset->mode) |
683 | if (modeset->mode) |
684 | drm_mode_destroy(dev, modeset->mode); |
684 | drm_mode_destroy(dev, modeset->mode); |
685 | modeset->mode = drm_mode_duplicate(dev, |
685 | modeset->mode = drm_mode_duplicate(dev, |
686 | modeset->crtc->desired_mode); |
686 | modeset->crtc->desired_mode); |
687 | } |
687 | } |
688 | } |
688 | } |
689 | fb_helper->crtc_count = crtc_count; |
689 | fb_helper->crtc_count = crtc_count; |
690 | fb_helper->fb = fb; |
690 | fb_helper->fb = fb; |
691 | 691 | ||
692 | if (new_fb) { |
692 | if (new_fb) { |
693 | info->var.pixclock = -1; |
693 | info->var.pixclock = -1; |
694 | // if (register_framebuffer(info) < 0) |
694 | // if (register_framebuffer(info) < 0) |
695 | // return -EINVAL; |
695 | // return -EINVAL; |
696 | } else { |
696 | } else { |
697 | drm_fb_helper_set_par(info); |
697 | drm_fb_helper_set_par(info); |
698 | } |
698 | } |
699 | printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, |
699 | printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, |
700 | info->fix.id); |
700 | info->fix.id); |
701 | 701 | ||
702 | /* Switch back to kernel console on panic */ |
702 | /* Switch back to kernel console on panic */ |
703 | /* multi card linked list maybe */ |
703 | /* multi card linked list maybe */ |
704 | list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list); |
704 | list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list); |
705 | return 0; |
705 | return 0; |
706 | } |
706 | } |
707 | EXPORT_SYMBOL(drm_fb_helper_single_fb_probe); |
707 | EXPORT_SYMBOL(drm_fb_helper_single_fb_probe); |
708 | 708 | ||
709 | void drm_fb_helper_free(struct drm_fb_helper *helper) |
709 | void drm_fb_helper_free(struct drm_fb_helper *helper) |
710 | { |
710 | { |
711 | list_del(&helper->kernel_fb_list); |
711 | list_del(&helper->kernel_fb_list); |
712 | drm_fb_helper_crtc_free(helper); |
712 | drm_fb_helper_crtc_free(helper); |
713 | } |
713 | } |
714 | EXPORT_SYMBOL(drm_fb_helper_free); |
714 | EXPORT_SYMBOL(drm_fb_helper_free); |
715 | 715 | ||
716 | void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, |
716 | void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, |
717 | uint32_t depth) |
717 | uint32_t depth) |
718 | { |
718 | { |
719 | info->fix.type = FB_TYPE_PACKED_PIXELS; |
719 | info->fix.type = FB_TYPE_PACKED_PIXELS; |
720 | info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR : |
720 | info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR : |
721 | FB_VISUAL_TRUECOLOR; |
721 | FB_VISUAL_TRUECOLOR; |
722 | info->fix.type_aux = 0; |
722 | info->fix.type_aux = 0; |
723 | info->fix.xpanstep = 1; /* doing it in hw */ |
723 | info->fix.xpanstep = 1; /* doing it in hw */ |
724 | info->fix.ypanstep = 1; /* doing it in hw */ |
724 | info->fix.ypanstep = 1; /* doing it in hw */ |
725 | info->fix.ywrapstep = 0; |
725 | info->fix.ywrapstep = 0; |
726 | info->fix.accel = FB_ACCEL_NONE; |
726 | info->fix.accel = FB_ACCEL_NONE; |
727 | info->fix.type_aux = 0; |
727 | info->fix.type_aux = 0; |
728 | 728 | ||
729 | info->fix.line_length = pitch; |
729 | info->fix.line_length = pitch; |
730 | return; |
730 | return; |
731 | } |
731 | } |
732 | EXPORT_SYMBOL(drm_fb_helper_fill_fix); |
732 | EXPORT_SYMBOL(drm_fb_helper_fill_fix); |
733 | 733 | ||
734 | void drm_fb_helper_fill_var(struct fb_info *info, struct drm_framebuffer *fb, |
734 | void drm_fb_helper_fill_var(struct fb_info *info, struct drm_framebuffer *fb, |
735 | uint32_t fb_width, uint32_t fb_height) |
735 | uint32_t fb_width, uint32_t fb_height) |
736 | { |
736 | { |
737 | info->pseudo_palette = fb->pseudo_palette; |
737 | info->pseudo_palette = fb->pseudo_palette; |
738 | info->var.xres_virtual = fb->width; |
738 | info->var.xres_virtual = fb->width; |
739 | info->var.yres_virtual = fb->height; |
739 | info->var.yres_virtual = fb->height; |
740 | info->var.bits_per_pixel = fb->bits_per_pixel; |
740 | info->var.bits_per_pixel = fb->bits_per_pixel; |
741 | info->var.xoffset = 0; |
741 | info->var.xoffset = 0; |
742 | info->var.yoffset = 0; |
742 | info->var.yoffset = 0; |
743 | info->var.activate = FB_ACTIVATE_NOW; |
743 | info->var.activate = FB_ACTIVATE_NOW; |
744 | info->var.height = -1; |
744 | info->var.height = -1; |
745 | info->var.width = -1; |
745 | info->var.width = -1; |
746 | 746 | ||
747 | switch (fb->depth) { |
747 | switch (fb->depth) { |
748 | case 8: |
748 | case 8: |
749 | info->var.red.offset = 0; |
749 | info->var.red.offset = 0; |
750 | info->var.green.offset = 0; |
750 | info->var.green.offset = 0; |
751 | info->var.blue.offset = 0; |
751 | info->var.blue.offset = 0; |
752 | info->var.red.length = 8; /* 8bit DAC */ |
752 | info->var.red.length = 8; /* 8bit DAC */ |
753 | info->var.green.length = 8; |
753 | info->var.green.length = 8; |
754 | info->var.blue.length = 8; |
754 | info->var.blue.length = 8; |
755 | info->var.transp.offset = 0; |
755 | info->var.transp.offset = 0; |
756 | info->var.transp.length = 0; |
756 | info->var.transp.length = 0; |
757 | break; |
757 | break; |
758 | case 15: |
758 | case 15: |
759 | info->var.red.offset = 10; |
759 | info->var.red.offset = 10; |
760 | info->var.green.offset = 5; |
760 | info->var.green.offset = 5; |
761 | info->var.blue.offset = 0; |
761 | info->var.blue.offset = 0; |
762 | info->var.red.length = 5; |
762 | info->var.red.length = 5; |
763 | info->var.green.length = 5; |
763 | info->var.green.length = 5; |
764 | info->var.blue.length = 5; |
764 | info->var.blue.length = 5; |
765 | info->var.transp.offset = 15; |
765 | info->var.transp.offset = 15; |
766 | info->var.transp.length = 1; |
766 | info->var.transp.length = 1; |
767 | break; |
767 | break; |
768 | case 16: |
768 | case 16: |
769 | info->var.red.offset = 11; |
769 | info->var.red.offset = 11; |
770 | info->var.green.offset = 5; |
770 | info->var.green.offset = 5; |
771 | info->var.blue.offset = 0; |
771 | info->var.blue.offset = 0; |
772 | info->var.red.length = 5; |
772 | info->var.red.length = 5; |
773 | info->var.green.length = 6; |
773 | info->var.green.length = 6; |
774 | info->var.blue.length = 5; |
774 | info->var.blue.length = 5; |
775 | info->var.transp.offset = 0; |
775 | info->var.transp.offset = 0; |
776 | break; |
776 | break; |
777 | case 24: |
777 | case 24: |
778 | info->var.red.offset = 16; |
778 | info->var.red.offset = 16; |
779 | info->var.green.offset = 8; |
779 | info->var.green.offset = 8; |
780 | info->var.blue.offset = 0; |
780 | info->var.blue.offset = 0; |
781 | info->var.red.length = 8; |
781 | info->var.red.length = 8; |
782 | info->var.green.length = 8; |
782 | info->var.green.length = 8; |
783 | info->var.blue.length = 8; |
783 | info->var.blue.length = 8; |
784 | info->var.transp.offset = 0; |
784 | info->var.transp.offset = 0; |
785 | info->var.transp.length = 0; |
785 | info->var.transp.length = 0; |
786 | break; |
786 | break; |
787 | case 32: |
787 | case 32: |
788 | info->var.red.offset = 16; |
788 | info->var.red.offset = 16; |
789 | info->var.green.offset = 8; |
789 | info->var.green.offset = 8; |
790 | info->var.blue.offset = 0; |
790 | info->var.blue.offset = 0; |
791 | info->var.red.length = 8; |
791 | info->var.red.length = 8; |
792 | info->var.green.length = 8; |
792 | info->var.green.length = 8; |
793 | info->var.blue.length = 8; |
793 | info->var.blue.length = 8; |
794 | info->var.transp.offset = 24; |
794 | info->var.transp.offset = 24; |
795 | info->var.transp.length = 8; |
795 | info->var.transp.length = 8; |
796 | break; |
796 | break; |
797 | default: |
797 | default: |
798 | break; |
798 | break; |
799 | } |
799 | } |
800 | 800 | ||
801 | info->var.xres = fb_width; |
801 | info->var.xres = fb_width; |
802 | info->var.yres = fb_height; |
802 | info->var.yres = fb_height; |
803 | } |
803 | } |
804 | EXPORT_SYMBOL(drm_fb_helper_fill_var);>>>>>>>>>>>>>>><>><>><>><>>>>>> |
804 | EXPORT_SYMBOL(drm_fb_helper_fill_var);>>>>>>>>>>>>>>><>><>><>><>>>>>> |