diff options
author | Nicolas Ferre <nicolas.ferre@atmel.com> | 2008-07-24 00:31:34 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-07-24 13:47:40 -0400 |
commit | 968910bd03b226ed410d092c2da59dffe5bfe8de (patch) | |
tree | 8a49cbca6597dd67d6c21515c4c774a9d83ec522 /drivers/video/atmel_lcdfb.c | |
parent | 84c41ce83e9b2987ccef352f28ba0055b26c8f8e (diff) |
atmel_lcdfb: avoid division by zero
Avoid division by zero in atmel_lcdfb_check_var() function.
If pixclock is not specified while passing a var structure in
the check_var() funtion, a division by zero occurs (when
translating pixclock to KHz).
This patch adds a checking of this value and try to choose a
video mode in the modelist.
The mode found in the probe function in added to the modelist.
[akpm@linux-foundation.org: coding-style fixes]
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Cc: Haavard Skinnemoen <hskinnemoen@atmel.com>
Cc: Andrew Victor <linux@maxim.org.za>
Cc: "Antonino A. Daplas" <adaplas@pol.net>
Cc: Krzysztof Helt <krzysztof.h1@poczta.fm>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/video/atmel_lcdfb.c')
-rw-r--r-- | drivers/video/atmel_lcdfb.c | 35 |
1 files changed, 35 insertions, 0 deletions
diff --git a/drivers/video/atmel_lcdfb.c b/drivers/video/atmel_lcdfb.c index d335bb96b03b..5b3a15dffb5f 100644 --- a/drivers/video/atmel_lcdfb.c +++ b/drivers/video/atmel_lcdfb.c | |||
@@ -256,6 +256,20 @@ static int atmel_lcdfb_alloc_video_memory(struct atmel_lcdfb_info *sinfo) | |||
256 | return 0; | 256 | return 0; |
257 | } | 257 | } |
258 | 258 | ||
259 | static const struct fb_videomode *atmel_lcdfb_choose_mode(struct fb_var_screeninfo *var, | ||
260 | struct fb_info *info) | ||
261 | { | ||
262 | struct fb_videomode varfbmode; | ||
263 | const struct fb_videomode *fbmode = NULL; | ||
264 | |||
265 | fb_var_to_videomode(&varfbmode, var); | ||
266 | fbmode = fb_find_nearest_mode(&varfbmode, &info->modelist); | ||
267 | if (fbmode) | ||
268 | fb_videomode_to_var(var, fbmode); | ||
269 | return fbmode; | ||
270 | } | ||
271 | |||
272 | |||
259 | /** | 273 | /** |
260 | * atmel_lcdfb_check_var - Validates a var passed in. | 274 | * atmel_lcdfb_check_var - Validates a var passed in. |
261 | * @var: frame buffer variable screen structure | 275 | * @var: frame buffer variable screen structure |
@@ -289,6 +303,15 @@ static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var, | |||
289 | clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000; | 303 | clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000; |
290 | 304 | ||
291 | dev_dbg(dev, "%s:\n", __func__); | 305 | dev_dbg(dev, "%s:\n", __func__); |
306 | |||
307 | if (!(var->pixclock && var->bits_per_pixel)) { | ||
308 | /* choose a suitable mode if possible */ | ||
309 | if (!atmel_lcdfb_choose_mode(var, info)) { | ||
310 | dev_err(dev, "needed value not specified\n"); | ||
311 | return -EINVAL; | ||
312 | } | ||
313 | } | ||
314 | |||
292 | dev_dbg(dev, " resolution: %ux%u\n", var->xres, var->yres); | 315 | dev_dbg(dev, " resolution: %ux%u\n", var->xres, var->yres); |
293 | dev_dbg(dev, " pixclk: %lu KHz\n", PICOS2KHZ(var->pixclock)); | 316 | dev_dbg(dev, " pixclk: %lu KHz\n", PICOS2KHZ(var->pixclock)); |
294 | dev_dbg(dev, " bpp: %u\n", var->bits_per_pixel); | 317 | dev_dbg(dev, " bpp: %u\n", var->bits_per_pixel); |
@@ -299,6 +322,13 @@ static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var, | |||
299 | return -EINVAL; | 322 | return -EINVAL; |
300 | } | 323 | } |
301 | 324 | ||
325 | /* Do not allow to have real resoulution larger than virtual */ | ||
326 | if (var->xres > var->xres_virtual) | ||
327 | var->xres_virtual = var->xres; | ||
328 | |||
329 | if (var->yres > var->yres_virtual) | ||
330 | var->yres_virtual = var->yres; | ||
331 | |||
302 | /* Force same alignment for each line */ | 332 | /* Force same alignment for each line */ |
303 | var->xres = (var->xres + 3) & ~3UL; | 333 | var->xres = (var->xres + 3) & ~3UL; |
304 | var->xres_virtual = (var->xres_virtual + 3) & ~3UL; | 334 | var->xres_virtual = (var->xres_virtual + 3) & ~3UL; |
@@ -740,6 +770,7 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev) | |||
740 | struct fb_info *info; | 770 | struct fb_info *info; |
741 | struct atmel_lcdfb_info *sinfo; | 771 | struct atmel_lcdfb_info *sinfo; |
742 | struct atmel_lcdfb_info *pdata_sinfo; | 772 | struct atmel_lcdfb_info *pdata_sinfo; |
773 | struct fb_videomode fbmode; | ||
743 | struct resource *regs = NULL; | 774 | struct resource *regs = NULL; |
744 | struct resource *map = NULL; | 775 | struct resource *map = NULL; |
745 | int ret; | 776 | int ret; |
@@ -906,6 +937,10 @@ static int __init atmel_lcdfb_probe(struct platform_device *pdev) | |||
906 | goto free_cmap; | 937 | goto free_cmap; |
907 | } | 938 | } |
908 | 939 | ||
940 | /* add selected videomode to modelist */ | ||
941 | fb_var_to_videomode(&fbmode, &info->var); | ||
942 | fb_add_videomode(&fbmode, &info->modelist); | ||
943 | |||
909 | /* Power up the LCDC screen */ | 944 | /* Power up the LCDC screen */ |
910 | if (sinfo->atmel_lcdfb_power_control) | 945 | if (sinfo->atmel_lcdfb_power_control) |
911 | sinfo->atmel_lcdfb_power_control(1); | 946 | sinfo->atmel_lcdfb_power_control(1); |