Rev 5271 | Rev 6088 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 5271 | Rev 6084 | ||
---|---|---|---|
Line 36... | Line 36... | ||
36 | #include |
36 | #include |
37 | #include |
37 | #include |
38 | #include |
38 | #include |
39 | #include |
39 | #include |
40 | #include |
40 | #include |
- | 41 | #include |
|
- | 42 | #include |
|
- | 43 | ||
- | 44 | static bool drm_fbdev_emulation = true; |
|
- | 45 | module_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600); |
|
- | 46 | MODULE_PARM_DESC(fbdev_emulation, |
|
- | 47 | "Enable legacy fbdev emulation [default=true]"); |
|
Line 41... | Line 48... | ||
41 | 48 | ||
Line 42... | Line 49... | ||
42 | static LIST_HEAD(kernel_fb_helper_list); |
49 | static LIST_HEAD(kernel_fb_helper_list); |
43 | 50 | ||
Line 54... | Line 61... | ||
54 | * drm_fb_helper_initial_config(). Drivers with fancier requirements than the |
61 | * drm_fb_helper_initial_config(). Drivers with fancier requirements than the |
55 | * default behaviour can override the third step with their own code. |
62 | * default behaviour can override the third step with their own code. |
56 | * Teardown is done with drm_fb_helper_fini(). |
63 | * Teardown is done with drm_fb_helper_fini(). |
57 | * |
64 | * |
58 | * At runtime drivers should restore the fbdev console by calling |
65 | * At runtime drivers should restore the fbdev console by calling |
59 | * drm_fb_helper_restore_fbdev_mode() from their ->lastclose callback. They |
66 | * drm_fb_helper_restore_fbdev_mode_unlocked() from their ->lastclose callback. |
60 | * should also notify the fb helper code from updates to the output |
67 | * They should also notify the fb helper code from updates to the output |
61 | * configuration by calling drm_fb_helper_hotplug_event(). For easier |
68 | * configuration by calling drm_fb_helper_hotplug_event(). For easier |
62 | * integration with the output polling code in drm_crtc_helper.c the modeset |
69 | * integration with the output polling code in drm_crtc_helper.c the modeset |
63 | * code provides a ->output_poll_changed callback. |
70 | * code provides a ->output_poll_changed callback. |
64 | * |
71 | * |
65 | * All other functions exported by the fb helper library can be used to |
72 | * All other functions exported by the fb helper library can be used to |
Line 87... | Line 94... | ||
87 | * This functions adds all the available connectors for use with the given |
94 | * This functions adds all the available connectors for use with the given |
88 | * fb_helper. This is a separate step to allow drivers to freely assign |
95 | * fb_helper. This is a separate step to allow drivers to freely assign |
89 | * connectors to the fbdev, e.g. if some are reserved for special purposes or |
96 | * connectors to the fbdev, e.g. if some are reserved for special purposes or |
90 | * not adequate to be used for the fbcon. |
97 | * not adequate to be used for the fbcon. |
91 | * |
98 | * |
92 | * Since this is part of the initial setup before the fbdev is published, no |
99 | * This function is protected against concurrent connector hotadds/removals |
- | 100 | * using drm_fb_helper_add_one_connector() and |
|
93 | * locking is required. |
101 | * drm_fb_helper_remove_one_connector(). |
94 | */ |
102 | */ |
95 | int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) |
103 | int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) |
96 | { |
104 | { |
97 | struct drm_device *dev = fb_helper->dev; |
105 | struct drm_device *dev = fb_helper->dev; |
98 | struct drm_connector *connector; |
106 | struct drm_connector *connector; |
99 | int i; |
107 | int i; |
Line -... | Line 108... | ||
- | 108 | ||
- | 109 | if (!drm_fbdev_emulation) |
|
- | 110 | return 0; |
|
- | 111 | ||
100 | 112 | mutex_lock(&dev->mode_config.mutex); |
|
101 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { |
113 | drm_for_each_connector(connector, dev) { |
Line 102... | Line 114... | ||
102 | struct drm_fb_helper_connector *fb_helper_connector; |
114 | struct drm_fb_helper_connector *fb_helper_connector; |
103 | 115 | ||
104 | fb_helper_connector = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL); |
116 | fb_helper_connector = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL); |
Line 105... | Line 117... | ||
105 | if (!fb_helper_connector) |
117 | if (!fb_helper_connector) |
106 | goto fail; |
118 | goto fail; |
107 | 119 | ||
- | 120 | fb_helper_connector->connector = connector; |
|
108 | fb_helper_connector->connector = connector; |
121 | fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector; |
109 | fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector; |
122 | } |
110 | } |
123 | mutex_unlock(&dev->mode_config.mutex); |
111 | return 0; |
124 | return 0; |
112 | fail: |
125 | fail: |
113 | for (i = 0; i < fb_helper->connector_count; i++) { |
126 | for (i = 0; i < fb_helper->connector_count; i++) { |
114 | kfree(fb_helper->connector_info[i]); |
127 | kfree(fb_helper->connector_info[i]); |
- | 128 | fb_helper->connector_info[i] = NULL; |
|
- | 129 | } |
|
115 | fb_helper->connector_info[i] = NULL; |
130 | fb_helper->connector_count = 0; |
116 | } |
131 | mutex_unlock(&dev->mode_config.mutex); |
117 | fb_helper->connector_count = 0; |
132 | |
Line 118... | Line 133... | ||
118 | return -ENOMEM; |
133 | return -ENOMEM; |
119 | } |
134 | } |
120 | EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors); |
135 | EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors); |
121 | 136 | ||
Line -... | Line 137... | ||
- | 137 | int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_connector *connector) |
|
- | 138 | { |
|
- | 139 | struct drm_fb_helper_connector **temp; |
|
122 | int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_connector *connector) |
140 | struct drm_fb_helper_connector *fb_helper_connector; |
123 | { |
141 | |
124 | struct drm_fb_helper_connector **temp; |
142 | if (!drm_fbdev_emulation) |
125 | struct drm_fb_helper_connector *fb_helper_connector; |
143 | return 0; |
126 | 144 | ||
Line 143... | Line 161... | ||
143 | fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector; |
161 | fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector; |
144 | return 0; |
162 | return 0; |
145 | } |
163 | } |
146 | EXPORT_SYMBOL(drm_fb_helper_add_one_connector); |
164 | EXPORT_SYMBOL(drm_fb_helper_add_one_connector); |
Line -... | Line 165... | ||
- | 165 | ||
- | 166 | static void remove_from_modeset(struct drm_mode_set *set, |
|
- | 167 | struct drm_connector *connector) |
|
- | 168 | { |
|
- | 169 | int i, j; |
|
- | 170 | ||
- | 171 | for (i = 0; i < set->num_connectors; i++) { |
|
- | 172 | if (set->connectors[i] == connector) |
|
- | 173 | break; |
|
- | 174 | } |
|
- | 175 | ||
- | 176 | if (i == set->num_connectors) |
|
- | 177 | return; |
|
- | 178 | ||
- | 179 | for (j = i + 1; j < set->num_connectors; j++) { |
|
- | 180 | set->connectors[j - 1] = set->connectors[j]; |
|
- | 181 | } |
|
- | 182 | set->num_connectors--; |
|
- | 183 | ||
- | 184 | /* |
|
- | 185 | * TODO maybe need to makes sure we set it back to !=NULL somewhere? |
|
- | 186 | */ |
|
- | 187 | if (set->num_connectors == 0) { |
|
- | 188 | set->fb = NULL; |
|
- | 189 | drm_mode_destroy(connector->dev, set->mode); |
|
- | 190 | set->mode = NULL; |
|
- | 191 | } |
|
- | 192 | } |
|
147 | 193 | ||
148 | int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, |
194 | int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper, |
149 | struct drm_connector *connector) |
195 | struct drm_connector *connector) |
150 | { |
196 | { |
151 | struct drm_fb_helper_connector *fb_helper_connector; |
197 | struct drm_fb_helper_connector *fb_helper_connector; |
Line -... | Line 198... | ||
- | 198 | int i, j; |
|
- | 199 | ||
- | 200 | if (!drm_fbdev_emulation) |
|
152 | int i, j; |
201 | return 0; |
Line 153... | Line 202... | ||
153 | 202 | ||
154 | WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex)); |
203 | WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex)); |
155 | 204 | ||
Line 165... | Line 214... | ||
165 | for (j = i + 1; j < fb_helper->connector_count; j++) { |
214 | for (j = i + 1; j < fb_helper->connector_count; j++) { |
166 | fb_helper->connector_info[j - 1] = fb_helper->connector_info[j]; |
215 | fb_helper->connector_info[j - 1] = fb_helper->connector_info[j]; |
167 | } |
216 | } |
168 | fb_helper->connector_count--; |
217 | fb_helper->connector_count--; |
169 | kfree(fb_helper_connector); |
218 | kfree(fb_helper_connector); |
- | 219 | ||
- | 220 | /* also cleanup dangling references to the connector: */ |
|
- | 221 | for (i = 0; i < fb_helper->crtc_count; i++) |
|
- | 222 | remove_from_modeset(&fb_helper->crtc_info[i].mode_set, connector); |
|
- | 223 | ||
170 | return 0; |
224 | return 0; |
171 | } |
225 | } |
172 | EXPORT_SYMBOL(drm_fb_helper_remove_one_connector); |
226 | EXPORT_SYMBOL(drm_fb_helper_remove_one_connector); |
Line 173... | Line 227... | ||
173 | 227 | ||
Line 199... | Line 253... | ||
199 | b_base = g_base + crtc->gamma_size; |
253 | b_base = g_base + crtc->gamma_size; |
Line 200... | Line 254... | ||
200 | 254 | ||
201 | crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size); |
255 | crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size); |
Line -... | Line 256... | ||
- | 256 | } |
|
- | 257 | ||
- | 258 | /* Find the real fb for a given fb helper CRTC */ |
|
- | 259 | static struct drm_framebuffer *drm_mode_config_fb(struct drm_crtc *crtc) |
|
- | 260 | { |
|
- | 261 | struct drm_device *dev = crtc->dev; |
|
- | 262 | struct drm_crtc *c; |
|
- | 263 | ||
- | 264 | drm_for_each_crtc(c, dev) { |
|
- | 265 | if (crtc->base.id == c->base.id) |
|
- | 266 | return c->primary->fb; |
|
- | 267 | } |
|
- | 268 | ||
- | 269 | return NULL; |
|
- | 270 | } |
|
- | 271 | static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper) |
|
- | 272 | { |
|
- | 273 | struct drm_device *dev = fb_helper->dev; |
|
- | 274 | struct drm_plane *plane; |
|
- | 275 | struct drm_atomic_state *state; |
|
- | 276 | int i, ret; |
|
- | 277 | unsigned plane_mask; |
|
- | 278 | ||
- | 279 | state = drm_atomic_state_alloc(dev); |
|
- | 280 | if (!state) |
|
- | 281 | return -ENOMEM; |
|
- | 282 | ||
- | 283 | state->acquire_ctx = dev->mode_config.acquire_ctx; |
|
- | 284 | retry: |
|
- | 285 | plane_mask = 0; |
|
- | 286 | drm_for_each_plane(plane, dev) { |
|
- | 287 | struct drm_plane_state *plane_state; |
|
- | 288 | ||
- | 289 | plane_state = drm_atomic_get_plane_state(state, plane); |
|
- | 290 | if (IS_ERR(plane_state)) { |
|
- | 291 | ret = PTR_ERR(plane_state); |
|
- | 292 | goto fail; |
|
- | 293 | } |
|
- | 294 | ||
- | 295 | plane_state->rotation = BIT(DRM_ROTATE_0); |
|
- | 296 | ||
- | 297 | plane->old_fb = plane->fb; |
|
- | 298 | plane_mask |= 1 << drm_plane_index(plane); |
|
- | 299 | ||
- | 300 | /* disable non-primary: */ |
|
- | 301 | if (plane->type == DRM_PLANE_TYPE_PRIMARY) |
|
- | 302 | continue; |
|
- | 303 | ||
- | 304 | ret = __drm_atomic_helper_disable_plane(plane, plane_state); |
|
- | 305 | if (ret != 0) |
|
- | 306 | goto fail; |
|
- | 307 | } |
|
- | 308 | ||
- | 309 | for(i = 0; i < fb_helper->crtc_count; i++) { |
|
- | 310 | struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; |
|
- | 311 | ||
- | 312 | ret = __drm_atomic_helper_set_config(mode_set, state); |
|
- | 313 | if (ret != 0) |
|
- | 314 | goto fail; |
|
- | 315 | } |
|
- | 316 | ||
- | 317 | ret = drm_atomic_commit(state); |
|
- | 318 | ||
- | 319 | fail: |
|
- | 320 | drm_atomic_clean_old_fb(dev, plane_mask, ret); |
|
- | 321 | ||
- | 322 | if (ret == -EDEADLK) |
|
- | 323 | goto backoff; |
|
- | 324 | ||
- | 325 | if (ret != 0) |
|
- | 326 | drm_atomic_state_free(state); |
|
- | 327 | ||
- | 328 | return ret; |
|
- | 329 | ||
- | 330 | backoff: |
|
Line -... | Line 331... | ||
- | 331 | drm_atomic_state_clear(state); |
|
- | 332 | drm_atomic_legacy_backoff(state); |
|
- | 333 | ||
202 | } |
334 | goto retry; |
203 | 335 | } |
|
204 | 336 | ||
205 | static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper) |
337 | static int restore_fbdev_mode(struct drm_fb_helper *fb_helper) |
206 | { |
- | |
207 | struct drm_device *dev = fb_helper->dev; |
338 | { |
Line 208... | Line 339... | ||
208 | struct drm_plane *plane; |
339 | struct drm_device *dev = fb_helper->dev; |
Line -... | Line 340... | ||
- | 340 | struct drm_plane *plane; |
|
- | 341 | int i; |
|
- | 342 | ||
209 | bool error = false; |
343 | drm_warn_on_modeset_not_all_locked(dev); |
210 | int i; |
344 | |
211 | 345 | if (fb_helper->atomic) |
|
Line 212... | Line 346... | ||
212 | drm_warn_on_modeset_not_all_locked(dev); |
346 | return restore_fbdev_mode_atomic(fb_helper); |
213 | 347 | ||
Line 225... | Line 359... | ||
225 | for (i = 0; i < fb_helper->crtc_count; i++) { |
359 | for (i = 0; i < fb_helper->crtc_count; i++) { |
226 | struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; |
360 | struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; |
227 | struct drm_crtc *crtc = mode_set->crtc; |
361 | struct drm_crtc *crtc = mode_set->crtc; |
228 | int ret; |
362 | int ret; |
Line 229... | Line 363... | ||
229 | 363 | ||
- | 364 | if (crtc->funcs->cursor_set2) { |
|
- | 365 | ret = crtc->funcs->cursor_set2(crtc, NULL, 0, 0, 0, 0, 0); |
|
- | 366 | if (ret) |
|
- | 367 | return ret; |
|
230 | if (crtc->funcs->cursor_set) { |
368 | } else if (crtc->funcs->cursor_set) { |
231 | ret = crtc->funcs->cursor_set(crtc, NULL, 0, 0, 0); |
369 | ret = crtc->funcs->cursor_set(crtc, NULL, 0, 0, 0); |
232 | if (ret) |
370 | if (ret) |
233 | error = true; |
371 | return ret; |
Line 234... | Line 372... | ||
234 | } |
372 | } |
235 | 373 | ||
236 | ret = drm_mode_set_config_internal(mode_set); |
374 | ret = drm_mode_set_config_internal(mode_set); |
237 | if (ret) |
375 | if (ret) |
- | 376 | return ret; |
|
238 | error = true; |
377 | } |
239 | } |
378 | |
- | 379 | return 0; |
|
240 | return error; |
380 | } |
241 | } |
381 | |
242 | /** |
382 | /** |
243 | * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration |
383 | * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration |
244 | * @fb_helper: fbcon to restore |
384 | * @fb_helper: fbcon to restore |
245 | * |
385 | * |
246 | * This should be called from driver's drm ->lastclose callback |
386 | * This should be called from driver's drm ->lastclose callback |
- | 387 | * when implementing an fbcon on top of kms using this helper. This ensures that |
|
- | 388 | * the user isn't greeted with a black screen when e.g. X dies. |
|
- | 389 | * |
|
247 | * when implementing an fbcon on top of kms using this helper. This ensures that |
390 | * RETURNS: |
248 | * the user isn't greeted with a black screen when e.g. X dies. |
391 | * Zero if everything went ok, negative error code otherwise. |
249 | */ |
392 | */ |
250 | bool drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper) |
393 | int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper) |
- | 394 | { |
|
251 | { |
395 | struct drm_device *dev = fb_helper->dev; |
- | 396 | bool do_delayed; |
|
252 | struct drm_device *dev = fb_helper->dev; |
397 | int ret; |
- | 398 | ||
Line 253... | Line 399... | ||
253 | bool ret; |
399 | if (!drm_fbdev_emulation) |
254 | bool do_delayed = false; |
400 | return -ENODEV; |
- | 401 | ||
- | 402 | drm_modeset_lock_all(dev); |
|
- | 403 | ret = restore_fbdev_mode(fb_helper); |
|
- | 404 | ||
255 | 405 | do_delayed = fb_helper->delayed_hotplug; |
|
- | 406 | if (do_delayed) |
|
- | 407 | fb_helper->delayed_hotplug = false; |
|
- | 408 | drm_modeset_unlock_all(dev); |
|
256 | drm_modeset_lock_all(dev); |
409 | |
257 | ret = restore_fbdev_mode(fb_helper); |
410 | if (do_delayed) |
258 | drm_modeset_unlock_all(dev); |
411 | drm_fb_helper_hotplug_event(fb_helper); |
Line 259... | Line 412... | ||
259 | return ret; |
412 | return ret; |
260 | } |
413 | } |
261 | EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked); |
414 | EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked); |
262 | 415 | ||
263 | static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper) |
416 | static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper) |
Line -... | Line 417... | ||
- | 417 | { |
|
- | 418 | struct drm_device *dev = fb_helper->dev; |
|
- | 419 | struct drm_crtc *crtc; |
|
- | 420 | int bound = 0, crtcs_bound = 0; |
|
- | 421 | ||
264 | { |
422 | /* Sometimes user space wants everything disabled, so don't steal the |
265 | struct drm_device *dev = fb_helper->dev; |
423 | * display if there's a master. */ |
266 | struct drm_crtc *crtc; |
424 | if (dev->primary->master) |
267 | int bound = 0, crtcs_bound = 0; |
425 | return false; |
268 | 426 | ||
269 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
427 | drm_for_each_crtc(crtc, dev) { |
Line 310... | Line 468... | ||
310 | struct drm_crtc *crtc; |
468 | struct drm_crtc *crtc; |
311 | struct drm_connector *connector; |
469 | struct drm_connector *connector; |
312 | int i, j; |
470 | int i, j; |
Line 313... | Line 471... | ||
313 | 471 | ||
314 | /* |
- | |
315 | * fbdev->blank can be called from irq context in case of a panic. |
- | |
316 | * Since we already have our own special panic handler which will |
- | |
317 | * restore the fbdev console mode completely, just bail out early. |
- | |
318 | */ |
- | |
319 | - | ||
320 | /* |
472 | /* |
321 | * For each CRTC in this fb, turn the connectors on/off. |
473 | * For each CRTC in this fb, turn the connectors on/off. |
322 | */ |
474 | */ |
323 | drm_modeset_lock_all(dev); |
475 | drm_modeset_lock_all(dev); |
324 | if (!drm_fb_helper_is_bound(fb_helper)) { |
476 | if (!drm_fb_helper_is_bound(fb_helper)) { |
Line 348... | Line 500... | ||
348 | * @blank: desired blanking state |
500 | * @blank: desired blanking state |
349 | * @info: fbdev registered by the helper |
501 | * @info: fbdev registered by the helper |
350 | */ |
502 | */ |
351 | int drm_fb_helper_blank(int blank, struct fb_info *info) |
503 | int drm_fb_helper_blank(int blank, struct fb_info *info) |
352 | { |
504 | { |
- | 505 | if (oops_in_progress) |
|
- | 506 | return -EBUSY; |
|
- | 507 | ||
353 | switch (blank) { |
508 | switch (blank) { |
354 | /* Display: On; HSync: On, VSync: On */ |
509 | /* Display: On; HSync: On, VSync: On */ |
355 | case FB_BLANK_UNBLANK: |
510 | case FB_BLANK_UNBLANK: |
356 | drm_fb_helper_dpms(info, DRM_MODE_DPMS_ON); |
511 | drm_fb_helper_dpms(info, DRM_MODE_DPMS_ON); |
357 | break; |
512 | break; |
Line 431... | Line 586... | ||
431 | int crtc_count, int max_conn_count) |
586 | int crtc_count, int max_conn_count) |
432 | { |
587 | { |
433 | struct drm_crtc *crtc; |
588 | struct drm_crtc *crtc; |
434 | int i; |
589 | int i; |
Line -... | Line 590... | ||
- | 590 | ||
- | 591 | if (!drm_fbdev_emulation) |
|
- | 592 | return 0; |
|
435 | 593 | ||
436 | if (!max_conn_count) |
594 | if (!max_conn_count) |
Line 437... | Line 595... | ||
437 | return -EINVAL; |
595 | return -EINVAL; |
438 | 596 | ||
Line 459... | Line 617... | ||
459 | goto out_free; |
617 | goto out_free; |
460 | fb_helper->crtc_info[i].mode_set.num_connectors = 0; |
618 | fb_helper->crtc_info[i].mode_set.num_connectors = 0; |
461 | } |
619 | } |
Line 462... | Line 620... | ||
462 | 620 | ||
463 | i = 0; |
621 | i = 0; |
464 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
622 | drm_for_each_crtc(crtc, dev) { |
465 | fb_helper->crtc_info[i].mode_set.crtc = crtc; |
623 | fb_helper->crtc_info[i].mode_set.crtc = crtc; |
466 | i++; |
624 | i++; |
Line -... | Line 625... | ||
- | 625 | } |
|
- | 626 | ||
467 | } |
627 | fb_helper->atomic = !!drm_core_check_feature(dev, DRIVER_ATOMIC); |
468 | 628 | ||
469 | return 0; |
629 | return 0; |
470 | out_free: |
630 | out_free: |
471 | drm_fb_helper_crtc_free(fb_helper); |
631 | drm_fb_helper_crtc_free(fb_helper); |
472 | return -ENOMEM; |
632 | return -ENOMEM; |
Line -... | Line 633... | ||
- | 633 | } |
|
- | 634 | EXPORT_SYMBOL(drm_fb_helper_init); |
|
- | 635 | ||
- | 636 | /** |
|
- | 637 | * drm_fb_helper_alloc_fbi - allocate fb_info and some of its members |
|
- | 638 | * @fb_helper: driver-allocated fbdev helper |
|
- | 639 | * |
|
- | 640 | * A helper to alloc fb_info and the members cmap and apertures. Called |
|
- | 641 | * by the driver within the fb_probe fb_helper callback function. |
|
- | 642 | * |
|
- | 643 | * RETURNS: |
|
- | 644 | * fb_info pointer if things went okay, pointer containing error code |
|
- | 645 | * otherwise |
|
- | 646 | */ |
|
- | 647 | struct fb_info *drm_fb_helper_alloc_fbi(struct drm_fb_helper *fb_helper) |
|
- | 648 | { |
|
- | 649 | struct device *dev = fb_helper->dev->dev; |
|
- | 650 | struct fb_info *info; |
|
- | 651 | int ret; |
|
- | 652 | ||
- | 653 | info = framebuffer_alloc(0, dev); |
|
- | 654 | if (!info) |
|
- | 655 | return ERR_PTR(-ENOMEM); |
|
- | 656 | ||
- | 657 | ||
- | 658 | fb_helper->fbdev = info; |
|
- | 659 | ||
- | 660 | return info; |
|
473 | } |
661 | |
474 | EXPORT_SYMBOL(drm_fb_helper_init); |
662 | } |
475 | 663 | EXPORT_SYMBOL(drm_fb_helper_alloc_fbi); |
|
476 | static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, |
664 | static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, |
477 | u16 blue, u16 regno, struct fb_info *info) |
665 | u16 blue, u16 regno, struct fb_info *info) |
Line 552... | Line 740... | ||
552 | */ |
740 | */ |
553 | int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) |
741 | int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) |
554 | { |
742 | { |
555 | struct drm_fb_helper *fb_helper = info->par; |
743 | struct drm_fb_helper *fb_helper = info->par; |
556 | struct drm_device *dev = fb_helper->dev; |
744 | struct drm_device *dev = fb_helper->dev; |
557 | struct drm_crtc_helper_funcs *crtc_funcs; |
745 | const struct drm_crtc_helper_funcs *crtc_funcs; |
558 | u16 *red, *green, *blue, *transp; |
746 | u16 *red, *green, *blue, *transp; |
559 | struct drm_crtc *crtc; |
747 | struct drm_crtc *crtc; |
560 | int i, j, rc = 0; |
748 | int i, j, rc = 0; |
561 | int start; |
749 | int start; |
Line -... | Line 750... | ||
- | 750 | ||
- | 751 | if (oops_in_progress) |
|
- | 752 | return -EBUSY; |
|
562 | 753 | ||
563 | drm_modeset_lock_all(dev); |
754 | drm_modeset_lock_all(dev); |
564 | if (!drm_fb_helper_is_bound(fb_helper)) { |
755 | if (!drm_fb_helper_is_bound(fb_helper)) { |
565 | drm_modeset_unlock_all(dev); |
756 | drm_modeset_unlock_all(dev); |
566 | return -EBUSY; |
757 | return -EBUSY; |
Line 707... | Line 898... | ||
707 | int drm_fb_helper_set_par(struct fb_info *info) |
898 | int drm_fb_helper_set_par(struct fb_info *info) |
708 | { |
899 | { |
709 | struct drm_fb_helper *fb_helper = info->par; |
900 | struct drm_fb_helper *fb_helper = info->par; |
710 | struct fb_var_screeninfo *var = &info->var; |
901 | struct fb_var_screeninfo *var = &info->var; |
Line -... | Line 902... | ||
- | 902 | ||
- | 903 | if (oops_in_progress) |
|
- | 904 | return -EBUSY; |
|
711 | 905 | ||
712 | if (var->pixclock != 0) { |
906 | if (var->pixclock != 0) { |
713 | DRM_ERROR("PIXEL CLOCK SET\n"); |
907 | DRM_ERROR("PIXEL CLOCK SET\n"); |
714 | return -EINVAL; |
908 | return -EINVAL; |
Line 718... | Line 912... | ||
718 | 912 | ||
719 | return 0; |
913 | return 0; |
720 | } |
914 | } |
Line -... | Line 915... | ||
- | 915 | EXPORT_SYMBOL(drm_fb_helper_set_par); |
|
- | 916 | ||
- | 917 | static int pan_display_atomic(struct fb_var_screeninfo *var, |
|
- | 918 | struct fb_info *info) |
|
- | 919 | { |
|
- | 920 | struct drm_fb_helper *fb_helper = info->par; |
|
- | 921 | struct drm_device *dev = fb_helper->dev; |
|
- | 922 | struct drm_atomic_state *state; |
|
- | 923 | struct drm_plane *plane; |
|
- | 924 | int i, ret; |
|
- | 925 | unsigned plane_mask; |
|
- | 926 | ||
- | 927 | state = drm_atomic_state_alloc(dev); |
|
- | 928 | if (!state) |
|
- | 929 | return -ENOMEM; |
|
- | 930 | ||
- | 931 | state->acquire_ctx = dev->mode_config.acquire_ctx; |
|
- | 932 | retry: |
|
- | 933 | plane_mask = 0; |
|
- | 934 | for(i = 0; i < fb_helper->crtc_count; i++) { |
|
- | 935 | struct drm_mode_set *mode_set; |
|
- | 936 | ||
- | 937 | mode_set = &fb_helper->crtc_info[i].mode_set; |
|
- | 938 | ||
- | 939 | mode_set->x = var->xoffset; |
|
- | 940 | mode_set->y = var->yoffset; |
|
- | 941 | ||
- | 942 | ret = __drm_atomic_helper_set_config(mode_set, state); |
|
- | 943 | if (ret != 0) |
|
- | 944 | goto fail; |
|
- | 945 | ||
- | 946 | plane = mode_set->crtc->primary; |
|
- | 947 | plane_mask |= drm_plane_index(plane); |
|
- | 948 | plane->old_fb = plane->fb; |
|
- | 949 | } |
|
- | 950 | ||
- | 951 | ret = drm_atomic_commit(state); |
|
- | 952 | if (ret != 0) |
|
- | 953 | goto fail; |
|
- | 954 | ||
- | 955 | info->var.xoffset = var->xoffset; |
|
- | 956 | info->var.yoffset = var->yoffset; |
|
- | 957 | ||
- | 958 | ||
- | 959 | fail: |
|
- | 960 | drm_atomic_clean_old_fb(dev, plane_mask, ret); |
|
- | 961 | ||
- | 962 | if (ret == -EDEADLK) |
|
- | 963 | goto backoff; |
|
- | 964 | ||
- | 965 | if (ret != 0) |
|
- | 966 | drm_atomic_state_free(state); |
|
- | 967 | ||
- | 968 | return ret; |
|
- | 969 | ||
- | 970 | backoff: |
|
- | 971 | drm_atomic_state_clear(state); |
|
- | 972 | drm_atomic_legacy_backoff(state); |
|
- | 973 | ||
- | 974 | goto retry; |
|
721 | EXPORT_SYMBOL(drm_fb_helper_set_par); |
975 | } |
722 | 976 | ||
723 | /** |
977 | /** |
724 | * drm_fb_helper_pan_display - implementation for ->fb_pan_display |
978 | * drm_fb_helper_pan_display - implementation for ->fb_pan_display |
725 | * @var: updated screen information |
979 | * @var: updated screen information |
Line 732... | Line 986... | ||
732 | struct drm_device *dev = fb_helper->dev; |
986 | struct drm_device *dev = fb_helper->dev; |
733 | struct drm_mode_set *modeset; |
987 | struct drm_mode_set *modeset; |
734 | int ret = 0; |
988 | int ret = 0; |
735 | int i; |
989 | int i; |
Line -... | Line 990... | ||
- | 990 | ||
- | 991 | if (oops_in_progress) |
|
- | 992 | return -EBUSY; |
|
736 | 993 | ||
737 | drm_modeset_lock_all(dev); |
994 | drm_modeset_lock_all(dev); |
738 | if (!drm_fb_helper_is_bound(fb_helper)) { |
995 | if (!drm_fb_helper_is_bound(fb_helper)) { |
739 | drm_modeset_unlock_all(dev); |
996 | drm_modeset_unlock_all(dev); |
740 | return -EBUSY; |
997 | return -EBUSY; |
Line -... | Line 998... | ||
- | 998 | } |
|
- | 999 | ||
- | 1000 | if (fb_helper->atomic) { |
|
- | 1001 | ret = pan_display_atomic(var, info); |
|
- | 1002 | goto unlock; |
|
741 | } |
1003 | } |
742 | 1004 | ||
Line 743... | Line 1005... | ||
743 | for (i = 0; i < fb_helper->crtc_count; i++) { |
1005 | for (i = 0; i < fb_helper->crtc_count; i++) { |
744 | modeset = &fb_helper->crtc_info[i].mode_set; |
1006 | modeset = &fb_helper->crtc_info[i].mode_set; |
Line 752... | Line 1014... | ||
752 | info->var.xoffset = var->xoffset; |
1014 | info->var.xoffset = var->xoffset; |
753 | info->var.yoffset = var->yoffset; |
1015 | info->var.yoffset = var->yoffset; |
754 | } |
1016 | } |
755 | } |
1017 | } |
756 | } |
1018 | } |
- | 1019 | unlock: |
|
757 | drm_modeset_unlock_all(dev); |
1020 | drm_modeset_unlock_all(dev); |
758 | return ret; |
1021 | return ret; |
759 | } |
1022 | } |
760 | EXPORT_SYMBOL(drm_fb_helper_pan_display); |
1023 | EXPORT_SYMBOL(drm_fb_helper_pan_display); |
Line 817... | Line 1080... | ||
817 | } |
1080 | } |
Line 818... | Line 1081... | ||
818 | 1081 | ||
819 | crtc_count = 0; |
1082 | crtc_count = 0; |
820 | for (i = 0; i < fb_helper->crtc_count; i++) { |
1083 | for (i = 0; i < fb_helper->crtc_count; i++) { |
- | 1084 | struct drm_display_mode *desired_mode; |
|
821 | struct drm_display_mode *desired_mode; |
1085 | struct drm_mode_set *mode_set; |
- | 1086 | int x, y, j; |
|
- | 1087 | /* in case of tile group, are we the last tile vert or horiz? |
|
- | 1088 | * If no tile group you are always the last one both vertically |
|
- | 1089 | * and horizontally |
|
- | 1090 | */ |
|
- | 1091 | bool lastv = true, lasth = true; |
|
822 | int x, y; |
1092 | |
- | 1093 | desired_mode = fb_helper->crtc_info[i].desired_mode; |
|
- | 1094 | mode_set = &fb_helper->crtc_info[i].mode_set; |
|
- | 1095 | ||
- | 1096 | if (!desired_mode) |
|
- | 1097 | continue; |
|
- | 1098 | ||
- | 1099 | crtc_count++; |
|
823 | desired_mode = fb_helper->crtc_info[i].desired_mode; |
1100 | |
824 | x = fb_helper->crtc_info[i].x; |
1101 | x = fb_helper->crtc_info[i].x; |
825 | y = fb_helper->crtc_info[i].y; |
- | |
- | 1102 | y = fb_helper->crtc_info[i].y; |
|
826 | if (desired_mode) { |
1103 | |
827 | if (gamma_size == 0) |
1104 | if (gamma_size == 0) |
- | 1105 | gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size; |
|
828 | gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size; |
1106 | |
829 | if (desired_mode->hdisplay + x < sizes.fb_width) |
1107 | sizes.surface_width = max_t(u32, desired_mode->hdisplay + x, sizes.surface_width); |
- | 1108 | sizes.surface_height = max_t(u32, desired_mode->vdisplay + y, sizes.surface_height); |
|
830 | sizes.fb_width = desired_mode->hdisplay + x; |
1109 | |
831 | if (desired_mode->vdisplay + y < sizes.fb_height) |
1110 | for (j = 0; j < mode_set->num_connectors; j++) { |
832 | sizes.fb_height = desired_mode->vdisplay + y; |
1111 | struct drm_connector *connector = mode_set->connectors[j]; |
833 | if (desired_mode->hdisplay + x > sizes.surface_width) |
1112 | if (connector->has_tile) { |
834 | sizes.surface_width = desired_mode->hdisplay + x; |
1113 | lasth = (connector->tile_h_loc == (connector->num_h_tile - 1)); |
835 | if (desired_mode->vdisplay + y > sizes.surface_height) |
1114 | lastv = (connector->tile_v_loc == (connector->num_v_tile - 1)); |
836 | sizes.surface_height = desired_mode->vdisplay + y; |
1115 | /* cloning to multiple tiles is just crazy-talk, so: */ |
- | 1116 | break; |
|
837 | crtc_count++; |
1117 | } |
- | 1118 | } |
|
- | 1119 | ||
- | 1120 | if (lasth) |
|
- | 1121 | sizes.fb_width = min_t(u32, desired_mode->hdisplay + x, sizes.fb_width); |
|
- | 1122 | if (lastv) |
|
838 | } |
1123 | sizes.fb_height = min_t(u32, desired_mode->vdisplay + y, sizes.fb_height); |
Line 839... | Line 1124... | ||
839 | } |
1124 | } |
840 | 1125 | ||
841 | if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) { |
1126 | if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) { |
Line 864... | Line 1149... | ||
864 | if (fb_helper->crtc_info[i].mode_set.num_connectors) |
1149 | if (fb_helper->crtc_info[i].mode_set.num_connectors) |
865 | fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb; |
1150 | fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb; |
Line 866... | Line 1151... | ||
866 | 1151 | ||
- | 1152 | ||
867 | 1153 | info->var.pixclock = 0; |
|
868 | info->var.pixclock = 0; |
1154 | |
Line -... | Line 1155... | ||
- | 1155 | dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n", |
|
869 | dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n", |
1156 | info->node, info->fix.id); |
Line 870... | Line 1157... | ||
870 | info->node, info->fix.id); |
1157 | |
871 | 1158 | ||
Line 1032... | Line 1319... | ||
1032 | 1319 | ||
1033 | struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn, |
1320 | struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn, |
1034 | int width, int height) |
1321 | int width, int height) |
1035 | { |
1322 | { |
1036 | struct drm_cmdline_mode *cmdline_mode; |
1323 | struct drm_cmdline_mode *cmdline_mode; |
1037 | struct drm_display_mode *mode = NULL; |
1324 | struct drm_display_mode *mode; |
Line 1038... | Line 1325... | ||
1038 | bool prefer_non_interlace; |
1325 | bool prefer_non_interlace; |
1039 | 1326 | ||
- | 1327 | return NULL; |
|
1040 | return NULL; |
1328 | #if 0 |
1041 | 1329 | cmdline_mode = &fb_helper_conn->connector->cmdline_mode; |
|
Line 1042... | Line 1330... | ||
1042 | if (cmdline_mode->specified == false) |
1330 | if (cmdline_mode->specified == false) |
1043 | return mode; |
1331 | return NULL; |
1044 | 1332 | ||
1045 | /* attempt to find a matching mode in the list of modes |
1333 | /* attempt to find a matching mode in the list of modes |
Line 1079... | Line 1367... | ||
1079 | create_mode: |
1367 | create_mode: |
1080 | mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev, |
1368 | mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev, |
1081 | cmdline_mode); |
1369 | cmdline_mode); |
1082 | list_add(&mode->head, &fb_helper_conn->connector->modes); |
1370 | list_add(&mode->head, &fb_helper_conn->connector->modes); |
1083 | return mode; |
1371 | return mode; |
- | 1372 | #endif |
|
1084 | } |
1373 | } |
1085 | EXPORT_SYMBOL(drm_pick_cmdline_mode); |
1374 | EXPORT_SYMBOL(drm_pick_cmdline_mode); |
Line 1086... | Line 1375... | ||
1086 | 1375 | ||
1087 | static bool drm_connector_enabled(struct drm_connector *connector, bool strict) |
1376 | static bool drm_connector_enabled(struct drm_connector *connector, bool strict) |
Line 1301... | Line 1590... | ||
1301 | int n, int width, int height) |
1590 | int n, int width, int height) |
1302 | { |
1591 | { |
1303 | int c, o; |
1592 | int c, o; |
1304 | struct drm_device *dev = fb_helper->dev; |
1593 | struct drm_device *dev = fb_helper->dev; |
1305 | struct drm_connector *connector; |
1594 | struct drm_connector *connector; |
1306 | struct drm_connector_helper_funcs *connector_funcs; |
1595 | const struct drm_connector_helper_funcs *connector_funcs; |
1307 | struct drm_encoder *encoder; |
1596 | struct drm_encoder *encoder; |
1308 | int my_score, best_score, score; |
1597 | int my_score, best_score, score; |
1309 | struct drm_fb_helper_crtc **crtcs, *crtc; |
1598 | struct drm_fb_helper_crtc **crtcs, *crtc; |
1310 | struct drm_fb_helper_connector *fb_helper_conn; |
1599 | struct drm_fb_helper_connector *fb_helper_conn; |
Line 1494... | Line 1783... | ||
1494 | * values for the fbdev info structure. |
1783 | * values for the fbdev info structure. |
1495 | * |
1784 | * |
1496 | * RETURNS: |
1785 | * RETURNS: |
1497 | * Zero if everything went ok, nonzero otherwise. |
1786 | * Zero if everything went ok, nonzero otherwise. |
1498 | */ |
1787 | */ |
1499 | bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel) |
1788 | int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel) |
1500 | { |
1789 | { |
1501 | struct drm_device *dev = fb_helper->dev; |
1790 | struct drm_device *dev = fb_helper->dev; |
1502 | int count = 0; |
1791 | int count = 0; |
Line -... | Line 1792... | ||
- | 1792 | ||
- | 1793 | if (!drm_fbdev_emulation) |
|
- | 1794 | return 0; |
|
1503 | 1795 | ||
1504 | mutex_lock(&dev->mode_config.mutex); |
1796 | mutex_lock(&dev->mode_config.mutex); |
1505 | count = drm_fb_helper_probe_connector_modes(fb_helper, |
1797 | count = drm_fb_helper_probe_connector_modes(fb_helper, |
1506 | dev->mode_config.max_width, |
1798 | dev->mode_config.max_width, |
1507 | dev->mode_config.max_height); |
1799 | dev->mode_config.max_height); |
Line 1542... | Line 1834... | ||
1542 | int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) |
1834 | int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) |
1543 | { |
1835 | { |
1544 | struct drm_device *dev = fb_helper->dev; |
1836 | struct drm_device *dev = fb_helper->dev; |
1545 | u32 max_width, max_height; |
1837 | u32 max_width, max_height; |
Line -... | Line 1838... | ||
- | 1838 | ||
- | 1839 | if (!drm_fbdev_emulation) |
|
- | 1840 | return 0; |
|
1546 | 1841 | ||
1547 | mutex_lock(&fb_helper->dev->mode_config.mutex); |
1842 | mutex_lock(&fb_helper->dev->mode_config.mutex); |
1548 | if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) { |
1843 | if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) { |
1549 | fb_helper->delayed_hotplug = true; |
1844 | fb_helper->delayed_hotplug = true; |
1550 | mutex_unlock(&fb_helper->dev->mode_config.mutex); |
1845 | mutex_unlock(&fb_helper->dev->mode_config.mutex); |