diff options
author | Laurent Pinchart <laurent.pinchart@ideasonboard.com> | 2011-08-31 07:00:53 -0400 |
---|---|---|
committer | Florian Tobias Schandinat <FlorianSchandinat@gmx.de> | 2011-09-05 12:37:13 -0400 |
commit | 0386219441d48e0f0902e9f145f0d75ad952d753 (patch) | |
tree | c63b4cce6f9e1012837befb4b3422a35822157bc /drivers/video/sh_mobile_lcdcfb.c | |
parent | da6cf5125f66ed1810616937777884cea021e66a (diff) |
fbdev: sh_mobile_lcdc: Adjust requested parameters in .fb_check_var
Instead of failing when the requested fb_var_screeninfo parameters are
not supported, adjust the parameters according to the hardware
capabilities.
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
Diffstat (limited to 'drivers/video/sh_mobile_lcdcfb.c')
-rw-r--r-- | drivers/video/sh_mobile_lcdcfb.c | 103 |
1 files changed, 88 insertions, 15 deletions
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index 088cb17857e3..33b0ff83c154 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c | |||
@@ -1055,28 +1055,101 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in | |||
1055 | { | 1055 | { |
1056 | struct sh_mobile_lcdc_chan *ch = info->par; | 1056 | struct sh_mobile_lcdc_chan *ch = info->par; |
1057 | struct sh_mobile_lcdc_priv *p = ch->lcdc; | 1057 | struct sh_mobile_lcdc_priv *p = ch->lcdc; |
1058 | unsigned int best_dist = (unsigned int)-1; | ||
1059 | unsigned int best_xres = 0; | ||
1060 | unsigned int best_yres = 0; | ||
1061 | unsigned int i; | ||
1058 | 1062 | ||
1059 | if (var->xres > MAX_XRES || var->yres > MAX_YRES || | 1063 | if (var->xres > MAX_XRES || var->yres > MAX_YRES) |
1060 | var->xres * var->yres * (ch->cfg.bpp / 8) * 2 > info->fix.smem_len) { | ||
1061 | dev_warn(info->dev, "Invalid info: %u-%u-%u-%u x %u-%u-%u-%u @ %lukHz!\n", | ||
1062 | var->left_margin, var->xres, var->right_margin, var->hsync_len, | ||
1063 | var->upper_margin, var->yres, var->lower_margin, var->vsync_len, | ||
1064 | PICOS2KHZ(var->pixclock)); | ||
1065 | return -EINVAL; | 1064 | return -EINVAL; |
1065 | |||
1066 | /* If board code provides us with a list of available modes, make sure | ||
1067 | * we use one of them. Find the mode closest to the requested one. The | ||
1068 | * distance between two modes is defined as the size of the | ||
1069 | * non-overlapping parts of the two rectangles. | ||
1070 | */ | ||
1071 | for (i = 0; i < ch->cfg.num_cfg; ++i) { | ||
1072 | const struct fb_videomode *mode = &ch->cfg.lcd_cfg[i]; | ||
1073 | unsigned int dist; | ||
1074 | |||
1075 | /* We can only round up. */ | ||
1076 | if (var->xres > mode->xres || var->yres > mode->yres) | ||
1077 | continue; | ||
1078 | |||
1079 | dist = var->xres * var->yres + mode->xres * mode->yres | ||
1080 | - 2 * min(var->xres, mode->xres) | ||
1081 | * min(var->yres, mode->yres); | ||
1082 | |||
1083 | if (dist < best_dist) { | ||
1084 | best_xres = mode->xres; | ||
1085 | best_yres = mode->yres; | ||
1086 | best_dist = dist; | ||
1087 | } | ||
1066 | } | 1088 | } |
1067 | 1089 | ||
1068 | /* only accept the forced_bpp for dual channel configurations */ | 1090 | /* If no available mode can be used, return an error. */ |
1069 | if (p->forced_bpp && p->forced_bpp != var->bits_per_pixel) | 1091 | if (ch->cfg.num_cfg != 0) { |
1092 | if (best_dist == (unsigned int)-1) | ||
1093 | return -EINVAL; | ||
1094 | |||
1095 | var->xres = best_xres; | ||
1096 | var->yres = best_yres; | ||
1097 | } | ||
1098 | |||
1099 | /* Make sure the virtual resolution is at least as big as the visible | ||
1100 | * resolution. | ||
1101 | */ | ||
1102 | if (var->xres_virtual < var->xres) | ||
1103 | var->xres_virtual = var->xres; | ||
1104 | if (var->yres_virtual < var->yres) | ||
1105 | var->yres_virtual = var->yres; | ||
1106 | |||
1107 | if (var->bits_per_pixel <= 16) { /* RGB 565 */ | ||
1108 | var->bits_per_pixel = 16; | ||
1109 | var->red.offset = 11; | ||
1110 | var->red.length = 5; | ||
1111 | var->green.offset = 5; | ||
1112 | var->green.length = 6; | ||
1113 | var->blue.offset = 0; | ||
1114 | var->blue.length = 5; | ||
1115 | var->transp.offset = 0; | ||
1116 | var->transp.length = 0; | ||
1117 | } else if (var->bits_per_pixel <= 24) { /* RGB 888 */ | ||
1118 | var->bits_per_pixel = 24; | ||
1119 | var->red.offset = 16; | ||
1120 | var->red.length = 8; | ||
1121 | var->green.offset = 8; | ||
1122 | var->green.length = 8; | ||
1123 | var->blue.offset = 0; | ||
1124 | var->blue.length = 8; | ||
1125 | var->transp.offset = 0; | ||
1126 | var->transp.length = 0; | ||
1127 | } else if (var->bits_per_pixel <= 32) { /* RGBA 888 */ | ||
1128 | var->bits_per_pixel = 32; | ||
1129 | var->red.offset = 16; | ||
1130 | var->red.length = 8; | ||
1131 | var->green.offset = 8; | ||
1132 | var->green.length = 8; | ||
1133 | var->blue.offset = 0; | ||
1134 | var->blue.length = 8; | ||
1135 | var->transp.offset = 24; | ||
1136 | var->transp.length = 8; | ||
1137 | } else | ||
1070 | return -EINVAL; | 1138 | return -EINVAL; |
1071 | 1139 | ||
1072 | switch (var->bits_per_pixel) { | 1140 | var->red.msb_right = 0; |
1073 | case 16: /* PKF[4:0] = 00011 - RGB 565 */ | 1141 | var->green.msb_right = 0; |
1074 | case 24: /* PKF[4:0] = 01011 - RGB 888 */ | 1142 | var->blue.msb_right = 0; |
1075 | case 32: /* PKF[4:0] = 00000 - RGBA 888 */ | 1143 | var->transp.msb_right = 0; |
1076 | break; | 1144 | |
1077 | default: | 1145 | /* Make sure we don't exceed our allocated memory. */ |
1146 | if (var->xres_virtual * var->yres_virtual * var->bits_per_pixel / 8 > | ||
1147 | info->fix.smem_len) | ||
1148 | return -EINVAL; | ||
1149 | |||
1150 | /* only accept the forced_bpp for dual channel configurations */ | ||
1151 | if (p->forced_bpp && p->forced_bpp != var->bits_per_pixel) | ||
1078 | return -EINVAL; | 1152 | return -EINVAL; |
1079 | } | ||
1080 | 1153 | ||
1081 | return 0; | 1154 | return 0; |
1082 | } | 1155 | } |