diff options
author | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
---|---|---|
committer | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
commit | c71c03bda1e86c9d5198c5d83f712e695c4f2a1e (patch) | |
tree | ecb166cb3e2b7e2adb3b5e292245fefd23381ac8 /drivers/gpu/drm/drm_fb_helper.c | |
parent | ea53c912f8a86a8567697115b6a0d8152beee5c8 (diff) | |
parent | 6a00f206debf8a5c8899055726ad127dbeeed098 (diff) |
Merge branch 'mpi-master' into wip-k-fmlpwip-k-fmlp
Conflicts:
litmus/sched_cedf.c
Diffstat (limited to 'drivers/gpu/drm/drm_fb_helper.c')
-rw-r--r-- | drivers/gpu/drm/drm_fb_helper.c | 321 |
1 files changed, 136 insertions, 185 deletions
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 6a5e403f9aa1..802b61ac3139 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c | |||
@@ -70,176 +70,76 @@ fail: | |||
70 | } | 70 | } |
71 | EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors); | 71 | EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors); |
72 | 72 | ||
73 | /** | 73 | static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper) |
74 | * drm_fb_helper_connector_parse_command_line - parse command line for connector | ||
75 | * @connector - connector to parse line for | ||
76 | * @mode_option - per connector mode option | ||
77 | * | ||
78 | * This parses the connector specific then generic command lines for | ||
79 | * modes and options to configure the connector. | ||
80 | * | ||
81 | * This uses the same parameters as the fb modedb.c, except for extra | ||
82 | * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd] | ||
83 | * | ||
84 | * enable/enable Digital/disable bit at the end | ||
85 | */ | ||
86 | static bool drm_fb_helper_connector_parse_command_line(struct drm_fb_helper_connector *fb_helper_conn, | ||
87 | const char *mode_option) | ||
88 | { | 74 | { |
89 | const char *name; | 75 | struct drm_fb_helper_connector *fb_helper_conn; |
90 | unsigned int namelen; | ||
91 | int res_specified = 0, bpp_specified = 0, refresh_specified = 0; | ||
92 | unsigned int xres = 0, yres = 0, bpp = 32, refresh = 0; | ||
93 | int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0; | ||
94 | int i; | 76 | int i; |
95 | enum drm_connector_force force = DRM_FORCE_UNSPECIFIED; | ||
96 | struct drm_fb_helper_cmdline_mode *cmdline_mode; | ||
97 | struct drm_connector *connector; | ||
98 | |||
99 | if (!fb_helper_conn) | ||
100 | return false; | ||
101 | connector = fb_helper_conn->connector; | ||
102 | 77 | ||
103 | cmdline_mode = &fb_helper_conn->cmdline_mode; | 78 | for (i = 0; i < fb_helper->connector_count; i++) { |
104 | if (!mode_option) | 79 | struct drm_cmdline_mode *mode; |
105 | mode_option = fb_mode_option; | 80 | struct drm_connector *connector; |
106 | 81 | char *option = NULL; | |
107 | if (!mode_option) { | ||
108 | cmdline_mode->specified = false; | ||
109 | return false; | ||
110 | } | ||
111 | 82 | ||
112 | name = mode_option; | 83 | fb_helper_conn = fb_helper->connector_info[i]; |
113 | namelen = strlen(name); | 84 | connector = fb_helper_conn->connector; |
114 | for (i = namelen-1; i >= 0; i--) { | 85 | mode = &fb_helper_conn->cmdline_mode; |
115 | switch (name[i]) { | ||
116 | case '@': | ||
117 | namelen = i; | ||
118 | if (!refresh_specified && !bpp_specified && | ||
119 | !yres_specified) { | ||
120 | refresh = simple_strtol(&name[i+1], NULL, 10); | ||
121 | refresh_specified = 1; | ||
122 | if (cvt || rb) | ||
123 | cvt = 0; | ||
124 | } else | ||
125 | goto done; | ||
126 | break; | ||
127 | case '-': | ||
128 | namelen = i; | ||
129 | if (!bpp_specified && !yres_specified) { | ||
130 | bpp = simple_strtol(&name[i+1], NULL, 10); | ||
131 | bpp_specified = 1; | ||
132 | if (cvt || rb) | ||
133 | cvt = 0; | ||
134 | } else | ||
135 | goto done; | ||
136 | break; | ||
137 | case 'x': | ||
138 | if (!yres_specified) { | ||
139 | yres = simple_strtol(&name[i+1], NULL, 10); | ||
140 | yres_specified = 1; | ||
141 | } else | ||
142 | goto done; | ||
143 | case '0' ... '9': | ||
144 | break; | ||
145 | case 'M': | ||
146 | if (!yres_specified) | ||
147 | cvt = 1; | ||
148 | break; | ||
149 | case 'R': | ||
150 | if (cvt) | ||
151 | rb = 1; | ||
152 | break; | ||
153 | case 'm': | ||
154 | if (!cvt) | ||
155 | margins = 1; | ||
156 | break; | ||
157 | case 'i': | ||
158 | if (!cvt) | ||
159 | interlace = 1; | ||
160 | break; | ||
161 | case 'e': | ||
162 | force = DRM_FORCE_ON; | ||
163 | break; | ||
164 | case 'D': | ||
165 | if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) && | ||
166 | (connector->connector_type != DRM_MODE_CONNECTOR_HDMIB)) | ||
167 | force = DRM_FORCE_ON; | ||
168 | else | ||
169 | force = DRM_FORCE_ON_DIGITAL; | ||
170 | break; | ||
171 | case 'd': | ||
172 | force = DRM_FORCE_OFF; | ||
173 | break; | ||
174 | default: | ||
175 | goto done; | ||
176 | } | ||
177 | } | ||
178 | if (i < 0 && yres_specified) { | ||
179 | xres = simple_strtol(name, NULL, 10); | ||
180 | res_specified = 1; | ||
181 | } | ||
182 | done: | ||
183 | |||
184 | DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n", | ||
185 | drm_get_connector_name(connector), xres, yres, | ||
186 | (refresh) ? refresh : 60, (rb) ? " reduced blanking" : | ||
187 | "", (margins) ? " with margins" : "", (interlace) ? | ||
188 | " interlaced" : ""); | ||
189 | |||
190 | if (force) { | ||
191 | const char *s; | ||
192 | switch (force) { | ||
193 | case DRM_FORCE_OFF: s = "OFF"; break; | ||
194 | case DRM_FORCE_ON_DIGITAL: s = "ON - dig"; break; | ||
195 | default: | ||
196 | case DRM_FORCE_ON: s = "ON"; break; | ||
197 | } | ||
198 | 86 | ||
199 | DRM_INFO("forcing %s connector %s\n", | 87 | /* do something on return - turn off connector maybe */ |
200 | drm_get_connector_name(connector), s); | 88 | if (fb_get_options(drm_get_connector_name(connector), &option)) |
201 | connector->force = force; | 89 | continue; |
202 | } | ||
203 | 90 | ||
204 | if (res_specified) { | 91 | if (drm_mode_parse_command_line_for_connector(option, |
205 | cmdline_mode->specified = true; | 92 | connector, |
206 | cmdline_mode->xres = xres; | 93 | mode)) { |
207 | cmdline_mode->yres = yres; | 94 | if (mode->force) { |
208 | } | 95 | const char *s; |
96 | switch (mode->force) { | ||
97 | case DRM_FORCE_OFF: s = "OFF"; break; | ||
98 | case DRM_FORCE_ON_DIGITAL: s = "ON - dig"; break; | ||
99 | default: | ||
100 | case DRM_FORCE_ON: s = "ON"; break; | ||
101 | } | ||
102 | |||
103 | DRM_INFO("forcing %s connector %s\n", | ||
104 | drm_get_connector_name(connector), s); | ||
105 | connector->force = mode->force; | ||
106 | } | ||
209 | 107 | ||
210 | if (refresh_specified) { | 108 | DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n", |
211 | cmdline_mode->refresh_specified = true; | 109 | drm_get_connector_name(connector), |
212 | cmdline_mode->refresh = refresh; | 110 | mode->xres, mode->yres, |
213 | } | 111 | mode->refresh_specified ? mode->refresh : 60, |
112 | mode->rb ? " reduced blanking" : "", | ||
113 | mode->margins ? " with margins" : "", | ||
114 | mode->interlace ? " interlaced" : ""); | ||
115 | } | ||
214 | 116 | ||
215 | if (bpp_specified) { | ||
216 | cmdline_mode->bpp_specified = true; | ||
217 | cmdline_mode->bpp = bpp; | ||
218 | } | 117 | } |
219 | cmdline_mode->rb = rb ? true : false; | 118 | return 0; |
220 | cmdline_mode->cvt = cvt ? true : false; | ||
221 | cmdline_mode->interlace = interlace ? true : false; | ||
222 | |||
223 | return true; | ||
224 | } | 119 | } |
225 | 120 | ||
226 | static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper) | 121 | static void drm_fb_helper_save_lut_atomic(struct drm_crtc *crtc, struct drm_fb_helper *helper) |
227 | { | 122 | { |
228 | struct drm_fb_helper_connector *fb_helper_conn; | 123 | uint16_t *r_base, *g_base, *b_base; |
229 | int i; | 124 | int i; |
230 | 125 | ||
231 | for (i = 0; i < fb_helper->connector_count; i++) { | 126 | r_base = crtc->gamma_store; |
232 | char *option = NULL; | 127 | g_base = r_base + crtc->gamma_size; |
128 | b_base = g_base + crtc->gamma_size; | ||
233 | 129 | ||
234 | fb_helper_conn = fb_helper->connector_info[i]; | 130 | for (i = 0; i < crtc->gamma_size; i++) |
131 | helper->funcs->gamma_get(crtc, &r_base[i], &g_base[i], &b_base[i], i); | ||
132 | } | ||
235 | 133 | ||
236 | /* do something on return - turn off connector maybe */ | 134 | static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc) |
237 | if (fb_get_options(drm_get_connector_name(fb_helper_conn->connector), &option)) | 135 | { |
238 | continue; | 136 | uint16_t *r_base, *g_base, *b_base; |
239 | 137 | ||
240 | drm_fb_helper_connector_parse_command_line(fb_helper_conn, option); | 138 | r_base = crtc->gamma_store; |
241 | } | 139 | g_base = r_base + crtc->gamma_size; |
242 | return 0; | 140 | b_base = g_base + crtc->gamma_size; |
141 | |||
142 | crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size); | ||
243 | } | 143 | } |
244 | 144 | ||
245 | int drm_fb_helper_debug_enter(struct fb_info *info) | 145 | int drm_fb_helper_debug_enter(struct fb_info *info) |
@@ -260,11 +160,12 @@ int drm_fb_helper_debug_enter(struct fb_info *info) | |||
260 | continue; | 160 | continue; |
261 | 161 | ||
262 | funcs = mode_set->crtc->helper_private; | 162 | funcs = mode_set->crtc->helper_private; |
163 | drm_fb_helper_save_lut_atomic(mode_set->crtc, helper); | ||
263 | funcs->mode_set_base_atomic(mode_set->crtc, | 164 | funcs->mode_set_base_atomic(mode_set->crtc, |
264 | mode_set->fb, | 165 | mode_set->fb, |
265 | mode_set->x, | 166 | mode_set->x, |
266 | mode_set->y); | 167 | mode_set->y, |
267 | 168 | ENTER_ATOMIC_MODE_SET); | |
268 | } | 169 | } |
269 | } | 170 | } |
270 | 171 | ||
@@ -308,17 +209,31 @@ int drm_fb_helper_debug_leave(struct fb_info *info) | |||
308 | continue; | 209 | continue; |
309 | } | 210 | } |
310 | 211 | ||
212 | drm_fb_helper_restore_lut_atomic(mode_set->crtc); | ||
311 | funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x, | 213 | funcs->mode_set_base_atomic(mode_set->crtc, fb, crtc->x, |
312 | crtc->y); | 214 | crtc->y, LEAVE_ATOMIC_MODE_SET); |
313 | } | 215 | } |
314 | 216 | ||
315 | return 0; | 217 | return 0; |
316 | } | 218 | } |
317 | EXPORT_SYMBOL(drm_fb_helper_debug_leave); | 219 | EXPORT_SYMBOL(drm_fb_helper_debug_leave); |
318 | 220 | ||
221 | bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper) | ||
222 | { | ||
223 | bool error = false; | ||
224 | int i, ret; | ||
225 | for (i = 0; i < fb_helper->crtc_count; i++) { | ||
226 | struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set; | ||
227 | ret = drm_crtc_helper_set_config(mode_set); | ||
228 | if (ret) | ||
229 | error = true; | ||
230 | } | ||
231 | return error; | ||
232 | } | ||
233 | EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode); | ||
234 | |||
319 | bool drm_fb_helper_force_kernel_mode(void) | 235 | bool drm_fb_helper_force_kernel_mode(void) |
320 | { | 236 | { |
321 | int i = 0; | ||
322 | bool ret, error = false; | 237 | bool ret, error = false; |
323 | struct drm_fb_helper *helper; | 238 | struct drm_fb_helper *helper; |
324 | 239 | ||
@@ -326,12 +241,12 @@ bool drm_fb_helper_force_kernel_mode(void) | |||
326 | return false; | 241 | return false; |
327 | 242 | ||
328 | list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) { | 243 | list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) { |
329 | for (i = 0; i < helper->crtc_count; i++) { | 244 | if (helper->dev->switch_power_state == DRM_SWITCH_POWER_OFF) |
330 | struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set; | 245 | continue; |
331 | ret = drm_crtc_helper_set_config(mode_set); | 246 | |
332 | if (ret) | 247 | ret = drm_fb_helper_restore_fbdev_mode(helper); |
333 | error = true; | 248 | if (ret) |
334 | } | 249 | error = true; |
335 | } | 250 | } |
336 | return error; | 251 | return error; |
337 | } | 252 | } |
@@ -601,6 +516,11 @@ static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, | |||
601 | value = (red << info->var.red.offset) | | 516 | value = (red << info->var.red.offset) | |
602 | (green << info->var.green.offset) | | 517 | (green << info->var.green.offset) | |
603 | (blue << info->var.blue.offset); | 518 | (blue << info->var.blue.offset); |
519 | if (info->var.transp.length > 0) { | ||
520 | u32 mask = (1 << info->var.transp.length) - 1; | ||
521 | mask <<= info->var.transp.offset; | ||
522 | value |= mask; | ||
523 | } | ||
604 | palette[regno] = value; | 524 | palette[regno] = value; |
605 | return 0; | 525 | return 0; |
606 | } | 526 | } |
@@ -646,7 +566,7 @@ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) | |||
646 | struct drm_crtc_helper_funcs *crtc_funcs; | 566 | struct drm_crtc_helper_funcs *crtc_funcs; |
647 | u16 *red, *green, *blue, *transp; | 567 | u16 *red, *green, *blue, *transp; |
648 | struct drm_crtc *crtc; | 568 | struct drm_crtc *crtc; |
649 | int i, rc = 0; | 569 | int i, j, rc = 0; |
650 | int start; | 570 | int start; |
651 | 571 | ||
652 | for (i = 0; i < fb_helper->crtc_count; i++) { | 572 | for (i = 0; i < fb_helper->crtc_count; i++) { |
@@ -659,7 +579,7 @@ int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info) | |||
659 | transp = cmap->transp; | 579 | transp = cmap->transp; |
660 | start = cmap->start; | 580 | start = cmap->start; |
661 | 581 | ||
662 | for (i = 0; i < cmap->len; i++) { | 582 | for (j = 0; j < cmap->len; j++) { |
663 | u16 hred, hgreen, hblue, htransp = 0xffff; | 583 | u16 hred, hgreen, hblue, htransp = 0xffff; |
664 | 584 | ||
665 | hred = *red++; | 585 | hred = *red++; |
@@ -857,7 +777,7 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, | |||
857 | /* first up get a count of crtcs now in use and new min/maxes width/heights */ | 777 | /* first up get a count of crtcs now in use and new min/maxes width/heights */ |
858 | for (i = 0; i < fb_helper->connector_count; i++) { | 778 | for (i = 0; i < fb_helper->connector_count; i++) { |
859 | struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i]; | 779 | struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i]; |
860 | struct drm_fb_helper_cmdline_mode *cmdline_mode; | 780 | struct drm_cmdline_mode *cmdline_mode; |
861 | 781 | ||
862 | cmdline_mode = &fb_helper_conn->cmdline_mode; | 782 | cmdline_mode = &fb_helper_conn->cmdline_mode; |
863 | 783 | ||
@@ -959,6 +879,8 @@ void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, | |||
959 | info->fix.type = FB_TYPE_PACKED_PIXELS; | 879 | info->fix.type = FB_TYPE_PACKED_PIXELS; |
960 | info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR : | 880 | info->fix.visual = depth == 8 ? FB_VISUAL_PSEUDOCOLOR : |
961 | FB_VISUAL_TRUECOLOR; | 881 | FB_VISUAL_TRUECOLOR; |
882 | info->fix.mmio_start = 0; | ||
883 | info->fix.mmio_len = 0; | ||
962 | info->fix.type_aux = 0; | 884 | info->fix.type_aux = 0; |
963 | info->fix.xpanstep = 1; /* doing it in hw */ | 885 | info->fix.xpanstep = 1; /* doing it in hw */ |
964 | info->fix.ypanstep = 1; /* doing it in hw */ | 886 | info->fix.ypanstep = 1; /* doing it in hw */ |
@@ -979,6 +901,7 @@ void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helpe | |||
979 | info->var.xres_virtual = fb->width; | 901 | info->var.xres_virtual = fb->width; |
980 | info->var.yres_virtual = fb->height; | 902 | info->var.yres_virtual = fb->height; |
981 | info->var.bits_per_pixel = fb->bits_per_pixel; | 903 | info->var.bits_per_pixel = fb->bits_per_pixel; |
904 | info->var.accel_flags = FB_ACCELF_TEXT; | ||
982 | info->var.xoffset = 0; | 905 | info->var.xoffset = 0; |
983 | info->var.yoffset = 0; | 906 | info->var.yoffset = 0; |
984 | info->var.activate = FB_ACTIVATE_NOW; | 907 | info->var.activate = FB_ACTIVATE_NOW; |
@@ -1076,7 +999,7 @@ static struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_conn | |||
1076 | 999 | ||
1077 | static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector) | 1000 | static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector) |
1078 | { | 1001 | { |
1079 | struct drm_fb_helper_cmdline_mode *cmdline_mode; | 1002 | struct drm_cmdline_mode *cmdline_mode; |
1080 | cmdline_mode = &fb_connector->cmdline_mode; | 1003 | cmdline_mode = &fb_connector->cmdline_mode; |
1081 | return cmdline_mode->specified; | 1004 | return cmdline_mode->specified; |
1082 | } | 1005 | } |
@@ -1084,7 +1007,7 @@ static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector) | |||
1084 | static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn, | 1007 | static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn, |
1085 | int width, int height) | 1008 | int width, int height) |
1086 | { | 1009 | { |
1087 | struct drm_fb_helper_cmdline_mode *cmdline_mode; | 1010 | struct drm_cmdline_mode *cmdline_mode; |
1088 | struct drm_display_mode *mode = NULL; | 1011 | struct drm_display_mode *mode = NULL; |
1089 | 1012 | ||
1090 | cmdline_mode = &fb_helper_conn->cmdline_mode; | 1013 | cmdline_mode = &fb_helper_conn->cmdline_mode; |
@@ -1116,19 +1039,8 @@ static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_conne | |||
1116 | } | 1039 | } |
1117 | 1040 | ||
1118 | create_mode: | 1041 | create_mode: |
1119 | if (cmdline_mode->cvt) | 1042 | mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev, |
1120 | mode = drm_cvt_mode(fb_helper_conn->connector->dev, | 1043 | cmdline_mode); |
1121 | cmdline_mode->xres, cmdline_mode->yres, | ||
1122 | cmdline_mode->refresh_specified ? cmdline_mode->refresh : 60, | ||
1123 | cmdline_mode->rb, cmdline_mode->interlace, | ||
1124 | cmdline_mode->margins); | ||
1125 | else | ||
1126 | mode = drm_gtf_mode(fb_helper_conn->connector->dev, | ||
1127 | cmdline_mode->xres, cmdline_mode->yres, | ||
1128 | cmdline_mode->refresh_specified ? cmdline_mode->refresh : 60, | ||
1129 | cmdline_mode->interlace, | ||
1130 | cmdline_mode->margins); | ||
1131 | drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); | ||
1132 | list_add(&mode->head, &fb_helper_conn->connector->modes); | 1044 | list_add(&mode->head, &fb_helper_conn->connector->modes); |
1133 | return mode; | 1045 | return mode; |
1134 | } | 1046 | } |
@@ -1469,17 +1381,33 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel) | |||
1469 | } | 1381 | } |
1470 | EXPORT_SYMBOL(drm_fb_helper_initial_config); | 1382 | EXPORT_SYMBOL(drm_fb_helper_initial_config); |
1471 | 1383 | ||
1472 | bool drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) | 1384 | /** |
1385 | * drm_fb_helper_hotplug_event - respond to a hotplug notification by | ||
1386 | * probing all the outputs attached to the fb. | ||
1387 | * @fb_helper: the drm_fb_helper | ||
1388 | * | ||
1389 | * LOCKING: | ||
1390 | * Called at runtime, must take mode config lock. | ||
1391 | * | ||
1392 | * Scan the connectors attached to the fb_helper and try to put together a | ||
1393 | * setup after *notification of a change in output configuration. | ||
1394 | * | ||
1395 | * RETURNS: | ||
1396 | * 0 on success and a non-zero error code otherwise. | ||
1397 | */ | ||
1398 | int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) | ||
1473 | { | 1399 | { |
1400 | struct drm_device *dev = fb_helper->dev; | ||
1474 | int count = 0; | 1401 | int count = 0; |
1475 | u32 max_width, max_height, bpp_sel; | 1402 | u32 max_width, max_height, bpp_sel; |
1476 | bool bound = false, crtcs_bound = false; | 1403 | bool bound = false, crtcs_bound = false; |
1477 | struct drm_crtc *crtc; | 1404 | struct drm_crtc *crtc; |
1478 | 1405 | ||
1479 | if (!fb_helper->fb) | 1406 | if (!fb_helper->fb) |
1480 | return false; | 1407 | return 0; |
1481 | 1408 | ||
1482 | list_for_each_entry(crtc, &fb_helper->dev->mode_config.crtc_list, head) { | 1409 | mutex_lock(&dev->mode_config.mutex); |
1410 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | ||
1483 | if (crtc->fb) | 1411 | if (crtc->fb) |
1484 | crtcs_bound = true; | 1412 | crtcs_bound = true; |
1485 | if (crtc->fb == fb_helper->fb) | 1413 | if (crtc->fb == fb_helper->fb) |
@@ -1488,7 +1416,8 @@ bool drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) | |||
1488 | 1416 | ||
1489 | if (!bound && crtcs_bound) { | 1417 | if (!bound && crtcs_bound) { |
1490 | fb_helper->delayed_hotplug = true; | 1418 | fb_helper->delayed_hotplug = true; |
1491 | return false; | 1419 | mutex_unlock(&dev->mode_config.mutex); |
1420 | return 0; | ||
1492 | } | 1421 | } |
1493 | DRM_DEBUG_KMS("\n"); | 1422 | DRM_DEBUG_KMS("\n"); |
1494 | 1423 | ||
@@ -1499,8 +1428,30 @@ bool drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) | |||
1499 | count = drm_fb_helper_probe_connector_modes(fb_helper, max_width, | 1428 | count = drm_fb_helper_probe_connector_modes(fb_helper, max_width, |
1500 | max_height); | 1429 | max_height); |
1501 | drm_setup_crtcs(fb_helper); | 1430 | drm_setup_crtcs(fb_helper); |
1431 | mutex_unlock(&dev->mode_config.mutex); | ||
1502 | 1432 | ||
1503 | return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); | 1433 | return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); |
1504 | } | 1434 | } |
1505 | EXPORT_SYMBOL(drm_fb_helper_hotplug_event); | 1435 | EXPORT_SYMBOL(drm_fb_helper_hotplug_event); |
1506 | 1436 | ||
1437 | /* The Kconfig DRM_KMS_HELPER selects FRAMEBUFFER_CONSOLE (if !EXPERT) | ||
1438 | * but the module doesn't depend on any fb console symbols. At least | ||
1439 | * attempt to load fbcon to avoid leaving the system without a usable console. | ||
1440 | */ | ||
1441 | #if defined(CONFIG_FRAMEBUFFER_CONSOLE_MODULE) && !defined(CONFIG_EXPERT) | ||
1442 | static int __init drm_fb_helper_modinit(void) | ||
1443 | { | ||
1444 | const char *name = "fbcon"; | ||
1445 | struct module *fbcon; | ||
1446 | |||
1447 | mutex_lock(&module_mutex); | ||
1448 | fbcon = find_module(name); | ||
1449 | mutex_unlock(&module_mutex); | ||
1450 | |||
1451 | if (!fbcon) | ||
1452 | request_module_nowait(name); | ||
1453 | return 0; | ||
1454 | } | ||
1455 | |||
1456 | module_init(drm_fb_helper_modinit); | ||
1457 | #endif | ||