diff options
author | Dave Jones <davej@redhat.com> | 2006-12-12 18:13:32 -0500 |
---|---|---|
committer | Dave Jones <davej@redhat.com> | 2006-12-12 18:13:32 -0500 |
commit | f0eef25339f92f7cd4aeea23d9ae97987a5a1e82 (patch) | |
tree | 2472e94d39f43a9580a6d2d5d92de0b749023263 /drivers/video/pxafb.c | |
parent | 0cfea5dd98205f2fa318836da664a7d7df1afbc1 (diff) | |
parent | e1036502e5263851259d147771226161e5ccc85a (diff) |
Merge ../linus
Diffstat (limited to 'drivers/video/pxafb.c')
-rw-r--r-- | drivers/video/pxafb.c | 115 |
1 files changed, 83 insertions, 32 deletions
diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c index bbb07106cd54..38eb0b69c2d7 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 | ||
61 | static void (*pxafb_backlight_power)(int); | 61 | static void (*pxafb_backlight_power)(int); |
62 | static void (*pxafb_lcd_power)(int); | 62 | static void (*pxafb_lcd_power)(int, struct fb_var_screeninfo *); |
63 | 63 | ||
64 | static int pxafb_activate_var(struct fb_var_screeninfo *var, struct pxafb_info *); | 64 | static int pxafb_activate_var(struct fb_var_screeninfo *var, struct pxafb_info *); |
65 | static void set_ctrlr_state(struct pxafb_info *fbi, u_int state); | 65 | static 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 | */ | ||
220 | static 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 | |||
240 | static 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); | |||
225 | static int pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) | 267 | static 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 | ||
699 | static void pxafb_setup_gpio(struct pxafb_info *fbi) | 755 | static void pxafb_setup_gpio(struct pxafb_info *fbi) |
@@ -790,7 +846,7 @@ static void pxafb_disable_controller(struct pxafb_info *fbi) | |||
790 | /* | 846 | /* |
791 | * pxafb_handle_irq: Handle 'LCD DONE' interrupts. | 847 | * pxafb_handle_irq: Handle 'LCD DONE' interrupts. |
792 | */ | 848 | */ |
793 | static irqreturn_t pxafb_handle_irq(int irq, void *dev_id, struct pt_regs *regs) | 849 | static irqreturn_t pxafb_handle_irq(int irq, void *dev_id) |
794 | { | 850 | { |
795 | struct pxafb_info *fbi = dev_id; | 851 | struct pxafb_info *fbi = dev_id; |
796 | unsigned int lcsr = LCSR; | 852 | unsigned int lcsr = LCSR; |
@@ -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 | ||
@@ -906,9 +964,10 @@ static void set_ctrlr_state(struct pxafb_info *fbi, u_int state) | |||
906 | * Our LCD controller task (which is called when we blank or unblank) | 964 | * Our LCD controller task (which is called when we blank or unblank) |
907 | * via keventd. | 965 | * via keventd. |
908 | */ | 966 | */ |
909 | static void pxafb_task(void *dummy) | 967 | static void pxafb_task(struct work_struct *work) |
910 | { | 968 | { |
911 | struct pxafb_info *fbi = dummy; | 969 | struct pxafb_info *fbi = |
970 | container_of(work, struct pxafb_info, task); | ||
912 | u_int state = xchg(&fbi->task_state, -1); | 971 | u_int state = xchg(&fbi->task_state, -1); |
913 | 972 | ||
914 | set_ctrlr_state(fbi, state); | 973 | set_ctrlr_state(fbi, state); |
@@ -1049,6 +1108,8 @@ static struct pxafb_info * __init pxafb_init_fbinfo(struct device *dev) | |||
1049 | struct pxafb_info *fbi; | 1108 | struct pxafb_info *fbi; |
1050 | void *addr; | 1109 | void *addr; |
1051 | struct pxafb_mach_info *inf = dev->platform_data; | 1110 | struct pxafb_mach_info *inf = dev->platform_data; |
1111 | struct pxafb_mode_info *mode = inf->modes; | ||
1112 | int i, smemlen; | ||
1052 | 1113 | ||
1053 | /* Alloc the pxafb_info and pseudo_palette in one step */ | 1114 | /* Alloc the pxafb_info and pseudo_palette in one step */ |
1054 | fbi = kmalloc(sizeof(struct pxafb_info) + sizeof(u32) * 16, GFP_KERNEL); | 1115 | fbi = kmalloc(sizeof(struct pxafb_info) + sizeof(u32) * 16, GFP_KERNEL); |
@@ -1082,34 +1143,24 @@ static struct pxafb_info * __init pxafb_init_fbinfo(struct device *dev) | |||
1082 | addr = addr + sizeof(struct pxafb_info); | 1143 | addr = addr + sizeof(struct pxafb_info); |
1083 | fbi->fb.pseudo_palette = addr; | 1144 | fbi->fb.pseudo_palette = addr; |
1084 | 1145 | ||
1085 | fbi->max_xres = inf->xres; | 1146 | pxafb_setmode(&fbi->fb.var, mode); |
1086 | fbi->fb.var.xres = inf->xres; | 1147 | |
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; | 1148 | fbi->cmap_inverse = inf->cmap_inverse; |
1103 | fbi->cmap_static = inf->cmap_static; | 1149 | fbi->cmap_static = inf->cmap_static; |
1150 | |||
1104 | fbi->lccr0 = inf->lccr0; | 1151 | fbi->lccr0 = inf->lccr0; |
1105 | fbi->lccr3 = inf->lccr3; | 1152 | fbi->lccr3 = inf->lccr3; |
1106 | fbi->state = C_STARTUP; | 1153 | fbi->state = C_STARTUP; |
1107 | fbi->task_state = (u_char)-1; | 1154 | fbi->task_state = (u_char)-1; |
1108 | fbi->fb.fix.smem_len = fbi->max_xres * fbi->max_yres * | 1155 | |
1109 | fbi->max_bpp / 8; | 1156 | for (i = 0; i < inf->num_modes; i++) { |
1157 | smemlen = mode[i].xres * mode[i].yres * mode[i].bpp / 8; | ||
1158 | if (smemlen > fbi->fb.fix.smem_len) | ||
1159 | fbi->fb.fix.smem_len = smemlen; | ||
1160 | } | ||
1110 | 1161 | ||
1111 | init_waitqueue_head(&fbi->ctrlr_wait); | 1162 | init_waitqueue_head(&fbi->ctrlr_wait); |
1112 | INIT_WORK(&fbi->task, pxafb_task, fbi); | 1163 | INIT_WORK(&fbi->task, pxafb_task); |
1113 | init_MUTEX(&fbi->ctrlr_sem); | 1164 | init_MUTEX(&fbi->ctrlr_sem); |
1114 | 1165 | ||
1115 | return fbi; | 1166 | return fbi; |
@@ -1307,12 +1358,12 @@ int __init pxafb_probe(struct platform_device *dev) | |||
1307 | (inf->lccr0 & LCCR0_SDS) == LCCR0_Dual) | 1358 | (inf->lccr0 & LCCR0_SDS) == LCCR0_Dual) |
1308 | dev_warn(&dev->dev, "Dual panel only valid in passive mode\n"); | 1359 | dev_warn(&dev->dev, "Dual panel only valid in passive mode\n"); |
1309 | if ((inf->lccr0 & LCCR0_PAS) == LCCR0_Pas && | 1360 | if ((inf->lccr0 & LCCR0_PAS) == LCCR0_Pas && |
1310 | (inf->upper_margin || inf->lower_margin)) | 1361 | (inf->modes->upper_margin || inf->modes->lower_margin)) |
1311 | dev_warn(&dev->dev, "Upper and lower margins must be 0 in passive mode\n"); | 1362 | dev_warn(&dev->dev, "Upper and lower margins must be 0 in passive mode\n"); |
1312 | #endif | 1363 | #endif |
1313 | 1364 | ||
1314 | dev_dbg(&dev->dev, "got a %dx%dx%d LCD\n",inf->xres, inf->yres, inf->bpp); | 1365 | 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) { | 1366 | if (inf->modes->xres == 0 || inf->modes->yres == 0 || inf->modes->bpp == 0) { |
1316 | dev_err(&dev->dev, "Invalid resolution or bit depth\n"); | 1367 | dev_err(&dev->dev, "Invalid resolution or bit depth\n"); |
1317 | ret = -EINVAL; | 1368 | ret = -EINVAL; |
1318 | goto failed; | 1369 | goto failed; |