aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/drm_fb_helper.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/drm_fb_helper.c')
-rw-r--r--drivers/gpu/drm/drm_fb_helper.c235
1 files changed, 234 insertions, 1 deletions
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 2c4671314884..819ddcbfcce5 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -40,6 +40,199 @@ MODULE_LICENSE("GPL and additional rights");
40 40
41static LIST_HEAD(kernel_fb_helper_list); 41static LIST_HEAD(kernel_fb_helper_list);
42 42
43int drm_fb_helper_add_connector(struct drm_connector *connector)
44{
45 connector->fb_helper_private = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
46 if (!connector->fb_helper_private)
47 return -ENOMEM;
48
49 return 0;
50}
51EXPORT_SYMBOL(drm_fb_helper_add_connector);
52
53static int my_atoi(const char *name)
54{
55 int val = 0;
56
57 for (;; name++) {
58 switch (*name) {
59 case '0' ... '9':
60 val = 10*val+(*name-'0');
61 break;
62 default:
63 return val;
64 }
65 }
66}
67
68/**
69 * drm_fb_helper_connector_parse_command_line - parse command line for connector
70 * @connector - connector to parse line for
71 * @mode_option - per connector mode option
72 *
73 * This parses the connector specific then generic command lines for
74 * modes and options to configure the connector.
75 *
76 * This uses the same parameters as the fb modedb.c, except for extra
77 * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd]
78 *
79 * enable/enable Digital/disable bit at the end
80 */
81static bool drm_fb_helper_connector_parse_command_line(struct drm_connector *connector,
82 const char *mode_option)
83{
84 const char *name;
85 unsigned int namelen;
86 int res_specified = 0, bpp_specified = 0, refresh_specified = 0;
87 unsigned int xres = 0, yres = 0, bpp = 32, refresh = 0;
88 int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0;
89 int i;
90 enum drm_connector_force force = DRM_FORCE_UNSPECIFIED;
91 struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
92 struct drm_fb_helper_cmdline_mode *cmdline_mode;
93
94 if (!fb_help_conn)
95 return false;
96
97 cmdline_mode = &fb_help_conn->cmdline_mode;
98 if (!mode_option)
99 mode_option = fb_mode_option;
100
101 if (!mode_option) {
102 cmdline_mode->specified = false;
103 return false;
104 }
105
106 name = mode_option;
107 namelen = strlen(name);
108 for (i = namelen-1; i >= 0; i--) {
109 switch (name[i]) {
110 case '@':
111 namelen = i;
112 if (!refresh_specified && !bpp_specified &&
113 !yres_specified) {
114 refresh = my_atoi(&name[i+1]);
115 refresh_specified = 1;
116 if (cvt || rb)
117 cvt = 0;
118 } else
119 goto done;
120 break;
121 case '-':
122 namelen = i;
123 if (!bpp_specified && !yres_specified) {
124 bpp = my_atoi(&name[i+1]);
125 bpp_specified = 1;
126 if (cvt || rb)
127 cvt = 0;
128 } else
129 goto done;
130 break;
131 case 'x':
132 if (!yres_specified) {
133 yres = my_atoi(&name[i+1]);
134 yres_specified = 1;
135 } else
136 goto done;
137 case '0' ... '9':
138 break;
139 case 'M':
140 if (!yres_specified)
141 cvt = 1;
142 break;
143 case 'R':
144 if (!cvt)
145 rb = 1;
146 break;
147 case 'm':
148 if (!cvt)
149 margins = 1;
150 break;
151 case 'i':
152 if (!cvt)
153 interlace = 1;
154 break;
155 case 'e':
156 force = DRM_FORCE_ON;
157 break;
158 case 'D':
159 if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) ||
160 (connector->connector_type != DRM_MODE_CONNECTOR_HDMIB))
161 force = DRM_FORCE_ON;
162 else
163 force = DRM_FORCE_ON_DIGITAL;
164 break;
165 case 'd':
166 force = DRM_FORCE_OFF;
167 break;
168 default:
169 goto done;
170 }
171 }
172 if (i < 0 && yres_specified) {
173 xres = my_atoi(name);
174 res_specified = 1;
175 }
176done:
177
178 DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
179 drm_get_connector_name(connector), xres, yres,
180 (refresh) ? refresh : 60, (rb) ? " reduced blanking" :
181 "", (margins) ? " with margins" : "", (interlace) ?
182 " interlaced" : "");
183
184 if (force) {
185 const char *s;
186 switch (force) {
187 case DRM_FORCE_OFF: s = "OFF"; break;
188 case DRM_FORCE_ON_DIGITAL: s = "ON - dig"; break;
189 default:
190 case DRM_FORCE_ON: s = "ON"; break;
191 }
192
193 DRM_INFO("forcing %s connector %s\n",
194 drm_get_connector_name(connector), s);
195 connector->force = force;
196 }
197
198 if (res_specified) {
199 cmdline_mode->specified = true;
200 cmdline_mode->xres = xres;
201 cmdline_mode->yres = yres;
202 }
203
204 if (refresh_specified) {
205 cmdline_mode->refresh_specified = true;
206 cmdline_mode->refresh = refresh;
207 }
208
209 if (bpp_specified) {
210 cmdline_mode->bpp_specified = true;
211 cmdline_mode->bpp = bpp;
212 }
213 cmdline_mode->rb = rb ? true : false;
214 cmdline_mode->cvt = cvt ? true : false;
215 cmdline_mode->interlace = interlace ? true : false;
216
217 return true;
218}
219
220int drm_fb_helper_parse_command_line(struct drm_device *dev)
221{
222 struct drm_connector *connector;
223
224 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
225 char *option = NULL;
226
227 /* do something on return - turn off connector maybe */
228 if (fb_get_options(drm_get_connector_name(connector), &option))
229 continue;
230
231 drm_fb_helper_connector_parse_command_line(connector, option);
232 }
233 return 0;
234}
235
43bool drm_fb_helper_force_kernel_mode(void) 236bool drm_fb_helper_force_kernel_mode(void)
44{ 237{
45 int i = 0; 238 int i = 0;
@@ -87,6 +280,7 @@ void drm_fb_helper_restore(void)
87} 280}
88EXPORT_SYMBOL(drm_fb_helper_restore); 281EXPORT_SYMBOL(drm_fb_helper_restore);
89 282
283#ifdef CONFIG_MAGIC_SYSRQ
90static void drm_fb_helper_restore_work_fn(struct work_struct *ignored) 284static void drm_fb_helper_restore_work_fn(struct work_struct *ignored)
91{ 285{
92 drm_fb_helper_restore(); 286 drm_fb_helper_restore();
@@ -103,6 +297,7 @@ static struct sysrq_key_op sysrq_drm_fb_helper_restore_op = {
103 .help_msg = "force-fb(V)", 297 .help_msg = "force-fb(V)",
104 .action_msg = "Restore framebuffer console", 298 .action_msg = "Restore framebuffer console",
105}; 299};
300#endif
106 301
107static void drm_fb_helper_on(struct fb_info *info) 302static void drm_fb_helper_on(struct fb_info *info)
108{ 303{
@@ -484,6 +679,8 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev,
484 uint32_t fb_height, 679 uint32_t fb_height,
485 uint32_t surface_width, 680 uint32_t surface_width,
486 uint32_t surface_height, 681 uint32_t surface_height,
682 uint32_t surface_depth,
683 uint32_t surface_bpp,
487 struct drm_framebuffer **fb_ptr)) 684 struct drm_framebuffer **fb_ptr))
488{ 685{
489 struct drm_crtc *crtc; 686 struct drm_crtc *crtc;
@@ -497,8 +694,43 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev,
497 struct drm_framebuffer *fb; 694 struct drm_framebuffer *fb;
498 struct drm_mode_set *modeset = NULL; 695 struct drm_mode_set *modeset = NULL;
499 struct drm_fb_helper *fb_helper; 696 struct drm_fb_helper *fb_helper;
697 uint32_t surface_depth = 24, surface_bpp = 32;
500 698
501 /* first up get a count of crtcs now in use and new min/maxes width/heights */ 699 /* first up get a count of crtcs now in use and new min/maxes width/heights */
700 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
701 struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
702
703 struct drm_fb_helper_cmdline_mode *cmdline_mode;
704
705 if (!fb_help_conn)
706 continue;
707
708 cmdline_mode = &fb_help_conn->cmdline_mode;
709
710 if (cmdline_mode->bpp_specified) {
711 switch (cmdline_mode->bpp) {
712 case 8:
713 surface_depth = surface_bpp = 8;
714 break;
715 case 15:
716 surface_depth = 15;
717 surface_bpp = 16;
718 break;
719 case 16:
720 surface_depth = surface_bpp = 16;
721 break;
722 case 24:
723 surface_depth = surface_bpp = 24;
724 break;
725 case 32:
726 surface_depth = 24;
727 surface_bpp = 32;
728 break;
729 }
730 break;
731 }
732 }
733
502 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 734 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
503 if (drm_helper_crtc_in_use(crtc)) { 735 if (drm_helper_crtc_in_use(crtc)) {
504 if (crtc->desired_mode) { 736 if (crtc->desired_mode) {
@@ -527,7 +759,8 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev,
527 /* do we have an fb already? */ 759 /* do we have an fb already? */
528 if (list_empty(&dev->mode_config.fb_kernel_list)) { 760 if (list_empty(&dev->mode_config.fb_kernel_list)) {
529 ret = (*fb_create)(dev, fb_width, fb_height, surface_width, 761 ret = (*fb_create)(dev, fb_width, fb_height, surface_width,
530 surface_height, &fb); 762 surface_height, surface_depth, surface_bpp,
763 &fb);
531 if (ret) 764 if (ret)
532 return -EINVAL; 765 return -EINVAL;
533 new_fb = 1; 766 new_fb = 1;