aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/pxafb.c
diff options
context:
space:
mode:
authorRichard Purdie <rpurdie@net.rmk.(none)>2006-09-20 17:54:21 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2006-10-02 08:33:37 -0400
commitd14b272bc63f35a8f20b4b1df16c080b8d24f8f1 (patch)
treef754f3b333388e0ab78c934fd492906688fab2a6 /drivers/video/pxafb.c
parentc5d311c7e93c7d84941028835bc6ae5e1bc4e73c (diff)
[ARM] 3848/1: pxafb: Add option of fixing video modes and spitz QVGA mode support
Add the ability to have pxafb use only certain fixed video modes (selected on a per platform basis). This is useful on production hardware such as the Zaurus cxx00 models where the valid modes are known in advance and any other modes could result in hardware damage. Following this, add support for the cxx00 QVGA mode. Mode information is passed to the lcd_power call to allowing the panel drivers to configure the display hardware accordingly (corgi_lcd already contains the functionality for the cxx00 panel). This mirrors the setup already used by w100fb. Signed-off-by: Richard Purdie <rpurdie@rpsys.net> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'drivers/video/pxafb.c')
-rw-r--r--drivers/video/pxafb.c106
1 files changed, 78 insertions, 28 deletions
diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c
index bbb07106cd54..3bc5da4a57ca 100644
--- a/drivers/video/pxafb.c
+++ b/drivers/video/pxafb.c
@@ -59,7 +59,7 @@
59#define LCCR3_INVALID_CONFIG_MASK (LCCR3_HSP|LCCR3_VSP|LCCR3_PCD|LCCR3_BPP) 59#define LCCR3_INVALID_CONFIG_MASK (LCCR3_HSP|LCCR3_VSP|LCCR3_PCD|LCCR3_BPP)
60 60
61static void (*pxafb_backlight_power)(int); 61static void (*pxafb_backlight_power)(int);
62static void (*pxafb_lcd_power)(int); 62static void (*pxafb_lcd_power)(int, struct fb_var_screeninfo *);
63 63
64static int pxafb_activate_var(struct fb_var_screeninfo *var, struct pxafb_info *); 64static int pxafb_activate_var(struct fb_var_screeninfo *var, struct pxafb_info *);
65static void set_ctrlr_state(struct pxafb_info *fbi, u_int state); 65static void set_ctrlr_state(struct pxafb_info *fbi, u_int state);
@@ -214,6 +214,48 @@ extern unsigned int get_clk_frequency_khz(int info);
214#endif 214#endif
215 215
216/* 216/*
217 * Select the smallest mode that allows the desired resolution to be
218 * displayed. If desired parameters can be rounded up.
219 */
220static struct pxafb_mode_info *pxafb_getmode(struct pxafb_mach_info *mach, struct fb_var_screeninfo *var)
221{
222 struct pxafb_mode_info *mode = NULL;
223 struct pxafb_mode_info *modelist = mach->modes;
224 unsigned int best_x = 0xffffffff, best_y = 0xffffffff;
225 unsigned int i;
226
227 for (i = 0 ; i < mach->num_modes ; i++) {
228 if (modelist[i].xres >= var->xres && modelist[i].yres >= var->yres &&
229 modelist[i].xres < best_x && modelist[i].yres < best_y &&
230 modelist[i].bpp >= var->bits_per_pixel ) {
231 best_x = modelist[i].xres;
232 best_y = modelist[i].yres;
233 mode = &modelist[i];
234 }
235 }
236
237 return mode;
238}
239
240static void pxafb_setmode(struct fb_var_screeninfo *var, struct pxafb_mode_info *mode)
241{
242 var->xres = mode->xres;
243 var->yres = mode->yres;
244 var->bits_per_pixel = mode->bpp;
245 var->pixclock = mode->pixclock;
246 var->hsync_len = mode->hsync_len;
247 var->left_margin = mode->left_margin;
248 var->right_margin = mode->right_margin;
249 var->vsync_len = mode->vsync_len;
250 var->upper_margin = mode->upper_margin;
251 var->lower_margin = mode->lower_margin;
252 var->sync = mode->sync;
253 var->grayscale = mode->cmap_greyscale;
254 var->xres_virtual = var->xres;
255 var->yres_virtual = var->yres;
256}
257
258/*
217 * pxafb_check_var(): 259 * pxafb_check_var():
218 * Get the video params out of 'var'. If a value doesn't fit, round it up, 260 * Get the video params out of 'var'. If a value doesn't fit, round it up,
219 * if it's too big, return -EINVAL. 261 * if it's too big, return -EINVAL.
@@ -225,15 +267,29 @@ extern unsigned int get_clk_frequency_khz(int info);
225static int pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) 267static int pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
226{ 268{
227 struct pxafb_info *fbi = (struct pxafb_info *)info; 269 struct pxafb_info *fbi = (struct pxafb_info *)info;
270 struct pxafb_mach_info *inf = fbi->dev->platform_data;
228 271
229 if (var->xres < MIN_XRES) 272 if (var->xres < MIN_XRES)
230 var->xres = MIN_XRES; 273 var->xres = MIN_XRES;
231 if (var->yres < MIN_YRES) 274 if (var->yres < MIN_YRES)
232 var->yres = MIN_YRES; 275 var->yres = MIN_YRES;
233 if (var->xres > fbi->max_xres) 276
234 return -EINVAL; 277 if (inf->fixed_modes) {
235 if (var->yres > fbi->max_yres) 278 struct pxafb_mode_info *mode;
236 return -EINVAL; 279
280 mode = pxafb_getmode(inf, var);
281 if (!mode)
282 return -EINVAL;
283 pxafb_setmode(var, mode);
284 } else {
285 if (var->xres > inf->modes->xres)
286 return -EINVAL;
287 if (var->yres > inf->modes->yres)
288 return -EINVAL;
289 if (var->bits_per_pixel > inf->modes->bpp)
290 return -EINVAL;
291 }
292
237 var->xres_virtual = 293 var->xres_virtual =
238 max(var->xres_virtual, var->xres); 294 max(var->xres_virtual, var->xres);
239 var->yres_virtual = 295 var->yres_virtual =
@@ -693,7 +749,7 @@ static inline void __pxafb_lcd_power(struct pxafb_info *fbi, int on)
693 pr_debug("pxafb: LCD power o%s\n", on ? "n" : "ff"); 749 pr_debug("pxafb: LCD power o%s\n", on ? "n" : "ff");
694 750
695 if (pxafb_lcd_power) 751 if (pxafb_lcd_power)
696 pxafb_lcd_power(on); 752 pxafb_lcd_power(on, &fbi->fb.var);
697} 753}
698 754
699static void pxafb_setup_gpio(struct pxafb_info *fbi) 755static void pxafb_setup_gpio(struct pxafb_info *fbi)
@@ -869,9 +925,11 @@ static void set_ctrlr_state(struct pxafb_info *fbi, u_int state)
869 * registers. 925 * registers.
870 */ 926 */
871 if (old_state == C_ENABLE) { 927 if (old_state == C_ENABLE) {
928 __pxafb_lcd_power(fbi, 0);
872 pxafb_disable_controller(fbi); 929 pxafb_disable_controller(fbi);
873 pxafb_setup_gpio(fbi); 930 pxafb_setup_gpio(fbi);
874 pxafb_enable_controller(fbi); 931 pxafb_enable_controller(fbi);
932 __pxafb_lcd_power(fbi, 1);
875 } 933 }
876 break; 934 break;
877 935
@@ -1049,6 +1107,8 @@ static struct pxafb_info * __init pxafb_init_fbinfo(struct device *dev)
1049 struct pxafb_info *fbi; 1107 struct pxafb_info *fbi;
1050 void *addr; 1108 void *addr;
1051 struct pxafb_mach_info *inf = dev->platform_data; 1109 struct pxafb_mach_info *inf = dev->platform_data;
1110 struct pxafb_mode_info *mode = inf->modes;
1111 int i, smemlen;
1052 1112
1053 /* Alloc the pxafb_info and pseudo_palette in one step */ 1113 /* Alloc the pxafb_info and pseudo_palette in one step */
1054 fbi = kmalloc(sizeof(struct pxafb_info) + sizeof(u32) * 16, GFP_KERNEL); 1114 fbi = kmalloc(sizeof(struct pxafb_info) + sizeof(u32) * 16, GFP_KERNEL);
@@ -1082,31 +1142,21 @@ static struct pxafb_info * __init pxafb_init_fbinfo(struct device *dev)
1082 addr = addr + sizeof(struct pxafb_info); 1142 addr = addr + sizeof(struct pxafb_info);
1083 fbi->fb.pseudo_palette = addr; 1143 fbi->fb.pseudo_palette = addr;
1084 1144
1085 fbi->max_xres = inf->xres; 1145 pxafb_setmode(&fbi->fb.var, mode);
1086 fbi->fb.var.xres = inf->xres; 1146
1087 fbi->fb.var.xres_virtual = inf->xres;
1088 fbi->max_yres = inf->yres;
1089 fbi->fb.var.yres = inf->yres;
1090 fbi->fb.var.yres_virtual = inf->yres;
1091 fbi->max_bpp = inf->bpp;
1092 fbi->fb.var.bits_per_pixel = inf->bpp;
1093 fbi->fb.var.pixclock = inf->pixclock;
1094 fbi->fb.var.hsync_len = inf->hsync_len;
1095 fbi->fb.var.left_margin = inf->left_margin;
1096 fbi->fb.var.right_margin = inf->right_margin;
1097 fbi->fb.var.vsync_len = inf->vsync_len;
1098 fbi->fb.var.upper_margin = inf->upper_margin;
1099 fbi->fb.var.lower_margin = inf->lower_margin;
1100 fbi->fb.var.sync = inf->sync;
1101 fbi->fb.var.grayscale = inf->cmap_greyscale;
1102 fbi->cmap_inverse = inf->cmap_inverse; 1147 fbi->cmap_inverse = inf->cmap_inverse;
1103 fbi->cmap_static = inf->cmap_static; 1148 fbi->cmap_static = inf->cmap_static;
1149
1104 fbi->lccr0 = inf->lccr0; 1150 fbi->lccr0 = inf->lccr0;
1105 fbi->lccr3 = inf->lccr3; 1151 fbi->lccr3 = inf->lccr3;
1106 fbi->state = C_STARTUP; 1152 fbi->state = C_STARTUP;
1107 fbi->task_state = (u_char)-1; 1153 fbi->task_state = (u_char)-1;
1108 fbi->fb.fix.smem_len = fbi->max_xres * fbi->max_yres * 1154
1109 fbi->max_bpp / 8; 1155 for (i = 0; i < inf->num_modes; i++) {
1156 smemlen = mode[i].xres * mode[i].yres * mode[i].bpp / 8;
1157 if (smemlen > fbi->fb.fix.smem_len)
1158 fbi->fb.fix.smem_len = smemlen;
1159 }
1110 1160
1111 init_waitqueue_head(&fbi->ctrlr_wait); 1161 init_waitqueue_head(&fbi->ctrlr_wait);
1112 INIT_WORK(&fbi->task, pxafb_task, fbi); 1162 INIT_WORK(&fbi->task, pxafb_task, fbi);
@@ -1307,12 +1357,12 @@ int __init pxafb_probe(struct platform_device *dev)
1307 (inf->lccr0 & LCCR0_SDS) == LCCR0_Dual) 1357 (inf->lccr0 & LCCR0_SDS) == LCCR0_Dual)
1308 dev_warn(&dev->dev, "Dual panel only valid in passive mode\n"); 1358 dev_warn(&dev->dev, "Dual panel only valid in passive mode\n");
1309 if ((inf->lccr0 & LCCR0_PAS) == LCCR0_Pas && 1359 if ((inf->lccr0 & LCCR0_PAS) == LCCR0_Pas &&
1310 (inf->upper_margin || inf->lower_margin)) 1360 (inf->modes->upper_margin || inf->modes->lower_margin))
1311 dev_warn(&dev->dev, "Upper and lower margins must be 0 in passive mode\n"); 1361 dev_warn(&dev->dev, "Upper and lower margins must be 0 in passive mode\n");
1312#endif 1362#endif
1313 1363
1314 dev_dbg(&dev->dev, "got a %dx%dx%d LCD\n",inf->xres, inf->yres, inf->bpp); 1364 dev_dbg(&dev->dev, "got a %dx%dx%d LCD\n",inf->modes->xres, inf->modes->yres, inf->modes->bpp);
1315 if (inf->xres == 0 || inf->yres == 0 || inf->bpp == 0) { 1365 if (inf->modes->xres == 0 || inf->modes->yres == 0 || inf->modes->bpp == 0) {
1316 dev_err(&dev->dev, "Invalid resolution or bit depth\n"); 1366 dev_err(&dev->dev, "Invalid resolution or bit depth\n");
1317 ret = -EINVAL; 1367 ret = -EINVAL;
1318 goto failed; 1368 goto failed;