Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

udlfb: allow reallocating the framebuffer

This patch changes udlfb so that it may reallocate the framebuffer when
setting higher-resolution mode. If we boot the system without monitor
attached, udlfb creates a framebuffer with the size 800x600. This patch
makes it possible to select higher videomode with the fbset command when
a monitor is attached.

Note that there is no reliable way to prevent the system from touching the
old framebuffer, so we must not free it. We add it to the list
dlfb->deferred_free and free it when the driver is unloaded.

Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
[b.zolnierkie: sparse fixes]
Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>

authored by

Mikulas Patocka and committed by
Bartlomiej Zolnierkiewicz
7433914e 0ac319b7

+50 -25
+49 -25
drivers/video/fbdev/udlfb.c
··· 73 73 static bool shadow = 1; /* Optionally disable shadow framebuffer */ 74 74 static int pixel_limit; /* Optionally force a pixel resolution limit */ 75 75 76 + struct dlfb_deferred_free { 77 + struct list_head list; 78 + void *mem; 79 + }; 80 + 81 + static int dlfb_realloc_framebuffer(struct dlfb_data *dlfb, struct fb_info *info, u32 new_len); 82 + 76 83 /* dlfb keeps a list of urbs for efficient bulk transfers */ 77 84 static void dlfb_urb_completion(struct urb *urb); 78 85 static struct urb *dlfb_get_urb(struct dlfb_data *dlfb); ··· 934 927 { 935 928 struct dlfb_data *dlfb = container_of(kref, struct dlfb_data, kref); 936 929 930 + while (!list_empty(&dlfb->deferred_free)) { 931 + struct dlfb_deferred_free *d = list_entry(dlfb->deferred_free.next, struct dlfb_deferred_free, list); 932 + list_del(&d->list); 933 + vfree(d->mem); 934 + kfree(d); 935 + } 937 936 vfree(dlfb->backing_buffer); 938 937 kfree(dlfb->edid); 939 938 kfree(dlfb); ··· 1033 1020 struct fb_videomode mode; 1034 1021 struct dlfb_data *dlfb = info->par; 1035 1022 1036 - /* TODO: support dynamically changing framebuffer size */ 1037 - if ((var->xres * var->yres * 2) > info->fix.smem_len) 1038 - return -EINVAL; 1039 - 1040 1023 /* set device-specific elements of var unrelated to mode */ 1041 1024 dlfb_var_color_format(var); 1042 1025 ··· 1051 1042 u16 *pix_framebuffer; 1052 1043 int i; 1053 1044 struct fb_var_screeninfo fvs; 1045 + u32 line_length = info->var.xres * (info->var.bits_per_pixel / 8); 1054 1046 1055 1047 /* clear the activate field because it causes spurious miscompares */ 1056 1048 fvs = info->var; ··· 1061 1051 if (!memcmp(&dlfb->current_mode, &fvs, sizeof(struct fb_var_screeninfo))) 1062 1052 return 0; 1063 1053 1054 + result = dlfb_realloc_framebuffer(dlfb, info, info->var.yres * line_length); 1055 + if (result) 1056 + return result; 1057 + 1064 1058 result = dlfb_set_video_mode(dlfb, &info->var); 1065 1059 1066 1060 if (result) 1067 1061 return result; 1068 1062 1069 1063 dlfb->current_mode = fvs; 1070 - info->fix.line_length = info->var.xres * (info->var.bits_per_pixel / 8); 1064 + info->fix.line_length = line_length; 1071 1065 1072 1066 if (dlfb->fb_count == 0) { 1073 1067 ··· 1080 1066 pix_framebuffer = (u16 *) info->screen_base; 1081 1067 for (i = 0; i < info->fix.smem_len / 2; i++) 1082 1068 pix_framebuffer[i] = 0x37e6; 1083 - 1084 - dlfb_handle_damage(dlfb, 0, 0, info->var.xres, info->var.yres, 1085 - info->screen_base); 1086 1069 } 1070 + 1071 + dlfb_handle_damage(dlfb, 0, 0, info->var.xres, info->var.yres, 1072 + info->screen_base); 1087 1073 1088 1074 return 0; 1089 1075 } ··· 1160 1146 }; 1161 1147 1162 1148 1149 + static void dlfb_deferred_vfree(struct dlfb_data *dlfb, void *mem) 1150 + { 1151 + struct dlfb_deferred_free *d = kmalloc(sizeof(struct dlfb_deferred_free), GFP_KERNEL); 1152 + if (!d) 1153 + return; 1154 + d->mem = mem; 1155 + list_add(&d->list, &dlfb->deferred_free); 1156 + } 1157 + 1163 1158 /* 1164 1159 * Assumes &info->lock held by caller 1165 1160 * Assumes no active clients have framebuffer open 1166 1161 */ 1167 - static int dlfb_realloc_framebuffer(struct dlfb_data *dlfb, struct fb_info *info) 1162 + static int dlfb_realloc_framebuffer(struct dlfb_data *dlfb, struct fb_info *info, u32 new_len) 1168 1163 { 1169 - int old_len = info->fix.smem_len; 1170 - int new_len; 1171 - unsigned char *old_fb = info->screen_base; 1164 + u32 old_len = info->fix.smem_len; 1165 + const void *old_fb = (const void __force *)info->screen_base; 1172 1166 unsigned char *new_fb; 1173 1167 unsigned char *new_back = NULL; 1174 1168 1175 - new_len = info->fix.line_length * info->var.yres; 1169 + new_len = PAGE_ALIGN(new_len); 1176 1170 1177 - if (PAGE_ALIGN(new_len) > old_len) { 1171 + if (new_len > old_len) { 1178 1172 /* 1179 1173 * Alloc system memory for virtual framebuffer 1180 1174 */ ··· 1191 1169 dev_err(info->dev, "Virtual framebuffer alloc failed\n"); 1192 1170 return -ENOMEM; 1193 1171 } 1172 + memset(new_fb, 0xff, new_len); 1194 1173 1195 1174 if (info->screen_base) { 1196 1175 memcpy(new_fb, old_fb, old_len); 1197 - vfree(info->screen_base); 1176 + dlfb_deferred_vfree(dlfb, (void __force *)info->screen_base); 1198 1177 } 1199 1178 1200 - info->screen_base = new_fb; 1201 - info->fix.smem_len = PAGE_ALIGN(new_len); 1179 + info->screen_base = (char __iomem *)new_fb; 1180 + info->fix.smem_len = new_len; 1202 1181 info->fix.smem_start = (unsigned long) new_fb; 1203 1182 info->flags = udlfb_info_flags; 1204 1183 ··· 1215 1192 dev_info(info->dev, 1216 1193 "No shadow/backing buffer allocated\n"); 1217 1194 else { 1218 - vfree(dlfb->backing_buffer); 1195 + dlfb_deferred_vfree(dlfb, dlfb->backing_buffer); 1219 1196 dlfb->backing_buffer = new_back; 1220 1197 } 1221 1198 } ··· 1367 1344 * with mode size info, we can now alloc our framebuffer. 1368 1345 */ 1369 1346 memcpy(&info->fix, &dlfb_fix, sizeof(dlfb_fix)); 1370 - info->fix.line_length = info->var.xres * 1371 - (info->var.bits_per_pixel / 8); 1372 - 1373 - result = dlfb_realloc_framebuffer(dlfb, info); 1374 - 1375 1347 } else 1376 1348 result = -EINVAL; 1377 1349 ··· 1454 1436 if (!dlfb->edid || memcmp(src, dlfb->edid, src_size)) 1455 1437 return -EINVAL; 1456 1438 1457 - dlfb_ops_set_par(fb_info); 1439 + ret = dlfb_ops_set_par(fb_info); 1440 + if (ret) 1441 + return ret; 1442 + 1458 1443 return src_size; 1459 1444 } 1460 1445 ··· 1617 1596 } 1618 1597 1619 1598 kref_init(&dlfb->kref); /* matching kref_put in usb .disconnect fn */ 1599 + INIT_LIST_HEAD(&dlfb->deferred_free); 1620 1600 1621 1601 dlfb->udev = usbdev; 1622 1602 usb_set_intfdata(intf, dlfb); ··· 1715 1693 dlfb_select_std_channel(dlfb); 1716 1694 1717 1695 dlfb_ops_check_var(&info->var, info); 1718 - dlfb_ops_set_par(info); 1696 + retval = dlfb_ops_set_par(info); 1697 + if (retval) 1698 + goto error; 1719 1699 1720 1700 retval = register_framebuffer(info); 1721 1701 if (retval < 0) {
+1
include/video/udlfb.h
··· 58 58 atomic_t bytes_sent; /* to usb, after compression including overhead */ 59 59 atomic_t cpu_kcycles_used; /* transpired during pixel processing */ 60 60 struct fb_var_screeninfo current_mode; 61 + struct list_head deferred_free; 61 62 }; 62 63 63 64 #define NR_USB_REQUEST_I2C_SUB_IO 0x02