blob: 04a3a5ce370a059d8fb36f700a7f93a48182e2c6 [file] [log] [blame]
Dave Airlie785b93e2009-08-28 15:46:53 +10001/*
2 * Copyright (c) 2006-2009 Red Hat Inc.
3 * Copyright (c) 2006-2008 Intel Corporation
4 * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
5 *
6 * DRM framebuffer helper functions
7 *
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
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
12 * that the name of the copyright holders not be used in advertising or
13 * publicity pertaining to distribution of the software without specific,
14 * written prior permission. The copyright holders make no representations
15 * about the suitability of this software for any purpose. It is provided "as
16 * is" without express or implied warranty.
17 *
18 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
19 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
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,
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
24 * OF THIS SOFTWARE.
25 *
26 * Authors:
27 * Dave Airlie <airlied@linux.ie>
28 * Jesse Barnes <jesse.barnes@intel.com>
29 */
Sachin Kamatd56b1b92012-11-15 03:43:29 +000030#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
31
Noralf Trønnescfe63422016-08-23 13:54:06 +020032#include <linux/console.h>
Andy Shevchenko3b40a442010-02-02 14:40:32 -080033#include <linux/kernel.h>
Dave Airlie785b93e2009-08-28 15:46:53 +100034#include <linux/sysrq.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090035#include <linux/slab.h>
Paul Gortmakere0cd3602011-08-30 11:04:30 -040036#include <linux/module.h>
David Howells760285e2012-10-02 18:01:07 +010037#include <drm/drmP.h>
38#include <drm/drm_crtc.h>
39#include <drm/drm_fb_helper.h>
40#include <drm/drm_crtc_helper.h>
Rob Clarkbbb1e522015-08-25 15:35:58 -040041#include <drm/drm_atomic.h>
42#include <drm/drm_atomic_helper.h>
Dave Airlie785b93e2009-08-28 15:46:53 +100043
Hans de Goede8f0cb412017-11-25 20:35:50 +010044#include "drm_crtc_internal.h"
Ville Syrjälä699fbee2016-09-19 16:33:44 +030045#include "drm_crtc_helper_internal.h"
46
Daniel Vetterf64c5572015-08-25 15:45:13 +020047static bool drm_fbdev_emulation = true;
48module_param_named(fbdev_emulation, drm_fbdev_emulation, bool, 0600);
49MODULE_PARM_DESC(fbdev_emulation,
50 "Enable legacy fbdev emulation [default=true]");
51
Xinliang Liu5f152572017-02-15 17:19:08 +010052static int drm_fbdev_overalloc = CONFIG_DRM_FBDEV_OVERALLOC;
53module_param(drm_fbdev_overalloc, int, 0444);
54MODULE_PARM_DESC(drm_fbdev_overalloc,
55 "Overallocation of the fbdev buffer (%) [default="
56 __MODULE_STRING(CONFIG_DRM_FBDEV_OVERALLOC) "]");
57
Dave Airlie785b93e2009-08-28 15:46:53 +100058static LIST_HEAD(kernel_fb_helper_list);
Chris Wilsona53ca632016-11-29 12:02:17 +000059static DEFINE_MUTEX(kernel_fb_helper_lock);
Dave Airlie785b93e2009-08-28 15:46:53 +100060
Daniel Vetterd0ddc0332012-11-01 14:45:17 +010061/**
62 * DOC: fbdev helpers
63 *
64 * The fb helper functions are useful to provide an fbdev on top of a drm kernel
Thierry Reding83c617c2014-04-29 11:44:35 +020065 * mode setting driver. They can be used mostly independently from the crtc
Daniel Vetterd0ddc0332012-11-01 14:45:17 +010066 * helper functions used by many drivers to implement the kernel mode setting
67 * interfaces.
Daniel Vetter207fd322013-01-20 22:13:14 +010068 *
Thierry Reding10a23102014-06-27 17:19:24 +020069 * Initialization is done as a four-step process with drm_fb_helper_prepare(),
70 * drm_fb_helper_init(), drm_fb_helper_single_add_all_connectors() and
71 * drm_fb_helper_initial_config(). Drivers with fancier requirements than the
72 * default behaviour can override the third step with their own code.
Daniel Vettered84e252017-02-07 15:10:49 +010073 * Teardown is done with drm_fb_helper_fini() after the fbdev device is
74 * unregisters using drm_fb_helper_unregister_fbi().
Daniel Vetter207fd322013-01-20 22:13:14 +010075 *
76 * At runtime drivers should restore the fbdev console by calling
Daniel Vetter6806cdf2017-01-25 07:26:43 +010077 * drm_fb_helper_restore_fbdev_mode_unlocked() from their &drm_driver.lastclose
78 * callback. They should also notify the fb helper code from updates to the
79 * output configuration by calling drm_fb_helper_hotplug_event(). For easier
Daniel Vetter207fd322013-01-20 22:13:14 +010080 * integration with the output polling code in drm_crtc_helper.c the modeset
Daniel Vetter6806cdf2017-01-25 07:26:43 +010081 * code provides a &drm_mode_config_funcs.output_poll_changed callback.
Daniel Vetter207fd322013-01-20 22:13:14 +010082 *
83 * All other functions exported by the fb helper library can be used to
84 * implement the fbdev driver interface by the driver.
Thierry Reding10a23102014-06-27 17:19:24 +020085 *
86 * It is possible, though perhaps somewhat tricky, to implement race-free
87 * hotplug detection using the fbdev helpers. The drm_fb_helper_prepare()
88 * helper must be called first to initialize the minimum required to make
89 * hotplug detection work. Drivers also need to make sure to properly set up
Daniel Vetter6806cdf2017-01-25 07:26:43 +010090 * the &drm_mode_config.funcs member. After calling drm_kms_helper_poll_init()
Thierry Reding10a23102014-06-27 17:19:24 +020091 * it is safe to enable interrupts and start processing hotplug events. At the
92 * same time, drivers should initialize all modeset objects such as CRTCs,
93 * encoders and connectors. To finish up the fbdev helper initialization, the
94 * drm_fb_helper_init() function is called. To probe for all attached displays
95 * and set up an initial configuration using the detected hardware, drivers
96 * should call drm_fb_helper_single_add_all_connectors() followed by
97 * drm_fb_helper_initial_config().
Noralf Trønneseaa434d2016-04-28 17:18:33 +020098 *
Daniel Vetter6806cdf2017-01-25 07:26:43 +010099 * If &drm_framebuffer_funcs.dirty is set, the
Noralf Trønnes2dad5512016-05-11 18:09:17 +0200100 * drm_fb_helper_{cfb,sys}_{write,fillrect,copyarea,imageblit} functions will
Daniel Vetter6806cdf2017-01-25 07:26:43 +0100101 * accumulate changes and schedule &drm_fb_helper.dirty_work to run right
Noralf Trønnes2dad5512016-05-11 18:09:17 +0200102 * away. This worker then calls the dirty() function ensuring that it will
103 * always run in process context since the fb_*() function could be running in
104 * atomic context. If drm_fb_helper_deferred_io() is used as the deferred_io
105 * callback it will also schedule dirty_work with the damage collected from the
106 * mmap page writes.
Daniel Vetterd0ddc0332012-11-01 14:45:17 +0100107 */
108
Chris Wilson966a6a12016-11-29 12:02:15 +0000109#define drm_fb_helper_for_each_connector(fbh, i__) \
Daniel Vettere13a0582017-07-05 06:56:29 +0200110 for (({ lockdep_assert_held(&(fbh)->lock); }), \
Chris Wilson966a6a12016-11-29 12:02:15 +0000111 i__ = 0; i__ < (fbh)->connector_count; i__++)
112
Thierry Redingaf2405a2017-07-04 17:18:21 +0200113static int __drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper,
114 struct drm_connector *connector)
Thierry Reding39b8b2e2017-03-29 16:43:52 +0200115{
Thierry Reding50021ff2017-03-29 16:43:53 +0200116 struct drm_fb_helper_connector *fb_conn;
Thierry Reding39b8b2e2017-03-29 16:43:52 +0200117 struct drm_fb_helper_connector **temp;
Thierry Reding50021ff2017-03-29 16:43:53 +0200118 unsigned int count;
Thierry Reding39b8b2e2017-03-29 16:43:52 +0200119
120 if (!drm_fbdev_emulation)
121 return 0;
122
Thierry Redinge9827d82017-07-04 17:18:23 +0200123 lockdep_assert_held(&fb_helper->lock);
Thierry Reding50021ff2017-03-29 16:43:53 +0200124
125 count = fb_helper->connector_count + 1;
126
127 if (count > fb_helper->connector_info_alloc_count) {
128 size_t size = count * sizeof(fb_conn);
129
130 temp = krealloc(fb_helper->connector_info, size, GFP_KERNEL);
Thierry Reding39b8b2e2017-03-29 16:43:52 +0200131 if (!temp)
132 return -ENOMEM;
133
Thierry Reding50021ff2017-03-29 16:43:53 +0200134 fb_helper->connector_info_alloc_count = count;
Thierry Reding39b8b2e2017-03-29 16:43:52 +0200135 fb_helper->connector_info = temp;
136 }
137
Thierry Reding50021ff2017-03-29 16:43:53 +0200138 fb_conn = kzalloc(sizeof(*fb_conn), GFP_KERNEL);
139 if (!fb_conn)
Thierry Reding39b8b2e2017-03-29 16:43:52 +0200140 return -ENOMEM;
141
142 drm_connector_get(connector);
Thierry Reding50021ff2017-03-29 16:43:53 +0200143 fb_conn->connector = connector;
144 fb_helper->connector_info[fb_helper->connector_count++] = fb_conn;
Thierry Redingaf2405a2017-07-04 17:18:21 +0200145
Thierry Reding39b8b2e2017-03-29 16:43:52 +0200146 return 0;
147}
Thierry Redingaf2405a2017-07-04 17:18:21 +0200148
149int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper,
150 struct drm_connector *connector)
151{
152 int err;
153
Noralf Trønnesc7779902017-10-30 16:39:37 +0100154 if (!fb_helper)
155 return 0;
156
Thierry Redinge9827d82017-07-04 17:18:23 +0200157 mutex_lock(&fb_helper->lock);
Thierry Redingaf2405a2017-07-04 17:18:21 +0200158 err = __drm_fb_helper_add_one_connector(fb_helper, connector);
Thierry Redinge9827d82017-07-04 17:18:23 +0200159 mutex_unlock(&fb_helper->lock);
Thierry Redingaf2405a2017-07-04 17:18:21 +0200160
161 return err;
162}
Thierry Reding39b8b2e2017-03-29 16:43:52 +0200163EXPORT_SYMBOL(drm_fb_helper_add_one_connector);
164
Daniel Vetter207fd322013-01-20 22:13:14 +0100165/**
166 * drm_fb_helper_single_add_all_connectors() - add all connectors to fbdev
167 * emulation helper
Noralf Trønnesc7779902017-10-30 16:39:37 +0100168 * @fb_helper: fbdev initialized with drm_fb_helper_init, can be NULL
Daniel Vetter207fd322013-01-20 22:13:14 +0100169 *
170 * This functions adds all the available connectors for use with the given
171 * fb_helper. This is a separate step to allow drivers to freely assign
172 * connectors to the fbdev, e.g. if some are reserved for special purposes or
173 * not adequate to be used for the fbcon.
174 *
Daniel Vetter169faec2015-07-09 23:44:27 +0200175 * This function is protected against concurrent connector hotadds/removals
176 * using drm_fb_helper_add_one_connector() and
177 * drm_fb_helper_remove_one_connector().
Daniel Vetter207fd322013-01-20 22:13:14 +0100178 */
Dave Airlie0b4c0f32010-03-30 05:34:15 +0000179int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
Dave Airlied50ba252009-09-23 14:44:08 +1000180{
Gustavo A. R. Silva89f3f352017-12-05 11:46:28 -0600181 struct drm_device *dev;
Dave Airlie0b4c0f32010-03-30 05:34:15 +0000182 struct drm_connector *connector;
Daniel Vetterc36a3252016-12-15 16:58:43 +0100183 struct drm_connector_list_iter conn_iter;
184 int i, ret = 0;
Dave Airlied50ba252009-09-23 14:44:08 +1000185
Noralf Trønnesc7779902017-10-30 16:39:37 +0100186 if (!drm_fbdev_emulation || !fb_helper)
Daniel Vetterf64c5572015-08-25 15:45:13 +0200187 return 0;
188
Gustavo A. R. Silva89f3f352017-12-05 11:46:28 -0600189 dev = fb_helper->dev;
190
Thierry Redinge9827d82017-07-04 17:18:23 +0200191 mutex_lock(&fb_helper->lock);
Thierry Redingb982dab2017-02-28 15:46:43 +0100192 drm_connector_list_iter_begin(dev, &conn_iter);
Daniel Vetterc36a3252016-12-15 16:58:43 +0100193 drm_for_each_connector_iter(connector, &conn_iter) {
Thierry Redingaf2405a2017-07-04 17:18:21 +0200194 ret = __drm_fb_helper_add_one_connector(fb_helper, connector);
Maarten Lankhorst15fce292016-02-15 13:45:16 +0100195 if (ret)
Dave Airlie0b4c0f32010-03-30 05:34:15 +0000196 goto fail;
Dave Airlie0b4c0f32010-03-30 05:34:15 +0000197 }
Daniel Vetterc36a3252016-12-15 16:58:43 +0100198 goto out;
199
Dave Airlie0b4c0f32010-03-30 05:34:15 +0000200fail:
Chris Wilson966a6a12016-11-29 12:02:15 +0000201 drm_fb_helper_for_each_connector(fb_helper, i) {
Ville Syrjälä7dfcb362016-10-26 12:05:52 +0300202 struct drm_fb_helper_connector *fb_helper_connector =
203 fb_helper->connector_info[i];
204
Thierry Redingad093602017-02-28 15:46:39 +0100205 drm_connector_put(fb_helper_connector->connector);
Ville Syrjälä7dfcb362016-10-26 12:05:52 +0300206
207 kfree(fb_helper_connector);
Dave Airlie0b4c0f32010-03-30 05:34:15 +0000208 fb_helper->connector_info[i] = NULL;
209 }
210 fb_helper->connector_count = 0;
Daniel Vetterc36a3252016-12-15 16:58:43 +0100211out:
Thierry Redingb982dab2017-02-28 15:46:43 +0100212 drm_connector_list_iter_end(&conn_iter);
Thierry Redinge9827d82017-07-04 17:18:23 +0200213 mutex_unlock(&fb_helper->lock);
Daniel Vetter169faec2015-07-09 23:44:27 +0200214
Maarten Lankhorst15fce292016-02-15 13:45:16 +0100215 return ret;
Dave Airlied50ba252009-09-23 14:44:08 +1000216}
Dave Airlie0b4c0f32010-03-30 05:34:15 +0000217EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors);
Dave Airlied50ba252009-09-23 14:44:08 +1000218
Thierry Redingaf2405a2017-07-04 17:18:21 +0200219static int __drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
220 struct drm_connector *connector)
Dave Airlie65c2a892014-06-05 14:01:30 +1000221{
222 struct drm_fb_helper_connector *fb_helper_connector;
223 int i, j;
224
Daniel Vetterf64c5572015-08-25 15:45:13 +0200225 if (!drm_fbdev_emulation)
226 return 0;
227
Thierry Redinge9827d82017-07-04 17:18:23 +0200228 lockdep_assert_held(&fb_helper->lock);
Dave Airlie65c2a892014-06-05 14:01:30 +1000229
Thierry Redinge9827d82017-07-04 17:18:23 +0200230 drm_fb_helper_for_each_connector(fb_helper, i) {
Dave Airlie65c2a892014-06-05 14:01:30 +1000231 if (fb_helper->connector_info[i]->connector == connector)
232 break;
233 }
234
235 if (i == fb_helper->connector_count)
236 return -EINVAL;
237 fb_helper_connector = fb_helper->connector_info[i];
Thierry Redingad093602017-02-28 15:46:39 +0100238 drm_connector_put(fb_helper_connector->connector);
Dave Airlie65c2a892014-06-05 14:01:30 +1000239
Thierry Reding4b4f99f2017-03-29 16:43:51 +0200240 for (j = i + 1; j < fb_helper->connector_count; j++)
Dave Airlie65c2a892014-06-05 14:01:30 +1000241 fb_helper->connector_info[j - 1] = fb_helper->connector_info[j];
Thierry Reding4b4f99f2017-03-29 16:43:51 +0200242
Dave Airlie65c2a892014-06-05 14:01:30 +1000243 fb_helper->connector_count--;
244 kfree(fb_helper_connector);
Rob Clark2148f182015-01-26 10:11:08 -0500245
Dave Airlie65c2a892014-06-05 14:01:30 +1000246 return 0;
247}
Thierry Redingaf2405a2017-07-04 17:18:21 +0200248
249int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
250 struct drm_connector *connector)
251{
252 int err;
253
Noralf Trønnesc7779902017-10-30 16:39:37 +0100254 if (!fb_helper)
255 return 0;
256
Thierry Redinge9827d82017-07-04 17:18:23 +0200257 mutex_lock(&fb_helper->lock);
Thierry Redingaf2405a2017-07-04 17:18:21 +0200258 err = __drm_fb_helper_remove_one_connector(fb_helper, connector);
Thierry Redinge9827d82017-07-04 17:18:23 +0200259 mutex_unlock(&fb_helper->lock);
Thierry Redingaf2405a2017-07-04 17:18:21 +0200260
261 return err;
262}
Dave Airlie65c2a892014-06-05 14:01:30 +1000263EXPORT_SYMBOL(drm_fb_helper_remove_one_connector);
264
Jason Wessel99231022010-10-13 14:09:43 -0500265static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc)
266{
267 uint16_t *r_base, *g_base, *b_base;
268
Laurent Pinchartebe0f242012-05-17 13:27:24 +0200269 if (crtc->funcs->gamma_set == NULL)
270 return;
271
Jason Wessel99231022010-10-13 14:09:43 -0500272 r_base = crtc->gamma_store;
273 g_base = r_base + crtc->gamma_size;
274 b_base = g_base + crtc->gamma_size;
275
Daniel Vetter6d124ff2017-04-03 10:33:01 +0200276 crtc->funcs->gamma_set(crtc, r_base, g_base, b_base,
277 crtc->gamma_size, NULL);
Jason Wessel99231022010-10-13 14:09:43 -0500278}
279
Daniel Vetter207fd322013-01-20 22:13:14 +0100280/**
Daniel Vetter6806cdf2017-01-25 07:26:43 +0100281 * drm_fb_helper_debug_enter - implementation for &fb_ops.fb_debug_enter
Daniel Vetter207fd322013-01-20 22:13:14 +0100282 * @info: fbdev registered by the helper
283 */
Jesse Barnes1a7aba72010-08-05 09:22:31 -0500284int drm_fb_helper_debug_enter(struct fb_info *info)
285{
286 struct drm_fb_helper *helper = info->par;
Jani Nikulabe26a662015-03-11 11:51:06 +0200287 const struct drm_crtc_helper_funcs *funcs;
Jesse Barnes1a7aba72010-08-05 09:22:31 -0500288 int i;
289
Jesse Barnes1a7aba72010-08-05 09:22:31 -0500290 list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
291 for (i = 0; i < helper->crtc_count; i++) {
292 struct drm_mode_set *mode_set =
293 &helper->crtc_info[i].mode_set;
294
295 if (!mode_set->crtc->enabled)
296 continue;
297
298 funcs = mode_set->crtc->helper_private;
Stefan Christ1b99b722016-11-14 00:03:11 +0100299 if (funcs->mode_set_base_atomic == NULL)
300 continue;
301
Daniel Vetter9c79e0b2017-04-03 10:32:59 +0200302 if (drm_drv_uses_atomic_modeset(mode_set->crtc->dev))
303 continue;
304
Jesse Barnes1a7aba72010-08-05 09:22:31 -0500305 funcs->mode_set_base_atomic(mode_set->crtc,
306 mode_set->fb,
307 mode_set->x,
Jason Wessel413d45d2010-09-26 06:47:25 -0500308 mode_set->y,
Jason Wessel21c74a82010-10-13 14:09:44 -0500309 ENTER_ATOMIC_MODE_SET);
Jesse Barnes1a7aba72010-08-05 09:22:31 -0500310 }
311 }
312
313 return 0;
314}
315EXPORT_SYMBOL(drm_fb_helper_debug_enter);
316
Daniel Vetter207fd322013-01-20 22:13:14 +0100317/**
Daniel Vetter6806cdf2017-01-25 07:26:43 +0100318 * drm_fb_helper_debug_leave - implementation for &fb_ops.fb_debug_leave
Daniel Vetter207fd322013-01-20 22:13:14 +0100319 * @info: fbdev registered by the helper
320 */
Jesse Barnes1a7aba72010-08-05 09:22:31 -0500321int drm_fb_helper_debug_leave(struct fb_info *info)
322{
323 struct drm_fb_helper *helper = info->par;
324 struct drm_crtc *crtc;
Jani Nikulabe26a662015-03-11 11:51:06 +0200325 const struct drm_crtc_helper_funcs *funcs;
Jesse Barnes1a7aba72010-08-05 09:22:31 -0500326 struct drm_framebuffer *fb;
327 int i;
328
329 for (i = 0; i < helper->crtc_count; i++) {
330 struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set;
Thierry Reding4b4f99f2017-03-29 16:43:51 +0200331
Jesse Barnes1a7aba72010-08-05 09:22:31 -0500332 crtc = mode_set->crtc;
Maarten Lankhorst7114d2e2017-07-03 13:51:06 +0200333 if (drm_drv_uses_atomic_modeset(crtc->dev))
334 continue;
335
Jesse Barnes1a7aba72010-08-05 09:22:31 -0500336 funcs = crtc->helper_private;
Maarten Lankhorst7114d2e2017-07-03 13:51:06 +0200337 fb = crtc->primary->fb;
Jesse Barnes1a7aba72010-08-05 09:22:31 -0500338
339 if (!crtc->enabled)
340 continue;
341
342 if (!fb) {
343 DRM_ERROR("no fb to restore??\n");
344 continue;
345 }
346
Stefan Christ1b99b722016-11-14 00:03:11 +0100347 if (funcs->mode_set_base_atomic == NULL)
348 continue;
349
Jason Wessel99231022010-10-13 14:09:43 -0500350 drm_fb_helper_restore_lut_atomic(mode_set->crtc);
Jesse Barnes1a7aba72010-08-05 09:22:31 -0500351 funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x,
Jason Wessel21c74a82010-10-13 14:09:44 -0500352 crtc->y, LEAVE_ATOMIC_MODE_SET);
Jesse Barnes1a7aba72010-08-05 09:22:31 -0500353 }
354
355 return 0;
356}
357EXPORT_SYMBOL(drm_fb_helper_debug_leave);
358
Daniel Vetter6b7dc6e2017-07-04 17:18:29 +0200359static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper, bool active)
Rob Clarkbbb1e522015-08-25 15:35:58 -0400360{
361 struct drm_device *dev = fb_helper->dev;
Hans de Goede8f0cb412017-11-25 20:35:50 +0100362 struct drm_plane_state *plane_state;
Rob Clarkbbb1e522015-08-25 15:35:58 -0400363 struct drm_plane *plane;
364 struct drm_atomic_state *state;
365 int i, ret;
Thierry Reding4b4f99f2017-03-29 16:43:51 +0200366 unsigned int plane_mask;
Daniel Vetter1d0c641092017-07-04 17:18:27 +0200367 struct drm_modeset_acquire_ctx ctx;
368
369 drm_modeset_acquire_init(&ctx, 0);
Rob Clarkbbb1e522015-08-25 15:35:58 -0400370
371 state = drm_atomic_state_alloc(dev);
Daniel Vetter1d0c641092017-07-04 17:18:27 +0200372 if (!state) {
373 ret = -ENOMEM;
374 goto out_ctx;
375 }
Rob Clarkbbb1e522015-08-25 15:35:58 -0400376
Daniel Vetter1d0c641092017-07-04 17:18:27 +0200377 state->acquire_ctx = &ctx;
Rob Clarkbbb1e522015-08-25 15:35:58 -0400378retry:
Maarten Lankhorstf72c6b32015-11-11 11:29:10 +0100379 plane_mask = 0;
Rob Clarkbbb1e522015-08-25 15:35:58 -0400380 drm_for_each_plane(plane, dev) {
Rob Clarkbbb1e522015-08-25 15:35:58 -0400381 plane_state = drm_atomic_get_plane_state(state, plane);
382 if (IS_ERR(plane_state)) {
383 ret = PTR_ERR(plane_state);
Daniel Vetter1d0c641092017-07-04 17:18:27 +0200384 goto out_state;
Rob Clarkbbb1e522015-08-25 15:35:58 -0400385 }
386
Robert Fossc2c446a2017-05-19 16:50:17 -0400387 plane_state->rotation = DRM_MODE_ROTATE_0;
Rob Clarkbbb1e522015-08-25 15:35:58 -0400388
Maarten Lankhorstf72c6b32015-11-11 11:29:10 +0100389 plane->old_fb = plane->fb;
390 plane_mask |= 1 << drm_plane_index(plane);
391
Rob Clarkbbb1e522015-08-25 15:35:58 -0400392 /* disable non-primary: */
393 if (plane->type == DRM_PLANE_TYPE_PRIMARY)
394 continue;
395
396 ret = __drm_atomic_helper_disable_plane(plane, plane_state);
397 if (ret != 0)
Daniel Vetter1d0c641092017-07-04 17:18:27 +0200398 goto out_state;
Rob Clarkbbb1e522015-08-25 15:35:58 -0400399 }
400
Thierry Reding4b4f99f2017-03-29 16:43:51 +0200401 for (i = 0; i < fb_helper->crtc_count; i++) {
Rob Clarkbbb1e522015-08-25 15:35:58 -0400402 struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
Hans de Goede8f0cb412017-11-25 20:35:50 +0100403 struct drm_plane *primary = mode_set->crtc->primary;
404
405 /* Cannot fail as we've already gotten the plane state above */
406 plane_state = drm_atomic_get_new_plane_state(state, primary);
407 plane_state->rotation = fb_helper->crtc_info[i].rotation;
Rob Clarkbbb1e522015-08-25 15:35:58 -0400408
409 ret = __drm_atomic_helper_set_config(mode_set, state);
410 if (ret != 0)
Daniel Vetter1d0c641092017-07-04 17:18:27 +0200411 goto out_state;
Daniel Vetter6b7dc6e2017-07-04 17:18:29 +0200412
413 /*
414 * __drm_atomic_helper_set_config() sets active when a
415 * mode is set, unconditionally clear it if we force DPMS off
416 */
417 if (!active) {
418 struct drm_crtc *crtc = mode_set->crtc;
419 struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
420
421 crtc_state->active = false;
422 }
Rob Clarkbbb1e522015-08-25 15:35:58 -0400423 }
424
425 ret = drm_atomic_commit(state);
Rob Clarkbbb1e522015-08-25 15:35:58 -0400426
Daniel Vetter1d0c641092017-07-04 17:18:27 +0200427out_state:
Maarten Lankhorstf72c6b32015-11-11 11:29:10 +0100428 drm_atomic_clean_old_fb(dev, plane_mask, ret);
Matt Roper94284032015-09-21 17:21:48 -0700429
Rob Clarkbbb1e522015-08-25 15:35:58 -0400430 if (ret == -EDEADLK)
431 goto backoff;
432
Chris Wilson08536952016-10-14 13:18:18 +0100433 drm_atomic_state_put(state);
Daniel Vetter1d0c641092017-07-04 17:18:27 +0200434out_ctx:
435 drm_modeset_drop_locks(&ctx);
436 drm_modeset_acquire_fini(&ctx);
437
Rob Clarkbbb1e522015-08-25 15:35:58 -0400438 return ret;
439
440backoff:
441 drm_atomic_state_clear(state);
Daniel Vetter1d0c641092017-07-04 17:18:27 +0200442 drm_modeset_backoff(&ctx);
Rob Clarkbbb1e522015-08-25 15:35:58 -0400443
444 goto retry;
445}
446
Daniel Vetter71286452017-04-03 10:33:04 +0200447static int restore_fbdev_mode_legacy(struct drm_fb_helper *fb_helper)
Dave Airliee8e7a2b2011-04-21 22:18:32 +0100448{
Ville Syrjälä3858bc52013-06-03 16:10:42 +0300449 struct drm_device *dev = fb_helper->dev;
450 struct drm_plane *plane;
Daniel Vetter1d0c641092017-07-04 17:18:27 +0200451 int i, ret = 0;
Daniel Vetter6aed8ec2013-01-20 17:32:21 +0100452
Daniel Vetter1d0c641092017-07-04 17:18:27 +0200453 drm_modeset_lock_all(fb_helper->dev);
Daniel Vetter6295d602015-07-09 23:44:25 +0200454 drm_for_each_plane(plane, dev) {
Matt Ropere27dde32014-04-01 15:22:30 -0700455 if (plane->type != DRM_PLANE_TYPE_PRIMARY)
456 drm_plane_force_disable(plane);
Daniel Vetter6aed8ec2013-01-20 17:32:21 +0100457
Ville Syrjälä6686df82016-10-21 22:22:45 +0300458 if (plane->rotation_property)
Ville Syrjäläd138dd32016-09-26 19:30:48 +0300459 drm_mode_plane_set_obj_prop(plane,
460 plane->rotation_property,
Robert Fossc2c446a2017-05-19 16:50:17 -0400461 DRM_MODE_ROTATE_0);
Sonika Jindal9783de22014-08-05 11:26:57 +0530462 }
463
Dave Airliee8e7a2b2011-04-21 22:18:32 +0100464 for (i = 0; i < fb_helper->crtc_count; i++) {
465 struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
Ville Syrjälä3858bc52013-06-03 16:10:42 +0300466 struct drm_crtc *crtc = mode_set->crtc;
Ville Syrjälä3858bc52013-06-03 16:10:42 +0300467
Alex Deucher03f9abb2015-09-30 14:47:37 -0400468 if (crtc->funcs->cursor_set2) {
469 ret = crtc->funcs->cursor_set2(crtc, NULL, 0, 0, 0, 0, 0);
470 if (ret)
Daniel Vetter1d0c641092017-07-04 17:18:27 +0200471 goto out;
Alex Deucher03f9abb2015-09-30 14:47:37 -0400472 } else if (crtc->funcs->cursor_set) {
Ville Syrjälä3858bc52013-06-03 16:10:42 +0300473 ret = crtc->funcs->cursor_set(crtc, NULL, 0, 0, 0);
474 if (ret)
Daniel Vetter1d0c641092017-07-04 17:18:27 +0200475 goto out;
Ville Syrjälä3858bc52013-06-03 16:10:42 +0300476 }
477
Daniel Vetter2d13b672012-12-11 13:47:23 +0100478 ret = drm_mode_set_config_internal(mode_set);
Dave Airliee8e7a2b2011-04-21 22:18:32 +0100479 if (ret)
Daniel Vetter1d0c641092017-07-04 17:18:27 +0200480 goto out;
Dave Airliee8e7a2b2011-04-21 22:18:32 +0100481 }
Daniel Vetter1d0c641092017-07-04 17:18:27 +0200482out:
483 drm_modeset_unlock_all(fb_helper->dev);
Daniel Vetterb7bdf0a82015-08-25 17:20:28 +0200484
Daniel Vetter1d0c641092017-07-04 17:18:27 +0200485 return ret;
Dave Airliee8e7a2b2011-04-21 22:18:32 +0100486}
Rob Clark5ea1f752014-05-30 12:29:48 -0400487
Daniel Vetter71286452017-04-03 10:33:04 +0200488static int restore_fbdev_mode(struct drm_fb_helper *fb_helper)
489{
490 struct drm_device *dev = fb_helper->dev;
491
Daniel Vetter71286452017-04-03 10:33:04 +0200492 if (drm_drv_uses_atomic_modeset(dev))
Daniel Vetter6b7dc6e2017-07-04 17:18:29 +0200493 return restore_fbdev_mode_atomic(fb_helper, true);
Daniel Vetter71286452017-04-03 10:33:04 +0200494 else
495 return restore_fbdev_mode_legacy(fb_helper);
496}
497
Rob Clark5ea1f752014-05-30 12:29:48 -0400498/**
499 * drm_fb_helper_restore_fbdev_mode_unlocked - restore fbdev configuration
Noralf Trønnesc7779902017-10-30 16:39:37 +0100500 * @fb_helper: driver-allocated fbdev helper, can be NULL
Rob Clark5ea1f752014-05-30 12:29:48 -0400501 *
Daniel Vetter6806cdf2017-01-25 07:26:43 +0100502 * This should be called from driver's drm &drm_driver.lastclose callback
Rob Clark5ea1f752014-05-30 12:29:48 -0400503 * when implementing an fbcon on top of kms using this helper. This ensures that
504 * the user isn't greeted with a black screen when e.g. X dies.
Daniel Vetterb7bdf0a82015-08-25 17:20:28 +0200505 *
506 * RETURNS:
507 * Zero if everything went ok, negative error code otherwise.
Rob Clark5ea1f752014-05-30 12:29:48 -0400508 */
Daniel Vetterb7bdf0a82015-08-25 17:20:28 +0200509int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
Rob Clark5ea1f752014-05-30 12:29:48 -0400510{
Daniel Vetterb7bdf0a82015-08-25 17:20:28 +0200511 bool do_delayed;
512 int ret;
Dave Airliee2809c72014-11-26 13:15:24 +1000513
Noralf Trønnesc7779902017-10-30 16:39:37 +0100514 if (!drm_fbdev_emulation || !fb_helper)
Daniel Vetterf64c5572015-08-25 15:45:13 +0200515 return -ENODEV;
516
Daniel Vetterca91a272017-07-06 15:00:21 +0200517 if (READ_ONCE(fb_helper->deferred_setup))
518 return 0;
519
Thierry Redinge9827d82017-07-04 17:18:23 +0200520 mutex_lock(&fb_helper->lock);
Rob Clark5ea1f752014-05-30 12:29:48 -0400521 ret = restore_fbdev_mode(fb_helper);
Dave Airliee2809c72014-11-26 13:15:24 +1000522
523 do_delayed = fb_helper->delayed_hotplug;
524 if (do_delayed)
525 fb_helper->delayed_hotplug = false;
Thierry Redinge9827d82017-07-04 17:18:23 +0200526 mutex_unlock(&fb_helper->lock);
Dave Airliee2809c72014-11-26 13:15:24 +1000527
528 if (do_delayed)
529 drm_fb_helper_hotplug_event(fb_helper);
Thierry Redinge9827d82017-07-04 17:18:23 +0200530
Rob Clark5ea1f752014-05-30 12:29:48 -0400531 return ret;
532}
533EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked);
Dave Airliee8e7a2b2011-04-21 22:18:32 +0100534
Geert Uytterhoeven2c4124f2015-08-04 15:22:11 +0200535static bool drm_fb_helper_is_bound(struct drm_fb_helper *fb_helper)
536{
537 struct drm_device *dev = fb_helper->dev;
538 struct drm_crtc *crtc;
539 int bound = 0, crtcs_bound = 0;
540
Thierry Reding4b4f99f2017-03-29 16:43:51 +0200541 /*
542 * Sometimes user space wants everything disabled, so don't steal the
543 * display if there's a master.
544 */
Johannes Bergf17b3ea2016-08-11 11:50:21 +0200545 if (READ_ONCE(dev->master))
Geert Uytterhoeven2c4124f2015-08-04 15:22:11 +0200546 return false;
547
548 drm_for_each_crtc(crtc, dev) {
Daniel Vetterbdac4a02017-07-04 17:18:24 +0200549 drm_modeset_lock(&crtc->mutex, NULL);
Geert Uytterhoeven2c4124f2015-08-04 15:22:11 +0200550 if (crtc->primary->fb)
551 crtcs_bound++;
552 if (crtc->primary->fb == fb_helper->fb)
553 bound++;
Daniel Vetterbdac4a02017-07-04 17:18:24 +0200554 drm_modeset_unlock(&crtc->mutex);
Geert Uytterhoeven2c4124f2015-08-04 15:22:11 +0200555 }
556
557 if (bound < crtcs_bound)
558 return false;
559
560 return true;
561}
562
563#ifdef CONFIG_MAGIC_SYSRQ
Daniel Vetterd21bf462013-01-20 18:09:52 +0100564/*
565 * restore fbcon display for all kms driver's using this helper, used for sysrq
566 * and panic handling.
567 */
Sachin Kamat78b9c352012-08-01 17:15:32 +0530568static bool drm_fb_helper_force_kernel_mode(void)
Dave Airlie785b93e2009-08-28 15:46:53 +1000569{
Dave Airlie785b93e2009-08-28 15:46:53 +1000570 bool ret, error = false;
571 struct drm_fb_helper *helper;
572
573 if (list_empty(&kernel_fb_helper_list))
574 return false;
575
576 list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
Thierry Redingb77f0762014-04-29 11:44:32 +0200577 struct drm_device *dev = helper->dev;
578
579 if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
Dave Airliee8e7a2b2011-04-21 22:18:32 +0100580 continue;
581
Thierry Redinge9827d82017-07-04 17:18:23 +0200582 mutex_lock(&helper->lock);
Geert Uytterhoeven3d9e35a2015-08-04 15:22:10 +0200583 ret = restore_fbdev_mode(helper);
Dave Airliee8e7a2b2011-04-21 22:18:32 +0100584 if (ret)
585 error = true;
Thierry Redinge9827d82017-07-04 17:18:23 +0200586 mutex_unlock(&helper->lock);
Dave Airlie785b93e2009-08-28 15:46:53 +1000587 }
588 return error;
589}
590
Dave Airlie785b93e2009-08-28 15:46:53 +1000591static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
592{
Daniel Vetterd21bf462013-01-20 18:09:52 +0100593 bool ret;
Thierry Reding4b4f99f2017-03-29 16:43:51 +0200594
Daniel Vetterd21bf462013-01-20 18:09:52 +0100595 ret = drm_fb_helper_force_kernel_mode();
596 if (ret == true)
597 DRM_ERROR("Failed to restore crtc configuration\n");
Dave Airlie785b93e2009-08-28 15:46:53 +1000598}
599static DECLARE_WORK(drm_fb_helper_restore_work, drm_fb_helper_restore_work_fn);
600
Dmitry Torokhov1495cc92010-08-17 21:15:46 -0700601static void drm_fb_helper_sysrq(int dummy1)
Dave Airlie785b93e2009-08-28 15:46:53 +1000602{
603 schedule_work(&drm_fb_helper_restore_work);
604}
605
606static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
607 .handler = drm_fb_helper_sysrq,
608 .help_msg = "force-fb(V)",
609 .action_msg = "Restore framebuffer console",
610};
Randy Dunlapb8c40d62010-03-25 18:29:05 +0000611#else
612static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = { };
Mikael Petterssonbea1d352009-09-28 18:26:25 +0200613#endif
Dave Airlie785b93e2009-08-28 15:46:53 +1000614
Daniel Vetter6b7dc6e2017-07-04 17:18:29 +0200615static void dpms_legacy(struct drm_fb_helper *fb_helper, int dpms_mode)
Dave Airlie785b93e2009-08-28 15:46:53 +1000616{
Dave Airlie785b93e2009-08-28 15:46:53 +1000617 struct drm_device *dev = fb_helper->dev;
618 struct drm_crtc *crtc;
Jesse Barnes023eb572010-07-02 10:48:08 -0700619 struct drm_connector *connector;
Jesse Barnes023eb572010-07-02 10:48:08 -0700620 int i, j;
Dave Airlie785b93e2009-08-28 15:46:53 +1000621
Daniel Vetterbdac4a02017-07-04 17:18:24 +0200622 drm_modeset_lock_all(dev);
Jesse Barnese87b2c42009-09-17 18:14:41 -0700623 for (i = 0; i < fb_helper->crtc_count; i++) {
Dave Airlie8be48d92010-03-30 05:34:14 +0000624 crtc = fb_helper->crtc_info[i].mode_set.crtc;
Dave Airlie785b93e2009-08-28 15:46:53 +1000625
Dave Airlie8be48d92010-03-30 05:34:14 +0000626 if (!crtc->enabled)
627 continue;
Dave Airlie785b93e2009-08-28 15:46:53 +1000628
Sascha Hauer3a8148c2012-02-01 11:38:24 +0100629 /* Walk the connectors & encoders on this fb turning them on/off */
Chris Wilson966a6a12016-11-29 12:02:15 +0000630 drm_fb_helper_for_each_connector(fb_helper, j) {
Jesse Barnes023eb572010-07-02 10:48:08 -0700631 connector = fb_helper->connector_info[j]->connector;
Daniel Vettere04190e2012-09-07 10:14:52 +0200632 connector->funcs->dpms(connector, dpms_mode);
Rob Clark58495562012-10-11 20:50:56 -0500633 drm_object_property_set_value(&connector->base,
Sascha Hauer3a8148c2012-02-01 11:38:24 +0100634 dev->mode_config.dpms_property, dpms_mode);
Jesse Barnes023eb572010-07-02 10:48:08 -0700635 }
Dave Airlie785b93e2009-08-28 15:46:53 +1000636 }
Daniel Vetter84849902012-12-02 00:28:11 +0100637 drm_modeset_unlock_all(dev);
Daniel Vetter6b7dc6e2017-07-04 17:18:29 +0200638}
639
640static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode)
641{
642 struct drm_fb_helper *fb_helper = info->par;
643
644 /*
645 * For each CRTC in this fb, turn the connectors on/off.
646 */
647 mutex_lock(&fb_helper->lock);
648 if (!drm_fb_helper_is_bound(fb_helper)) {
649 mutex_unlock(&fb_helper->lock);
650 return;
651 }
652
653 if (drm_drv_uses_atomic_modeset(fb_helper->dev))
654 restore_fbdev_mode_atomic(fb_helper, dpms_mode == DRM_MODE_DPMS_ON);
655 else
656 dpms_legacy(fb_helper, dpms_mode);
Thierry Redinge9827d82017-07-04 17:18:23 +0200657 mutex_unlock(&fb_helper->lock);
Dave Airlie785b93e2009-08-28 15:46:53 +1000658}
659
Daniel Vetter207fd322013-01-20 22:13:14 +0100660/**
Daniel Vetter6806cdf2017-01-25 07:26:43 +0100661 * drm_fb_helper_blank - implementation for &fb_ops.fb_blank
Daniel Vetter207fd322013-01-20 22:13:14 +0100662 * @blank: desired blanking state
663 * @info: fbdev registered by the helper
664 */
Dave Airlie785b93e2009-08-28 15:46:53 +1000665int drm_fb_helper_blank(int blank, struct fb_info *info)
666{
Daniel Vetterc50bfd02015-07-28 13:18:40 +0200667 if (oops_in_progress)
668 return -EBUSY;
669
Dave Airlie785b93e2009-08-28 15:46:53 +1000670 switch (blank) {
James Simmons731b5a12009-10-29 20:39:07 +0000671 /* Display: On; HSync: On, VSync: On */
Dave Airlie785b93e2009-08-28 15:46:53 +1000672 case FB_BLANK_UNBLANK:
Sascha Hauer3a8148c2012-02-01 11:38:24 +0100673 drm_fb_helper_dpms(info, DRM_MODE_DPMS_ON);
Dave Airlie785b93e2009-08-28 15:46:53 +1000674 break;
James Simmons731b5a12009-10-29 20:39:07 +0000675 /* Display: Off; HSync: On, VSync: On */
Dave Airlie785b93e2009-08-28 15:46:53 +1000676 case FB_BLANK_NORMAL:
Sascha Hauer3a8148c2012-02-01 11:38:24 +0100677 drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY);
Dave Airlie785b93e2009-08-28 15:46:53 +1000678 break;
James Simmons731b5a12009-10-29 20:39:07 +0000679 /* Display: Off; HSync: Off, VSync: On */
Dave Airlie785b93e2009-08-28 15:46:53 +1000680 case FB_BLANK_HSYNC_SUSPEND:
Sascha Hauer3a8148c2012-02-01 11:38:24 +0100681 drm_fb_helper_dpms(info, DRM_MODE_DPMS_STANDBY);
Dave Airlie785b93e2009-08-28 15:46:53 +1000682 break;
James Simmons731b5a12009-10-29 20:39:07 +0000683 /* Display: Off; HSync: On, VSync: Off */
Dave Airlie785b93e2009-08-28 15:46:53 +1000684 case FB_BLANK_VSYNC_SUSPEND:
Sascha Hauer3a8148c2012-02-01 11:38:24 +0100685 drm_fb_helper_dpms(info, DRM_MODE_DPMS_SUSPEND);
Dave Airlie785b93e2009-08-28 15:46:53 +1000686 break;
James Simmons731b5a12009-10-29 20:39:07 +0000687 /* Display: Off; HSync: Off, VSync: Off */
Dave Airlie785b93e2009-08-28 15:46:53 +1000688 case FB_BLANK_POWERDOWN:
Sascha Hauer3a8148c2012-02-01 11:38:24 +0100689 drm_fb_helper_dpms(info, DRM_MODE_DPMS_OFF);
Dave Airlie785b93e2009-08-28 15:46:53 +1000690 break;
691 }
692 return 0;
693}
694EXPORT_SYMBOL(drm_fb_helper_blank);
695
Ville Syrjäläa2889602016-10-26 17:41:18 +0300696static void drm_fb_helper_modeset_release(struct drm_fb_helper *helper,
697 struct drm_mode_set *modeset)
698{
699 int i;
700
701 for (i = 0; i < modeset->num_connectors; i++) {
Thierry Redingad093602017-02-28 15:46:39 +0100702 drm_connector_put(modeset->connectors[i]);
Ville Syrjäläa2889602016-10-26 17:41:18 +0300703 modeset->connectors[i] = NULL;
704 }
705 modeset->num_connectors = 0;
706
707 drm_mode_destroy(helper->dev, modeset->mode);
708 modeset->mode = NULL;
709
710 /* FIXME should hold a ref? */
711 modeset->fb = NULL;
712}
713
Dave Airlie785b93e2009-08-28 15:46:53 +1000714static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
715{
716 int i;
717
Dave Airlie6e86d582016-04-27 11:24:51 +1000718 for (i = 0; i < helper->connector_count; i++) {
Thierry Redingad093602017-02-28 15:46:39 +0100719 drm_connector_put(helper->connector_info[i]->connector);
Dave Airlie0b4c0f32010-03-30 05:34:15 +0000720 kfree(helper->connector_info[i]);
Dave Airlie6e86d582016-04-27 11:24:51 +1000721 }
Dave Airlie0b4c0f32010-03-30 05:34:15 +0000722 kfree(helper->connector_info);
Ville Syrjäläa2889602016-10-26 17:41:18 +0300723
Sascha Hauera1b77362012-02-01 11:38:22 +0100724 for (i = 0; i < helper->crtc_count; i++) {
Ville Syrjäläa2889602016-10-26 17:41:18 +0300725 struct drm_mode_set *modeset = &helper->crtc_info[i].mode_set;
726
727 drm_fb_helper_modeset_release(helper, modeset);
728 kfree(modeset->connectors);
Sascha Hauera1b77362012-02-01 11:38:22 +0100729 }
Dave Airlie785b93e2009-08-28 15:46:53 +1000730 kfree(helper->crtc_info);
731}
732
Noralf Trønnescfe63422016-08-23 13:54:06 +0200733static void drm_fb_helper_resume_worker(struct work_struct *work)
734{
735 struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper,
736 resume_work);
737
738 console_lock();
739 fb_set_suspend(helper->fbdev, 0);
740 console_unlock();
741}
742
Noralf Trønneseaa434d2016-04-28 17:18:33 +0200743static void drm_fb_helper_dirty_work(struct work_struct *work)
744{
745 struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper,
746 dirty_work);
747 struct drm_clip_rect *clip = &helper->dirty_clip;
748 struct drm_clip_rect clip_copy;
749 unsigned long flags;
750
751 spin_lock_irqsave(&helper->dirty_lock, flags);
752 clip_copy = *clip;
753 clip->x1 = clip->y1 = ~0;
754 clip->x2 = clip->y2 = 0;
755 spin_unlock_irqrestore(&helper->dirty_lock, flags);
756
Takashi Iwai87d3b652016-10-20 17:05:30 +0200757 /* call dirty callback only when it has been really touched */
758 if (clip_copy.x1 < clip_copy.x2 && clip_copy.y1 < clip_copy.y2)
759 helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, &clip_copy, 1);
Noralf Trønneseaa434d2016-04-28 17:18:33 +0200760}
761
Daniel Vetter207fd322013-01-20 22:13:14 +0100762/**
Thierry Reding10a23102014-06-27 17:19:24 +0200763 * drm_fb_helper_prepare - setup a drm_fb_helper structure
764 * @dev: DRM device
765 * @helper: driver-allocated fbdev helper structure to set up
766 * @funcs: pointer to structure of functions associate with this helper
767 *
768 * Sets up the bare minimum to make the framebuffer helper usable. This is
769 * useful to implement race-free initialization of the polling helpers.
770 */
771void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
772 const struct drm_fb_helper_funcs *funcs)
773{
774 INIT_LIST_HEAD(&helper->kernel_fb_list);
Noralf Trønneseaa434d2016-04-28 17:18:33 +0200775 spin_lock_init(&helper->dirty_lock);
Noralf Trønnescfe63422016-08-23 13:54:06 +0200776 INIT_WORK(&helper->resume_work, drm_fb_helper_resume_worker);
Noralf Trønneseaa434d2016-04-28 17:18:33 +0200777 INIT_WORK(&helper->dirty_work, drm_fb_helper_dirty_work);
778 helper->dirty_clip.x1 = helper->dirty_clip.y1 = ~0;
Thierry Redinge9827d82017-07-04 17:18:23 +0200779 mutex_init(&helper->lock);
Thierry Reding10a23102014-06-27 17:19:24 +0200780 helper->funcs = funcs;
781 helper->dev = dev;
782}
783EXPORT_SYMBOL(drm_fb_helper_prepare);
784
785/**
Daniel Vettered84e252017-02-07 15:10:49 +0100786 * drm_fb_helper_init - initialize a &struct drm_fb_helper
Daniel Vetter207fd322013-01-20 22:13:14 +0100787 * @dev: drm device
788 * @fb_helper: driver-allocated fbdev helper structure to initialize
Daniel Vetter207fd322013-01-20 22:13:14 +0100789 * @max_conn_count: max connector count
790 *
791 * This allocates the structures for the fbdev helper with the given limits.
792 * Note that this won't yet touch the hardware (through the driver interfaces)
793 * nor register the fbdev. This is only done in drm_fb_helper_initial_config()
794 * to allow driver writes more control over the exact init sequence.
795 *
Thierry Reding10a23102014-06-27 17:19:24 +0200796 * Drivers must call drm_fb_helper_prepare() before calling this function.
Daniel Vetter207fd322013-01-20 22:13:14 +0100797 *
798 * RETURNS:
799 * Zero if everything went ok, nonzero otherwise.
800 */
Dave Airlie4abe3522010-03-30 05:34:18 +0000801int drm_fb_helper_init(struct drm_device *dev,
802 struct drm_fb_helper *fb_helper,
Gabriel Krisman Bertazie4563f62017-02-02 14:26:40 -0200803 int max_conn_count)
Dave Airlie785b93e2009-08-28 15:46:53 +1000804{
Dave Airlie785b93e2009-08-28 15:46:53 +1000805 struct drm_crtc *crtc;
Gabriel Krisman Bertazie4563f62017-02-02 14:26:40 -0200806 struct drm_mode_config *config = &dev->mode_config;
Dave Airlie785b93e2009-08-28 15:46:53 +1000807 int i;
808
Noralf Trønnes29ad20b2017-10-30 16:39:38 +0100809 if (!drm_fbdev_emulation) {
810 dev->fb_helper = fb_helper;
Daniel Vetterf64c5572015-08-25 15:45:13 +0200811 return 0;
Noralf Trønnes29ad20b2017-10-30 16:39:38 +0100812 }
Daniel Vetterf64c5572015-08-25 15:45:13 +0200813
Xiubo Li04cfe972014-03-10 09:33:58 +0800814 if (!max_conn_count)
815 return -EINVAL;
816
Gabriel Krisman Bertazie4563f62017-02-02 14:26:40 -0200817 fb_helper->crtc_info = kcalloc(config->num_crtc, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
Dave Airlie4abe3522010-03-30 05:34:18 +0000818 if (!fb_helper->crtc_info)
Dave Airlie785b93e2009-08-28 15:46:53 +1000819 return -ENOMEM;
820
Gabriel Krisman Bertazie4563f62017-02-02 14:26:40 -0200821 fb_helper->crtc_count = config->num_crtc;
Dave Airlie4abe3522010-03-30 05:34:18 +0000822 fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL);
823 if (!fb_helper->connector_info) {
824 kfree(fb_helper->crtc_info);
Dave Airlie0b4c0f32010-03-30 05:34:15 +0000825 return -ENOMEM;
826 }
Dave Airlie65c2a892014-06-05 14:01:30 +1000827 fb_helper->connector_info_alloc_count = dev->mode_config.num_connector;
Dave Airlie4abe3522010-03-30 05:34:18 +0000828 fb_helper->connector_count = 0;
Dave Airlie785b93e2009-08-28 15:46:53 +1000829
Gabriel Krisman Bertazie4563f62017-02-02 14:26:40 -0200830 for (i = 0; i < fb_helper->crtc_count; i++) {
Dave Airlie4abe3522010-03-30 05:34:18 +0000831 fb_helper->crtc_info[i].mode_set.connectors =
Dave Airlie785b93e2009-08-28 15:46:53 +1000832 kcalloc(max_conn_count,
833 sizeof(struct drm_connector *),
834 GFP_KERNEL);
835
Laurent Pinchart4a1b0712012-05-17 13:27:21 +0200836 if (!fb_helper->crtc_info[i].mode_set.connectors)
Dave Airlie785b93e2009-08-28 15:46:53 +1000837 goto out_free;
Dave Airlie4abe3522010-03-30 05:34:18 +0000838 fb_helper->crtc_info[i].mode_set.num_connectors = 0;
Hans de Goede8f0cb412017-11-25 20:35:50 +0100839 fb_helper->crtc_info[i].rotation = DRM_MODE_ROTATE_0;
Dave Airlie785b93e2009-08-28 15:46:53 +1000840 }
841
842 i = 0;
Daniel Vetter6295d602015-07-09 23:44:25 +0200843 drm_for_each_crtc(crtc, dev) {
Dave Airlie4abe3522010-03-30 05:34:18 +0000844 fb_helper->crtc_info[i].mode_set.crtc = crtc;
Dave Airlie785b93e2009-08-28 15:46:53 +1000845 i++;
846 }
Sascha Hauere9ad3182012-02-01 11:38:25 +0100847
Noralf Trønnes29ad20b2017-10-30 16:39:38 +0100848 dev->fb_helper = fb_helper;
849
Dave Airlie785b93e2009-08-28 15:46:53 +1000850 return 0;
851out_free:
Dave Airlie4abe3522010-03-30 05:34:18 +0000852 drm_fb_helper_crtc_free(fb_helper);
Dave Airlie785b93e2009-08-28 15:46:53 +1000853 return -ENOMEM;
854}
Dave Airlie4abe3522010-03-30 05:34:18 +0000855EXPORT_SYMBOL(drm_fb_helper_init);
856
Archit Tanejab8017d62015-07-22 14:57:56 +0530857/**
858 * drm_fb_helper_alloc_fbi - allocate fb_info and some of its members
859 * @fb_helper: driver-allocated fbdev helper
860 *
861 * A helper to alloc fb_info and the members cmap and apertures. Called
Daniel Vetterda7bdda2017-02-07 17:16:03 +0100862 * by the driver within the fb_probe fb_helper callback function. Drivers do not
863 * need to release the allocated fb_info structure themselves, this is
864 * automatically done when calling drm_fb_helper_fini().
Archit Tanejab8017d62015-07-22 14:57:56 +0530865 *
866 * RETURNS:
867 * fb_info pointer if things went okay, pointer containing error code
868 * otherwise
869 */
870struct fb_info *drm_fb_helper_alloc_fbi(struct drm_fb_helper *fb_helper)
871{
872 struct device *dev = fb_helper->dev->dev;
873 struct fb_info *info;
874 int ret;
875
876 info = framebuffer_alloc(0, dev);
877 if (!info)
878 return ERR_PTR(-ENOMEM);
879
880 ret = fb_alloc_cmap(&info->cmap, 256, 0);
881 if (ret)
882 goto err_release;
883
884 info->apertures = alloc_apertures(1);
885 if (!info->apertures) {
886 ret = -ENOMEM;
887 goto err_free_cmap;
888 }
889
890 fb_helper->fbdev = info;
891
892 return info;
893
894err_free_cmap:
895 fb_dealloc_cmap(&info->cmap);
896err_release:
897 framebuffer_release(info);
898 return ERR_PTR(ret);
899}
900EXPORT_SYMBOL(drm_fb_helper_alloc_fbi);
901
902/**
903 * drm_fb_helper_unregister_fbi - unregister fb_info framebuffer device
Noralf Trønnesc7779902017-10-30 16:39:37 +0100904 * @fb_helper: driver-allocated fbdev helper, can be NULL
Archit Tanejab8017d62015-07-22 14:57:56 +0530905 *
906 * A wrapper around unregister_framebuffer, to release the fb_info
Daniel Vettered84e252017-02-07 15:10:49 +0100907 * framebuffer device. This must be called before releasing all resources for
908 * @fb_helper by calling drm_fb_helper_fini().
Archit Tanejab8017d62015-07-22 14:57:56 +0530909 */
910void drm_fb_helper_unregister_fbi(struct drm_fb_helper *fb_helper)
911{
912 if (fb_helper && fb_helper->fbdev)
913 unregister_framebuffer(fb_helper->fbdev);
914}
915EXPORT_SYMBOL(drm_fb_helper_unregister_fbi);
916
917/**
Daniel Vettered84e252017-02-07 15:10:49 +0100918 * drm_fb_helper_fini - finialize a &struct drm_fb_helper
Noralf Trønnesc7779902017-10-30 16:39:37 +0100919 * @fb_helper: driver-allocated fbdev helper, can be NULL
Daniel Vettered84e252017-02-07 15:10:49 +0100920 *
921 * This cleans up all remaining resources associated with @fb_helper. Must be
922 * called after drm_fb_helper_unlink_fbi() was called.
923 */
Dave Airlie4abe3522010-03-30 05:34:18 +0000924void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
925{
Daniel Vetterda7bdda2017-02-07 17:16:03 +0100926 struct fb_info *info;
927
Noralf Trønnes29ad20b2017-10-30 16:39:38 +0100928 if (!fb_helper)
929 return;
930
931 fb_helper->dev->fb_helper = NULL;
932
933 if (!drm_fbdev_emulation)
Daniel Vetterf64c5572015-08-25 15:45:13 +0200934 return;
935
Noralf Trønnesb52f09c2017-08-28 19:17:43 +0200936 cancel_work_sync(&fb_helper->resume_work);
937 cancel_work_sync(&fb_helper->dirty_work);
938
Daniel Vetterda7bdda2017-02-07 17:16:03 +0100939 info = fb_helper->fbdev;
940 if (info) {
941 if (info->cmap.len)
942 fb_dealloc_cmap(&info->cmap);
943 framebuffer_release(info);
944 }
945 fb_helper->fbdev = NULL;
946
Chris Wilsona53ca632016-11-29 12:02:17 +0000947 mutex_lock(&kernel_fb_helper_lock);
Dave Airlie4abe3522010-03-30 05:34:18 +0000948 if (!list_empty(&fb_helper->kernel_fb_list)) {
949 list_del(&fb_helper->kernel_fb_list);
Thierry Reding4b4f99f2017-03-29 16:43:51 +0200950 if (list_empty(&kernel_fb_helper_list))
Dave Airlie4abe3522010-03-30 05:34:18 +0000951 unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
Dave Airlie4abe3522010-03-30 05:34:18 +0000952 }
Chris Wilsona53ca632016-11-29 12:02:17 +0000953 mutex_unlock(&kernel_fb_helper_lock);
Dave Airlie4abe3522010-03-30 05:34:18 +0000954
Thierry Redinge9827d82017-07-04 17:18:23 +0200955 mutex_destroy(&fb_helper->lock);
Dave Airlie4abe3522010-03-30 05:34:18 +0000956 drm_fb_helper_crtc_free(fb_helper);
957
Dave Airlie4abe3522010-03-30 05:34:18 +0000958}
959EXPORT_SYMBOL(drm_fb_helper_fini);
Dave Airlie785b93e2009-08-28 15:46:53 +1000960
Archit Taneja47074ab2015-07-22 14:57:57 +0530961/**
962 * drm_fb_helper_unlink_fbi - wrapper around unlink_framebuffer
Noralf Trønnesc7779902017-10-30 16:39:37 +0100963 * @fb_helper: driver-allocated fbdev helper, can be NULL
Archit Taneja47074ab2015-07-22 14:57:57 +0530964 *
965 * A wrapper around unlink_framebuffer implemented by fbdev core
966 */
967void drm_fb_helper_unlink_fbi(struct drm_fb_helper *fb_helper)
968{
969 if (fb_helper && fb_helper->fbdev)
970 unlink_framebuffer(fb_helper->fbdev);
971}
972EXPORT_SYMBOL(drm_fb_helper_unlink_fbi);
973
Noralf Trønneseaa434d2016-04-28 17:18:33 +0200974static void drm_fb_helper_dirty(struct fb_info *info, u32 x, u32 y,
975 u32 width, u32 height)
976{
977 struct drm_fb_helper *helper = info->par;
978 struct drm_clip_rect *clip = &helper->dirty_clip;
979 unsigned long flags;
980
981 if (!helper->fb->funcs->dirty)
982 return;
983
984 spin_lock_irqsave(&helper->dirty_lock, flags);
985 clip->x1 = min_t(u32, clip->x1, x);
986 clip->y1 = min_t(u32, clip->y1, y);
987 clip->x2 = max_t(u32, clip->x2, x + width);
988 clip->y2 = max_t(u32, clip->y2, y + height);
989 spin_unlock_irqrestore(&helper->dirty_lock, flags);
990
991 schedule_work(&helper->dirty_work);
992}
993
994/**
995 * drm_fb_helper_deferred_io() - fbdev deferred_io callback function
996 * @info: fb_info struct pointer
997 * @pagelist: list of dirty mmap framebuffer pages
998 *
Daniel Vetter6806cdf2017-01-25 07:26:43 +0100999 * This function is used as the &fb_deferred_io.deferred_io
Noralf Trønneseaa434d2016-04-28 17:18:33 +02001000 * callback function for flushing the fbdev mmap writes.
1001 */
1002void drm_fb_helper_deferred_io(struct fb_info *info,
1003 struct list_head *pagelist)
1004{
1005 unsigned long start, end, min, max;
1006 struct page *page;
1007 u32 y1, y2;
1008
1009 min = ULONG_MAX;
1010 max = 0;
1011 list_for_each_entry(page, pagelist, lru) {
1012 start = page->index << PAGE_SHIFT;
1013 end = start + PAGE_SIZE - 1;
1014 min = min(min, start);
1015 max = max(max, end);
1016 }
1017
1018 if (min < max) {
1019 y1 = min / info->fix.line_length;
1020 y2 = min_t(u32, DIV_ROUND_UP(max, info->fix.line_length),
1021 info->var.yres);
1022 drm_fb_helper_dirty(info, 0, y1, info->var.xres, y2 - y1);
1023 }
1024}
1025EXPORT_SYMBOL(drm_fb_helper_deferred_io);
1026
Archit Tanejacbb1a822015-07-31 16:21:41 +05301027/**
1028 * drm_fb_helper_sys_read - wrapper around fb_sys_read
1029 * @info: fb_info struct pointer
1030 * @buf: userspace buffer to read from framebuffer memory
1031 * @count: number of bytes to read from framebuffer memory
1032 * @ppos: read offset within framebuffer memory
1033 *
1034 * A wrapper around fb_sys_read implemented by fbdev core
1035 */
1036ssize_t drm_fb_helper_sys_read(struct fb_info *info, char __user *buf,
1037 size_t count, loff_t *ppos)
1038{
1039 return fb_sys_read(info, buf, count, ppos);
1040}
1041EXPORT_SYMBOL(drm_fb_helper_sys_read);
1042
1043/**
1044 * drm_fb_helper_sys_write - wrapper around fb_sys_write
1045 * @info: fb_info struct pointer
1046 * @buf: userspace buffer to write to framebuffer memory
1047 * @count: number of bytes to write to framebuffer memory
1048 * @ppos: write offset within framebuffer memory
1049 *
1050 * A wrapper around fb_sys_write implemented by fbdev core
1051 */
1052ssize_t drm_fb_helper_sys_write(struct fb_info *info, const char __user *buf,
1053 size_t count, loff_t *ppos)
1054{
Noralf Trønneseaa434d2016-04-28 17:18:33 +02001055 ssize_t ret;
1056
1057 ret = fb_sys_write(info, buf, count, ppos);
1058 if (ret > 0)
1059 drm_fb_helper_dirty(info, 0, 0, info->var.xres,
1060 info->var.yres);
1061
1062 return ret;
Archit Tanejacbb1a822015-07-31 16:21:41 +05301063}
1064EXPORT_SYMBOL(drm_fb_helper_sys_write);
1065
Archit Taneja742547b2015-07-31 16:21:42 +05301066/**
1067 * drm_fb_helper_sys_fillrect - wrapper around sys_fillrect
1068 * @info: fbdev registered by the helper
1069 * @rect: info about rectangle to fill
1070 *
1071 * A wrapper around sys_fillrect implemented by fbdev core
1072 */
1073void drm_fb_helper_sys_fillrect(struct fb_info *info,
1074 const struct fb_fillrect *rect)
1075{
1076 sys_fillrect(info, rect);
Noralf Trønneseaa434d2016-04-28 17:18:33 +02001077 drm_fb_helper_dirty(info, rect->dx, rect->dy,
1078 rect->width, rect->height);
Archit Taneja742547b2015-07-31 16:21:42 +05301079}
1080EXPORT_SYMBOL(drm_fb_helper_sys_fillrect);
1081
1082/**
1083 * drm_fb_helper_sys_copyarea - wrapper around sys_copyarea
1084 * @info: fbdev registered by the helper
1085 * @area: info about area to copy
1086 *
1087 * A wrapper around sys_copyarea implemented by fbdev core
1088 */
1089void drm_fb_helper_sys_copyarea(struct fb_info *info,
1090 const struct fb_copyarea *area)
1091{
1092 sys_copyarea(info, area);
Noralf Trønneseaa434d2016-04-28 17:18:33 +02001093 drm_fb_helper_dirty(info, area->dx, area->dy,
1094 area->width, area->height);
Archit Taneja742547b2015-07-31 16:21:42 +05301095}
1096EXPORT_SYMBOL(drm_fb_helper_sys_copyarea);
1097
1098/**
1099 * drm_fb_helper_sys_imageblit - wrapper around sys_imageblit
1100 * @info: fbdev registered by the helper
1101 * @image: info about image to blit
1102 *
1103 * A wrapper around sys_imageblit implemented by fbdev core
1104 */
1105void drm_fb_helper_sys_imageblit(struct fb_info *info,
1106 const struct fb_image *image)
1107{
1108 sys_imageblit(info, image);
Noralf Trønneseaa434d2016-04-28 17:18:33 +02001109 drm_fb_helper_dirty(info, image->dx, image->dy,
1110 image->width, image->height);
Archit Taneja742547b2015-07-31 16:21:42 +05301111}
1112EXPORT_SYMBOL(drm_fb_helper_sys_imageblit);
1113
1114/**
1115 * drm_fb_helper_cfb_fillrect - wrapper around cfb_fillrect
1116 * @info: fbdev registered by the helper
1117 * @rect: info about rectangle to fill
1118 *
1119 * A wrapper around cfb_imageblit implemented by fbdev core
1120 */
1121void drm_fb_helper_cfb_fillrect(struct fb_info *info,
1122 const struct fb_fillrect *rect)
1123{
1124 cfb_fillrect(info, rect);
Noralf Trønneseaa434d2016-04-28 17:18:33 +02001125 drm_fb_helper_dirty(info, rect->dx, rect->dy,
1126 rect->width, rect->height);
Archit Taneja742547b2015-07-31 16:21:42 +05301127}
1128EXPORT_SYMBOL(drm_fb_helper_cfb_fillrect);
1129
1130/**
1131 * drm_fb_helper_cfb_copyarea - wrapper around cfb_copyarea
1132 * @info: fbdev registered by the helper
1133 * @area: info about area to copy
1134 *
1135 * A wrapper around cfb_copyarea implemented by fbdev core
1136 */
1137void drm_fb_helper_cfb_copyarea(struct fb_info *info,
1138 const struct fb_copyarea *area)
1139{
1140 cfb_copyarea(info, area);
Noralf Trønneseaa434d2016-04-28 17:18:33 +02001141 drm_fb_helper_dirty(info, area->dx, area->dy,
1142 area->width, area->height);
Archit Taneja742547b2015-07-31 16:21:42 +05301143}
1144EXPORT_SYMBOL(drm_fb_helper_cfb_copyarea);
1145
1146/**
1147 * drm_fb_helper_cfb_imageblit - wrapper around cfb_imageblit
1148 * @info: fbdev registered by the helper
1149 * @image: info about image to blit
1150 *
1151 * A wrapper around cfb_imageblit implemented by fbdev core
1152 */
1153void drm_fb_helper_cfb_imageblit(struct fb_info *info,
1154 const struct fb_image *image)
1155{
1156 cfb_imageblit(info, image);
Noralf Trønneseaa434d2016-04-28 17:18:33 +02001157 drm_fb_helper_dirty(info, image->dx, image->dy,
1158 image->width, image->height);
Archit Taneja742547b2015-07-31 16:21:42 +05301159}
1160EXPORT_SYMBOL(drm_fb_helper_cfb_imageblit);
1161
Archit Tanejafdefa582015-07-31 16:21:43 +05301162/**
1163 * drm_fb_helper_set_suspend - wrapper around fb_set_suspend
Noralf Trønnesc7779902017-10-30 16:39:37 +01001164 * @fb_helper: driver-allocated fbdev helper, can be NULL
Daniel Vetter28579f32016-08-23 17:27:27 +02001165 * @suspend: whether to suspend or resume
Archit Tanejafdefa582015-07-31 16:21:43 +05301166 *
Noralf Trønnescfe63422016-08-23 13:54:06 +02001167 * A wrapper around fb_set_suspend implemented by fbdev core.
1168 * Use drm_fb_helper_set_suspend_unlocked() if you don't need to take
1169 * the lock yourself
Archit Tanejafdefa582015-07-31 16:21:43 +05301170 */
Daniel Vetter28579f32016-08-23 17:27:27 +02001171void drm_fb_helper_set_suspend(struct drm_fb_helper *fb_helper, bool suspend)
Archit Tanejafdefa582015-07-31 16:21:43 +05301172{
1173 if (fb_helper && fb_helper->fbdev)
Daniel Vetter28579f32016-08-23 17:27:27 +02001174 fb_set_suspend(fb_helper->fbdev, suspend);
Archit Tanejafdefa582015-07-31 16:21:43 +05301175}
1176EXPORT_SYMBOL(drm_fb_helper_set_suspend);
1177
Noralf Trønnescfe63422016-08-23 13:54:06 +02001178/**
1179 * drm_fb_helper_set_suspend_unlocked - wrapper around fb_set_suspend that also
1180 * takes the console lock
Noralf Trønnesc7779902017-10-30 16:39:37 +01001181 * @fb_helper: driver-allocated fbdev helper, can be NULL
Daniel Vetter28579f32016-08-23 17:27:27 +02001182 * @suspend: whether to suspend or resume
Noralf Trønnescfe63422016-08-23 13:54:06 +02001183 *
1184 * A wrapper around fb_set_suspend() that takes the console lock. If the lock
1185 * isn't available on resume, a worker is tasked with waiting for the lock
1186 * to become available. The console lock can be pretty contented on resume
1187 * due to all the printk activity.
1188 *
1189 * This function can be called multiple times with the same state since
Daniel Vetter6806cdf2017-01-25 07:26:43 +01001190 * &fb_info.state is checked to see if fbdev is running or not before locking.
Noralf Trønnescfe63422016-08-23 13:54:06 +02001191 *
1192 * Use drm_fb_helper_set_suspend() if you need to take the lock yourself.
1193 */
1194void drm_fb_helper_set_suspend_unlocked(struct drm_fb_helper *fb_helper,
Daniel Vetter28579f32016-08-23 17:27:27 +02001195 bool suspend)
Noralf Trønnescfe63422016-08-23 13:54:06 +02001196{
1197 if (!fb_helper || !fb_helper->fbdev)
1198 return;
1199
1200 /* make sure there's no pending/ongoing resume */
1201 flush_work(&fb_helper->resume_work);
1202
1203 if (suspend) {
1204 if (fb_helper->fbdev->state != FBINFO_STATE_RUNNING)
1205 return;
1206
1207 console_lock();
1208
1209 } else {
1210 if (fb_helper->fbdev->state == FBINFO_STATE_RUNNING)
1211 return;
1212
1213 if (!console_trylock()) {
1214 schedule_work(&fb_helper->resume_work);
1215 return;
1216 }
1217 }
1218
1219 fb_set_suspend(fb_helper->fbdev, suspend);
1220 console_unlock();
1221}
1222EXPORT_SYMBOL(drm_fb_helper_set_suspend_unlocked);
1223
Peter Rosinb8e2b012017-07-04 12:36:57 +02001224static int setcmap_pseudo_palette(struct fb_cmap *cmap, struct fb_info *info)
1225{
1226 u32 *palette = (u32 *)info->pseudo_palette;
1227 int i;
1228
1229 if (cmap->start + cmap->len > 16)
1230 return -EINVAL;
1231
1232 for (i = 0; i < cmap->len; ++i) {
1233 u16 red = cmap->red[i];
1234 u16 green = cmap->green[i];
1235 u16 blue = cmap->blue[i];
1236 u32 value;
1237
1238 red >>= 16 - info->var.red.length;
1239 green >>= 16 - info->var.green.length;
1240 blue >>= 16 - info->var.blue.length;
1241 value = (red << info->var.red.offset) |
1242 (green << info->var.green.offset) |
1243 (blue << info->var.blue.offset);
1244 if (info->var.transp.length > 0) {
1245 u32 mask = (1 << info->var.transp.length) - 1;
1246
1247 mask <<= info->var.transp.offset;
1248 value |= mask;
1249 }
1250 palette[cmap->start + i] = value;
1251 }
1252
1253 return 0;
1254}
1255
Peter Rosin964c6002017-07-13 18:25:27 +02001256static int setcmap_legacy(struct fb_cmap *cmap, struct fb_info *info)
Dave Airlie068143d2009-10-05 09:58:02 +10001257{
1258 struct drm_fb_helper *fb_helper = info->par;
Dave Airlie068143d2009-10-05 09:58:02 +10001259 struct drm_crtc *crtc;
Peter Rosina3562a02017-07-04 12:36:58 +02001260 u16 *r, *g, *b;
Peter Rosin964c6002017-07-13 18:25:27 +02001261 int i, ret = 0;
Dave Airlie068143d2009-10-05 09:58:02 +10001262
Peter Rosin964c6002017-07-13 18:25:27 +02001263 drm_modeset_lock_all(fb_helper->dev);
Dave Airlie8be48d92010-03-30 05:34:14 +00001264 for (i = 0; i < fb_helper->crtc_count; i++) {
1265 crtc = fb_helper->crtc_info[i].mode_set.crtc;
Peter Rosin964c6002017-07-13 18:25:27 +02001266 if (!crtc->funcs->gamma_set || !crtc->gamma_size)
1267 return -EINVAL;
Dave Airlie068143d2009-10-05 09:58:02 +10001268
Peter Rosin964c6002017-07-13 18:25:27 +02001269 if (cmap->start + cmap->len > crtc->gamma_size)
1270 return -EINVAL;
Peter Rosina3562a02017-07-04 12:36:58 +02001271
1272 r = crtc->gamma_store;
1273 g = r + crtc->gamma_size;
1274 b = g + crtc->gamma_size;
1275
1276 memcpy(r + cmap->start, cmap->red, cmap->len * sizeof(*r));
1277 memcpy(g + cmap->start, cmap->green, cmap->len * sizeof(*g));
1278 memcpy(b + cmap->start, cmap->blue, cmap->len * sizeof(*b));
1279
Peter Rosin964c6002017-07-13 18:25:27 +02001280 ret = crtc->funcs->gamma_set(crtc, r, g, b,
1281 crtc->gamma_size, NULL);
1282 if (ret)
1283 return ret;
Dave Airlie068143d2009-10-05 09:58:02 +10001284 }
Peter Rosin964c6002017-07-13 18:25:27 +02001285 drm_modeset_unlock_all(fb_helper->dev);
1286
1287 return ret;
1288}
1289
1290static struct drm_property_blob *setcmap_new_gamma_lut(struct drm_crtc *crtc,
1291 struct fb_cmap *cmap)
1292{
1293 struct drm_device *dev = crtc->dev;
1294 struct drm_property_blob *gamma_lut;
1295 struct drm_color_lut *lut;
1296 int size = crtc->gamma_size;
1297 int i;
1298
1299 if (!size || cmap->start + cmap->len > size)
1300 return ERR_PTR(-EINVAL);
1301
1302 gamma_lut = drm_property_create_blob(dev, sizeof(*lut) * size, NULL);
1303 if (IS_ERR(gamma_lut))
1304 return gamma_lut;
1305
1306 lut = (struct drm_color_lut *)gamma_lut->data;
1307 if (cmap->start || cmap->len != size) {
1308 u16 *r = crtc->gamma_store;
1309 u16 *g = r + crtc->gamma_size;
1310 u16 *b = g + crtc->gamma_size;
1311
1312 for (i = 0; i < cmap->start; i++) {
1313 lut[i].red = r[i];
1314 lut[i].green = g[i];
1315 lut[i].blue = b[i];
1316 }
1317 for (i = cmap->start + cmap->len; i < size; i++) {
1318 lut[i].red = r[i];
1319 lut[i].green = g[i];
1320 lut[i].blue = b[i];
1321 }
1322 }
1323
1324 for (i = 0; i < cmap->len; i++) {
1325 lut[cmap->start + i].red = cmap->red[i];
1326 lut[cmap->start + i].green = cmap->green[i];
1327 lut[cmap->start + i].blue = cmap->blue[i];
1328 }
1329
1330 return gamma_lut;
1331}
1332
1333static int setcmap_atomic(struct fb_cmap *cmap, struct fb_info *info)
1334{
1335 struct drm_fb_helper *fb_helper = info->par;
1336 struct drm_device *dev = fb_helper->dev;
1337 struct drm_property_blob *gamma_lut = NULL;
1338 struct drm_modeset_acquire_ctx ctx;
1339 struct drm_crtc_state *crtc_state;
1340 struct drm_atomic_state *state;
1341 struct drm_crtc *crtc;
1342 u16 *r, *g, *b;
1343 int i, ret = 0;
1344 bool replaced;
1345
1346 drm_modeset_acquire_init(&ctx, 0);
1347
1348 state = drm_atomic_state_alloc(dev);
1349 if (!state) {
1350 ret = -ENOMEM;
1351 goto out_ctx;
1352 }
1353
1354 state->acquire_ctx = &ctx;
1355retry:
1356 for (i = 0; i < fb_helper->crtc_count; i++) {
1357 crtc = fb_helper->crtc_info[i].mode_set.crtc;
1358
1359 if (!gamma_lut)
1360 gamma_lut = setcmap_new_gamma_lut(crtc, cmap);
1361 if (IS_ERR(gamma_lut)) {
1362 ret = PTR_ERR(gamma_lut);
1363 gamma_lut = NULL;
1364 goto out_state;
1365 }
1366
1367 crtc_state = drm_atomic_get_crtc_state(state, crtc);
1368 if (IS_ERR(crtc_state)) {
1369 ret = PTR_ERR(crtc_state);
1370 goto out_state;
1371 }
1372
1373 replaced = drm_property_replace_blob(&crtc_state->degamma_lut,
1374 NULL);
1375 replaced |= drm_property_replace_blob(&crtc_state->ctm, NULL);
1376 replaced |= drm_property_replace_blob(&crtc_state->gamma_lut,
1377 gamma_lut);
1378 crtc_state->color_mgmt_changed |= replaced;
1379 }
1380
1381 ret = drm_atomic_commit(state);
1382 if (ret)
1383 goto out_state;
1384
1385 for (i = 0; i < fb_helper->crtc_count; i++) {
1386 crtc = fb_helper->crtc_info[i].mode_set.crtc;
1387
1388 r = crtc->gamma_store;
1389 g = r + crtc->gamma_size;
1390 b = g + crtc->gamma_size;
1391
1392 memcpy(r + cmap->start, cmap->red, cmap->len * sizeof(*r));
1393 memcpy(g + cmap->start, cmap->green, cmap->len * sizeof(*g));
1394 memcpy(b + cmap->start, cmap->blue, cmap->len * sizeof(*b));
1395 }
1396
1397out_state:
1398 if (ret == -EDEADLK)
1399 goto backoff;
1400
1401 drm_property_blob_put(gamma_lut);
1402 drm_atomic_state_put(state);
1403out_ctx:
1404 drm_modeset_drop_locks(&ctx);
1405 drm_modeset_acquire_fini(&ctx);
1406
1407 return ret;
1408
1409backoff:
1410 drm_atomic_state_clear(state);
1411 drm_modeset_backoff(&ctx);
1412 goto retry;
1413}
1414
1415/**
1416 * drm_fb_helper_setcmap - implementation for &fb_ops.fb_setcmap
1417 * @cmap: cmap to set
1418 * @info: fbdev registered by the helper
1419 */
1420int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
1421{
1422 struct drm_fb_helper *fb_helper = info->par;
1423 int ret;
1424
1425 if (oops_in_progress)
1426 return -EBUSY;
1427
1428 mutex_lock(&fb_helper->lock);
1429
1430 if (!drm_fb_helper_is_bound(fb_helper)) {
1431 ret = -EBUSY;
1432 goto out;
1433 }
1434
1435 if (info->fix.visual == FB_VISUAL_TRUECOLOR)
1436 ret = setcmap_pseudo_palette(cmap, info);
1437 else if (drm_drv_uses_atomic_modeset(fb_helper->dev))
1438 ret = setcmap_atomic(cmap, info);
1439 else
1440 ret = setcmap_legacy(cmap, info);
1441
1442out:
Thierry Redinge9827d82017-07-04 17:18:23 +02001443 mutex_unlock(&fb_helper->lock);
Peter Rosin964c6002017-07-13 18:25:27 +02001444
1445 return ret;
Dave Airlie068143d2009-10-05 09:58:02 +10001446}
1447EXPORT_SYMBOL(drm_fb_helper_setcmap);
1448
Daniel Vetter207fd322013-01-20 22:13:14 +01001449/**
Maxime Ripard0f3bbe02017-02-28 16:36:51 +01001450 * drm_fb_helper_ioctl - legacy ioctl implementation
1451 * @info: fbdev registered by the helper
1452 * @cmd: ioctl command
1453 * @arg: ioctl argument
1454 *
1455 * A helper to implement the standard fbdev ioctl. Only
1456 * FBIO_WAITFORVSYNC is implemented for now.
1457 */
1458int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd,
1459 unsigned long arg)
1460{
1461 struct drm_fb_helper *fb_helper = info->par;
Maxime Ripard0f3bbe02017-02-28 16:36:51 +01001462 struct drm_mode_set *mode_set;
1463 struct drm_crtc *crtc;
1464 int ret = 0;
1465
Thierry Redinge9827d82017-07-04 17:18:23 +02001466 mutex_lock(&fb_helper->lock);
Maxime Ripard0f3bbe02017-02-28 16:36:51 +01001467 if (!drm_fb_helper_is_bound(fb_helper)) {
1468 ret = -EBUSY;
1469 goto unlock;
1470 }
1471
1472 switch (cmd) {
1473 case FBIO_WAITFORVSYNC:
1474 /*
1475 * Only consider the first CRTC.
1476 *
1477 * This ioctl is supposed to take the CRTC number as
1478 * an argument, but in fbdev times, what that number
1479 * was supposed to be was quite unclear, different
1480 * drivers were passing that argument differently
1481 * (some by reference, some by value), and most of the
1482 * userspace applications were just hardcoding 0 as an
1483 * argument.
1484 *
1485 * The first CRTC should be the integrated panel on
1486 * most drivers, so this is the best choice we can
1487 * make. If we're not smart enough here, one should
1488 * just consider switch the userspace to KMS.
1489 */
1490 mode_set = &fb_helper->crtc_info[0].mode_set;
1491 crtc = mode_set->crtc;
1492
1493 /*
1494 * Only wait for a vblank event if the CRTC is
1495 * enabled, otherwise just don't do anythintg,
1496 * not even report an error.
1497 */
1498 ret = drm_crtc_vblank_get(crtc);
1499 if (!ret) {
1500 drm_crtc_wait_one_vblank(crtc);
1501 drm_crtc_vblank_put(crtc);
1502 }
1503
1504 ret = 0;
1505 goto unlock;
1506 default:
1507 ret = -ENOTTY;
1508 }
1509
1510unlock:
Thierry Redinge9827d82017-07-04 17:18:23 +02001511 mutex_unlock(&fb_helper->lock);
Maxime Ripard0f3bbe02017-02-28 16:36:51 +01001512 return ret;
1513}
1514EXPORT_SYMBOL(drm_fb_helper_ioctl);
1515
1516/**
Daniel Vetter6806cdf2017-01-25 07:26:43 +01001517 * drm_fb_helper_check_var - implementation for &fb_ops.fb_check_var
Daniel Vetter207fd322013-01-20 22:13:14 +01001518 * @var: screeninfo to check
1519 * @info: fbdev registered by the helper
1520 */
Dave Airlie785b93e2009-08-28 15:46:53 +10001521int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
1522 struct fb_info *info)
1523{
1524 struct drm_fb_helper *fb_helper = info->par;
1525 struct drm_framebuffer *fb = fb_helper->fb;
1526 int depth;
1527
Jason Wesself90ebd92010-08-05 09:22:32 -05001528 if (var->pixclock != 0 || in_dbg_master())
Dave Airlie785b93e2009-08-28 15:46:53 +10001529 return -EINVAL;
1530
Stefan Agner865afb12016-10-11 16:15:04 -07001531 /*
1532 * Changes struct fb_var_screeninfo are currently not pushed back
1533 * to KMS, hence fail if different settings are requested.
1534 */
Ville Syrjälä272725c2016-12-14 23:32:20 +02001535 if (var->bits_per_pixel != fb->format->cpp[0] * 8 ||
Michel Dänzer12ffed92017-03-23 17:53:26 +09001536 var->xres > fb->width || var->yres > fb->height ||
1537 var->xres_virtual > fb->width || var->yres_virtual > fb->height) {
1538 DRM_DEBUG("fb requested width/height/bpp can't fit in current fb "
Chris Wilson62fb3762012-03-26 21:15:53 +01001539 "request %dx%d-%d (virtual %dx%d) > %dx%d-%d\n",
1540 var->xres, var->yres, var->bits_per_pixel,
1541 var->xres_virtual, var->yres_virtual,
Ville Syrjälä272725c2016-12-14 23:32:20 +02001542 fb->width, fb->height, fb->format->cpp[0] * 8);
Dave Airlie785b93e2009-08-28 15:46:53 +10001543 return -EINVAL;
1544 }
1545
1546 switch (var->bits_per_pixel) {
1547 case 16:
1548 depth = (var->green.length == 6) ? 16 : 15;
1549 break;
1550 case 32:
1551 depth = (var->transp.length > 0) ? 32 : 24;
1552 break;
1553 default:
1554 depth = var->bits_per_pixel;
1555 break;
1556 }
1557
1558 switch (depth) {
1559 case 8:
1560 var->red.offset = 0;
1561 var->green.offset = 0;
1562 var->blue.offset = 0;
1563 var->red.length = 8;
1564 var->green.length = 8;
1565 var->blue.length = 8;
1566 var->transp.length = 0;
1567 var->transp.offset = 0;
1568 break;
1569 case 15:
1570 var->red.offset = 10;
1571 var->green.offset = 5;
1572 var->blue.offset = 0;
1573 var->red.length = 5;
1574 var->green.length = 5;
1575 var->blue.length = 5;
1576 var->transp.length = 1;
1577 var->transp.offset = 15;
1578 break;
1579 case 16:
1580 var->red.offset = 11;
1581 var->green.offset = 5;
1582 var->blue.offset = 0;
1583 var->red.length = 5;
1584 var->green.length = 6;
1585 var->blue.length = 5;
1586 var->transp.length = 0;
1587 var->transp.offset = 0;
1588 break;
1589 case 24:
1590 var->red.offset = 16;
1591 var->green.offset = 8;
1592 var->blue.offset = 0;
1593 var->red.length = 8;
1594 var->green.length = 8;
1595 var->blue.length = 8;
1596 var->transp.length = 0;
1597 var->transp.offset = 0;
1598 break;
1599 case 32:
1600 var->red.offset = 16;
1601 var->green.offset = 8;
1602 var->blue.offset = 0;
1603 var->red.length = 8;
1604 var->green.length = 8;
1605 var->blue.length = 8;
1606 var->transp.length = 8;
1607 var->transp.offset = 24;
1608 break;
1609 default:
1610 return -EINVAL;
1611 }
1612 return 0;
1613}
1614EXPORT_SYMBOL(drm_fb_helper_check_var);
1615
Daniel Vetter207fd322013-01-20 22:13:14 +01001616/**
Daniel Vetter6806cdf2017-01-25 07:26:43 +01001617 * drm_fb_helper_set_par - implementation for &fb_ops.fb_set_par
Daniel Vetter207fd322013-01-20 22:13:14 +01001618 * @info: fbdev registered by the helper
1619 *
1620 * This will let fbcon do the mode init and is called at initialization time by
1621 * the fbdev core when registering the driver, and later on through the hotplug
1622 * callback.
1623 */
Dave Airlie785b93e2009-08-28 15:46:53 +10001624int drm_fb_helper_set_par(struct fb_info *info)
1625{
1626 struct drm_fb_helper *fb_helper = info->par;
Dave Airlie785b93e2009-08-28 15:46:53 +10001627 struct fb_var_screeninfo *var = &info->var;
Dave Airlie785b93e2009-08-28 15:46:53 +10001628
Daniel Vetterc50bfd02015-07-28 13:18:40 +02001629 if (oops_in_progress)
1630 return -EBUSY;
1631
Clemens Ladisch5349ef32009-11-04 09:42:52 +01001632 if (var->pixclock != 0) {
Pavel Roskin172e91f2010-02-11 14:31:32 +10001633 DRM_ERROR("PIXEL CLOCK SET\n");
Dave Airlie785b93e2009-08-28 15:46:53 +10001634 return -EINVAL;
1635 }
1636
Rob Clark5ea1f752014-05-30 12:29:48 -04001637 drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);
Dave Airlie4abe3522010-03-30 05:34:18 +00001638
Dave Airlie785b93e2009-08-28 15:46:53 +10001639 return 0;
1640}
1641EXPORT_SYMBOL(drm_fb_helper_set_par);
1642
Daniel Vetter6b7dc6e2017-07-04 17:18:29 +02001643static void pan_set(struct drm_fb_helper *fb_helper, int x, int y)
Rob Clark1edf0262015-08-25 15:35:59 -04001644{
Daniel Vetter6b7dc6e2017-07-04 17:18:29 +02001645 int i;
Daniel Vetter5c2e3442017-07-04 17:18:26 +02001646
Thierry Reding4b4f99f2017-03-29 16:43:51 +02001647 for (i = 0; i < fb_helper->crtc_count; i++) {
Rob Clark1edf0262015-08-25 15:35:59 -04001648 struct drm_mode_set *mode_set;
1649
1650 mode_set = &fb_helper->crtc_info[i].mode_set;
1651
Daniel Vetter6b7dc6e2017-07-04 17:18:29 +02001652 mode_set->x = x;
1653 mode_set->y = y;
Rob Clark1edf0262015-08-25 15:35:59 -04001654 }
Daniel Vetter6b7dc6e2017-07-04 17:18:29 +02001655}
Rob Clark1edf0262015-08-25 15:35:59 -04001656
Daniel Vetter6b7dc6e2017-07-04 17:18:29 +02001657static int pan_display_atomic(struct fb_var_screeninfo *var,
1658 struct fb_info *info)
1659{
1660 struct drm_fb_helper *fb_helper = info->par;
1661 int ret;
Rob Clark1edf0262015-08-25 15:35:59 -04001662
Daniel Vetter6b7dc6e2017-07-04 17:18:29 +02001663 pan_set(fb_helper, var->xoffset, var->yoffset);
Rob Clark1edf0262015-08-25 15:35:59 -04001664
Daniel Vetter6b7dc6e2017-07-04 17:18:29 +02001665 ret = restore_fbdev_mode_atomic(fb_helper, true);
1666 if (!ret) {
1667 info->var.xoffset = var->xoffset;
1668 info->var.yoffset = var->yoffset;
1669 } else
1670 pan_set(fb_helper, info->var.xoffset, info->var.yoffset);
Daniel Vetter5c2e3442017-07-04 17:18:26 +02001671
Rob Clark1edf0262015-08-25 15:35:59 -04001672 return ret;
Rob Clark1edf0262015-08-25 15:35:59 -04001673}
1674
Daniel Vetter71286452017-04-03 10:33:04 +02001675static int pan_display_legacy(struct fb_var_screeninfo *var,
Dave Airlie785b93e2009-08-28 15:46:53 +10001676 struct fb_info *info)
1677{
1678 struct drm_fb_helper *fb_helper = info->par;
Dave Airlie785b93e2009-08-28 15:46:53 +10001679 struct drm_mode_set *modeset;
Dave Airlie785b93e2009-08-28 15:46:53 +10001680 int ret = 0;
1681 int i;
1682
Daniel Vetter5c2e3442017-07-04 17:18:26 +02001683 drm_modeset_lock_all(fb_helper->dev);
Dave Airlie8be48d92010-03-30 05:34:14 +00001684 for (i = 0; i < fb_helper->crtc_count; i++) {
Dave Airlie785b93e2009-08-28 15:46:53 +10001685 modeset = &fb_helper->crtc_info[i].mode_set;
1686
1687 modeset->x = var->xoffset;
1688 modeset->y = var->yoffset;
1689
1690 if (modeset->num_connectors) {
Daniel Vetter2d13b672012-12-11 13:47:23 +01001691 ret = drm_mode_set_config_internal(modeset);
Dave Airlie785b93e2009-08-28 15:46:53 +10001692 if (!ret) {
1693 info->var.xoffset = var->xoffset;
1694 info->var.yoffset = var->yoffset;
1695 }
1696 }
1697 }
Daniel Vetter5c2e3442017-07-04 17:18:26 +02001698 drm_modeset_unlock_all(fb_helper->dev);
Daniel Vetter71286452017-04-03 10:33:04 +02001699
1700 return ret;
1701}
1702
1703/**
1704 * drm_fb_helper_pan_display - implementation for &fb_ops.fb_pan_display
1705 * @var: updated screen information
1706 * @info: fbdev registered by the helper
1707 */
1708int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
1709 struct fb_info *info)
1710{
1711 struct drm_fb_helper *fb_helper = info->par;
1712 struct drm_device *dev = fb_helper->dev;
1713 int ret;
1714
1715 if (oops_in_progress)
1716 return -EBUSY;
1717
Thierry Redinge9827d82017-07-04 17:18:23 +02001718 mutex_lock(&fb_helper->lock);
Daniel Vetter71286452017-04-03 10:33:04 +02001719 if (!drm_fb_helper_is_bound(fb_helper)) {
Thierry Redinge9827d82017-07-04 17:18:23 +02001720 mutex_unlock(&fb_helper->lock);
Daniel Vetter71286452017-04-03 10:33:04 +02001721 return -EBUSY;
1722 }
1723
1724 if (drm_drv_uses_atomic_modeset(dev))
1725 ret = pan_display_atomic(var, info);
1726 else
1727 ret = pan_display_legacy(var, info);
Thierry Redinge9827d82017-07-04 17:18:23 +02001728 mutex_unlock(&fb_helper->lock);
Daniel Vetter71286452017-04-03 10:33:04 +02001729
Dave Airlie785b93e2009-08-28 15:46:53 +10001730 return ret;
1731}
1732EXPORT_SYMBOL(drm_fb_helper_pan_display);
1733
Daniel Vetter8acf6582013-01-21 23:38:37 +01001734/*
Daniel Vetter207fd322013-01-20 22:13:14 +01001735 * Allocates the backing storage and sets up the fbdev info structure through
Daniel Vetterca91a272017-07-06 15:00:21 +02001736 * the ->fb_probe callback.
Daniel Vetter8acf6582013-01-21 23:38:37 +01001737 */
Daniel Vetterde1ace52013-01-20 21:50:49 +01001738static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
1739 int preferred_bpp)
Dave Airlie785b93e2009-08-28 15:46:53 +10001740{
Daniel Vetter8acf6582013-01-21 23:38:37 +01001741 int ret = 0;
Dave Airlie785b93e2009-08-28 15:46:53 +10001742 int crtc_count = 0;
Dave Airlie4abe3522010-03-30 05:34:18 +00001743 int i;
Dave Airlie38651672010-03-30 05:34:13 +00001744 struct drm_fb_helper_surface_size sizes;
Dave Airlie8be48d92010-03-30 05:34:14 +00001745 int gamma_size = 0;
Dave Airlie38651672010-03-30 05:34:13 +00001746
1747 memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size));
1748 sizes.surface_depth = 24;
1749 sizes.surface_bpp = 32;
Thierry Reding4b4f99f2017-03-29 16:43:51 +02001750 sizes.fb_width = (u32)-1;
1751 sizes.fb_height = (u32)-1;
Dave Airlie785b93e2009-08-28 15:46:53 +10001752
Thierry Reding4b4f99f2017-03-29 16:43:51 +02001753 /* if driver picks 8 or 16 by default use that for both depth/bpp */
Sachin Kamat96081cd2012-11-15 03:43:30 +00001754 if (preferred_bpp != sizes.surface_bpp)
Dave Airlie38651672010-03-30 05:34:13 +00001755 sizes.surface_depth = sizes.surface_bpp = preferred_bpp;
Sachin Kamat96081cd2012-11-15 03:43:30 +00001756
Dave Airlie785b93e2009-08-28 15:46:53 +10001757 /* first up get a count of crtcs now in use and new min/maxes width/heights */
Chris Wilson966a6a12016-11-29 12:02:15 +00001758 drm_fb_helper_for_each_connector(fb_helper, i) {
Dave Airlie0b4c0f32010-03-30 05:34:15 +00001759 struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i];
Chris Wilson1794d252011-04-17 07:43:32 +01001760 struct drm_cmdline_mode *cmdline_mode;
Dave Airlie8ef86782009-09-26 06:39:00 +10001761
Chris Wilsoneaf99c72014-08-06 10:08:32 +02001762 cmdline_mode = &fb_helper_conn->connector->cmdline_mode;
Dave Airlied50ba252009-09-23 14:44:08 +10001763
1764 if (cmdline_mode->bpp_specified) {
1765 switch (cmdline_mode->bpp) {
1766 case 8:
Dave Airlie38651672010-03-30 05:34:13 +00001767 sizes.surface_depth = sizes.surface_bpp = 8;
Dave Airlied50ba252009-09-23 14:44:08 +10001768 break;
1769 case 15:
Dave Airlie38651672010-03-30 05:34:13 +00001770 sizes.surface_depth = 15;
1771 sizes.surface_bpp = 16;
Dave Airlied50ba252009-09-23 14:44:08 +10001772 break;
1773 case 16:
Dave Airlie38651672010-03-30 05:34:13 +00001774 sizes.surface_depth = sizes.surface_bpp = 16;
Dave Airlied50ba252009-09-23 14:44:08 +10001775 break;
1776 case 24:
Dave Airlie38651672010-03-30 05:34:13 +00001777 sizes.surface_depth = sizes.surface_bpp = 24;
Dave Airlied50ba252009-09-23 14:44:08 +10001778 break;
1779 case 32:
Dave Airlie38651672010-03-30 05:34:13 +00001780 sizes.surface_depth = 24;
1781 sizes.surface_bpp = 32;
Dave Airlied50ba252009-09-23 14:44:08 +10001782 break;
1783 }
1784 break;
1785 }
1786 }
1787
Dave Airlie8be48d92010-03-30 05:34:14 +00001788 crtc_count = 0;
1789 for (i = 0; i < fb_helper->crtc_count; i++) {
1790 struct drm_display_mode *desired_mode;
Rob Clark0e3704c2015-03-11 10:23:14 -04001791 struct drm_mode_set *mode_set;
1792 int x, y, j;
1793 /* in case of tile group, are we the last tile vert or horiz?
1794 * If no tile group you are always the last one both vertically
1795 * and horizontally
1796 */
1797 bool lastv = true, lasth = true;
Rob Clark675c8322015-03-11 10:23:13 -04001798
Dave Airlie8be48d92010-03-30 05:34:14 +00001799 desired_mode = fb_helper->crtc_info[i].desired_mode;
Rob Clark0e3704c2015-03-11 10:23:14 -04001800 mode_set = &fb_helper->crtc_info[i].mode_set;
Rob Clark675c8322015-03-11 10:23:13 -04001801
1802 if (!desired_mode)
1803 continue;
1804
1805 crtc_count++;
1806
Dave Airlieb0ee9e72014-10-20 16:31:53 +10001807 x = fb_helper->crtc_info[i].x;
1808 y = fb_helper->crtc_info[i].y;
Rob Clark675c8322015-03-11 10:23:13 -04001809
1810 if (gamma_size == 0)
1811 gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size;
1812
1813 sizes.surface_width = max_t(u32, desired_mode->hdisplay + x, sizes.surface_width);
1814 sizes.surface_height = max_t(u32, desired_mode->vdisplay + y, sizes.surface_height);
Rob Clark0e3704c2015-03-11 10:23:14 -04001815
1816 for (j = 0; j < mode_set->num_connectors; j++) {
1817 struct drm_connector *connector = mode_set->connectors[j];
Thierry Reding4b4f99f2017-03-29 16:43:51 +02001818
Rob Clark0e3704c2015-03-11 10:23:14 -04001819 if (connector->has_tile) {
1820 lasth = (connector->tile_h_loc == (connector->num_h_tile - 1));
1821 lastv = (connector->tile_v_loc == (connector->num_v_tile - 1));
1822 /* cloning to multiple tiles is just crazy-talk, so: */
1823 break;
1824 }
1825 }
1826
1827 if (lasth)
1828 sizes.fb_width = min_t(u32, desired_mode->hdisplay + x, sizes.fb_width);
1829 if (lastv)
1830 sizes.fb_height = min_t(u32, desired_mode->vdisplay + y, sizes.fb_height);
Dave Airlie785b93e2009-08-28 15:46:53 +10001831 }
1832
Dave Airlie38651672010-03-30 05:34:13 +00001833 if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) {
Daniel Vetterca91a272017-07-06 15:00:21 +02001834 DRM_INFO("Cannot find any crtc or sizes\n");
Maarten Lankhorst52dd0652017-11-28 12:16:03 +01001835
1836 /* First time: disable all crtc's.. */
1837 if (!fb_helper->deferred_setup && !READ_ONCE(fb_helper->dev->master))
1838 restore_fbdev_mode(fb_helper);
Daniel Vetterca91a272017-07-06 15:00:21 +02001839 return -EAGAIN;
Dave Airlie785b93e2009-08-28 15:46:53 +10001840 }
1841
Xinliang Liu5f152572017-02-15 17:19:08 +01001842 /* Handle our overallocation */
1843 sizes.surface_height *= drm_fbdev_overalloc;
1844 sizes.surface_height /= 100;
1845
Dave Airlie38651672010-03-30 05:34:13 +00001846 /* push down into drivers */
Daniel Vetter8acf6582013-01-21 23:38:37 +01001847 ret = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes);
1848 if (ret < 0)
1849 return ret;
Dave Airlie785b93e2009-08-28 15:46:53 +10001850
Dave Airlie785b93e2009-08-28 15:46:53 +10001851 return 0;
1852}
Dave Airlie785b93e2009-08-28 15:46:53 +10001853
Daniel Vetter207fd322013-01-20 22:13:14 +01001854/**
1855 * drm_fb_helper_fill_fix - initializes fixed fbdev information
1856 * @info: fbdev registered by the helper
1857 * @pitch: desired pitch
1858 * @depth: desired depth
1859 *
1860 * Helper to fill in the fixed fbdev information useful for a non-accelerated
1861 * fbdev emulations. Drivers which support acceleration methods which impose
1862 * additional constraints need to set up their own limits.
1863 *
1864 * Drivers should call this (or their equivalent setup code) from their
Daniel Vetter6806cdf2017-01-25 07:26:43 +01001865 * &drm_fb_helper_funcs.fb_probe callback.
Daniel Vetter207fd322013-01-20 22:13:14 +01001866 */
Dave Airlie3632ef82011-01-15 09:27:00 +10001867void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
1868 uint32_t depth)
1869{
1870 info->fix.type = FB_TYPE_PACKED_PIXELS;
1871 info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR :
1872 FB_VISUAL_TRUECOLOR;
1873 info->fix.mmio_start = 0;
1874 info->fix.mmio_len = 0;
1875 info->fix.type_aux = 0;
1876 info->fix.xpanstep = 1; /* doing it in hw */
1877 info->fix.ypanstep = 1; /* doing it in hw */
1878 info->fix.ywrapstep = 0;
1879 info->fix.accel = FB_ACCEL_NONE;
Dave Airlie3632ef82011-01-15 09:27:00 +10001880
1881 info->fix.line_length = pitch;
Dave Airlie3632ef82011-01-15 09:27:00 +10001882}
1883EXPORT_SYMBOL(drm_fb_helper_fill_fix);
1884
Daniel Vetter207fd322013-01-20 22:13:14 +01001885/**
1886 * drm_fb_helper_fill_var - initalizes variable fbdev information
1887 * @info: fbdev instance to set up
1888 * @fb_helper: fb helper instance to use as template
1889 * @fb_width: desired fb width
1890 * @fb_height: desired fb height
1891 *
1892 * Sets up the variable fbdev metainformation from the given fb helper instance
Daniel Vetter6806cdf2017-01-25 07:26:43 +01001893 * and the drm framebuffer allocated in &drm_fb_helper.fb.
Daniel Vetter207fd322013-01-20 22:13:14 +01001894 *
1895 * Drivers should call this (or their equivalent setup code) from their
Daniel Vetter6806cdf2017-01-25 07:26:43 +01001896 * &drm_fb_helper_funcs.fb_probe callback after having allocated the fbdev
1897 * backing storage framebuffer.
Daniel Vetter207fd322013-01-20 22:13:14 +01001898 */
Dave Airlie38651672010-03-30 05:34:13 +00001899void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper,
Dave Airlie785b93e2009-08-28 15:46:53 +10001900 uint32_t fb_width, uint32_t fb_height)
1901{
Dave Airlie38651672010-03-30 05:34:13 +00001902 struct drm_framebuffer *fb = fb_helper->fb;
Thierry Reding4b4f99f2017-03-29 16:43:51 +02001903
Dave Airlie38651672010-03-30 05:34:13 +00001904 info->pseudo_palette = fb_helper->pseudo_palette;
Dave Airlie785b93e2009-08-28 15:46:53 +10001905 info->var.xres_virtual = fb->width;
1906 info->var.yres_virtual = fb->height;
Ville Syrjälä272725c2016-12-14 23:32:20 +02001907 info->var.bits_per_pixel = fb->format->cpp[0] * 8;
James Simmons57084d02010-12-20 19:10:39 +00001908 info->var.accel_flags = FB_ACCELF_TEXT;
Dave Airlie785b93e2009-08-28 15:46:53 +10001909 info->var.xoffset = 0;
1910 info->var.yoffset = 0;
1911 info->var.activate = FB_ACTIVATE_NOW;
Dave Airlie785b93e2009-08-28 15:46:53 +10001912
Ville Syrjäläb00c6002016-12-14 23:31:35 +02001913 switch (fb->format->depth) {
Dave Airlie785b93e2009-08-28 15:46:53 +10001914 case 8:
1915 info->var.red.offset = 0;
1916 info->var.green.offset = 0;
1917 info->var.blue.offset = 0;
1918 info->var.red.length = 8; /* 8bit DAC */
1919 info->var.green.length = 8;
1920 info->var.blue.length = 8;
1921 info->var.transp.offset = 0;
1922 info->var.transp.length = 0;
1923 break;
1924 case 15:
1925 info->var.red.offset = 10;
1926 info->var.green.offset = 5;
1927 info->var.blue.offset = 0;
1928 info->var.red.length = 5;
1929 info->var.green.length = 5;
1930 info->var.blue.length = 5;
1931 info->var.transp.offset = 15;
1932 info->var.transp.length = 1;
1933 break;
1934 case 16:
1935 info->var.red.offset = 11;
1936 info->var.green.offset = 5;
1937 info->var.blue.offset = 0;
1938 info->var.red.length = 5;
1939 info->var.green.length = 6;
1940 info->var.blue.length = 5;
1941 info->var.transp.offset = 0;
1942 break;
1943 case 24:
1944 info->var.red.offset = 16;
1945 info->var.green.offset = 8;
1946 info->var.blue.offset = 0;
1947 info->var.red.length = 8;
1948 info->var.green.length = 8;
1949 info->var.blue.length = 8;
1950 info->var.transp.offset = 0;
1951 info->var.transp.length = 0;
1952 break;
1953 case 32:
1954 info->var.red.offset = 16;
1955 info->var.green.offset = 8;
1956 info->var.blue.offset = 0;
1957 info->var.red.length = 8;
1958 info->var.green.length = 8;
1959 info->var.blue.length = 8;
1960 info->var.transp.offset = 24;
1961 info->var.transp.length = 8;
1962 break;
1963 default:
1964 break;
1965 }
1966
1967 info->var.xres = fb_width;
1968 info->var.yres = fb_height;
1969}
1970EXPORT_SYMBOL(drm_fb_helper_fill_var);
Dave Airlie38651672010-03-30 05:34:13 +00001971
Dave Airlie0b4c0f32010-03-30 05:34:15 +00001972static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper,
Daniel Vettere13a0582017-07-05 06:56:29 +02001973 uint32_t maxX,
1974 uint32_t maxY)
Dave Airlie38651672010-03-30 05:34:13 +00001975{
1976 struct drm_connector *connector;
Daniel Vettere13a0582017-07-05 06:56:29 +02001977 int i, count = 0;
Dave Airlie38651672010-03-30 05:34:13 +00001978
Chris Wilson966a6a12016-11-29 12:02:15 +00001979 drm_fb_helper_for_each_connector(fb_helper, i) {
Dave Airlie0b4c0f32010-03-30 05:34:15 +00001980 connector = fb_helper->connector_info[i]->connector;
Dave Airlie38651672010-03-30 05:34:13 +00001981 count += connector->funcs->fill_modes(connector, maxX, maxY);
1982 }
1983
1984 return count;
1985}
1986
Jesse Barnes2f1046f2014-02-12 12:26:24 -08001987struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height)
Dave Airlie38651672010-03-30 05:34:13 +00001988{
1989 struct drm_display_mode *mode;
1990
Dave Airlie0b4c0f32010-03-30 05:34:15 +00001991 list_for_each_entry(mode, &fb_connector->connector->modes, head) {
Daniel Vetter9d3de132014-01-23 16:27:56 +01001992 if (mode->hdisplay > width ||
1993 mode->vdisplay > height)
Dave Airlie38651672010-03-30 05:34:13 +00001994 continue;
1995 if (mode->type & DRM_MODE_TYPE_PREFERRED)
1996 return mode;
1997 }
1998 return NULL;
1999}
Jesse Barnes2f1046f2014-02-12 12:26:24 -08002000EXPORT_SYMBOL(drm_has_preferred_mode);
Dave Airlie38651672010-03-30 05:34:13 +00002001
Dave Airlie0b4c0f32010-03-30 05:34:15 +00002002static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector)
Dave Airlie38651672010-03-30 05:34:13 +00002003{
Chris Wilsoneaf99c72014-08-06 10:08:32 +02002004 return fb_connector->connector->cmdline_mode.specified;
Dave Airlie38651672010-03-30 05:34:13 +00002005}
2006
Vincent Abrioua09759e2017-01-06 17:44:43 +01002007struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn)
Dave Airlie38651672010-03-30 05:34:13 +00002008{
Chris Wilson1794d252011-04-17 07:43:32 +01002009 struct drm_cmdline_mode *cmdline_mode;
Daniel Stonef3af5c72015-03-19 04:33:01 +00002010 struct drm_display_mode *mode;
Takashi Iwaic683f422014-03-19 14:53:13 +01002011 bool prefer_non_interlace;
Dave Airlie38651672010-03-30 05:34:13 +00002012
Chris Wilsoneaf99c72014-08-06 10:08:32 +02002013 cmdline_mode = &fb_helper_conn->connector->cmdline_mode;
Dave Airlie38651672010-03-30 05:34:13 +00002014 if (cmdline_mode->specified == false)
Daniel Stonef3af5c72015-03-19 04:33:01 +00002015 return NULL;
Dave Airlie38651672010-03-30 05:34:13 +00002016
2017 /* attempt to find a matching mode in the list of modes
2018 * we have gotten so far, if not add a CVT mode that conforms
2019 */
2020 if (cmdline_mode->rb || cmdline_mode->margins)
2021 goto create_mode;
2022
Takashi Iwaic683f422014-03-19 14:53:13 +01002023 prefer_non_interlace = !cmdline_mode->interlace;
Daniel Stonef3af5c72015-03-19 04:33:01 +00002024again:
Dave Airlie0b4c0f32010-03-30 05:34:15 +00002025 list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
Dave Airlie38651672010-03-30 05:34:13 +00002026 /* check width/height */
2027 if (mode->hdisplay != cmdline_mode->xres ||
2028 mode->vdisplay != cmdline_mode->yres)
2029 continue;
2030
2031 if (cmdline_mode->refresh_specified) {
2032 if (mode->vrefresh != cmdline_mode->refresh)
2033 continue;
2034 }
2035
2036 if (cmdline_mode->interlace) {
2037 if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
2038 continue;
Takashi Iwaic683f422014-03-19 14:53:13 +01002039 } else if (prefer_non_interlace) {
2040 if (mode->flags & DRM_MODE_FLAG_INTERLACE)
2041 continue;
Dave Airlie38651672010-03-30 05:34:13 +00002042 }
2043 return mode;
2044 }
2045
Takashi Iwaic683f422014-03-19 14:53:13 +01002046 if (prefer_non_interlace) {
2047 prefer_non_interlace = false;
2048 goto again;
2049 }
2050
Dave Airlie38651672010-03-30 05:34:13 +00002051create_mode:
Chris Wilson1794d252011-04-17 07:43:32 +01002052 mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev,
2053 cmdline_mode);
Dave Airlie0b4c0f32010-03-30 05:34:15 +00002054 list_add(&mode->head, &fb_helper_conn->connector->modes);
Dave Airlie38651672010-03-30 05:34:13 +00002055 return mode;
2056}
Jesse Barnes2f1046f2014-02-12 12:26:24 -08002057EXPORT_SYMBOL(drm_pick_cmdline_mode);
Dave Airlie38651672010-03-30 05:34:13 +00002058
2059static bool drm_connector_enabled(struct drm_connector *connector, bool strict)
2060{
2061 bool enable;
2062
Dave Airlieb5f05382017-10-16 05:08:39 +01002063 if (connector->display_info.non_desktop)
2064 return false;
2065
Sachin Kamat96081cd2012-11-15 03:43:30 +00002066 if (strict)
Dave Airlie38651672010-03-30 05:34:13 +00002067 enable = connector->status == connector_status_connected;
Sachin Kamat96081cd2012-11-15 03:43:30 +00002068 else
Dave Airlie38651672010-03-30 05:34:13 +00002069 enable = connector->status != connector_status_disconnected;
Sachin Kamat96081cd2012-11-15 03:43:30 +00002070
Dave Airlie38651672010-03-30 05:34:13 +00002071 return enable;
2072}
2073
Dave Airlie0b4c0f32010-03-30 05:34:15 +00002074static void drm_enable_connectors(struct drm_fb_helper *fb_helper,
2075 bool *enabled)
Dave Airlie38651672010-03-30 05:34:13 +00002076{
2077 bool any_enabled = false;
2078 struct drm_connector *connector;
2079 int i = 0;
2080
Chris Wilson966a6a12016-11-29 12:02:15 +00002081 drm_fb_helper_for_each_connector(fb_helper, i) {
Dave Airlie0b4c0f32010-03-30 05:34:15 +00002082 connector = fb_helper->connector_info[i]->connector;
Dave Airlie38651672010-03-30 05:34:13 +00002083 enabled[i] = drm_connector_enabled(connector, true);
2084 DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id,
Dave Airlieb5f05382017-10-16 05:08:39 +01002085 connector->display_info.non_desktop ? "non desktop" : enabled[i] ? "yes" : "no");
2086
Dave Airlie38651672010-03-30 05:34:13 +00002087 any_enabled |= enabled[i];
Dave Airlie38651672010-03-30 05:34:13 +00002088 }
2089
2090 if (any_enabled)
2091 return;
2092
Chris Wilson966a6a12016-11-29 12:02:15 +00002093 drm_fb_helper_for_each_connector(fb_helper, i) {
Dave Airlie0b4c0f32010-03-30 05:34:15 +00002094 connector = fb_helper->connector_info[i]->connector;
Dave Airlie38651672010-03-30 05:34:13 +00002095 enabled[i] = drm_connector_enabled(connector, false);
Dave Airlie38651672010-03-30 05:34:13 +00002096 }
2097}
2098
Dave Airlie1d42bbc2010-05-07 05:02:30 +00002099static bool drm_target_cloned(struct drm_fb_helper *fb_helper,
2100 struct drm_display_mode **modes,
Dave Airlieb0ee9e72014-10-20 16:31:53 +10002101 struct drm_fb_offset *offsets,
Dave Airlie1d42bbc2010-05-07 05:02:30 +00002102 bool *enabled, int width, int height)
2103{
2104 int count, i, j;
2105 bool can_clone = false;
2106 struct drm_fb_helper_connector *fb_helper_conn;
2107 struct drm_display_mode *dmt_mode, *mode;
2108
2109 /* only contemplate cloning in the single crtc case */
2110 if (fb_helper->crtc_count > 1)
2111 return false;
2112
2113 count = 0;
Chris Wilson966a6a12016-11-29 12:02:15 +00002114 drm_fb_helper_for_each_connector(fb_helper, i) {
Dave Airlie1d42bbc2010-05-07 05:02:30 +00002115 if (enabled[i])
2116 count++;
2117 }
2118
2119 /* only contemplate cloning if more than one connector is enabled */
2120 if (count <= 1)
2121 return false;
2122
2123 /* check the command line or if nothing common pick 1024x768 */
2124 can_clone = true;
Chris Wilson966a6a12016-11-29 12:02:15 +00002125 drm_fb_helper_for_each_connector(fb_helper, i) {
Dave Airlie1d42bbc2010-05-07 05:02:30 +00002126 if (!enabled[i])
2127 continue;
2128 fb_helper_conn = fb_helper->connector_info[i];
Vincent Abrioua09759e2017-01-06 17:44:43 +01002129 modes[i] = drm_pick_cmdline_mode(fb_helper_conn);
Dave Airlie1d42bbc2010-05-07 05:02:30 +00002130 if (!modes[i]) {
2131 can_clone = false;
2132 break;
2133 }
2134 for (j = 0; j < i; j++) {
2135 if (!enabled[j])
2136 continue;
2137 if (!drm_mode_equal(modes[j], modes[i]))
2138 can_clone = false;
2139 }
2140 }
2141
2142 if (can_clone) {
2143 DRM_DEBUG_KMS("can clone using command line\n");
2144 return true;
2145 }
2146
2147 /* try and find a 1024x768 mode on each connector */
2148 can_clone = true;
Adam Jacksonf6e252b2012-04-13 16:33:31 -04002149 dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60, false);
Dave Airlie1d42bbc2010-05-07 05:02:30 +00002150
Chris Wilson966a6a12016-11-29 12:02:15 +00002151 drm_fb_helper_for_each_connector(fb_helper, i) {
Dave Airlie1d42bbc2010-05-07 05:02:30 +00002152 if (!enabled[i])
2153 continue;
2154
2155 fb_helper_conn = fb_helper->connector_info[i];
2156 list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
2157 if (drm_mode_equal(mode, dmt_mode))
2158 modes[i] = mode;
2159 }
2160 if (!modes[i])
2161 can_clone = false;
2162 }
2163
2164 if (can_clone) {
2165 DRM_DEBUG_KMS("can clone using 1024x768\n");
2166 return true;
2167 }
2168 DRM_INFO("kms: can't enable cloning when we probably wanted to.\n");
2169 return false;
2170}
2171
Dave Airlieb0ee9e72014-10-20 16:31:53 +10002172static int drm_get_tile_offsets(struct drm_fb_helper *fb_helper,
2173 struct drm_display_mode **modes,
2174 struct drm_fb_offset *offsets,
2175 int idx,
2176 int h_idx, int v_idx)
2177{
2178 struct drm_fb_helper_connector *fb_helper_conn;
2179 int i;
2180 int hoffset = 0, voffset = 0;
2181
Chris Wilson966a6a12016-11-29 12:02:15 +00002182 drm_fb_helper_for_each_connector(fb_helper, i) {
Dave Airlieb0ee9e72014-10-20 16:31:53 +10002183 fb_helper_conn = fb_helper->connector_info[i];
2184 if (!fb_helper_conn->connector->has_tile)
2185 continue;
2186
2187 if (!modes[i] && (h_idx || v_idx)) {
2188 DRM_DEBUG_KMS("no modes for connector tiled %d %d\n", i,
2189 fb_helper_conn->connector->base.id);
2190 continue;
2191 }
2192 if (fb_helper_conn->connector->tile_h_loc < h_idx)
2193 hoffset += modes[i]->hdisplay;
2194
2195 if (fb_helper_conn->connector->tile_v_loc < v_idx)
2196 voffset += modes[i]->vdisplay;
2197 }
2198 offsets[idx].x = hoffset;
2199 offsets[idx].y = voffset;
2200 DRM_DEBUG_KMS("returned %d %d for %d %d\n", hoffset, voffset, h_idx, v_idx);
2201 return 0;
2202}
2203
Dave Airlie0b4c0f32010-03-30 05:34:15 +00002204static bool drm_target_preferred(struct drm_fb_helper *fb_helper,
Dave Airlie38651672010-03-30 05:34:13 +00002205 struct drm_display_mode **modes,
Dave Airlieb0ee9e72014-10-20 16:31:53 +10002206 struct drm_fb_offset *offsets,
Dave Airlie38651672010-03-30 05:34:13 +00002207 bool *enabled, int width, int height)
2208{
Dave Airlie0b4c0f32010-03-30 05:34:15 +00002209 struct drm_fb_helper_connector *fb_helper_conn;
Chris Wilsonc96521e2016-11-27 17:09:10 +00002210 const u64 mask = BIT_ULL(fb_helper->connector_count) - 1;
2211 u64 conn_configured = 0;
Dave Airlieb0ee9e72014-10-20 16:31:53 +10002212 int tile_pass = 0;
Chris Wilsonc96521e2016-11-27 17:09:10 +00002213 int i;
2214
Dave Airlieb0ee9e72014-10-20 16:31:53 +10002215retry:
Chris Wilson966a6a12016-11-29 12:02:15 +00002216 drm_fb_helper_for_each_connector(fb_helper, i) {
Dave Airlie0b4c0f32010-03-30 05:34:15 +00002217 fb_helper_conn = fb_helper->connector_info[i];
Dave Airlie38651672010-03-30 05:34:13 +00002218
Chris Wilsonc96521e2016-11-27 17:09:10 +00002219 if (conn_configured & BIT_ULL(i))
Dave Airlie38651672010-03-30 05:34:13 +00002220 continue;
Dave Airlie38651672010-03-30 05:34:13 +00002221
Dave Airlieb0ee9e72014-10-20 16:31:53 +10002222 if (enabled[i] == false) {
Chris Wilsonc96521e2016-11-27 17:09:10 +00002223 conn_configured |= BIT_ULL(i);
Dave Airlieb0ee9e72014-10-20 16:31:53 +10002224 continue;
2225 }
2226
2227 /* first pass over all the untiled connectors */
2228 if (tile_pass == 0 && fb_helper_conn->connector->has_tile)
2229 continue;
2230
2231 if (tile_pass == 1) {
2232 if (fb_helper_conn->connector->tile_h_loc != 0 ||
2233 fb_helper_conn->connector->tile_v_loc != 0)
2234 continue;
2235
2236 } else {
Thierry Reding4b4f99f2017-03-29 16:43:51 +02002237 if (fb_helper_conn->connector->tile_h_loc != tile_pass - 1 &&
Dave Airlieb0ee9e72014-10-20 16:31:53 +10002238 fb_helper_conn->connector->tile_v_loc != tile_pass - 1)
2239 /* if this tile_pass doesn't cover any of the tiles - keep going */
2240 continue;
2241
Thierry Reding4b4f99f2017-03-29 16:43:51 +02002242 /*
2243 * find the tile offsets for this pass - need to find
2244 * all tiles left and above
2245 */
Dave Airlieb0ee9e72014-10-20 16:31:53 +10002246 drm_get_tile_offsets(fb_helper, modes, offsets,
2247 i, fb_helper_conn->connector->tile_h_loc, fb_helper_conn->connector->tile_v_loc);
2248 }
Dave Airlie38651672010-03-30 05:34:13 +00002249 DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
Dave Airlie0b4c0f32010-03-30 05:34:15 +00002250 fb_helper_conn->connector->base.id);
Dave Airlie38651672010-03-30 05:34:13 +00002251
2252 /* got for command line mode first */
Vincent Abrioua09759e2017-01-06 17:44:43 +01002253 modes[i] = drm_pick_cmdline_mode(fb_helper_conn);
Dave Airlie38651672010-03-30 05:34:13 +00002254 if (!modes[i]) {
Dave Airlieb0ee9e72014-10-20 16:31:53 +10002255 DRM_DEBUG_KMS("looking for preferred mode on connector %d %d\n",
2256 fb_helper_conn->connector->base.id, fb_helper_conn->connector->tile_group ? fb_helper_conn->connector->tile_group->id : 0);
Dave Airlie0b4c0f32010-03-30 05:34:15 +00002257 modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height);
Dave Airlie38651672010-03-30 05:34:13 +00002258 }
2259 /* No preferred modes, pick one off the list */
Dave Airlie0b4c0f32010-03-30 05:34:15 +00002260 if (!modes[i] && !list_empty(&fb_helper_conn->connector->modes)) {
2261 list_for_each_entry(modes[i], &fb_helper_conn->connector->modes, head)
Dave Airlie38651672010-03-30 05:34:13 +00002262 break;
2263 }
2264 DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name :
2265 "none");
Chris Wilsonc96521e2016-11-27 17:09:10 +00002266 conn_configured |= BIT_ULL(i);
Dave Airlieb0ee9e72014-10-20 16:31:53 +10002267 }
2268
2269 if ((conn_configured & mask) != mask) {
2270 tile_pass++;
2271 goto retry;
Dave Airlie38651672010-03-30 05:34:13 +00002272 }
2273 return true;
2274}
2275
Dave Airlie8be48d92010-03-30 05:34:14 +00002276static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
2277 struct drm_fb_helper_crtc **best_crtcs,
Dave Airlie38651672010-03-30 05:34:13 +00002278 struct drm_display_mode **modes,
2279 int n, int width, int height)
2280{
2281 int c, o;
2282 struct drm_connector *connector;
Jani Nikulabe26a662015-03-11 11:51:06 +02002283 const struct drm_connector_helper_funcs *connector_funcs;
Dave Airlie38651672010-03-30 05:34:13 +00002284 struct drm_encoder *encoder;
Dave Airlie38651672010-03-30 05:34:13 +00002285 int my_score, best_score, score;
Dave Airlie8be48d92010-03-30 05:34:14 +00002286 struct drm_fb_helper_crtc **crtcs, *crtc;
Dave Airlie0b4c0f32010-03-30 05:34:15 +00002287 struct drm_fb_helper_connector *fb_helper_conn;
Dave Airlie38651672010-03-30 05:34:13 +00002288
Dave Airlie0b4c0f32010-03-30 05:34:15 +00002289 if (n == fb_helper->connector_count)
Dave Airlie38651672010-03-30 05:34:13 +00002290 return 0;
Dave Airlie0b4c0f32010-03-30 05:34:15 +00002291
2292 fb_helper_conn = fb_helper->connector_info[n];
2293 connector = fb_helper_conn->connector;
Dave Airlie38651672010-03-30 05:34:13 +00002294
2295 best_crtcs[n] = NULL;
Dave Airlie8be48d92010-03-30 05:34:14 +00002296 best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height);
Dave Airlie38651672010-03-30 05:34:13 +00002297 if (modes[n] == NULL)
2298 return best_score;
2299
Harsha Sharma4b947b1c2017-10-13 13:07:47 +05302300 crtcs = kcalloc(fb_helper->connector_count,
Dave Airlie8be48d92010-03-30 05:34:14 +00002301 sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
Dave Airlie38651672010-03-30 05:34:13 +00002302 if (!crtcs)
2303 return best_score;
2304
2305 my_score = 1;
2306 if (connector->status == connector_status_connected)
2307 my_score++;
Dave Airlie0b4c0f32010-03-30 05:34:15 +00002308 if (drm_has_cmdline_mode(fb_helper_conn))
Dave Airlie38651672010-03-30 05:34:13 +00002309 my_score++;
Dave Airlie0b4c0f32010-03-30 05:34:15 +00002310 if (drm_has_preferred_mode(fb_helper_conn, width, height))
Dave Airlie38651672010-03-30 05:34:13 +00002311 my_score++;
2312
2313 connector_funcs = connector->helper_private;
Boris Brezillonc61b93fe2016-06-07 13:47:56 +02002314
2315 /*
2316 * If the DRM device implements atomic hooks and ->best_encoder() is
2317 * NULL we fallback to the default drm_atomic_helper_best_encoder()
2318 * helper.
2319 */
Dhinakaran Pandiyana743d752016-12-22 00:50:42 -08002320 if (drm_drv_uses_atomic_modeset(fb_helper->dev) &&
Boris Brezillonc61b93fe2016-06-07 13:47:56 +02002321 !connector_funcs->best_encoder)
2322 encoder = drm_atomic_helper_best_encoder(connector);
2323 else
2324 encoder = connector_funcs->best_encoder(connector);
2325
Dave Airlie38651672010-03-30 05:34:13 +00002326 if (!encoder)
2327 goto out;
2328
Thierry Reding4b4f99f2017-03-29 16:43:51 +02002329 /*
2330 * select a crtc for this connector and then attempt to configure
2331 * remaining connectors
2332 */
Dave Airlie8be48d92010-03-30 05:34:14 +00002333 for (c = 0; c < fb_helper->crtc_count; c++) {
2334 crtc = &fb_helper->crtc_info[c];
Dave Airlie38651672010-03-30 05:34:13 +00002335
Sachin Kamat96081cd2012-11-15 03:43:30 +00002336 if ((encoder->possible_crtcs & (1 << c)) == 0)
Dave Airlie38651672010-03-30 05:34:13 +00002337 continue;
Dave Airlie38651672010-03-30 05:34:13 +00002338
2339 for (o = 0; o < n; o++)
2340 if (best_crtcs[o] == crtc)
2341 break;
2342
2343 if (o < n) {
Dave Airlie1d42bbc2010-05-07 05:02:30 +00002344 /* ignore cloning unless only a single crtc */
2345 if (fb_helper->crtc_count > 1)
2346 continue;
2347
2348 if (!drm_mode_equal(modes[o], modes[n]))
2349 continue;
Dave Airlie38651672010-03-30 05:34:13 +00002350 }
2351
2352 crtcs[n] = crtc;
Dave Airlie8be48d92010-03-30 05:34:14 +00002353 memcpy(crtcs, best_crtcs, n * sizeof(struct drm_fb_helper_crtc *));
2354 score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1,
Dave Airlie38651672010-03-30 05:34:13 +00002355 width, height);
2356 if (score > best_score) {
Dave Airlie38651672010-03-30 05:34:13 +00002357 best_score = score;
2358 memcpy(best_crtcs, crtcs,
Lyude255f0e72016-05-12 10:56:59 -04002359 fb_helper->connector_count *
Dave Airlie8be48d92010-03-30 05:34:14 +00002360 sizeof(struct drm_fb_helper_crtc *));
Dave Airlie38651672010-03-30 05:34:13 +00002361 }
Dave Airlie38651672010-03-30 05:34:13 +00002362 }
2363out:
2364 kfree(crtcs);
2365 return best_score;
2366}
2367
Hans de Goede8f0cb412017-11-25 20:35:50 +01002368/*
2369 * This function checks if rotation is necessary because of panel orientation
2370 * and if it is, if it is supported.
2371 * If rotation is necessary and supported, its gets set in fb_crtc.rotation.
2372 * If rotation is necessary but not supported, a DRM_MODE_ROTATE_* flag gets
2373 * or-ed into fb_helper->sw_rotations. In drm_setup_crtcs_fb() we check if only
2374 * one bit is set and then we set fb_info.fbcon_rotate_hint to make fbcon do
2375 * the unsupported rotation.
2376 */
2377static void drm_setup_crtc_rotation(struct drm_fb_helper *fb_helper,
2378 struct drm_fb_helper_crtc *fb_crtc,
2379 struct drm_connector *connector)
2380{
2381 struct drm_plane *plane = fb_crtc->mode_set.crtc->primary;
2382 uint64_t valid_mask = 0;
2383 int i, rotation;
2384
2385 fb_crtc->rotation = DRM_MODE_ROTATE_0;
2386
2387 switch (connector->display_info.panel_orientation) {
2388 case DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP:
2389 rotation = DRM_MODE_ROTATE_180;
2390 break;
2391 case DRM_MODE_PANEL_ORIENTATION_LEFT_UP:
2392 rotation = DRM_MODE_ROTATE_90;
2393 break;
2394 case DRM_MODE_PANEL_ORIENTATION_RIGHT_UP:
2395 rotation = DRM_MODE_ROTATE_270;
2396 break;
2397 default:
2398 rotation = DRM_MODE_ROTATE_0;
2399 }
2400
2401 /*
2402 * TODO: support 90 / 270 degree hardware rotation,
2403 * depending on the hardware this may require the framebuffer
2404 * to be in a specific tiling format.
2405 */
2406 if (rotation != DRM_MODE_ROTATE_180 || !plane->rotation_property) {
2407 fb_helper->sw_rotations |= rotation;
2408 return;
2409 }
2410
2411 for (i = 0; i < plane->rotation_property->num_values; i++)
2412 valid_mask |= (1ULL << plane->rotation_property->values[i]);
2413
2414 if (!(rotation & valid_mask)) {
2415 fb_helper->sw_rotations |= rotation;
2416 return;
2417 }
2418
2419 fb_crtc->rotation = rotation;
2420 /* Rotating in hardware, fbcon should not rotate */
2421 fb_helper->sw_rotations |= DRM_MODE_ROTATE_0;
2422}
2423
Chris Wilson64e94402016-11-29 12:02:16 +00002424static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,
2425 u32 width, u32 height)
Dave Airlie38651672010-03-30 05:34:13 +00002426{
Dave Airlie8be48d92010-03-30 05:34:14 +00002427 struct drm_device *dev = fb_helper->dev;
2428 struct drm_fb_helper_crtc **crtcs;
Dave Airlie38651672010-03-30 05:34:13 +00002429 struct drm_display_mode **modes;
Dave Airlieb0ee9e72014-10-20 16:31:53 +10002430 struct drm_fb_offset *offsets;
Dave Airlie38651672010-03-30 05:34:13 +00002431 bool *enabled;
Jesse Barnes11e17a02013-02-19 13:31:39 -08002432 int i;
Dave Airlie38651672010-03-30 05:34:13 +00002433
2434 DRM_DEBUG_KMS("\n");
Chris Wilson966a6a12016-11-29 12:02:15 +00002435 /* prevent concurrent modification of connector_count by hotplug */
Thierry Redinge9827d82017-07-04 17:18:23 +02002436 lockdep_assert_held(&fb_helper->lock);
Chris Wilson966a6a12016-11-29 12:02:15 +00002437
Maarten Lankhorst383b2e52016-02-15 13:45:15 +01002438 crtcs = kcalloc(fb_helper->connector_count,
Dave Airlie8be48d92010-03-30 05:34:14 +00002439 sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
Maarten Lankhorst383b2e52016-02-15 13:45:15 +01002440 modes = kcalloc(fb_helper->connector_count,
Dave Airlie38651672010-03-30 05:34:13 +00002441 sizeof(struct drm_display_mode *), GFP_KERNEL);
Maarten Lankhorst383b2e52016-02-15 13:45:15 +01002442 offsets = kcalloc(fb_helper->connector_count,
Dave Airlieb0ee9e72014-10-20 16:31:53 +10002443 sizeof(struct drm_fb_offset), GFP_KERNEL);
Maarten Lankhorst383b2e52016-02-15 13:45:15 +01002444 enabled = kcalloc(fb_helper->connector_count,
Dave Airlie38651672010-03-30 05:34:13 +00002445 sizeof(bool), GFP_KERNEL);
Dave Airlieb0ee9e72014-10-20 16:31:53 +10002446 if (!crtcs || !modes || !enabled || !offsets) {
Sachin Kamat8c5eaca2012-11-19 09:44:58 +00002447 DRM_ERROR("Memory allocation failed\n");
2448 goto out;
2449 }
2450
Daniel Vettere13a0582017-07-05 06:56:29 +02002451 mutex_lock(&fb_helper->dev->mode_config.mutex);
2452 if (drm_fb_helper_probe_connector_modes(fb_helper, width, height) == 0)
2453 DRM_DEBUG_KMS("No connectors reported connected with modes\n");
Dave Airlie0b4c0f32010-03-30 05:34:15 +00002454 drm_enable_connectors(fb_helper, enabled);
Dave Airlie38651672010-03-30 05:34:13 +00002455
Jesse Barnes11e17a02013-02-19 13:31:39 -08002456 if (!(fb_helper->funcs->initial_config &&
2457 fb_helper->funcs->initial_config(fb_helper, crtcs, modes,
Dave Airlieb0ee9e72014-10-20 16:31:53 +10002458 offsets,
Jesse Barnes11e17a02013-02-19 13:31:39 -08002459 enabled, width, height))) {
Maarten Lankhorst383b2e52016-02-15 13:45:15 +01002460 memset(modes, 0, fb_helper->connector_count*sizeof(modes[0]));
2461 memset(crtcs, 0, fb_helper->connector_count*sizeof(crtcs[0]));
2462 memset(offsets, 0, fb_helper->connector_count*sizeof(offsets[0]));
Jesse Barnes11e17a02013-02-19 13:31:39 -08002463
Dave Airlieb0ee9e72014-10-20 16:31:53 +10002464 if (!drm_target_cloned(fb_helper, modes, offsets,
2465 enabled, width, height) &&
2466 !drm_target_preferred(fb_helper, modes, offsets,
2467 enabled, width, height))
Dave Airlie1d42bbc2010-05-07 05:02:30 +00002468 DRM_ERROR("Unable to find initial modes\n");
Jesse Barnes11e17a02013-02-19 13:31:39 -08002469
2470 DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n",
2471 width, height);
2472
2473 drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height);
Dave Airlie1d42bbc2010-05-07 05:02:30 +00002474 }
Daniel Vettere13a0582017-07-05 06:56:29 +02002475 mutex_unlock(&fb_helper->dev->mode_config.mutex);
Dave Airlie38651672010-03-30 05:34:13 +00002476
Dave Airlie8be48d92010-03-30 05:34:14 +00002477 /* need to set the modesets up here for use later */
2478 /* fill out the connector<->crtc mappings into the modesets */
Ville Syrjäläa2889602016-10-26 17:41:18 +03002479 for (i = 0; i < fb_helper->crtc_count; i++)
2480 drm_fb_helper_modeset_release(fb_helper,
2481 &fb_helper->crtc_info[i].mode_set);
Dave Airlie38651672010-03-30 05:34:13 +00002482
Hans de Goede8f0cb412017-11-25 20:35:50 +01002483 fb_helper->sw_rotations = 0;
Chris Wilson966a6a12016-11-29 12:02:15 +00002484 drm_fb_helper_for_each_connector(fb_helper, i) {
Dave Airlie38651672010-03-30 05:34:13 +00002485 struct drm_display_mode *mode = modes[i];
Dave Airlie8be48d92010-03-30 05:34:14 +00002486 struct drm_fb_helper_crtc *fb_crtc = crtcs[i];
Dave Airlieb0ee9e72014-10-20 16:31:53 +10002487 struct drm_fb_offset *offset = &offsets[i];
Dave Airlie38651672010-03-30 05:34:13 +00002488
Dave Airlie8be48d92010-03-30 05:34:14 +00002489 if (mode && fb_crtc) {
David Lechner27a061f2017-08-02 13:00:13 -05002490 struct drm_mode_set *modeset = &fb_crtc->mode_set;
Ville Syrjäläa2889602016-10-26 17:41:18 +03002491 struct drm_connector *connector =
2492 fb_helper->connector_info[i]->connector;
2493
Dave Airlieb0ee9e72014-10-20 16:31:53 +10002494 DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n",
2495 mode->name, fb_crtc->mode_set.crtc->base.id, offset->x, offset->y);
Ville Syrjäläa2889602016-10-26 17:41:18 +03002496
Dave Airlie8be48d92010-03-30 05:34:14 +00002497 fb_crtc->desired_mode = mode;
Dave Airlieb0ee9e72014-10-20 16:31:53 +10002498 fb_crtc->x = offset->x;
2499 fb_crtc->y = offset->y;
Dave Airlie8be48d92010-03-30 05:34:14 +00002500 modeset->mode = drm_mode_duplicate(dev,
2501 fb_crtc->desired_mode);
Thierry Redingad093602017-02-28 15:46:39 +01002502 drm_connector_get(connector);
Hans de Goede8f0cb412017-11-25 20:35:50 +01002503 drm_setup_crtc_rotation(fb_helper, fb_crtc, connector);
Ville Syrjäläa2889602016-10-26 17:41:18 +03002504 modeset->connectors[modeset->num_connectors++] = connector;
Dave Airlieb0ee9e72014-10-20 16:31:53 +10002505 modeset->x = offset->x;
2506 modeset->y = offset->y;
Dave Airlie38651672010-03-30 05:34:13 +00002507 }
Dave Airlie38651672010-03-30 05:34:13 +00002508 }
Sachin Kamat8c5eaca2012-11-19 09:44:58 +00002509out:
Dave Airlie38651672010-03-30 05:34:13 +00002510 kfree(crtcs);
2511 kfree(modes);
Dave Airlieb0ee9e72014-10-20 16:31:53 +10002512 kfree(offsets);
Dave Airlie38651672010-03-30 05:34:13 +00002513 kfree(enabled);
2514}
2515
David Lechnerf461bd22017-08-03 11:19:08 -05002516/*
2517 * This is a continuation of drm_setup_crtcs() that sets up anything related
2518 * to the framebuffer. During initialization, drm_setup_crtcs() is called before
2519 * the framebuffer has been allocated (fb_helper->fb and fb_helper->fbdev).
2520 * So, any setup that touches those fields needs to be done here instead of in
2521 * drm_setup_crtcs().
2522 */
2523static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper)
2524{
David Lechner991a3992017-08-04 11:25:24 -05002525 struct fb_info *info = fb_helper->fbdev;
David Lechnerf461bd22017-08-03 11:19:08 -05002526 int i;
2527
2528 for (i = 0; i < fb_helper->crtc_count; i++)
2529 if (fb_helper->crtc_info[i].mode_set.num_connectors)
2530 fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb;
David Lechner991a3992017-08-04 11:25:24 -05002531
2532 mutex_lock(&fb_helper->dev->mode_config.mutex);
2533 drm_fb_helper_for_each_connector(fb_helper, i) {
2534 struct drm_connector *connector =
2535 fb_helper->connector_info[i]->connector;
2536
2537 /* use first connected connector for the physical dimensions */
2538 if (connector->status == connector_status_connected) {
2539 info->var.width = connector->display_info.width_mm;
2540 info->var.height = connector->display_info.height_mm;
2541 break;
2542 }
2543 }
2544 mutex_unlock(&fb_helper->dev->mode_config.mutex);
Hans de Goede8f0cb412017-11-25 20:35:50 +01002545
2546 switch (fb_helper->sw_rotations) {
2547 case DRM_MODE_ROTATE_0:
2548 info->fbcon_rotate_hint = FB_ROTATE_UR;
2549 break;
2550 case DRM_MODE_ROTATE_90:
2551 info->fbcon_rotate_hint = FB_ROTATE_CCW;
2552 break;
2553 case DRM_MODE_ROTATE_180:
2554 info->fbcon_rotate_hint = FB_ROTATE_UD;
2555 break;
2556 case DRM_MODE_ROTATE_270:
2557 info->fbcon_rotate_hint = FB_ROTATE_CW;
2558 break;
2559 default:
2560 /*
2561 * Multiple bits are set / multiple rotations requested
2562 * fbcon cannot handle separate rotation settings per
2563 * output, so fallback to unrotated.
2564 */
2565 info->fbcon_rotate_hint = FB_ROTATE_UR;
2566 }
David Lechnerf461bd22017-08-03 11:19:08 -05002567}
2568
Daniel Vetterca91a272017-07-06 15:00:21 +02002569/* Note: Drops fb_helper->lock before returning. */
2570static int
2571__drm_fb_helper_initial_config_and_unlock(struct drm_fb_helper *fb_helper,
2572 int bpp_sel)
2573{
2574 struct drm_device *dev = fb_helper->dev;
2575 struct fb_info *info;
2576 unsigned int width, height;
2577 int ret;
2578
2579 width = dev->mode_config.max_width;
2580 height = dev->mode_config.max_height;
2581
2582 drm_setup_crtcs(fb_helper, width, height);
2583 ret = drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
2584 if (ret < 0) {
2585 if (ret == -EAGAIN) {
2586 fb_helper->preferred_bpp = bpp_sel;
2587 fb_helper->deferred_setup = true;
2588 ret = 0;
2589 }
2590 mutex_unlock(&fb_helper->lock);
2591
2592 return ret;
2593 }
David Lechnerf461bd22017-08-03 11:19:08 -05002594 drm_setup_crtcs_fb(fb_helper);
Daniel Vetterca91a272017-07-06 15:00:21 +02002595
2596 fb_helper->deferred_setup = false;
2597
2598 info = fb_helper->fbdev;
2599 info->var.pixclock = 0;
2600
2601 /* Need to drop locks to avoid recursive deadlock in
2602 * register_framebuffer. This is ok because the only thing left to do is
2603 * register the fbdev emulation instance in kernel_fb_helper_list. */
2604 mutex_unlock(&fb_helper->lock);
2605
2606 ret = register_framebuffer(info);
2607 if (ret < 0)
2608 return ret;
2609
2610 dev_info(dev->dev, "fb%d: %s frame buffer device\n",
2611 info->node, info->fix.id);
2612
2613 mutex_lock(&kernel_fb_helper_lock);
2614 if (list_empty(&kernel_fb_helper_list))
2615 register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
2616
2617 list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
2618 mutex_unlock(&kernel_fb_helper_lock);
2619
2620 return 0;
2621}
2622
Dave Airlie38651672010-03-30 05:34:13 +00002623/**
Daniel Vetter207fd322013-01-20 22:13:14 +01002624 * drm_fb_helper_initial_config - setup a sane initial connector configuration
Daniel Vetterd0ddc0332012-11-01 14:45:17 +01002625 * @fb_helper: fb_helper device struct
2626 * @bpp_sel: bpp value to use for the framebuffer configuration
Dave Airlie38651672010-03-30 05:34:13 +00002627 *
Daniel Vetterd0ddc0332012-11-01 14:45:17 +01002628 * Scans the CRTCs and connectors and tries to put together an initial setup.
Dave Airlie38651672010-03-30 05:34:13 +00002629 * At the moment, this is a cloned configuration across all heads with
2630 * a new framebuffer object as the backing store.
2631 *
Daniel Vetter207fd322013-01-20 22:13:14 +01002632 * Note that this also registers the fbdev and so allows userspace to call into
2633 * the driver through the fbdev interfaces.
2634 *
Daniel Vetter6806cdf2017-01-25 07:26:43 +01002635 * This function will call down into the &drm_fb_helper_funcs.fb_probe callback
2636 * to let the driver allocate and initialize the fbdev info structure and the
2637 * drm framebuffer used to back the fbdev. drm_fb_helper_fill_var() and
Daniel Vetter207fd322013-01-20 22:13:14 +01002638 * drm_fb_helper_fill_fix() are provided as helpers to setup simple default
2639 * values for the fbdev info structure.
2640 *
Daniel Vetter40f8cf42016-01-22 08:53:45 +01002641 * HANG DEBUGGING:
2642 *
2643 * When you have fbcon support built-in or already loaded, this function will do
2644 * a full modeset to setup the fbdev console. Due to locking misdesign in the
2645 * VT/fbdev subsystem that entire modeset sequence has to be done while holding
2646 * console_lock. Until console_unlock is called no dmesg lines will be sent out
2647 * to consoles, not even serial console. This means when your driver crashes,
2648 * you will see absolutely nothing else but a system stuck in this function,
2649 * with no further output. Any kind of printk() you place within your own driver
2650 * or in the drm core modeset code will also never show up.
2651 *
2652 * Standard debug practice is to run the fbcon setup without taking the
2653 * console_lock as a hack, to be able to see backtraces and crashes on the
2654 * serial line. This can be done by setting the fb.lockless_register_fb=1 kernel
2655 * cmdline option.
2656 *
2657 * The other option is to just disable fbdev emulation since very likely the
Lyudeaf509d32016-05-04 11:28:53 -04002658 * first modeset from userspace will crash in the same way, and is even easier
2659 * to debug. This can be done by setting the drm_kms_helper.fbdev_emulation=0
Daniel Vetter40f8cf42016-01-22 08:53:45 +01002660 * kernel cmdline option.
2661 *
Dave Airlie38651672010-03-30 05:34:13 +00002662 * RETURNS:
2663 * Zero if everything went ok, nonzero otherwise.
2664 */
Thierry Reding01934c22014-12-19 11:21:32 +01002665int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
Dave Airlie38651672010-03-30 05:34:13 +00002666{
Chris Wilson966a6a12016-11-29 12:02:15 +00002667 int ret;
Dave Airlie38651672010-03-30 05:34:13 +00002668
Daniel Vetterf64c5572015-08-25 15:45:13 +02002669 if (!drm_fbdev_emulation)
2670 return 0;
2671
Thierry Redinge9827d82017-07-04 17:18:23 +02002672 mutex_lock(&fb_helper->lock);
Daniel Vetterca91a272017-07-06 15:00:21 +02002673 ret = __drm_fb_helper_initial_config_and_unlock(fb_helper, bpp_sel);
Dave Airlie38651672010-03-30 05:34:13 +00002674
Daniel Vetterca91a272017-07-06 15:00:21 +02002675 return ret;
Dave Airlie38651672010-03-30 05:34:13 +00002676}
Dave Airlie8be48d92010-03-30 05:34:14 +00002677EXPORT_SYMBOL(drm_fb_helper_initial_config);
Dave Airlie38651672010-03-30 05:34:13 +00002678
Chris Wilson73943712011-04-22 11:03:57 +01002679/**
2680 * drm_fb_helper_hotplug_event - respond to a hotplug notification by
Daniel Vetterd0ddc0332012-11-01 14:45:17 +01002681 * probing all the outputs attached to the fb
Noralf Trønnesc7779902017-10-30 16:39:37 +01002682 * @fb_helper: driver-allocated fbdev helper, can be NULL
Chris Wilson73943712011-04-22 11:03:57 +01002683 *
Chris Wilson73943712011-04-22 11:03:57 +01002684 * Scan the connectors attached to the fb_helper and try to put together a
Daniel Vetter62cacc72016-08-12 22:48:37 +02002685 * setup after notification of a change in output configuration.
Chris Wilson73943712011-04-22 11:03:57 +01002686 *
Daniel Vetter207fd322013-01-20 22:13:14 +01002687 * Called at runtime, takes the mode config locks to be able to check/change the
2688 * modeset configuration. Must be run from process context (which usually means
2689 * either the output polling work or a work item launched from the driver's
2690 * hotplug interrupt).
2691 *
Daniel Vetter50c3dc92014-06-27 17:19:22 +02002692 * Note that drivers may call this even before calling
Lyudeaf509d32016-05-04 11:28:53 -04002693 * drm_fb_helper_initial_config but only after drm_fb_helper_init. This allows
Daniel Vetter50c3dc92014-06-27 17:19:22 +02002694 * for a race-free fbcon setup and will make sure that the fbdev emulation will
2695 * not miss any hotplug events.
Daniel Vetter207fd322013-01-20 22:13:14 +01002696 *
Chris Wilson73943712011-04-22 11:03:57 +01002697 * RETURNS:
2698 * 0 on success and a non-zero error code otherwise.
2699 */
2700int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
Dave Airlie38651672010-03-30 05:34:13 +00002701{
Thierry Redinge9827d82017-07-04 17:18:23 +02002702 int err = 0;
Dave Airlie4abe3522010-03-30 05:34:18 +00002703
Noralf Trønnesc7779902017-10-30 16:39:37 +01002704 if (!drm_fbdev_emulation || !fb_helper)
Daniel Vetterf64c5572015-08-25 15:45:13 +02002705 return 0;
2706
Thierry Redinge9827d82017-07-04 17:18:23 +02002707 mutex_lock(&fb_helper->lock);
Daniel Vetterca91a272017-07-06 15:00:21 +02002708 if (fb_helper->deferred_setup) {
2709 err = __drm_fb_helper_initial_config_and_unlock(fb_helper,
2710 fb_helper->preferred_bpp);
2711 return err;
2712 }
2713
Daniel Vetter50c3dc92014-06-27 17:19:22 +02002714 if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) {
Dave Airlieeb1f8e42010-05-07 06:42:51 +00002715 fb_helper->delayed_hotplug = true;
Daniel Vetterbdac4a02017-07-04 17:18:24 +02002716 mutex_unlock(&fb_helper->lock);
2717 return err;
Dave Airlieeb1f8e42010-05-07 06:42:51 +00002718 }
Thierry Redinge9827d82017-07-04 17:18:23 +02002719
Dave Airlie38651672010-03-30 05:34:13 +00002720 DRM_DEBUG_KMS("\n");
2721
Chris Wilson64e94402016-11-29 12:02:16 +00002722 drm_setup_crtcs(fb_helper, fb_helper->fb->width, fb_helper->fb->height);
David Lechnerf461bd22017-08-03 11:19:08 -05002723 drm_setup_crtcs_fb(fb_helper);
Thierry Redinge9827d82017-07-04 17:18:23 +02002724 mutex_unlock(&fb_helper->lock);
Daniel Vetter89ced122013-04-11 14:26:55 +00002725
Daniel Vetter2180c3c2013-01-21 23:12:36 +01002726 drm_fb_helper_set_par(fb_helper->fbdev);
2727
2728 return 0;
Dave Airlie38651672010-03-30 05:34:13 +00002729}
Dave Airlieeb1f8e42010-05-07 06:42:51 +00002730EXPORT_SYMBOL(drm_fb_helper_hotplug_event);
Dave Airlie5c4426a2010-03-30 05:34:17 +00002731
Noralf Trønnes304a4f62017-10-30 16:39:39 +01002732/**
2733 * drm_fb_helper_lastclose - DRM driver lastclose helper for fbdev emulation
2734 * @dev: DRM device
2735 *
2736 * This function can be used as the &drm_driver->lastclose callback for drivers
2737 * that only need to call drm_fb_helper_restore_fbdev_mode_unlocked().
2738 */
2739void drm_fb_helper_lastclose(struct drm_device *dev)
2740{
2741 drm_fb_helper_restore_fbdev_mode_unlocked(dev->fb_helper);
2742}
2743EXPORT_SYMBOL(drm_fb_helper_lastclose);
2744
2745/**
2746 * drm_fb_helper_output_poll_changed - DRM mode config \.output_poll_changed
2747 * helper for fbdev emulation
2748 * @dev: DRM device
2749 *
2750 * This function can be used as the
2751 * &drm_mode_config_funcs.output_poll_changed callback for drivers that only
2752 * need to call drm_fb_helper_hotplug_event().
2753 */
2754void drm_fb_helper_output_poll_changed(struct drm_device *dev)
2755{
2756 drm_fb_helper_hotplug_event(dev->fb_helper);
2757}
2758EXPORT_SYMBOL(drm_fb_helper_output_poll_changed);
2759
David Rientjes6a108a12011-01-20 14:44:16 -08002760/* The Kconfig DRM_KMS_HELPER selects FRAMEBUFFER_CONSOLE (if !EXPERT)
David Fries3ce05162010-12-12 12:39:22 -06002761 * but the module doesn't depend on any fb console symbols. At least
2762 * attempt to load fbcon to avoid leaving the system without a usable console.
2763 */
Rafael Antognolli70412cf2016-01-21 15:10:18 -08002764int __init drm_fb_helper_modinit(void)
David Fries3ce05162010-12-12 12:39:22 -06002765{
Rafael Antognolli70412cf2016-01-21 15:10:18 -08002766#if defined(CONFIG_FRAMEBUFFER_CONSOLE_MODULE) && !defined(CONFIG_EXPERT)
Kees Cook06324662017-05-08 15:59:05 -07002767 const char name[] = "fbcon";
David Fries3ce05162010-12-12 12:39:22 -06002768 struct module *fbcon;
2769
2770 mutex_lock(&module_mutex);
2771 fbcon = find_module(name);
2772 mutex_unlock(&module_mutex);
2773
2774 if (!fbcon)
2775 request_module_nowait(name);
Rafael Antognolli70412cf2016-01-21 15:10:18 -08002776#endif
David Fries3ce05162010-12-12 12:39:22 -06002777 return 0;
2778}
Rafael Antognolli70412cf2016-01-21 15:10:18 -08002779EXPORT_SYMBOL(drm_fb_helper_modinit);