diff options
Diffstat (limited to 'drivers/video')
27 files changed, 578 insertions, 266 deletions
diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig index e91edef98633..787792c3d08d 100644 --- a/drivers/video/console/Kconfig +++ b/drivers/video/console/Kconfig @@ -152,7 +152,7 @@ config FRAMEBUFFER_CONSOLE_ROTATION config FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER bool "Framebuffer Console Deferred Takeover" - depends on FRAMEBUFFER_CONSOLE=y && DUMMY_CONSOLE=y + depends on FB=y && FRAMEBUFFER_CONSOLE && DUMMY_CONSOLE help If enabled this defers the framebuffer console taking over the console from the dummy console until the first text is displayed on diff --git a/drivers/video/console/dummycon.c b/drivers/video/console/dummycon.c index 0254251fdd79..45ad925ad5f8 100644 --- a/drivers/video/console/dummycon.c +++ b/drivers/video/console/dummycon.c @@ -38,13 +38,11 @@ void dummycon_register_output_notifier(struct notifier_block *nb) if (dummycon_putc_called) nb->notifier_call(nb, 0, NULL); } -EXPORT_SYMBOL_GPL(dummycon_register_output_notifier); void dummycon_unregister_output_notifier(struct notifier_block *nb) { raw_notifier_chain_unregister(&dummycon_output_nh, nb); } -EXPORT_SYMBOL_GPL(dummycon_unregister_output_notifier); static void dummycon_putc(struct vc_data *vc, int c, int ypos, int xpos) { diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c index f09e17b60e45..09731b2f6815 100644 --- a/drivers/video/console/vgacon.c +++ b/drivers/video/console/vgacon.c @@ -112,6 +112,11 @@ EXPORT_SYMBOL(vgacon_text_force); static int __init text_mode(char *str) { vgacon_text_mode_force = true; + + pr_warning("You have booted with nomodeset. This means your GPU drivers are DISABLED\n"); + pr_warning("Any video related functionality will be severely degraded, and you may not even be able to suspend the system properly\n"); + pr_warning("Unless you actually understand what nomodeset does, you should reboot without enabling it\n"); + return 1; } diff --git a/drivers/video/fbdev/amifb.c b/drivers/video/fbdev/amifb.c index cc11c6061298..0777aff211e5 100644 --- a/drivers/video/fbdev/amifb.c +++ b/drivers/video/fbdev/amifb.c @@ -2303,7 +2303,7 @@ static void ami_build_copper(struct fb_info *info) ami_rebuild_copper(info->par); } - +#ifndef MODULE static void __init amifb_setup_mcap(char *spec) { char *p; @@ -2368,7 +2368,7 @@ static int __init amifb_setup(char *options) return 0; } - +#endif static int amifb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 5fb156bdcf4e..75ebbbf0a1fb 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -2234,8 +2234,8 @@ static int fbcon_switch(struct vc_data *vc) * * info->currcon = vc->vc_num; */ - for (i = 0; i < FB_MAX; i++) { - if (registered_fb[i] != NULL && registered_fb[i]->fbcon_par) { + for_each_registered_fb(i) { + if (registered_fb[i]->fbcon_par) { struct fbcon_ops *o = registered_fb[i]->fbcon_par; o->currcon = vc->vc_num; @@ -3124,11 +3124,9 @@ static int fbcon_fb_unregistered(struct fb_info *info) if (idx == info_idx) { info_idx = -1; - for (i = 0; i < FB_MAX; i++) { - if (registered_fb[i] != NULL) { - info_idx = i; - break; - } + for_each_registered_fb(i) { + info_idx = i; + break; } } @@ -3594,13 +3592,24 @@ static int fbcon_init_device(void) } #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER +static void fbcon_register_existing_fbs(struct work_struct *work) +{ + int i; + + console_lock(); + + for_each_registered_fb(i) + fbcon_fb_registered(registered_fb[i]); + + console_unlock(); +} + static struct notifier_block fbcon_output_nb; +static DECLARE_WORK(fbcon_deferred_takeover_work, fbcon_register_existing_fbs); static int fbcon_output_notifier(struct notifier_block *nb, unsigned long action, void *data) { - int i; - WARN_CONSOLE_UNLOCKED(); pr_info("fbcon: Taking over console\n"); @@ -3609,45 +3618,37 @@ static int fbcon_output_notifier(struct notifier_block *nb, deferred_takeover = false; logo_shown = FBCON_LOGO_DONTSHOW; - for (i = 0; i < FB_MAX; i++) { - if (registered_fb[i]) - fbcon_fb_registered(registered_fb[i]); - } + /* We may get called in atomic context */ + schedule_work(&fbcon_deferred_takeover_work); return NOTIFY_OK; } - -static void fbcon_register_output_notifier(void) -{ - fbcon_output_nb.notifier_call = fbcon_output_notifier; - dummycon_register_output_notifier(&fbcon_output_nb); -} -#else -static inline void fbcon_register_output_notifier(void) {} #endif static void fbcon_start(void) { + WARN_CONSOLE_UNLOCKED(); + +#ifdef CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER + if (conswitchp != &dummy_con) + deferred_takeover = false; + if (deferred_takeover) { - fbcon_register_output_notifier(); + fbcon_output_nb.notifier_call = fbcon_output_notifier; + dummycon_register_output_notifier(&fbcon_output_nb); return; } +#endif if (num_registered_fb) { int i; - console_lock(); - - for (i = 0; i < FB_MAX; i++) { - if (registered_fb[i] != NULL) { - info_idx = i; - break; - } + for_each_registered_fb(i) { + info_idx = i; + break; } do_fbcon_takeover(0); - console_unlock(); - } } @@ -3669,15 +3670,12 @@ static void fbcon_exit(void) kfree((void *)softback_buf); softback_buf = 0UL; - for (i = 0; i < FB_MAX; i++) { + for_each_registered_fb(i) { int pending = 0; mapped = 0; info = registered_fb[i]; - if (info == NULL) - continue; - if (info->queue.func) pending = cancel_work_sync(&info->queue); DPRINTK("fbcon: %s pending work\n", (pending ? "canceled" : @@ -3733,8 +3731,8 @@ void __init fb_console_init(void) for (i = 0; i < MAX_NR_CONSOLES; i++) con2fb_map[i] = -1; - console_unlock(); fbcon_start(); + console_unlock(); } #ifdef MODULE diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c index 609438d2465b..20405421a5ed 100644 --- a/drivers/video/fbdev/core/fbmem.c +++ b/drivers/video/fbdev/core/fbmem.c @@ -1347,6 +1347,7 @@ static long fb_compat_ioctl(struct file *file, unsigned int cmd, case FBIOGET_CON2FBMAP: case FBIOPUT_CON2FBMAP: arg = (unsigned long) compat_ptr(arg); + /* fall through */ case FBIOBLANK: ret = do_fb_ioctl(info, cmd, arg); break; @@ -1593,10 +1594,8 @@ static int do_remove_conflicting_framebuffers(struct apertures_struct *a, int i, ret; /* check all firmware fbs and kick off if the base addr overlaps */ - for (i = 0 ; i < FB_MAX; i++) { + for_each_registered_fb(i) { struct apertures_struct *gen_aper; - if (!registered_fb[i]) - continue; if (!(registered_fb[i]->flags & FBINFO_MISC_FIRMWARE)) continue; @@ -1691,25 +1690,30 @@ static int do_register_framebuffer(struct fb_info *fb_info) event.info = fb_info; if (!lockless_register_fb) console_lock(); + else + atomic_inc(&ignore_console_lock_warning); if (!lock_fb_info(fb_info)) { - if (!lockless_register_fb) - console_unlock(); - return -ENODEV; + ret = -ENODEV; + goto unlock_console; } + ret = 0; fb_notifier_call_chain(FB_EVENT_FB_REGISTERED, &event); unlock_fb_info(fb_info); +unlock_console: if (!lockless_register_fb) console_unlock(); - return 0; + else + atomic_dec(&ignore_console_lock_warning); + return ret; } -static int do_unregister_framebuffer(struct fb_info *fb_info) +static int unbind_console(struct fb_info *fb_info) { struct fb_event event; - int i, ret = 0; + int ret; + int i = fb_info->node; - i = fb_info->node; if (i < 0 || i >= FB_MAX || registered_fb[i] != fb_info) return -EINVAL; @@ -1724,17 +1728,29 @@ static int do_unregister_framebuffer(struct fb_info *fb_info) unlock_fb_info(fb_info); console_unlock(); + return ret; +} + +static int __unlink_framebuffer(struct fb_info *fb_info); + +static int do_unregister_framebuffer(struct fb_info *fb_info) +{ + struct fb_event event; + int ret; + + ret = unbind_console(fb_info); + if (ret) return -EINVAL; pm_vt_switch_unregister(fb_info->dev); - unlink_framebuffer(fb_info); + __unlink_framebuffer(fb_info); if (fb_info->pixmap.addr && (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT)) kfree(fb_info->pixmap.addr); fb_destroy_modelist(&fb_info->modelist); - registered_fb[i] = NULL; + registered_fb[fb_info->node] = NULL; num_registered_fb--; fb_cleanup_device(fb_info); event.info = fb_info; @@ -1747,7 +1763,7 @@ static int do_unregister_framebuffer(struct fb_info *fb_info) return 0; } -int unlink_framebuffer(struct fb_info *fb_info) +static int __unlink_framebuffer(struct fb_info *fb_info) { int i; @@ -1759,6 +1775,20 @@ int unlink_framebuffer(struct fb_info *fb_info) device_destroy(fb_class, MKDEV(FB_MAJOR, i)); fb_info->dev = NULL; } + + return 0; +} + +int unlink_framebuffer(struct fb_info *fb_info) +{ + int ret; + + ret = __unlink_framebuffer(fb_info); + if (ret) + return ret; + + unbind_console(fb_info); + return 0; } EXPORT_SYMBOL(unlink_framebuffer); diff --git a/drivers/video/fbdev/core/modedb.c b/drivers/video/fbdev/core/modedb.c index 2510fa728d77..283d9307df21 100644 --- a/drivers/video/fbdev/core/modedb.c +++ b/drivers/video/fbdev/core/modedb.c @@ -628,45 +628,47 @@ static int fb_try_mode(struct fb_var_screeninfo *var, struct fb_info *info, } /** - * fb_find_mode - finds a valid video mode - * @var: frame buffer user defined part of display - * @info: frame buffer info structure - * @mode_option: string video mode to find - * @db: video mode database - * @dbsize: size of @db - * @default_mode: default video mode to fall back to - * @default_bpp: default color depth in bits per pixel + * fb_find_mode - finds a valid video mode + * @var: frame buffer user defined part of display + * @info: frame buffer info structure + * @mode_option: string video mode to find + * @db: video mode database + * @dbsize: size of @db + * @default_mode: default video mode to fall back to + * @default_bpp: default color depth in bits per pixel * - * Finds a suitable video mode, starting with the specified mode - * in @mode_option with fallback to @default_mode. If - * @default_mode fails, all modes in the video mode database will - * be tried. + * Finds a suitable video mode, starting with the specified mode + * in @mode_option with fallback to @default_mode. If + * @default_mode fails, all modes in the video mode database will + * be tried. * - * Valid mode specifiers for @mode_option: + * Valid mode specifiers for @mode_option:: * - * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][m] or - * <name>[-<bpp>][@<refresh>] + * <xres>x<yres>[M][R][-<bpp>][@<refresh>][i][p][m] + * + * or :: * - * with <xres>, <yres>, <bpp> and <refresh> decimal numbers and - * <name> a string. + * <name>[-<bpp>][@<refresh>] * - * If 'M' is present after yres (and before refresh/bpp if present), - * the function will compute the timings using VESA(tm) Coordinated - * Video Timings (CVT). If 'R' is present after 'M', will compute with - * reduced blanking (for flatpanels). If 'i' is present, compute - * interlaced mode. If 'm' is present, add margins equal to 1.8% - * of xres rounded down to 8 pixels, and 1.8% of yres. The char - * 'i' and 'm' must be after 'M' and 'R'. Example: + * with <xres>, <yres>, <bpp> and <refresh> decimal numbers and + * <name> a string. * - * 1024x768MR-8@60m - Reduced blank with margins at 60Hz. + * If 'M' is present after yres (and before refresh/bpp if present), + * the function will compute the timings using VESA(tm) Coordinated + * Video Timings (CVT). If 'R' is present after 'M', will compute with + * reduced blanking (for flatpanels). If 'i' or 'p' are present, compute + * interlaced or progressive mode. If 'm' is present, add margins equal + * to 1.8% of xres rounded down to 8 pixels, and 1.8% of yres. The char + * 'i', 'p' and 'm' must be after 'M' and 'R'. Example:: * - * NOTE: The passed struct @var is _not_ cleared! This allows you - * to supply values for e.g. the grayscale and accel_flags fields. + * 1024x768MR-8@60m - Reduced blank with margins at 60Hz. * - * Returns zero for failure, 1 if using specified @mode_option, - * 2 if using specified @mode_option with an ignored refresh rate, - * 3 if default mode is used, 4 if fall back to any valid mode. + * NOTE: The passed struct @var is _not_ cleared! This allows you + * to supply values for e.g. the grayscale and accel_flags fields. * + * Returns zero for failure, 1 if using specified @mode_option, + * 2 if using specified @mode_option with an ignored refresh rate, + * 3 if default mode is used, 4 if fall back to any valid mode. */ int fb_find_mode(struct fb_var_screeninfo *var, @@ -697,7 +699,8 @@ int fb_find_mode(struct fb_var_screeninfo *var, unsigned int namelen = strlen(name); int res_specified = 0, bpp_specified = 0, refresh_specified = 0; unsigned int xres = 0, yres = 0, bpp = default_bpp, refresh = 0; - int yres_specified = 0, cvt = 0, rb = 0, interlace = 0; + int yres_specified = 0, cvt = 0, rb = 0; + int interlace_specified = 0, interlace = 0; int margins = 0; u32 best, diff, tdiff; @@ -748,9 +751,17 @@ int fb_find_mode(struct fb_var_screeninfo *var, if (!cvt) margins = 1; break; + case 'p': + if (!cvt) { + interlace = 0; + interlace_specified = 1; + } + break; case 'i': - if (!cvt) + if (!cvt) { interlace = 1; + interlace_specified = 1; + } break; default: goto done; @@ -819,11 +830,21 @@ done: if ((name_matches(db[i], name, namelen) || (res_specified && res_matches(db[i], xres, yres))) && !fb_try_mode(var, info, &db[i], bpp)) { - if (refresh_specified && db[i].refresh == refresh) - return 1; + const int db_interlace = (db[i].vmode & + FB_VMODE_INTERLACED ? 1 : 0); + int score = abs(db[i].refresh - refresh); + + if (interlace_specified) + score += abs(db_interlace - interlace); + + if (!interlace_specified || + db_interlace == interlace) + if (refresh_specified && + db[i].refresh == refresh) + return 1; - if (abs(db[i].refresh - refresh) < diff) { - diff = abs(db[i].refresh - refresh); + if (score < diff) { + diff = score; best = i; } } diff --git a/drivers/video/fbdev/efifb.c b/drivers/video/fbdev/efifb.c index c6f78d27947b..3946649b85c8 100644 --- a/drivers/video/fbdev/efifb.c +++ b/drivers/video/fbdev/efifb.c @@ -9,16 +9,39 @@ #include <linux/kernel.h> #include <linux/efi.h> +#include <linux/efi-bgrt.h> #include <linux/errno.h> #include <linux/fb.h> #include <linux/pci.h> #include <linux/platform_device.h> +#include <linux/printk.h> #include <linux/screen_info.h> #include <video/vga.h> #include <asm/efi.h> #include <drm/drm_utils.h> /* For drm_get_panel_orientation_quirk */ #include <drm/drm_connector.h> /* For DRM_MODE_PANEL_ORIENTATION_* */ +struct bmp_file_header { + u16 id; + u32 file_size; + u32 reserved; + u32 bitmap_offset; +} __packed; + +struct bmp_dib_header { + u32 dib_header_size; + s32 width; + s32 height; + u16 planes; + u16 bpp; + u32 compression; + u32 bitmap_size; + u32 horz_resolution; + u32 vert_resolution; + u32 colors_used; + u32 colors_important; +} __packed; + static bool request_mem_succeeded = false; static u64 mem_flags = EFI_MEMORY_WC | EFI_MEMORY_UC; @@ -66,6 +89,164 @@ static int efifb_setcolreg(unsigned regno, unsigned red, unsigned green, return 0; } +/* + * If fbcon deffered console takeover is configured, the intent is for the + * framebuffer to show the boot graphics (e.g. vendor logo) until there is some + * (error) message to display. But the boot graphics may have been destroyed by + * e.g. option ROM output, detect this and restore the boot graphics. + */ +#if defined CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER && \ + defined CONFIG_ACPI_BGRT +static void efifb_copy_bmp(u8 *src, u32 *dst, int width, struct screen_info *si) +{ + u8 r, g, b; + + while (width--) { + b = *src++; + g = *src++; + r = *src++; + *dst++ = (r << si->red_pos) | + (g << si->green_pos) | + (b << si->blue_pos); + } +} + +#ifdef CONFIG_X86 +/* + * On x86 some firmwares use a low non native resolution for the display when + * they have shown some text messages. While keeping the bgrt filled with info + * for the native resolution. If the bgrt image intended for the native + * resolution still fits, it will be displayed very close to the right edge of + * the display looking quite bad. This function checks for this. + */ +static bool efifb_bgrt_sanity_check(struct screen_info *si, u32 bmp_width) +{ + static const int default_resolutions[][2] = { + { 800, 600 }, + { 1024, 768 }, + { 1280, 1024 }, + }; + u32 i, right_margin; + + for (i = 0; i < ARRAY_SIZE(default_resolutions); i++) { + if (default_resolutions[i][0] == si->lfb_width && + default_resolutions[i][1] == si->lfb_height) + break; + } + /* If not a default resolution used for textmode, this should be fine */ + if (i >= ARRAY_SIZE(default_resolutions)) + return true; + + /* If the right margin is 5 times smaller then the left one, reject */ + right_margin = si->lfb_width - (bgrt_tab.image_offset_x + bmp_width); + if (right_margin < (bgrt_tab.image_offset_x / 5)) + return false; + + return true; +} +#else +static bool efifb_bgrt_sanity_check(struct screen_info *si, u32 bmp_width) +{ + return true; +} +#endif + +static void efifb_show_boot_graphics(struct fb_info *info) +{ + u32 bmp_width, bmp_height, bmp_pitch, screen_pitch, dst_x, y, src_y; + struct screen_info *si = &screen_info; + struct bmp_file_header *file_header; + struct bmp_dib_header *dib_header; + void *bgrt_image = NULL; + u8 *dst = info->screen_base; + + if (!bgrt_tab.image_address) { + pr_info("efifb: No BGRT, not showing boot graphics\n"); + return; + } + + /* Avoid flashing the logo if we're going to print std probe messages */ + if (console_loglevel > CONSOLE_LOGLEVEL_QUIET) + return; + + /* bgrt_tab.status is unreliable, so we don't check it */ + + if (si->lfb_depth != 32) { + pr_info("efifb: not 32 bits, not showing boot graphics\n"); + return; + } + + bgrt_image = memremap(bgrt_tab.image_address, bgrt_image_size, + MEMREMAP_WB); + if (!bgrt_image) { + pr_warn("efifb: Ignoring BGRT: failed to map image memory\n"); + return; + } + + if (bgrt_image_size < (sizeof(*file_header) + sizeof(*dib_header))) + goto error; + + file_header = bgrt_image; + if (file_header->id != 0x4d42 || file_header->reserved != 0) + goto error; + + dib_header = bgrt_image + sizeof(*file_header); + if (dib_header->dib_header_size != 40 || dib_header->width < 0 || + dib_header->planes != 1 || dib_header->bpp != 24 || + dib_header->compression != 0) + goto error; + + bmp_width = dib_header->width; + bmp_height = abs(dib_header->height); + bmp_pitch = round_up(3 * bmp_width, 4); + screen_pitch = si->lfb_linelength; + + if ((file_header->bitmap_offset + bmp_pitch * bmp_height) > + bgrt_image_size) + goto error; + + if ((bgrt_tab.image_offset_x + bmp_width) > si->lfb_width || + (bgrt_tab.image_offset_y + bmp_height) > si->lfb_height) + goto error; + + if (!efifb_bgrt_sanity_check(si, bmp_width)) + goto error; + + pr_info("efifb: showing boot graphics\n"); + + for (y = 0; y < si->lfb_height; y++, dst += si->lfb_linelength) { + /* Only background? */ + if (y < bgrt_tab.image_offset_y || + y >= (bgrt_tab.image_offset_y + bmp_height)) { + memset(dst, 0, 4 * si->lfb_width); + continue; + } + + src_y = y - bgrt_tab.image_offset_y; + /* Positive header height means upside down row order */ + if (dib_header->height > 0) + src_y = (bmp_height - 1) - src_y; + + memset(dst, 0, bgrt_tab.image_offset_x * 4); + dst_x = bgrt_tab.image_offset_x; + efifb_copy_bmp(bgrt_image + file_header->bitmap_offset + + src_y * bmp_pitch, + (u32 *)dst + dst_x, bmp_width, si); + dst_x += bmp_width; + memset((u32 *)dst + dst_x, 0, (si->lfb_width - dst_x) * 4); + } + + memunmap(bgrt_image); + return; + +error: + memunmap(bgrt_image); + pr_warn("efifb: Ignoring BGRT: unexpected or invalid BMP data\n"); +} +#else +static inline void efifb_show_boot_graphics(struct fb_info *info) {} +#endif + static void efifb_destroy(struct fb_info *info) { if (info->screen_base) { @@ -311,6 +492,8 @@ static int efifb_probe(struct platform_device *dev) goto err_release_fb; } + efifb_show_boot_graphics(info); + pr_info("efifb: framebuffer at 0x%lx, using %dk, total %dk\n", efifb_fix.smem_start, size_remap/1024, size_total/1024); pr_info("efifb: mode is %dx%dx%d, linelength=%d, pages=%d\n", diff --git a/drivers/video/fbdev/fsl-diu-fb.c b/drivers/video/fbdev/fsl-diu-fb.c index 1bfd13cbd4e3..bc9eb8afc313 100644 --- a/drivers/video/fbdev/fsl-diu-fb.c +++ b/drivers/video/fbdev/fsl-diu-fb.c @@ -360,6 +360,10 @@ struct mfb_info { * @ad[]: Area Descriptors for each real AOI * @gamma: gamma color table * @cursor: hardware cursor data + * @blank_cursor: blank cursor for hiding cursor + * @next_cursor: scratch space to build load cursor + * @edid_data: EDID information buffer + * @has_edid: whether or not the EDID buffer is valid * * This data structure must be allocated with 32-byte alignment, so that the * internal fields can be aligned properly. @@ -381,6 +385,8 @@ struct fsl_diu_data { __le16 cursor[MAX_CURS * MAX_CURS] __aligned(32); /* Blank cursor data -- used to hide the cursor */ __le16 blank_cursor[MAX_CURS * MAX_CURS] __aligned(32); + /* Scratch cursor data -- used to build new cursor */ + __le16 next_cursor[MAX_CURS * MAX_CURS] __aligned(32); uint8_t edid_data[EDID_LENGTH]; bool has_edid; } __aligned(32); @@ -1056,13 +1062,17 @@ static int fsl_diu_cursor(struct fb_info *info, struct fb_cursor *cursor) * FB_CUR_SETSHAPE - the cursor bitmask has changed */ if (cursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETCMAP | FB_CUR_SETIMAGE)) { + /* + * Determine the size of the cursor image data. Normally, + * it's 8x16. + */ unsigned int image_size = - DIV_ROUND_UP(cursor->image.width, 8) * cursor->image.height; + DIV_ROUND_UP(cursor->image.width, 8) * + cursor->image.height; unsigned int image_words = DIV_ROUND_UP(image_size, sizeof(uint32_t)); unsigned int bg_idx = cursor->image.bg_color; unsigned int fg_idx = cursor->image.fg_color; - uint8_t buffer[image_size]; uint32_t *image, *source, *mask; uint16_t fg, bg; unsigned int i; @@ -1070,13 +1080,6 @@ static int fsl_diu_cursor(struct fb_info *info, struct fb_cursor *cursor) if (info->state != FBINFO_STATE_RUNNING) return 0; - /* - * Determine the size of the cursor image data. Normally, - * it's 8x16. - */ - image_size = DIV_ROUND_UP(cursor->image.width, 8) * - cursor->image.height; - bg = ((info->cmap.red[bg_idx] & 0xf8) << 7) | ((info->cmap.green[bg_idx] & 0xf8) << 2) | ((info->cmap.blue[bg_idx] & 0xf8) >> 3) | @@ -1088,7 +1091,7 @@ static int fsl_diu_cursor(struct fb_info *info, struct fb_cursor *cursor) 1 << 15; /* Use 32-bit operations on the data to improve performance */ - image = (uint32_t *)buffer; + image = (uint32_t *)data->next_cursor; source = (uint32_t *)cursor->image.data; mask = (uint32_t *)cursor->mask; diff --git a/drivers/video/fbdev/goldfishfb.c b/drivers/video/fbdev/goldfishfb.c index 3b70044773b6..4377e3442638 100644 --- a/drivers/video/fbdev/goldfishfb.c +++ b/drivers/video/fbdev/goldfishfb.c @@ -26,6 +26,7 @@ #include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/platform_device.h> +#include <linux/acpi.h> enum { FB_GET_WIDTH = 0x00, @@ -124,6 +125,7 @@ static int goldfish_fb_check_var(struct fb_var_screeninfo *var, static int goldfish_fb_set_par(struct fb_info *info) { struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb); + if (fb->rotation != fb->fb.var.rotate) { info->fix.line_length = info->var.xres * 2; fb->rotation = fb->fb.var.rotate; @@ -148,13 +150,14 @@ static int goldfish_fb_pan_display(struct fb_var_screeninfo *var, wait_event_timeout(fb->wait, fb->base_update_count != base_update_count, HZ / 15); if (fb->base_update_count == base_update_count) - pr_err("goldfish_fb_pan_display: timeout waiting for base update\n"); + pr_err("%s: timeout waiting for base update\n", __func__); return 0; } static int goldfish_fb_blank(int blank, struct fb_info *info) { struct goldfish_fb *fb = container_of(info, struct goldfish_fb, fb); + switch (blank) { case FB_BLANK_NORMAL: writel(1, fb->reg_base + FB_SET_BLANK); @@ -234,7 +237,7 @@ static int goldfish_fb_probe(struct platform_device *pdev) fb->fb.var.activate = FB_ACTIVATE_NOW; fb->fb.var.height = readl(fb->reg_base + FB_GET_PHYS_HEIGHT); fb->fb.var.width = readl(fb->reg_base + FB_GET_PHYS_WIDTH); - fb->fb.var.pixclock = 10000; + fb->fb.var.pixclock = 0; fb->fb.var.red.offset = 11; fb->fb.var.red.length = 5; @@ -301,6 +304,7 @@ static int goldfish_fb_remove(struct platform_device *pdev) dma_free_coherent(&pdev->dev, framesize, (void *)fb->fb.screen_base, fb->fb.fix.smem_start); iounmap(fb->reg_base); + kfree(fb); return 0; } @@ -310,12 +314,19 @@ static const struct of_device_id goldfish_fb_of_match[] = { }; MODULE_DEVICE_TABLE(of, goldfish_fb_of_match); +static const struct acpi_device_id goldfish_fb_acpi_match[] = { + { "GFSH0004", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, goldfish_fb_acpi_match); + static struct platform_driver goldfish_fb_driver = { .probe = goldfish_fb_probe, .remove = goldfish_fb_remove, .driver = { .name = "goldfish_fb", .of_match_table = goldfish_fb_of_match, + .acpi_match_table = ACPI_PTR(goldfish_fb_acpi_match), } }; diff --git a/drivers/video/fbdev/i740fb.c b/drivers/video/fbdev/i740fb.c index 7bc5f6056c77..f6d7b04d6dff 100644 --- a/drivers/video/fbdev/i740fb.c +++ b/drivers/video/fbdev/i740fb.c @@ -429,6 +429,7 @@ static int i740fb_decode_var(const struct fb_var_screeninfo *var, break; case 9 ... 15: bpp = 15; + /* fall through */ case 16: if ((1000000 / var->pixclock) > DACSPEED16) { dev_err(info->device, "requested pixclock %i MHz out of range (max. %i MHz at 15/16bpp)\n", diff --git a/drivers/video/fbdev/metronomefb.c b/drivers/video/fbdev/metronomefb.c index 9085e9525341..bb4fee52e501 100644 --- a/drivers/video/fbdev/metronomefb.c +++ b/drivers/video/fbdev/metronomefb.c @@ -233,7 +233,7 @@ static int load_waveform(u8 *mem, size_t size, int m, int t, /* check temperature range table checksum */ cksum_idx = sizeof(*wfm_hdr) + wfm_hdr->trc + 1; - if (cksum_idx > size) + if (cksum_idx >= size) return -EINVAL; cksum = calc_cksum(sizeof(*wfm_hdr), cksum_idx, mem); if (cksum != mem[cksum_idx]) { @@ -245,7 +245,7 @@ static int load_waveform(u8 *mem, size_t size, int m, int t, /* check waveform mode table address checksum */ wmta = get_unaligned_le32(wfm_hdr->wmta) & 0x00FFFFFF; cksum_idx = wmta + m*4 + 3; - if (cksum_idx > size) + if (cksum_idx >= size) return -EINVAL; cksum = calc_cksum(cksum_idx - 3, cksum_idx, mem); if (cksum != mem[cksum_idx]) { @@ -257,7 +257,7 @@ static int load_waveform(u8 *mem, size_t size, int m, int t, /* check waveform temperature table address checksum */ tta = get_unaligned_le32(mem + wmta + m * 4) & 0x00FFFFFF; cksum_idx = tta + trn*4 + 3; - if (cksum_idx > size) + if (cksum_idx >= size) return -EINVAL; cksum = calc_cksum(cksum_idx - 3, cksum_idx, mem); if (cksum != mem[cksum_idx]) { @@ -270,7 +270,7 @@ static int load_waveform(u8 *mem, size_t size, int m, int t, metromem buffer. this does runlength decoding of the waveform */ wfm_idx = get_unaligned_le32(mem + tta + trn * 4) & 0x00FFFFFF; owfm_idx = wfm_idx; - if (wfm_idx > size) + if (wfm_idx >= size) return -EINVAL; while (wfm_idx < size) { unsigned char rl; @@ -292,7 +292,7 @@ static int load_waveform(u8 *mem, size_t size, int m, int t, } cksum_idx = wfm_idx; - if (cksum_idx > size) + if (cksum_idx >= size) return -EINVAL; cksum = calc_cksum(owfm_idx, cksum_idx, mem); if (cksum != mem[cksum_idx]) { diff --git a/drivers/video/fbdev/omap/omapfb_main.c b/drivers/video/fbdev/omap/omapfb_main.c index 585f39efcff6..1c75f4806ed3 100644 --- a/drivers/video/fbdev/omap/omapfb_main.c +++ b/drivers/video/fbdev/omap/omapfb_main.c @@ -958,7 +958,7 @@ int omapfb_register_client(struct omapfb_notifier_block *omapfb_nb, { int r; - if ((unsigned)omapfb_nb->plane_idx > OMAPFB_PLANE_NUM) + if ((unsigned)omapfb_nb->plane_idx >= OMAPFB_PLANE_NUM) return -EINVAL; if (!notifier_inited) { diff --git a/drivers/video/fbdev/omap2/omapfb/Makefile b/drivers/video/fbdev/omap2/omapfb/Makefile index 602edfed09df..f54c3f56b641 100644 --- a/drivers/video/fbdev/omap2/omapfb/Makefile +++ b/drivers/video/fbdev/omap2/omapfb/Makefile @@ -2,5 +2,5 @@ obj-$(CONFIG_OMAP2_VRFB) += vrfb.o obj-y += dss/ obj-y += displays/ -obj-$(CONFIG_FB_OMAP2) += omapfb.o -omapfb-y := omapfb-main.o omapfb-sysfs.o omapfb-ioctl.o +obj-$(CONFIG_FB_OMAP2) += omap2fb.o +omap2fb-y := omapfb-main.o omapfb-sysfs.o omapfb-ioctl.o diff --git a/drivers/video/fbdev/omap2/omapfb/displays/encoder-tpd12s015.c b/drivers/video/fbdev/omap2/omapfb/displays/encoder-tpd12s015.c index 3079a3df8c37..47f0459e3551 100644 --- a/drivers/video/fbdev/omap2/omapfb/displays/encoder-tpd12s015.c +++ b/drivers/video/fbdev/omap2/omapfb/displays/encoder-tpd12s015.c @@ -219,7 +219,7 @@ static int tpd_probe_of(struct platform_device *pdev) static int tpd_probe(struct platform_device *pdev) { - struct omap_dss_device *in, *dssdev; + struct omap_dss_device *dssdev; struct panel_drv_data *ddata; int r; struct gpio_desc *gpio; @@ -238,25 +238,30 @@ static int tpd_probe(struct platform_device *pdev) return -ENODEV; } - gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 0, GPIOD_OUT_LOW); - if (IS_ERR(gpio)) + if (IS_ERR(gpio)) { + r = PTR_ERR(gpio); goto err_gpio; + } ddata->ct_cp_hpd_gpio = gpio; gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 1, GPIOD_OUT_LOW); - if (IS_ERR(gpio)) + if (IS_ERR(gpio)) { + r = PTR_ERR(gpio); goto err_gpio; + } ddata->ls_oe_gpio = gpio; gpio = devm_gpiod_get_index(&pdev->dev, NULL, 2, GPIOD_IN); - if (IS_ERR(gpio)) + if (IS_ERR(gpio)) { + r = PTR_ERR(gpio); goto err_gpio; + } ddata->hpd_gpio = gpio; @@ -268,8 +273,6 @@ static int tpd_probe(struct platform_device *pdev) dssdev->owner = THIS_MODULE; dssdev->port_num = 1; - in = ddata->in; - r = omapdss_register_output(dssdev); if (r) { dev_err(&pdev->dev, "Failed to register output\n"); diff --git a/drivers/video/fbdev/omap2/omapfb/dss/dispc.c b/drivers/video/fbdev/omap2/omapfb/dss/dispc.c index fb605aefd9b1..a06d9c25765c 100644 --- a/drivers/video/fbdev/omap2/omapfb/dss/dispc.c +++ b/drivers/video/fbdev/omap2/omapfb/dss/dispc.c @@ -1858,7 +1858,7 @@ static s32 pixinc(int pixels, u8 ps) return 1 - (-pixels + 1) * ps; else BUG(); - return 0; + return 0; } static void calc_vrfb_rotation_offset(u8 rotation, bool mirror, @@ -1905,6 +1905,7 @@ static void calc_vrfb_rotation_offset(u8 rotation, bool mirror, if (color_mode == OMAP_DSS_COLOR_YUV2 || color_mode == OMAP_DSS_COLOR_UYVY) width = width >> 1; + /* fall through */ case OMAP_DSS_ROT_90: case OMAP_DSS_ROT_270: *offset1 = 0; @@ -1927,6 +1928,7 @@ static void calc_vrfb_rotation_offset(u8 rotation, bool mirror, if (color_mode == OMAP_DSS_COLOR_YUV2 || color_mode == OMAP_DSS_COLOR_UYVY) width = width >> 1; + /* fall through */ case OMAP_DSS_ROT_90 + 4: case OMAP_DSS_ROT_270 + 4: *offset1 = 0; diff --git a/drivers/video/fbdev/omap2/omapfb/omapfb-main.c b/drivers/video/fbdev/omap2/omapfb/omapfb-main.c index 3e7887c53d7c..b8b5b4ac0e09 100644 --- a/drivers/video/fbdev/omap2/omapfb/omapfb-main.c +++ b/drivers/video/fbdev/omap2/omapfb/omapfb-main.c @@ -893,6 +893,7 @@ int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl, / (var->bits_per_pixel >> 2); break; } + /* fall through */ default: screen_width = fix->line_length / (var->bits_per_pixel >> 3); break; diff --git a/drivers/video/fbdev/pm2fb.c b/drivers/video/fbdev/pm2fb.c index bd6c2f5f6095..1dcf02e12af4 100644 --- a/drivers/video/fbdev/pm2fb.c +++ b/drivers/video/fbdev/pm2fb.c @@ -233,8 +233,10 @@ static u32 to3264(u32 timing, int bpp, int is64) switch (bpp) { case 24: timing *= 3; + /* fall through */ case 8: timing >>= 1; + /* fall through */ case 16: timing >>= 1; case 32: diff --git a/drivers/video/fbdev/pxa3xx-gcu.c b/drivers/video/fbdev/pxa3xx-gcu.c index 0955622a1227..69cfb337c857 100644 --- a/drivers/video/fbdev/pxa3xx-gcu.c +++ b/drivers/video/fbdev/pxa3xx-gcu.c @@ -44,6 +44,7 @@ #include <linux/clk.h> #include <linux/fs.h> #include <linux/io.h> +#include <linux/of.h> #include "pxa3xx-gcu.h" @@ -703,11 +704,20 @@ static int pxa3xx_gcu_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id pxa3xx_gcu_of_match[] = { + { .compatible = "marvell,pxa300-gcu", }, + { } +}; +MODULE_DEVICE_TABLE(of, pxa3xx_gcu_of_match); +#endif + static struct platform_driver pxa3xx_gcu_driver = { .probe = pxa3xx_gcu_probe, .remove = pxa3xx_gcu_remove, .driver = { .name = DRV_NAME, + .of_match_table = of_match_ptr(pxa3xx_gcu_of_match), }, }; diff --git a/drivers/video/fbdev/pxafb.c b/drivers/video/fbdev/pxafb.c index 76722a59f55e..bbed039617a4 100644 --- a/drivers/video/fbdev/pxafb.c +++ b/drivers/video/fbdev/pxafb.c @@ -56,6 +56,7 @@ #include <linux/freezer.h> #include <linux/console.h> #include <linux/of_graph.h> +#include <linux/regulator/consumer.h> #include <video/of_display_timing.h> #include <video/videomode.h> @@ -1423,6 +1424,21 @@ static inline void __pxafb_lcd_power(struct pxafb_info *fbi, int on) if (fbi->lcd_power) fbi->lcd_power(on, &fbi->fb.var); + + if (fbi->lcd_supply && fbi->lcd_supply_enabled != on) { + int ret; + + if (on) + ret = regulator_enable(fbi->lcd_supply); + else + ret = regulator_disable(fbi->lcd_supply); + + if (ret < 0) + pr_warn("Unable to %s LCD supply regulator: %d\n", + on ? "enable" : "disable", ret); + else + fbi->lcd_supply_enabled = on; + } } static void pxafb_enable_controller(struct pxafb_info *fbi) @@ -1799,19 +1815,17 @@ static struct pxafb_info *pxafb_init_fbinfo(struct device *dev, void *addr; /* Alloc the pxafb_info and pseudo_palette in one step */ - fbi = kmalloc(sizeof(struct pxafb_info) + sizeof(u32) * 16, GFP_KERNEL); + fbi = devm_kzalloc(dev, sizeof(struct pxafb_info) + sizeof(u32) * 16, + GFP_KERNEL); if (!fbi) - return NULL; + return ERR_PTR(-ENOMEM); - memset(fbi, 0, sizeof(struct pxafb_info)); fbi->dev = dev; fbi->inf = inf; - fbi->clk = clk_get(dev, NULL); - if (IS_ERR(fbi->clk)) { - kfree(fbi); - return NULL; - } + fbi->clk = devm_clk_get(dev, NULL); + if (IS_ERR(fbi->clk)) + return ERR_CAST(fbi->clk); strcpy(fbi->fb.fix.id, PXA_NAME); @@ -2128,8 +2142,9 @@ static int of_get_pxafb_display(struct device *dev, struct device_node *disp, return -EINVAL; ret = -ENOMEM; - info->modes = kmalloc_array(timings->num_timings, - sizeof(info->modes[0]), GFP_KERNEL); + info->modes = devm_kcalloc(dev, timings->num_timings, + sizeof(info->modes[0]), + GFP_KERNEL); if (!info->modes) goto out; info->num_modes = timings->num_timings; @@ -2288,10 +2303,9 @@ static int pxafb_probe(struct platform_device *dev) } fbi = pxafb_init_fbinfo(&dev->dev, inf); - if (!fbi) { - /* only reason for pxafb_init_fbinfo to fail is kmalloc */ + if (IS_ERR(fbi)) { dev_err(&dev->dev, "Failed to initialize framebuffer device\n"); - ret = -ENOMEM; + ret = PTR_ERR(fbi); goto failed; } @@ -2301,25 +2315,26 @@ static int pxafb_probe(struct platform_device *dev) fbi->backlight_power = inf->pxafb_backlight_power; fbi->lcd_power = inf->pxafb_lcd_power; + fbi->lcd_supply = devm_regulator_get_optional(&dev->dev, "lcd"); + if (IS_ERR(fbi->lcd_supply)) { + if (PTR_ERR(fbi->lcd_supply) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + fbi->lcd_supply = NULL; + } + r = platform_get_resource(dev, IORESOURCE_MEM, 0); if (r == NULL) { dev_err(&dev->dev, "no I/O memory resource defined\n"); ret = -ENODEV; - goto failed_fbi; - } - - r = request_mem_region(r->start, resource_size(r), dev->name); - if (r == NULL) { - dev_err(&dev->dev, "failed to request I/O memory\n"); - ret = -EBUSY; - goto failed_fbi; + goto failed; } - fbi->mmio_base = ioremap(r->start, resource_size(r)); - if (fbi->mmio_base == NULL) { - dev_err(&dev->dev, "failed to map I/O memory\n"); + fbi->mmio_base = devm_ioremap_resource(&dev->dev, r); + if (IS_ERR(fbi->mmio_base)) { + dev_err(&dev->dev, "failed to get I/O memory\n"); ret = -EBUSY; - goto failed_free_res; + goto failed; } fbi->dma_buff_size = PAGE_ALIGN(sizeof(struct pxafb_dma_buff)); @@ -2328,7 +2343,7 @@ static int pxafb_probe(struct platform_device *dev) if (fbi->dma_buff == NULL) { dev_err(&dev->dev, "failed to allocate memory for DMA\n"); ret = -ENOMEM; - goto failed_free_io; + goto failed; } ret = pxafb_init_video_memory(fbi); @@ -2345,7 +2360,7 @@ static int pxafb_probe(struct platform_device *dev) goto failed_free_mem; } - ret = request_irq(irq, pxafb_handle_irq, 0, "LCD", fbi); + ret = devm_request_irq(&dev->dev, irq, pxafb_handle_irq, 0, "LCD", fbi); if (ret) { dev_err(&dev->dev, "request_irq failed: %d\n", ret); ret = -EBUSY; @@ -2355,7 +2370,7 @@ static int pxafb_probe(struct platform_device *dev) ret = pxafb_smart_init(fbi); if (ret) { dev_err(&dev->dev, "failed to initialize smartpanel\n"); - goto failed_free_irq; + goto failed_free_mem; } /* @@ -2365,13 +2380,13 @@ static int pxafb_probe(struct platform_device *dev) ret = pxafb_check_var(&fbi->fb.var, &fbi->fb); if (ret) { dev_err(&dev->dev, "failed to get suitable mode\n"); - goto failed_free_irq; + goto failed_free_mem; } ret = pxafb_set_par(&fbi->fb); if (ret) { dev_err(&dev->dev, "Failed to set parameters\n"); - goto failed_free_irq; + goto failed_free_mem; } platform_set_drvdata(dev, fbi); @@ -2404,20 +2419,11 @@ static int pxafb_probe(struct platform_device *dev) failed_free_cmap: if (fbi->fb.cmap.len) fb_dealloc_cmap(&fbi->fb.cmap); -failed_free_irq: - free_irq(irq, fbi); failed_free_mem: free_pages_exact(fbi->video_mem, fbi->video_mem_size); failed_free_dma: dma_free_coherent(&dev->dev, fbi->dma_buff_size, fbi->dma_buff, fbi->dma_buff_phys); -failed_free_io: - iounmap(fbi->mmio_base); -failed_free_res: - release_mem_region(r->start, resource_size(r)); -failed_fbi: - clk_put(fbi->clk); - kfree(fbi); failed: return ret; } @@ -2425,8 +2431,6 @@ failed: static int pxafb_remove(struct platform_device *dev) { struct pxafb_info *fbi = platform_get_drvdata(dev); - struct resource *r; - int irq; struct fb_info *info; if (!fbi) @@ -2442,22 +2446,11 @@ static int pxafb_remove(struct platform_device *dev) if (fbi->fb.cmap.len) fb_dealloc_cmap(&fbi->fb.cmap); - irq = platform_get_irq(dev, 0); - free_irq(irq, fbi); - free_pages_exact(fbi->video_mem, fbi->video_mem_size); dma_free_wc(&dev->dev, fbi->dma_buff_size, fbi->dma_buff, fbi->dma_buff_phys); - iounmap(fbi->mmio_base); - - r = platform_get_resource(dev, IORESOURCE_MEM, 0); - release_mem_region(r->start, resource_size(r)); - - clk_put(fbi->clk); - kfree(fbi); - return 0; } diff --git a/drivers/video/fbdev/pxafb.h b/drivers/video/fbdev/pxafb.h index 5dc414e26fc8..b641289c8a99 100644 --- a/drivers/video/fbdev/pxafb.h +++ b/drivers/video/fbdev/pxafb.h @@ -165,6 +165,9 @@ struct pxafb_info { struct notifier_block freq_policy; #endif + struct regulator *lcd_supply; + bool lcd_supply_enabled; + void (*lcd_power)(int, struct fb_var_screeninfo *); void (*backlight_power)(int); diff --git a/drivers/video/fbdev/simplefb.c b/drivers/video/fbdev/simplefb.c index a3c44ecf4523..9a9d748b07f2 100644 --- a/drivers/video/fbdev/simplefb.c +++ b/drivers/video/fbdev/simplefb.c @@ -27,8 +27,8 @@ #include <linux/platform_data/simplefb.h> #include <linux/platform_device.h> #include <linux/clk.h> -#include <linux/clk-provider.h> #include <linux/of.h> +#include <linux/of_clk.h> #include <linux/of_platform.h> #include <linux/parser.h> #include <linux/regulator/consumer.h> diff --git a/drivers/video/fbdev/tdfxfb.c b/drivers/video/fbdev/tdfxfb.c index dec1fed9880e..fbbf26b170f7 100644 --- a/drivers/video/fbdev/tdfxfb.c +++ b/drivers/video/fbdev/tdfxfb.c @@ -522,6 +522,7 @@ static int tdfxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) case 32: var->transp.offset = 24; var->transp.length = 8; + /* fall through */ case 24: var->red.offset = 16; var->green.offset = 8; diff --git a/drivers/video/fbdev/tridentfb.c b/drivers/video/fbdev/tridentfb.c index 284706184b1b..f4b745590600 100644 --- a/drivers/video/fbdev/tridentfb.c +++ b/drivers/video/fbdev/tridentfb.c @@ -777,9 +777,6 @@ static int get_nativex(struct tridentfb_par *par) case 3: x = 800; y = 600; break; - case 4: - x = 1400; y = 1050; - break; case 1: default: x = 640; y = 480; diff --git a/drivers/video/fbdev/udlfb.c b/drivers/video/fbdev/udlfb.c index f365d4862015..afbd6101c78e 100644 --- a/drivers/video/fbdev/udlfb.c +++ b/drivers/video/fbdev/udlfb.c @@ -25,8 +25,8 @@ #include <linux/fb.h> #include <linux/vmalloc.h> #include <linux/slab.h> -#include <linux/prefetch.h> #include <linux/delay.h> +#include <asm/unaligned.h> #include <video/udlfb.h> #include "edid.h" @@ -72,6 +72,13 @@ static bool fb_defio = 1; /* Detect mmap writes using page faults */ static bool shadow = 1; /* Optionally disable shadow framebuffer */ static int pixel_limit; /* Optionally force a pixel resolution limit */ +struct dlfb_deferred_free { + struct list_head list; + void *mem; +}; + +static int dlfb_realloc_framebuffer(struct dlfb_data *dlfb, struct fb_info *info, u32 new_len); + /* dlfb keeps a list of urbs for efficient bulk transfers */ static void dlfb_urb_completion(struct urb *urb); static struct urb *dlfb_get_urb(struct dlfb_data *dlfb); @@ -367,9 +374,6 @@ static int dlfb_trim_hline(const u8 *bback, const u8 **bfront, int *width_bytes) int start = width; int end = width; - prefetch((void *) front); - prefetch((void *) back); - for (j = 0; j < width; j++) { if (back[j] != front[j]) { start = j; @@ -423,7 +427,9 @@ static void dlfb_compress_hline( const uint16_t *const pixel_end, uint32_t *device_address_ptr, uint8_t **command_buffer_ptr, - const uint8_t *const cmd_buffer_end) + const uint8_t *const cmd_buffer_end, + unsigned long back_buffer_offset, + int *ident_ptr) { const uint16_t *pixel = *pixel_start_ptr; uint32_t dev_addr = *device_address_ptr; @@ -436,7 +442,13 @@ static void dlfb_compress_hline( const uint16_t *raw_pixel_start = NULL; const uint16_t *cmd_pixel_start, *cmd_pixel_end = NULL; - prefetchw((void *) cmd); /* pull in one cache line at least */ + if (back_buffer_offset && + *pixel == *(u16 *)((u8 *)pixel + back_buffer_offset)) { + pixel++; + dev_addr += BPP; + (*ident_ptr)++; + continue; + } *cmd++ = 0xAF; *cmd++ = 0x6B; @@ -450,29 +462,39 @@ static void dlfb_compress_hline( raw_pixels_count_byte = cmd++; /* we'll know this later */ raw_pixel_start = pixel; - cmd_pixel_end = pixel + min(MAX_CMD_PIXELS + 1, - min((int)(pixel_end - pixel), - (int)(cmd_buffer_end - cmd) / BPP)); + cmd_pixel_end = pixel + min3(MAX_CMD_PIXELS + 1UL, + (unsigned long)(pixel_end - pixel), + (unsigned long)(cmd_buffer_end - 1 - cmd) / BPP); - prefetch_range((void *) pixel, (cmd_pixel_end - pixel) * BPP); + if (back_buffer_offset) { + /* note: the framebuffer may change under us, so we must test for underflow */ + while (cmd_pixel_end - 1 > pixel && + *(cmd_pixel_end - 1) == *(u16 *)((u8 *)(cmd_pixel_end - 1) + back_buffer_offset)) + cmd_pixel_end--; + } while (pixel < cmd_pixel_end) { const uint16_t * const repeating_pixel = pixel; + u16 pixel_value = *pixel; - *cmd++ = *pixel >> 8; - *cmd++ = *pixel; + put_unaligned_be16(pixel_value, cmd); + if (back_buffer_offset) + *(u16 *)((u8 *)pixel + back_buffer_offset) = pixel_value; + cmd += 2; pixel++; if (unlikely((pixel < cmd_pixel_end) && - (*pixel == *repeating_pixel))) { + (*pixel == pixel_value))) { /* go back and fill in raw pixel count */ *raw_pixels_count_byte = ((repeating_pixel - raw_pixel_start) + 1) & 0xFF; - while ((pixel < cmd_pixel_end) - && (*pixel == *repeating_pixel)) { + do { + if (back_buffer_offset) + *(u16 *)((u8 *)pixel + back_buffer_offset) = pixel_value; pixel++; - } + } while ((pixel < cmd_pixel_end) && + (*pixel == pixel_value)); /* immediately after raw data is repeat byte */ *cmd++ = ((pixel - repeating_pixel) - 1) & 0xFF; @@ -486,13 +508,16 @@ static void dlfb_compress_hline( if (pixel > raw_pixel_start) { /* finalize last RAW span */ *raw_pixels_count_byte = (pixel-raw_pixel_start) & 0xFF; + } else { + /* undo unused byte */ + cmd--; } *cmd_pixels_count_byte = (pixel - cmd_pixel_start) & 0xFF; - dev_addr += (pixel - cmd_pixel_start) * BPP; + dev_addr += (u8 *)pixel - (u8 *)cmd_pixel_start; } - if (cmd_buffer_end <= MIN_RLX_CMD_BYTES + cmd) { + if (cmd_buffer_end - MIN_RLX_CMD_BYTES <= cmd) { /* Fill leftover bytes with no-ops */ if (cmd_buffer_end > cmd) memset(cmd, 0xAF, cmd_buffer_end - cmd); @@ -520,6 +545,7 @@ static int dlfb_render_hline(struct dlfb_data *dlfb, struct urb **urb_ptr, struct urb *urb = *urb_ptr; u8 *cmd = *urb_buf_ptr; u8 *cmd_end = (u8 *) urb->transfer_buffer + urb->transfer_buffer_length; + unsigned long back_buffer_offset = 0; line_start = (u8 *) (front + byte_offset); next_pixel = line_start; @@ -530,6 +556,8 @@ static int dlfb_render_hline(struct dlfb_data *dlfb, struct urb **urb_ptr, const u8 *back_start = (u8 *) (dlfb->backing_buffer + byte_offset); + back_buffer_offset = (unsigned long)back_start - (unsigned long)line_start; + *ident_ptr += dlfb_trim_hline(back_start, &next_pixel, &byte_width); @@ -538,16 +566,14 @@ static int dlfb_render_hline(struct dlfb_data *dlfb, struct urb **urb_ptr, dev_addr += offset; back_start += offset; line_start += offset; - - memcpy((char *)back_start, (char *) line_start, - byte_width); } while (next_pixel < line_end) { dlfb_compress_hline((const uint16_t **) &next_pixel, (const uint16_t *) line_end, &dev_addr, - (u8 **) &cmd, (u8 *) cmd_end); + (u8 **) &cmd, (u8 *) cmd_end, back_buffer_offset, + ident_ptr); if (cmd >= cmd_end) { int len = cmd - (u8 *) urb->transfer_buffer; @@ -610,8 +636,11 @@ static int dlfb_handle_damage(struct dlfb_data *dlfb, int x, int y, } if (cmd > (char *) urb->transfer_buffer) { + int len; + if (cmd < (char *) urb->transfer_buffer + urb->transfer_buffer_length) + *cmd++ = 0xAF; /* Send partial buffer remaining before exiting */ - int len = cmd - (char *) urb->transfer_buffer; + len = cmd - (char *) urb->transfer_buffer; ret = dlfb_submit_urb(dlfb, urb, len); bytes_sent += len; } else @@ -735,8 +764,11 @@ static void dlfb_dpy_deferred_io(struct fb_info *info, } if (cmd > (char *) urb->transfer_buffer) { + int len; + if (cmd < (char *) urb->transfer_buffer + urb->transfer_buffer_length) + *cmd++ = 0xAF; /* Send partial buffer remaining before exiting */ - int len = cmd - (char *) urb->transfer_buffer; + len = cmd - (char *) urb->transfer_buffer; dlfb_submit_urb(dlfb, urb, len); bytes_sent += len; } else @@ -917,19 +949,17 @@ static void dlfb_free(struct kref *kref) { struct dlfb_data *dlfb = container_of(kref, struct dlfb_data, kref); + while (!list_empty(&dlfb->deferred_free)) { + struct dlfb_deferred_free *d = list_entry(dlfb->deferred_free.next, struct dlfb_deferred_free, list); + list_del(&d->list); + vfree(d->mem); + kfree(d); + } vfree(dlfb->backing_buffer); kfree(dlfb->edid); kfree(dlfb); } -static void dlfb_release_urb_work(struct work_struct *work) -{ - struct urb_node *unode = container_of(work, struct urb_node, - release_urb_work.work); - - up(&unode->dlfb->urbs.limit_sem); -} - static void dlfb_free_framebuffer(struct dlfb_data *dlfb) { struct fb_info *info = dlfb->info; @@ -1018,10 +1048,6 @@ static int dlfb_ops_check_var(struct fb_var_screeninfo *var, struct fb_videomode mode; struct dlfb_data *dlfb = info->par; - /* TODO: support dynamically changing framebuffer size */ - if ((var->xres * var->yres * 2) > info->fix.smem_len) - return -EINVAL; - /* set device-specific elements of var unrelated to mode */ dlfb_var_color_format(var); @@ -1039,22 +1065,42 @@ static int dlfb_ops_set_par(struct fb_info *info) int result; u16 *pix_framebuffer; int i; + struct fb_var_screeninfo fvs; + u32 line_length = info->var.xres * (info->var.bits_per_pixel / 8); + + /* clear the activate field because it causes spurious miscompares */ + fvs = info->var; + fvs.activate = 0; + fvs.vmode &= ~FB_VMODE_SMOOTH_XPAN; + + if (!memcmp(&dlfb->current_mode, &fvs, sizeof(struct fb_var_screeninfo))) + return 0; + + result = dlfb_realloc_framebuffer(dlfb, info, info->var.yres * line_length); + if (result) + return result; result = dlfb_set_video_mode(dlfb, &info->var); - if ((result == 0) && (dlfb->fb_count == 0)) { + if (result) + return result; + + dlfb->current_mode = fvs; + info->fix.line_length = line_length; + + if (dlfb->fb_count == 0) { /* paint greenscreen */ pix_framebuffer = (u16 *) info->screen_base; for (i = 0; i < info->fix.smem_len / 2; i++) pix_framebuffer[i] = 0x37e6; - - dlfb_handle_damage(dlfb, 0, 0, info->var.xres, info->var.yres, - info->screen_base); } - return result; + dlfb_handle_damage(dlfb, 0, 0, info->var.xres, info->var.yres, + info->screen_base); + + return 0; } /* To fonzi the jukebox (e.g. make blanking changes take effect) */ @@ -1129,21 +1175,29 @@ static struct fb_ops dlfb_ops = { }; +static void dlfb_deferred_vfree(struct dlfb_data *dlfb, void *mem) +{ + struct dlfb_deferred_free *d = kmalloc(sizeof(struct dlfb_deferred_free), GFP_KERNEL); + if (!d) + return; + d->mem = mem; + list_add(&d->list, &dlfb->deferred_free); +} + /* * Assumes &info->lock held by caller * Assumes no active clients have framebuffer open */ -static int dlfb_realloc_framebuffer(struct dlfb_data *dlfb, struct fb_info *info) +static int dlfb_realloc_framebuffer(struct dlfb_data *dlfb, struct fb_info *info, u32 new_len) { - int old_len = info->fix.smem_len; - int new_len; - unsigned char *old_fb = info->screen_base; + u32 old_len = info->fix.smem_len; + const void *old_fb = (const void __force *)info->screen_base; unsigned char *new_fb; unsigned char *new_back = NULL; - new_len = info->fix.line_length * info->var.yres; + new_len = PAGE_ALIGN(new_len); - if (PAGE_ALIGN(new_len) > old_len) { + if (new_len > old_len) { /* * Alloc system memory for virtual framebuffer */ @@ -1152,14 +1206,15 @@ static int dlfb_realloc_framebuffer(struct dlfb_data *dlfb, struct fb_info *info dev_err(info->dev, "Virtual framebuffer alloc failed\n"); return -ENOMEM; } + memset(new_fb, 0xff, new_len); if (info->screen_base) { memcpy(new_fb, old_fb, old_len); - vfree(info->screen_base); + dlfb_deferred_vfree(dlfb, (void __force *)info->screen_base); } - info->screen_base = new_fb; - info->fix.smem_len = PAGE_ALIGN(new_len); + info->screen_base = (char __iomem *)new_fb; + info->fix.smem_len = new_len; info->fix.smem_start = (unsigned long) new_fb; info->flags = udlfb_info_flags; @@ -1175,7 +1230,7 @@ static int dlfb_realloc_framebuffer(struct dlfb_data *dlfb, struct fb_info *info dev_info(info->dev, "No shadow/backing buffer allocated\n"); else { - vfree(dlfb->backing_buffer); + dlfb_deferred_vfree(dlfb, dlfb->backing_buffer); dlfb->backing_buffer = new_back; } } @@ -1327,11 +1382,6 @@ static int dlfb_setup_modes(struct dlfb_data *dlfb, * with mode size info, we can now alloc our framebuffer. */ memcpy(&info->fix, &dlfb_fix, sizeof(dlfb_fix)); - info->fix.line_length = info->var.xres * - (info->var.bits_per_pixel / 8); - - result = dlfb_realloc_framebuffer(dlfb, info); - } else result = -EINVAL; @@ -1419,7 +1469,10 @@ static ssize_t edid_store( if (!dlfb->edid || memcmp(src, dlfb->edid, src_size)) return -EINVAL; - dlfb_ops_set_par(fb_info); + ret = dlfb_ops_set_par(fb_info); + if (ret) + return ret; + return src_size; } @@ -1579,6 +1632,7 @@ static int dlfb_usb_probe(struct usb_interface *intf, } kref_init(&dlfb->kref); /* matching kref_put in usb .disconnect fn */ + INIT_LIST_HEAD(&dlfb->deferred_free); dlfb->udev = usbdev; usb_set_intfdata(intf, dlfb); @@ -1649,7 +1703,8 @@ static void dlfb_init_framebuffer_work(struct work_struct *work) dlfb->info = info; info->par = dlfb; info->pseudo_palette = dlfb->pseudo_palette; - info->fbops = &dlfb_ops; + dlfb->ops = dlfb_ops; + info->fbops = &dlfb->ops; retval = fb_alloc_cmap(&info->cmap, 256, 0); if (retval < 0) { @@ -1675,7 +1730,9 @@ static void dlfb_init_framebuffer_work(struct work_struct *work) dlfb_select_std_channel(dlfb); dlfb_ops_check_var(&info->var, info); - dlfb_ops_set_par(info); + retval = dlfb_ops_set_par(info); + if (retval) + goto error; retval = register_framebuffer(info); if (retval < 0) { @@ -1789,14 +1846,7 @@ static void dlfb_urb_completion(struct urb *urb) dlfb->urbs.available++; spin_unlock_irqrestore(&dlfb->urbs.lock, flags); - /* - * When using fb_defio, we deadlock if up() is called - * while another is waiting. So queue to another process. - */ - if (fb_defio) - schedule_delayed_work(&unode->release_urb_work, 0); - else - up(&dlfb->urbs.limit_sem); + up(&dlfb->urbs.limit_sem); } static void dlfb_free_urb_list(struct dlfb_data *dlfb) @@ -1805,23 +1855,17 @@ static void dlfb_free_urb_list(struct dlfb_data *dlfb) struct list_head *node; struct urb_node *unode; struct urb *urb; - int ret; - unsigned long flags; /* keep waiting and freeing, until we've got 'em all */ while (count--) { + down(&dlfb->urbs.limit_sem); - /* Getting interrupted means a leak, but ok at disconnect */ - ret = down_interruptible(&dlfb->urbs.limit_sem); - if (ret) - break; - - spin_lock_irqsave(&dlfb->urbs.lock, flags); + spin_lock_irq(&dlfb->urbs.lock); node = dlfb->urbs.list.next; /* have reserved one with sem */ list_del_init(node); - spin_unlock_irqrestore(&dlfb->urbs.lock, flags); + spin_unlock_irq(&dlfb->urbs.lock); unode = list_entry(node, struct urb_node, entry); urb = unode->urb; @@ -1838,25 +1882,27 @@ static void dlfb_free_urb_list(struct dlfb_data *dlfb) static int dlfb_alloc_urb_list(struct dlfb_data *dlfb, int count, size_t size) { - int i = 0; struct urb *urb; struct urb_node *unode; char *buf; + size_t wanted_size = count * size; spin_lock_init(&dlfb->urbs.lock); +retry: dlfb->urbs.size = size; INIT_LIST_HEAD(&dlfb->urbs.list); - while (i < count) { + sema_init(&dlfb->urbs.limit_sem, 0); + dlfb->urbs.count = 0; + dlfb->urbs.available = 0; + + while (dlfb->urbs.count * size < wanted_size) { unode = kzalloc(sizeof(*unode), GFP_KERNEL); if (!unode) break; unode->dlfb = dlfb; - INIT_DELAYED_WORK(&unode->release_urb_work, - dlfb_release_urb_work); - urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { kfree(unode); @@ -1864,11 +1910,16 @@ static int dlfb_alloc_urb_list(struct dlfb_data *dlfb, int count, size_t size) } unode->urb = urb; - buf = usb_alloc_coherent(dlfb->udev, MAX_TRANSFER, GFP_KERNEL, + buf = usb_alloc_coherent(dlfb->udev, size, GFP_KERNEL, &urb->transfer_dma); if (!buf) { kfree(unode); usb_free_urb(urb); + if (size > PAGE_SIZE) { + size /= 2; + dlfb_free_urb_list(dlfb); + goto retry; + } break; } @@ -1879,14 +1930,12 @@ static int dlfb_alloc_urb_list(struct dlfb_data *dlfb, int count, size_t size) list_add_tail(&unode->entry, &dlfb->urbs.list); - i++; + up(&dlfb->urbs.limit_sem); + dlfb->urbs.count++; + dlfb->urbs.available++; } - sema_init(&dlfb->urbs.limit_sem, i); - dlfb->urbs.count = i; - dlfb->urbs.available = i; - - return i; + return dlfb->urbs.count; } static struct urb *dlfb_get_urb(struct dlfb_data *dlfb) @@ -1894,7 +1943,6 @@ static struct urb *dlfb_get_urb(struct dlfb_data *dlfb) int ret; struct list_head *entry; struct urb_node *unode; - unsigned long flags; /* Wait for an in-flight buffer to complete and get re-queued */ ret = down_timeout(&dlfb->urbs.limit_sem, GET_URB_TIMEOUT); @@ -1906,14 +1954,14 @@ static struct urb *dlfb_get_urb(struct dlfb_data *dlfb) return NULL; } - spin_lock_irqsave(&dlfb->urbs.lock, flags); + spin_lock_irq(&dlfb->urbs.lock); BUG_ON(list_empty(&dlfb->urbs.list)); /* reserved one with limit_sem */ entry = dlfb->urbs.list.next; list_del_init(entry); dlfb->urbs.available--; - spin_unlock_irqrestore(&dlfb->urbs.lock, flags); + spin_unlock_irq(&dlfb->urbs.lock); unode = list_entry(entry, struct urb_node, entry); return unode->urb; diff --git a/drivers/video/fbdev/via/lcd.c b/drivers/video/fbdev/via/lcd.c index 5d21ff436ec8..b9305d73a1e5 100644 --- a/drivers/video/fbdev/via/lcd.c +++ b/drivers/video/fbdev/via/lcd.c @@ -758,6 +758,7 @@ static void set_lcd_output_path(int set_iga, int output_interface) viaparinfo->chip_info->gfx_chip_name)) viafb_write_reg_mask(CR97, VIACR, 0x84, BIT7 + BIT2 + BIT1 + BIT0); + /* fall through */ case INTERFACE_DVP0: case INTERFACE_DVP1: case INTERFACE_DFP_HIGH: diff --git a/drivers/video/fbdev/via/viafbdev.c b/drivers/video/fbdev/via/viafbdev.c index d2f785068ef4..7bb7e90b8f00 100644 --- a/drivers/video/fbdev/via/viafbdev.c +++ b/drivers/video/fbdev/via/viafbdev.c @@ -19,6 +19,7 @@ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include <linux/compiler.h> #include <linux/module.h> #include <linux/seq_file.h> #include <linux/slab.h> @@ -1468,7 +1469,7 @@ static const struct file_operations viafb_vt1636_proc_fops = { #endif /* CONFIG_FB_VIA_DIRECT_PROCFS */ -static int viafb_sup_odev_proc_show(struct seq_file *m, void *v) +static int __maybe_unused viafb_sup_odev_proc_show(struct seq_file *m, void *v) { via_odev_to_seq(m, supported_odev_map[ viaparinfo->shared->chip_info.gfx_chip_name]); |