aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/drm_fb_helper.c
diff options
context:
space:
mode:
authorDave Airlie <airlied@linux.ie>2009-09-23 00:44:08 -0400
committerDave Airlie <airlied@linux.ie>2009-09-24 23:08:20 -0400
commitd50ba256b5f1478e15accfcfda9b72fd7a661364 (patch)
tree7148b1294057a734ecdbc62b7df7ed0397adfeb4 /drivers/gpu/drm/drm_fb_helper.c
parentf80330357284c908e1f67cc8b4d8e56a3e2f6fc6 (diff)
drm/kms: start adding command line interface using fb.
[note this requires an fb patch posted to linux-fbdev-devel already] This uses the normal video= command line option to control the kms output setup at boot time. It is used to override the autodetection done by kms. video= normally takes a framebuffer as the first parameter, in kms it will take a connector name, DVI-I-1, or LVDS-1 etc. If no output connector is specified the mode string will apply to all connectors. The mode specification used will match down the probed modes, and if no mode is found it will add a CVT mode that matches. video=1024x768 - all connectors match a 1024x768 mode or add a CVT on video=VGA-1:1024x768, VGA-1 connector gets mode only. The same strings as used in current fb modedb.c are used, except I've added three more letters, e, D, d, e = enable, D = enable Digital, d = disable, which allow a connector to be forced into a certain state. Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/drm_fb_helper.c')
-rw-r--r--drivers/gpu/drm/drm_fb_helper.c224
1 files changed, 223 insertions, 1 deletions
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 2c4671314884..2537d2e81849 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -40,6 +40,196 @@ 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
51}
52EXPORT_SYMBOL(drm_fb_helper_add_connector);
53
54static int my_atoi(const char *name)
55{
56 int val = 0;
57
58 for (;; name++) {
59 switch (*name) {
60 case '0' ... '9':
61 val = 10*val+(*name-'0');
62 break;
63 default:
64 return val;
65 }
66 }
67}
68
69/**
70 * drm_fb_helper_connector_parse_command_line - parse command line for connector
71 * @connector - connector to parse line for
72 * @mode_option - per connector mode option
73 *
74 * This parses the connector specific then generic command lines for
75 * modes and options to configure the connector.
76 *
77 * This uses the same parameters as the fb modedb.c, except for extra
78 * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m][eDd]
79 *
80 * enable/enable Digital/disable bit at the end
81 */
82static bool drm_fb_helper_connector_parse_command_line(struct drm_connector *connector,
83 const char *mode_option)
84{
85 const char *name;
86 unsigned int namelen;
87 int res_specified = 0, bpp_specified = 0, refresh_specified = 0;
88 unsigned int xres = 0, yres = 0, bpp = 32, refresh = 0;
89 int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0;
90 int i;
91 enum drm_connector_force force = DRM_FORCE_UNSPECIFIED;
92 struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
93 struct drm_fb_helper_cmdline_mode *cmdline_mode = &fb_help_conn->cmdline_mode;
94
95 if (!mode_option)
96 mode_option = fb_mode_option;
97
98 if (!mode_option) {
99 cmdline_mode->specified = false;
100 return false;
101 }
102
103 name = mode_option;
104 namelen = strlen(name);
105 for (i = namelen-1; i >= 0; i--) {
106 switch (name[i]) {
107 case '@':
108 namelen = i;
109 if (!refresh_specified && !bpp_specified &&
110 !yres_specified) {
111 refresh = my_atoi(&name[i+1]);
112 refresh_specified = 1;
113 if (cvt || rb)
114 cvt = 0;
115 } else
116 goto done;
117 break;
118 case '-':
119 namelen = i;
120 if (!bpp_specified && !yres_specified) {
121 bpp = my_atoi(&name[i+1]);
122 bpp_specified = 1;
123 if (cvt || rb)
124 cvt = 0;
125 } else
126 goto done;
127 break;
128 case 'x':
129 if (!yres_specified) {
130 yres = my_atoi(&name[i+1]);
131 yres_specified = 1;
132 } else
133 goto done;
134 case '0' ... '9':
135 break;
136 case 'M':
137 if (!yres_specified)
138 cvt = 1;
139 break;
140 case 'R':
141 if (!cvt)
142 rb = 1;
143 break;
144 case 'm':
145 if (!cvt)
146 margins = 1;
147 break;
148 case 'i':
149 if (!cvt)
150 interlace = 1;
151 break;
152 case 'e':
153 force = DRM_FORCE_ON;
154 break;
155 case 'D':
156 if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) ||
157 (connector->connector_type != DRM_MODE_CONNECTOR_HDMIB))
158 force = DRM_FORCE_ON;
159 else
160 force = DRM_FORCE_ON_DIGITAL;
161 break;
162 case 'd':
163 force = DRM_FORCE_OFF;
164 break;
165 default:
166 goto done;
167 }
168 }
169 if (i < 0 && yres_specified) {
170 xres = my_atoi(name);
171 res_specified = 1;
172 }
173done:
174
175 DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n",
176 drm_get_connector_name(connector), xres, yres,
177 (refresh) ? refresh : 60, (rb) ? " reduced blanking" :
178 "", (margins) ? " with margins" : "", (interlace) ?
179 " interlaced" : "");
180
181 if (force) {
182 const char *s;
183 switch (force) {
184 case DRM_FORCE_OFF: s = "OFF"; break;
185 case DRM_FORCE_ON_DIGITAL: s = "ON - dig"; break;
186 default:
187 case DRM_FORCE_ON: s = "ON"; break;
188 }
189
190 DRM_INFO("forcing %s connector %s\n",
191 drm_get_connector_name(connector), s);
192 connector->force = force;
193 }
194
195 if (res_specified) {
196 cmdline_mode->specified = true;
197 cmdline_mode->xres = xres;
198 cmdline_mode->yres = yres;
199 }
200
201 if (refresh_specified) {
202 cmdline_mode->refresh_specified = true;
203 cmdline_mode->refresh = refresh;
204 }
205
206 if (bpp_specified) {
207 cmdline_mode->bpp_specified = true;
208 cmdline_mode->bpp = bpp;
209 }
210 cmdline_mode->rb = rb ? true : false;
211 cmdline_mode->cvt = cvt ? true : false;
212 cmdline_mode->interlace = interlace ? true : false;
213
214 return true;
215}
216
217int drm_fb_helper_parse_command_line(struct drm_device *dev)
218{
219 struct drm_connector *connector;
220
221 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
222 char *option = NULL;
223
224 /* do something on return - turn off connector maybe */
225 if (fb_get_options(drm_get_connector_name(connector), &option))
226 continue;
227
228 drm_fb_helper_connector_parse_command_line(connector, option);
229 }
230 return 0;
231}
232
43bool drm_fb_helper_force_kernel_mode(void) 233bool drm_fb_helper_force_kernel_mode(void)
44{ 234{
45 int i = 0; 235 int i = 0;
@@ -484,6 +674,8 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev,
484 uint32_t fb_height, 674 uint32_t fb_height,
485 uint32_t surface_width, 675 uint32_t surface_width,
486 uint32_t surface_height, 676 uint32_t surface_height,
677 uint32_t surface_depth,
678 uint32_t surface_bpp,
487 struct drm_framebuffer **fb_ptr)) 679 struct drm_framebuffer **fb_ptr))
488{ 680{
489 struct drm_crtc *crtc; 681 struct drm_crtc *crtc;
@@ -497,8 +689,37 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev,
497 struct drm_framebuffer *fb; 689 struct drm_framebuffer *fb;
498 struct drm_mode_set *modeset = NULL; 690 struct drm_mode_set *modeset = NULL;
499 struct drm_fb_helper *fb_helper; 691 struct drm_fb_helper *fb_helper;
692 uint32_t surface_depth = 24, surface_bpp = 32;
500 693
501 /* first up get a count of crtcs now in use and new min/maxes width/heights */ 694 /* first up get a count of crtcs now in use and new min/maxes width/heights */
695 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
696 struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
697 struct drm_fb_helper_cmdline_mode *cmdline_mode = &fb_help_conn->cmdline_mode;
698
699 if (cmdline_mode->bpp_specified) {
700 switch (cmdline_mode->bpp) {
701 case 8:
702 surface_depth = surface_bpp = 8;
703 break;
704 case 15:
705 surface_depth = 15;
706 surface_bpp = 16;
707 break;
708 case 16:
709 surface_depth = surface_bpp = 16;
710 break;
711 case 24:
712 surface_depth = surface_bpp = 24;
713 break;
714 case 32:
715 surface_depth = 24;
716 surface_bpp = 32;
717 break;
718 }
719 break;
720 }
721 }
722
502 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 723 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
503 if (drm_helper_crtc_in_use(crtc)) { 724 if (drm_helper_crtc_in_use(crtc)) {
504 if (crtc->desired_mode) { 725 if (crtc->desired_mode) {
@@ -527,7 +748,8 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev,
527 /* do we have an fb already? */ 748 /* do we have an fb already? */
528 if (list_empty(&dev->mode_config.fb_kernel_list)) { 749 if (list_empty(&dev->mode_config.fb_kernel_list)) {
529 ret = (*fb_create)(dev, fb_width, fb_height, surface_width, 750 ret = (*fb_create)(dev, fb_width, fb_height, surface_width,
530 surface_height, &fb); 751 surface_height, surface_depth, surface_bpp,
752 &fb);
531 if (ret) 753 if (ret)
532 return -EINVAL; 754 return -EINVAL;
533 new_fb = 1; 755 new_fb = 1;