diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-08-30 23:34:34 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-08-30 23:34:34 +0300 |
commit | 461f35f014466c4e26dca6be0f431f57297df3f2 (patch) | |
tree | 0bd2fded69ba0752ca16c304d3e1880d5f1eb30b /drivers/video/fbdev/core | |
parent | 53ea7f624fb91074c2f9458832ed74975ee5d64c (diff) | |
parent | 3698a75f5a98d0a6599e2878ab25d30a82dd836a (diff) | |
download | linux-461f35f014466c4e26dca6be0f431f57297df3f2.tar.xz |
Merge tag 'drm-next-2023-08-30' of git://anongit.freedesktop.org/drm/drm
Pull drm updates from Dave Airlie:
"The drm core grew a new generic gpu virtual address manager, and new
execution locking helpers. These are used by nouveau now to provide
uAPI support for the userspace Vulkan driver. AMD had a bunch of new
IP core support, loads of refactoring around fbdev, but mostly just
the usual amount of stuff across the board.
core:
- fix gfp flags in drmm_kmalloc
gpuva:
- add new generic GPU VA manager (for nouveau initially)
syncobj:
- add new DRM_IOCTL_SYNCOBJ_EVENTFD ioctl
dma-buf:
- acquire resv lock for mmap() in exporters
- support dma-buf self import automatically
- docs fixes
backlight:
- fix fbdev interactions
atomic:
- improve logging
prime:
- remove struct gem_prim_mmap plus driver updates
gem:
- drm_exec: add locking over multiple GEM objects
- fix lockdep checking
fbdev:
- make fbdev userspace interfaces optional
- use linux device instead of fbdev device
- use deferred i/o helper macros in various drivers
- Make FB core selectable without drivers
- Remove obsolete flags FBINFO_DEFAULT and FBINFO_FLAG_DEFAULT
- Add helper macros and Kconfig tokens for DMA-allocated framebuffer
ttm:
- support init_on_free
- swapout fixes
panel:
- panel-edp: Support AUO B116XAB01.4
- Support Visionox R66451 plus DT bindings
- ld9040:
- Backlight support
- magic improved
- Kconfig fix
- Convert to of_device_get_match_data()
- Fix Kconfig dependencies
- simple:
- Set bpc value to fix warning
- Set connector type for AUO T215HVN01
- Support Innolux G156HCE-L01 plus DT bindings
- ili9881: Support TDO TL050HDV35 LCD panel plus DT bindings
- startek: Support KD070FHFID015 MIPI-DSI panel plus DT bindings
- sitronix-st7789v:
- Support Inanbo T28CP45TN89 plus DT bindings
- Support EDT ET028013DMA plus DT bindings
- Various cleanups
- edp: Add timings for N140HCA-EAC
- Allow panels and touchscreens to power sequence together
- Fix Innolux G156HCE-L01 LVDS clock
bridge:
- debugfs for chains support
- dw-hdmi:
- Improve support for YUV420 bus format
- CEC suspend/resume
- update EDID on HDMI detect
- dw-mipi-dsi: Fix enable/disable of DSI controller
- lt9611uxc: Use MODULE_FIRMWARE()
- ps8640: Remove broken EDID code
- samsung-dsim: Fix command transfer
- tc358764:
- Handle HS/VS polarity
- Use BIT() macro
- Various cleanups
- adv7511: Fix low refresh rate
- anx7625:
- Switch to macros instead of hardcoded values
- locking fixes
- tc358767: fix hardware delays
- sitronix-st7789v:
- Support panel orientation
- Support rotation property
- Add support for Jasonic JT240MHQS-HWT-EK-E3 plus DT bindings
amdgpu:
- SDMA 6.1.0 support
- HDP 6.1 support
- SMUIO 14.0 support
- PSP 14.0 support
- IH 6.1 support
- Lots of checkpatch cleanups
- GFX 9.4.3 updates
- Add USB PD and IFWI flashing documentation
- GPUVM updates
- RAS fixes
- DRR fixes
- FAMS fixes
- Virtual display fixes
- Soft IH fixes
- SMU13 fixes
- Rework PSP firmware loading for other IPs
- Kernel doc fixes
- DCN 3.0.1 fixes
- LTTPR fixes
- DP MST fixes
- DCN 3.1.6 fixes
- SMU 13.x fixes
- PSP 13.x fixes
- SubVP fixes
- GC 9.4.3 fixes
- Display bandwidth calculation fixes
- VCN4 secure submission fixes
- Allow building DC on RISC-V
- Add visible FB info to bo_print_info
- HBR3 fixes
- GFX9 MCBP fix
- GMC10 vmhub index fix
- GMC11 vmhub index fix
- Create a new doorbell manager
- SR-IOV fixes
- initial freesync panel replay support
- revert zpos properly until igt regression is fixeed
- use TTM to manage doorbell BAR
- Expose both current and average power via hwmon if supported
amdkfd:
- Cleanup CRIU dma-buf handling
- Use KIQ to unmap HIQ
- GFX 9.4.3 debugger updates
- GFX 9.4.2 debugger fixes
- Enable cooperative groups fof gfx11
- SVM fixes
- Convert older APUs to use dGPU path like newer APUs
- Drop IOMMUv2 path as it is no longer used
- TBA fix for aldebaran
i915:
- ICL+ DSI modeset sequence
- HDCP improvements
- MTL display fixes and cleanups
- HSW/BDW PSR1 restored
- Init DDI ports in VBT order
- General display refactors
- Start using plane scale factor for relative data rate
- Use shmem for dpt objects
- Expose RPS thresholds in sysfs
- Apply GuC SLPC min frequency softlimit correctly
- Extend Wa_14015795083 to TGL, RKL, DG1 and ADL
- Fix a VMA UAF for multi-gt platform
- Do not use stolen on MTL due to HW bug
- Check HuC and GuC version compatibility on MTL
- avoid infinite GPU waits due to premature release of request memory
- Fixes and updates for GSC memory allocation
- Display SDVO fixes
- Take stolen handling out of FBC code
- Make i915_coherent_map_type GT-centric
- Simplify shmem_create_from_object map_type
msm:
- SM6125 MDSS support
- DPU: SM6125 DPU support
- DSI: runtime PM support, burst mode support
- DSI PHY: SM6125 support in 14nm DSI PHY driver
- GPU: prepare for a7xx
- fix a690 firmware
- disable relocs on a6xx and newer
radeon:
- Lots of checkpatch cleanups
ast:
- improve device-model detection
- Represent BMV as virtual connector
- Report DP connection status
nouveau:
- add new exec/bind interface to support Vulkan
- document some getparam ioctls
- improve VRAM detection
- various fixes/cleanups
- workraound DPCD issues
ivpu:
- MMU updates
- debugfs support
- Support vpu4
virtio:
- add sync object support
atmel-hlcdc:
- Support inverted pixclock polarity
etnaviv:
- runtime PM cleanups
- hang handling fixes
exynos:
- use fbdev DMA helpers
- fix possible NULL ptr dereference
komeda:
- always attach encoder
omapdrm:
- use fbdev DMA helpers
ingenic:
- kconfig regmap fixes
loongson:
- support display controller
mediatek:
- Small mtk-dpi cleanups
- DisplayPort: support eDP and aux-bus
- Fix coverity issues
- Fix potential memory leak if vmap() fail
mgag200:
- minor fixes
mxsfb:
- support disabling overlay planes
panfrost:
- fix sync in IRQ handling
ssd130x:
- Support per-controller default resolution plus DT bindings
- Reduce memory-allocation overhead
- Improve intermediate buffer size computation
- Fix allocation of temporary buffers
- Fix pitch computation
- Fix shadow plane allocation
tegra:
- use fbdev DMA helpers
- Convert to devm_platform_ioremap_resource()
- support bridge/connector
- enable PM
tidss:
- Support TI AM625 plus DT bindings
- Implement new connector model plus driver updates
vkms:
- improve write back support
- docs fixes
- support gamma LUT
zynqmp-dpsub:
- misc fixes"
* tag 'drm-next-2023-08-30' of git://anongit.freedesktop.org/drm/drm: (1327 commits)
drm/gpuva_mgr: remove unused prev pointer in __drm_gpuva_sm_map()
drm/tests/drm_kunit_helpers: Place correct function name in the comment header
drm/nouveau: uapi: don't pass NO_PREFETCH flag implicitly
drm/nouveau: uvmm: fix unset region pointer on remap
drm/nouveau: sched: avoid job races between entities
drm/i915: Fix HPD polling, reenabling the output poll work as needed
drm: Add an HPD poll helper to reschedule the poll work
drm/i915: Fix TLB-Invalidation seqno store
drm/ttm/tests: Fix type conversion in ttm_pool_test
drm/msm/a6xx: Bail out early if setting GPU OOB fails
drm/msm/a6xx: Move LLC accessors to the common header
drm/msm/a6xx: Introduce a6xx_llc_read
drm/ttm/tests: Require MMU when testing
drm/panel: simple: Fix Innolux G156HCE-L01 LVDS clock
Revert "Revert "drm/amdgpu/display: change pipe policy for DCN 2.0""
drm/amdgpu: Add memory vendor information
drm/amd: flush any delayed gfxoff on suspend entry
drm/amdgpu: skip fence GFX interrupts disable/enable for S0ix
drm/amdgpu: Remove gfxoff check in GFX v9.4.3
drm/amd/pm: Update pci link speed for smu v13.0.6
...
Diffstat (limited to 'drivers/video/fbdev/core')
-rw-r--r-- | drivers/video/fbdev/core/Kconfig | 198 | ||||
-rw-r--r-- | drivers/video/fbdev/core/Makefile | 11 | ||||
-rw-r--r-- | drivers/video/fbdev/core/fb_backlight.c | 33 | ||||
-rw-r--r-- | drivers/video/fbdev/core/fb_chrdev.c | 485 | ||||
-rw-r--r-- | drivers/video/fbdev/core/fb_info.c | 79 | ||||
-rw-r--r-- | drivers/video/fbdev/core/fb_internal.h | 67 | ||||
-rw-r--r-- | drivers/video/fbdev/core/fb_procfs.c | 62 | ||||
-rw-r--r-- | drivers/video/fbdev/core/fbcon.c | 7 | ||||
-rw-r--r-- | drivers/video/fbdev/core/fbmem.c | 592 | ||||
-rw-r--r-- | drivers/video/fbdev/core/fbsysfs.c | 134 |
10 files changed, 999 insertions, 669 deletions
diff --git a/drivers/video/fbdev/core/Kconfig b/drivers/video/fbdev/core/Kconfig new file mode 100644 index 000000000000..baf7e852c75b --- /dev/null +++ b/drivers/video/fbdev/core/Kconfig @@ -0,0 +1,198 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# fbdev core configuration +# + +config FB_CORE + select VIDEO_CMDLINE + tristate + +config FB_NOTIFY + bool + +config FIRMWARE_EDID + bool "Enable firmware EDID" + depends on FB + help + This enables access to the EDID transferred from the firmware. + On the i386, this is from the Video BIOS. Enable this if DDC/I2C + transfers do not work for your driver and if you are using + nvidiafb, i810fb or savagefb. + + In general, choosing Y for this option is safe. If you + experience extremely long delays while booting before you get + something on your display, try setting this to N. Matrox cards in + combination with certain motherboards and monitors are known to + suffer from this problem. + +config FB_DEVICE + bool "Provide legacy /dev/fb* device" + depends on FB_CORE + default y + help + Say Y here if you want the legacy /dev/fb* device file and + interfaces within sysfs anc procfs. It is only required if you + have userspace programs that depend on fbdev for graphics output. + This does not affect the framebuffer console. If unsure, say N. + +config FB_DDC + tristate + depends on FB + select I2C_ALGOBIT + select I2C + +config FB_CFB_FILLRECT + tristate + depends on FB_CORE + help + Include the cfb_fillrect function for generic software rectangle + filling. This is used by drivers that don't provide their own + (accelerated) version. + +config FB_CFB_COPYAREA + tristate + depends on FB_CORE + help + Include the cfb_copyarea function for generic software area copying. + This is used by drivers that don't provide their own (accelerated) + version. + +config FB_CFB_IMAGEBLIT + tristate + depends on FB_CORE + help + Include the cfb_imageblit function for generic software image + blitting. This is used by drivers that don't provide their own + (accelerated) version. + +config FB_CFB_REV_PIXELS_IN_BYTE + bool + depends on FB_CORE + help + Allow generic frame-buffer functions to work on displays with 1, 2 + and 4 bits per pixel depths which has opposite order of pixels in + byte order to bytes in long order. + +config FB_SYS_FILLRECT + tristate + depends on FB_CORE + help + Include the sys_fillrect function for generic software rectangle + filling. This is used by drivers that don't provide their own + (accelerated) version and the framebuffer is in system RAM. + +config FB_SYS_COPYAREA + tristate + depends on FB_CORE + help + Include the sys_copyarea function for generic software area copying. + This is used by drivers that don't provide their own (accelerated) + version and the framebuffer is in system RAM. + +config FB_SYS_IMAGEBLIT + tristate + depends on FB_CORE + help + Include the sys_imageblit function for generic software image + blitting. This is used by drivers that don't provide their own + (accelerated) version and the framebuffer is in system RAM. + +config FB_PROVIDE_GET_FB_UNMAPPED_AREA + bool + depends on FB + help + Allow generic frame-buffer to provide get_fb_unmapped_area + function to provide shareable character device support on nommu. + +menuconfig FB_FOREIGN_ENDIAN + bool "Framebuffer foreign endianness support" + depends on FB + help + This menu will let you enable support for the framebuffers with + non-native endianness (e.g. Little-Endian framebuffer on a + Big-Endian machine). Most probably you don't have such hardware, + so it's safe to say "n" here. + +choice + prompt "Choice endianness support" + depends on FB_FOREIGN_ENDIAN + +config FB_BOTH_ENDIAN + bool "Support for Big- and Little-Endian framebuffers" + +config FB_BIG_ENDIAN + bool "Support for Big-Endian framebuffers only" + +config FB_LITTLE_ENDIAN + bool "Support for Little-Endian framebuffers only" + +endchoice + +config FB_SYS_FOPS + tristate + depends on FB_CORE + +config FB_DEFERRED_IO + bool + depends on FB_CORE + +config FB_DMAMEM_HELPERS + bool + depends on FB_CORE + select FB_SYS_COPYAREA + select FB_SYS_FILLRECT + select FB_SYS_FOPS + select FB_SYS_IMAGEBLIT + +config FB_IOMEM_HELPERS + bool + depends on FB_CORE + select FB_CFB_COPYAREA + select FB_CFB_FILLRECT + select FB_CFB_IMAGEBLIT + +config FB_SYSMEM_HELPERS + bool + depends on FB_CORE + select FB_SYS_COPYAREA + select FB_SYS_FILLRECT + select FB_SYS_FOPS + select FB_SYS_IMAGEBLIT + +config FB_SYSMEM_HELPERS_DEFERRED + bool + depends on FB_CORE + select FB_DEFERRED_IO + select FB_SYSMEM_HELPERS + +config FB_BACKLIGHT + tristate + depends on FB + select BACKLIGHT_CLASS_DEVICE + +config FB_MODE_HELPERS + bool "Enable Video Mode Handling Helpers" + depends on FB + help + This enables functions for handling video modes using the + Generalized Timing Formula and the EDID parser. A few drivers rely + on this feature such as the radeonfb, rivafb, and the i810fb. If + your driver does not take advantage of this feature, choosing Y will + just increase the kernel size by about 5K. + +config FB_TILEBLITTING + bool "Enable Tile Blitting Support" + depends on FB + help + This enables tile blitting. Tile blitting is a drawing technique + where the screen is divided into rectangular sections (tiles), whereas + the standard blitting divides the screen into pixels. Because the + default drawing element is a tile, drawing functions will be passed + parameters in terms of number of tiles instead of number of pixels. + For example, to draw a single character, instead of using bitmaps, + an index to an array of bitmaps will be used. To clear or move a + rectangular section of a screen, the rectangle will be described in + terms of number of tiles in the x- and y-axis. + + This is particularly important to one driver, matroxfb. If + unsure, say N. diff --git a/drivers/video/fbdev/core/Makefile b/drivers/video/fbdev/core/Makefile index 8f0060160ffb..edfde2948e5c 100644 --- a/drivers/video/fbdev/core/Makefile +++ b/drivers/video/fbdev/core/Makefile @@ -1,9 +1,16 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_FB_NOTIFY) += fb_notify.o -obj-$(CONFIG_FB) += fb.o -fb-y := fbmem.o fbmon.o fbcmap.o fbsysfs.o \ +obj-$(CONFIG_FB_CORE) += fb.o +fb-y := fb_info.o \ + fbmem.o fbcmap.o \ modedb.o fbcvt.o fb_cmdline.o fb_io_fops.o +ifdef CONFIG_FB +fb-y += fb_backlight.o fbmon.o +endif fb-$(CONFIG_FB_DEFERRED_IO) += fb_defio.o +fb-$(CONFIG_FB_DEVICE) += fb_chrdev.o \ + fb_procfs.o \ + fbsysfs.o ifeq ($(CONFIG_FRAMEBUFFER_CONSOLE),y) fb-y += fbcon.o bitblit.o softcursor.o diff --git a/drivers/video/fbdev/core/fb_backlight.c b/drivers/video/fbdev/core/fb_backlight.c new file mode 100644 index 000000000000..e2d3b3adc870 --- /dev/null +++ b/drivers/video/fbdev/core/fb_backlight.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <linux/export.h> +#include <linux/fb.h> +#include <linux/mutex.h> + +#if IS_ENABLED(CONFIG_FB_BACKLIGHT) +/* + * This function generates a linear backlight curve + * + * 0: off + * 1-7: min + * 8-127: linear from min to max + */ +void fb_bl_default_curve(struct fb_info *fb_info, u8 off, u8 min, u8 max) +{ + unsigned int i, flat, count, range = (max - min); + + mutex_lock(&fb_info->bl_curve_mutex); + + fb_info->bl_curve[0] = off; + + for (flat = 1; flat < (FB_BACKLIGHT_LEVELS / 16); ++flat) + fb_info->bl_curve[flat] = min; + + count = FB_BACKLIGHT_LEVELS * 15 / 16; + for (i = 0; i < count; ++i) + fb_info->bl_curve[flat + i] = min + (range * (i + 1) / count); + + mutex_unlock(&fb_info->bl_curve_mutex); +} +EXPORT_SYMBOL_GPL(fb_bl_default_curve); +#endif diff --git a/drivers/video/fbdev/core/fb_chrdev.c b/drivers/video/fbdev/core/fb_chrdev.c new file mode 100644 index 000000000000..eadb81f53a82 --- /dev/null +++ b/drivers/video/fbdev/core/fb_chrdev.c @@ -0,0 +1,485 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/compat.h> +#include <linux/console.h> +#include <linux/fb.h> +#include <linux/fbcon.h> +#include <linux/major.h> + +#include "fb_internal.h" + +/* + * We hold a reference to the fb_info in file->private_data, + * but if the current registered fb has changed, we don't + * actually want to use it. + * + * So look up the fb_info using the inode minor number, + * and just verify it against the reference we have. + */ +static struct fb_info *file_fb_info(struct file *file) +{ + struct inode *inode = file_inode(file); + int fbidx = iminor(inode); + struct fb_info *info = registered_fb[fbidx]; + + if (info != file->private_data) + info = NULL; + return info; +} + +static ssize_t fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) +{ + struct fb_info *info = file_fb_info(file); + + if (!info) + return -ENODEV; + + if (info->state != FBINFO_STATE_RUNNING) + return -EPERM; + + if (info->fbops->fb_read) + return info->fbops->fb_read(info, buf, count, ppos); + + return fb_io_read(info, buf, count, ppos); +} + +static ssize_t fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) +{ + struct fb_info *info = file_fb_info(file); + + if (!info) + return -ENODEV; + + if (info->state != FBINFO_STATE_RUNNING) + return -EPERM; + + if (info->fbops->fb_write) + return info->fbops->fb_write(info, buf, count, ppos); + + return fb_io_write(info, buf, count, ppos); +} + +static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) +{ + const struct fb_ops *fb; + struct fb_var_screeninfo var; + struct fb_fix_screeninfo fix; + struct fb_cmap cmap_from; + struct fb_cmap_user cmap; + void __user *argp = (void __user *)arg; + long ret = 0; + + switch (cmd) { + case FBIOGET_VSCREENINFO: + lock_fb_info(info); + var = info->var; + unlock_fb_info(info); + + ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0; + break; + case FBIOPUT_VSCREENINFO: + if (copy_from_user(&var, argp, sizeof(var))) + return -EFAULT; + /* only for kernel-internal use */ + var.activate &= ~FB_ACTIVATE_KD_TEXT; + console_lock(); + lock_fb_info(info); + ret = fbcon_modechange_possible(info, &var); + if (!ret) + ret = fb_set_var(info, &var); + if (!ret) + fbcon_update_vcs(info, var.activate & FB_ACTIVATE_ALL); + unlock_fb_info(info); + console_unlock(); + if (!ret && copy_to_user(argp, &var, sizeof(var))) + ret = -EFAULT; + break; + case FBIOGET_FSCREENINFO: + lock_fb_info(info); + memcpy(&fix, &info->fix, sizeof(fix)); + if (info->flags & FBINFO_HIDE_SMEM_START) + fix.smem_start = 0; + unlock_fb_info(info); + + ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0; + break; + case FBIOPUTCMAP: + if (copy_from_user(&cmap, argp, sizeof(cmap))) + return -EFAULT; + ret = fb_set_user_cmap(&cmap, info); + break; + case FBIOGETCMAP: + if (copy_from_user(&cmap, argp, sizeof(cmap))) + return -EFAULT; + lock_fb_info(info); + cmap_from = info->cmap; + unlock_fb_info(info); + ret = fb_cmap_to_user(&cmap_from, &cmap); + break; + case FBIOPAN_DISPLAY: + if (copy_from_user(&var, argp, sizeof(var))) + return -EFAULT; + console_lock(); + lock_fb_info(info); + ret = fb_pan_display(info, &var); + unlock_fb_info(info); + console_unlock(); + if (ret == 0 && copy_to_user(argp, &var, sizeof(var))) + return -EFAULT; + break; + case FBIO_CURSOR: + ret = -EINVAL; + break; + case FBIOGET_CON2FBMAP: + ret = fbcon_get_con2fb_map_ioctl(argp); + break; + case FBIOPUT_CON2FBMAP: + ret = fbcon_set_con2fb_map_ioctl(argp); + break; + case FBIOBLANK: + if (arg > FB_BLANK_POWERDOWN) + return -EINVAL; + console_lock(); + lock_fb_info(info); + ret = fb_blank(info, arg); + /* might again call into fb_blank */ + fbcon_fb_blanked(info, arg); + unlock_fb_info(info); + console_unlock(); + break; + default: + lock_fb_info(info); + fb = info->fbops; + if (fb->fb_ioctl) + ret = fb->fb_ioctl(info, cmd, arg); + else + ret = -ENOTTY; + unlock_fb_info(info); + } + return ret; +} + +static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct fb_info *info = file_fb_info(file); + + if (!info) + return -ENODEV; + return do_fb_ioctl(info, cmd, arg); +} + +#ifdef CONFIG_COMPAT +struct fb_fix_screeninfo32 { + char id[16]; + compat_caddr_t smem_start; + u32 smem_len; + u32 type; + u32 type_aux; + u32 visual; + u16 xpanstep; + u16 ypanstep; + u16 ywrapstep; + u32 line_length; + compat_caddr_t mmio_start; + u32 mmio_len; + u32 accel; + u16 reserved[3]; +}; + +struct fb_cmap32 { + u32 start; + u32 len; + compat_caddr_t red; + compat_caddr_t green; + compat_caddr_t blue; + compat_caddr_t transp; +}; + +static int fb_getput_cmap(struct fb_info *info, unsigned int cmd, + unsigned long arg) +{ + struct fb_cmap32 cmap32; + struct fb_cmap cmap_from; + struct fb_cmap_user cmap; + + if (copy_from_user(&cmap32, compat_ptr(arg), sizeof(cmap32))) + return -EFAULT; + + cmap = (struct fb_cmap_user) { + .start = cmap32.start, + .len = cmap32.len, + .red = compat_ptr(cmap32.red), + .green = compat_ptr(cmap32.green), + .blue = compat_ptr(cmap32.blue), + .transp = compat_ptr(cmap32.transp), + }; + + if (cmd == FBIOPUTCMAP) + return fb_set_user_cmap(&cmap, info); + + lock_fb_info(info); + cmap_from = info->cmap; + unlock_fb_info(info); + + return fb_cmap_to_user(&cmap_from, &cmap); +} + +static int do_fscreeninfo_to_user(struct fb_fix_screeninfo *fix, + struct fb_fix_screeninfo32 __user *fix32) +{ + __u32 data; + int err; + + err = copy_to_user(&fix32->id, &fix->id, sizeof(fix32->id)); + + data = (__u32) (unsigned long) fix->smem_start; + err |= put_user(data, &fix32->smem_start); + + err |= put_user(fix->smem_len, &fix32->smem_len); + err |= put_user(fix->type, &fix32->type); + err |= put_user(fix->type_aux, &fix32->type_aux); + err |= put_user(fix->visual, &fix32->visual); + err |= put_user(fix->xpanstep, &fix32->xpanstep); + err |= put_user(fix->ypanstep, &fix32->ypanstep); + err |= put_user(fix->ywrapstep, &fix32->ywrapstep); + err |= put_user(fix->line_length, &fix32->line_length); + + data = (__u32) (unsigned long) fix->mmio_start; + err |= put_user(data, &fix32->mmio_start); + + err |= put_user(fix->mmio_len, &fix32->mmio_len); + err |= put_user(fix->accel, &fix32->accel); + err |= copy_to_user(fix32->reserved, fix->reserved, + sizeof(fix->reserved)); + + if (err) + return -EFAULT; + return 0; +} + +static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd, + unsigned long arg) +{ + struct fb_fix_screeninfo fix; + + lock_fb_info(info); + fix = info->fix; + if (info->flags & FBINFO_HIDE_SMEM_START) + fix.smem_start = 0; + unlock_fb_info(info); + return do_fscreeninfo_to_user(&fix, compat_ptr(arg)); +} + +static long fb_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct fb_info *info = file_fb_info(file); + const struct fb_ops *fb; + long ret = -ENOIOCTLCMD; + + if (!info) + return -ENODEV; + fb = info->fbops; + switch (cmd) { + case FBIOGET_VSCREENINFO: + case FBIOPUT_VSCREENINFO: + case FBIOPAN_DISPLAY: + case FBIOGET_CON2FBMAP: + case FBIOPUT_CON2FBMAP: + arg = (unsigned long) compat_ptr(arg); + fallthrough; + case FBIOBLANK: + ret = do_fb_ioctl(info, cmd, arg); + break; + + case FBIOGET_FSCREENINFO: + ret = fb_get_fscreeninfo(info, cmd, arg); + break; + + case FBIOGETCMAP: + case FBIOPUTCMAP: + ret = fb_getput_cmap(info, cmd, arg); + break; + + default: + if (fb->fb_compat_ioctl) + ret = fb->fb_compat_ioctl(info, cmd, arg); + break; + } + return ret; +} +#endif + +static int fb_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct fb_info *info = file_fb_info(file); + unsigned long mmio_pgoff; + unsigned long start; + u32 len; + + if (!info) + return -ENODEV; + mutex_lock(&info->mm_lock); + + if (info->fbops->fb_mmap) { + int res; + + /* + * The framebuffer needs to be accessed decrypted, be sure + * SME protection is removed ahead of the call + */ + vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot); + res = info->fbops->fb_mmap(info, vma); + mutex_unlock(&info->mm_lock); + return res; +#if IS_ENABLED(CONFIG_FB_DEFERRED_IO) + } else if (info->fbdefio) { + /* + * FB deferred I/O wants you to handle mmap in your drivers. At a + * minimum, point struct fb_ops.fb_mmap to fb_deferred_io_mmap(). + */ + dev_warn_once(info->dev, "fbdev mmap not set up for deferred I/O.\n"); + mutex_unlock(&info->mm_lock); + return -ENODEV; +#endif + } + + /* + * Ugh. This can be either the frame buffer mapping, or + * if pgoff points past it, the mmio mapping. + */ + start = info->fix.smem_start; + len = info->fix.smem_len; + mmio_pgoff = PAGE_ALIGN((start & ~PAGE_MASK) + len) >> PAGE_SHIFT; + if (vma->vm_pgoff >= mmio_pgoff) { + if (info->var.accel_flags) { + mutex_unlock(&info->mm_lock); + return -EINVAL; + } + + vma->vm_pgoff -= mmio_pgoff; + start = info->fix.mmio_start; + len = info->fix.mmio_len; + } + mutex_unlock(&info->mm_lock); + + vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); + fb_pgprotect(file, vma, start); + + return vm_iomap_memory(vma, start, len); +} + +static int fb_open(struct inode *inode, struct file *file) +__acquires(&info->lock) +__releases(&info->lock) +{ + int fbidx = iminor(inode); + struct fb_info *info; + int res = 0; + + info = get_fb_info(fbidx); + if (!info) { + request_module("fb%d", fbidx); + info = get_fb_info(fbidx); + if (!info) + return -ENODEV; + } + if (IS_ERR(info)) + return PTR_ERR(info); + + lock_fb_info(info); + if (!try_module_get(info->fbops->owner)) { + res = -ENODEV; + goto out; + } + file->private_data = info; + if (info->fbops->fb_open) { + res = info->fbops->fb_open(info, 1); + if (res) + module_put(info->fbops->owner); + } +#ifdef CONFIG_FB_DEFERRED_IO + if (info->fbdefio) + fb_deferred_io_open(info, inode, file); +#endif +out: + unlock_fb_info(info); + if (res) + put_fb_info(info); + return res; +} + +static int fb_release(struct inode *inode, struct file *file) +__acquires(&info->lock) +__releases(&info->lock) +{ + struct fb_info * const info = file->private_data; + + lock_fb_info(info); +#if IS_ENABLED(CONFIG_FB_DEFERRED_IO) + if (info->fbdefio) + fb_deferred_io_release(info); +#endif + if (info->fbops->fb_release) + info->fbops->fb_release(info, 1); + module_put(info->fbops->owner); + unlock_fb_info(info); + put_fb_info(info); + return 0; +} + +#if defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && !defined(CONFIG_MMU) +static unsigned long get_fb_unmapped_area(struct file *filp, + unsigned long addr, unsigned long len, + unsigned long pgoff, unsigned long flags) +{ + struct fb_info * const info = filp->private_data; + unsigned long fb_size = PAGE_ALIGN(info->fix.smem_len); + + if (pgoff > fb_size || len > fb_size - pgoff) + return -EINVAL; + + return (unsigned long)info->screen_base + pgoff; +} +#endif + +static const struct file_operations fb_fops = { + .owner = THIS_MODULE, + .read = fb_read, + .write = fb_write, + .unlocked_ioctl = fb_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = fb_compat_ioctl, +#endif + .mmap = fb_mmap, + .open = fb_open, + .release = fb_release, +#if defined(HAVE_ARCH_FB_UNMAPPED_AREA) || \ + (defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && \ + !defined(CONFIG_MMU)) + .get_unmapped_area = get_fb_unmapped_area, +#endif +#ifdef CONFIG_FB_DEFERRED_IO + .fsync = fb_deferred_io_fsync, +#endif + .llseek = default_llseek, +}; + +int fb_register_chrdev(void) +{ + int ret; + + ret = register_chrdev(FB_MAJOR, "fb", &fb_fops); + if (ret) { + pr_err("Unable to get major %d for fb devs\n", FB_MAJOR); + return ret; + } + + return ret; +} + +void fb_unregister_chrdev(void) +{ + unregister_chrdev(FB_MAJOR, "fb"); +} diff --git a/drivers/video/fbdev/core/fb_info.c b/drivers/video/fbdev/core/fb_info.c new file mode 100644 index 000000000000..4847ebe50d7d --- /dev/null +++ b/drivers/video/fbdev/core/fb_info.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <linux/export.h> +#include <linux/fb.h> +#include <linux/mutex.h> +#include <linux/slab.h> + +/** + * framebuffer_alloc - creates a new frame buffer info structure + * + * @size: size of driver private data, can be zero + * @dev: pointer to the device for this fb, this can be NULL + * + * Creates a new frame buffer info structure. Also reserves @size bytes + * for driver private data (info->par). info->par (if any) will be + * aligned to sizeof(long). The new instances of struct fb_info and + * the driver private data are both cleared to zero. + * + * Returns the new structure, or NULL if an error occurred. + * + */ +struct fb_info *framebuffer_alloc(size_t size, struct device *dev) +{ +#define BYTES_PER_LONG (BITS_PER_LONG/8) +#define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG)) + int fb_info_size = sizeof(struct fb_info); + struct fb_info *info; + char *p; + + if (size) + fb_info_size += PADDING; + + p = kzalloc(fb_info_size + size, GFP_KERNEL); + + if (!p) + return NULL; + + info = (struct fb_info *) p; + + if (size) + info->par = p + fb_info_size; + + info->device = dev; + info->fbcon_rotate_hint = -1; + +#if IS_ENABLED(CONFIG_FB_BACKLIGHT) + mutex_init(&info->bl_curve_mutex); +#endif + + return info; +#undef PADDING +#undef BYTES_PER_LONG +} +EXPORT_SYMBOL(framebuffer_alloc); + +/** + * framebuffer_release - marks the structure available for freeing + * + * @info: frame buffer info structure + * + * Drop the reference count of the device embedded in the + * framebuffer info structure. + * + */ +void framebuffer_release(struct fb_info *info) +{ + if (!info) + return; + + if (WARN_ON(refcount_read(&info->count))) + return; + +#if IS_ENABLED(CONFIG_FB_BACKLIGHT) + mutex_destroy(&info->bl_curve_mutex); +#endif + + kfree(info); +} +EXPORT_SYMBOL(framebuffer_release); diff --git a/drivers/video/fbdev/core/fb_internal.h b/drivers/video/fbdev/core/fb_internal.h new file mode 100644 index 000000000000..4c8d509a0026 --- /dev/null +++ b/drivers/video/fbdev/core/fb_internal.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _FB_INTERNAL_H +#define _FB_INTERNAL_H + +#include <linux/device.h> +#include <linux/fb.h> +#include <linux/mutex.h> + +/* fb_devfs.c */ +#if defined(CONFIG_FB_DEVICE) +int fb_register_chrdev(void); +void fb_unregister_chrdev(void); +#else +static inline int fb_register_chrdev(void) +{ + return 0; +} +static inline void fb_unregister_chrdev(void) +{ } +#endif + +/* fbmem.c */ +extern struct class *fb_class; +extern struct mutex registration_lock; +extern struct fb_info *registered_fb[FB_MAX]; +extern int num_registered_fb; +struct fb_info *get_fb_info(unsigned int idx); +void put_fb_info(struct fb_info *fb_info); + +/* fb_procfs.c */ +#if defined(CONFIG_FB_DEVICE) +int fb_init_procfs(void); +void fb_cleanup_procfs(void); +#else +static inline int fb_init_procfs(void) +{ + return 0; +} +static inline void fb_cleanup_procfs(void) +{ } +#endif + +/* fbsysfs.c */ +#if defined(CONFIG_FB_DEVICE) +int fb_device_create(struct fb_info *fb_info); +void fb_device_destroy(struct fb_info *fb_info); +#else +static inline int fb_device_create(struct fb_info *fb_info) +{ + /* + * Acquire a reference on the parent device to avoid + * unplug operations behind our back. With the fbdev + * device enabled, this is performed within register_device(). + */ + get_device(fb_info->device); + + return 0; +} +static inline void fb_device_destroy(struct fb_info *fb_info) +{ + /* Undo the get_device() from fb_device_create() */ + put_device(fb_info->device); +} +#endif + +#endif diff --git a/drivers/video/fbdev/core/fb_procfs.c b/drivers/video/fbdev/core/fb_procfs.c new file mode 100644 index 000000000000..59641142f8aa --- /dev/null +++ b/drivers/video/fbdev/core/fb_procfs.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/proc_fs.h> + +#include "fb_internal.h" + +static struct proc_dir_entry *fb_proc_dir_entry; + +static void *fb_seq_start(struct seq_file *m, loff_t *pos) +{ + mutex_lock(®istration_lock); + + return (*pos < FB_MAX) ? pos : NULL; +} + +static void fb_seq_stop(struct seq_file *m, void *v) +{ + mutex_unlock(®istration_lock); +} + +static void *fb_seq_next(struct seq_file *m, void *v, loff_t *pos) +{ + (*pos)++; + + return (*pos < FB_MAX) ? pos : NULL; +} + +static int fb_seq_show(struct seq_file *m, void *v) +{ + int i = *(loff_t *)v; + struct fb_info *fi = registered_fb[i]; + + if (fi) + seq_printf(m, "%d %s\n", fi->node, fi->fix.id); + + return 0; +} + +static const struct seq_operations __maybe_unused fb_proc_seq_ops = { + .start = fb_seq_start, + .stop = fb_seq_stop, + .next = fb_seq_next, + .show = fb_seq_show, +}; + +int fb_init_procfs(void) +{ + struct proc_dir_entry *proc; + + proc = proc_create_seq("fb", 0, NULL, &fb_proc_seq_ops); + if (!proc) + return -ENOMEM; + + fb_proc_dir_entry = proc; + + return 0; +} + +void fb_cleanup_procfs(void) +{ + proc_remove(fb_proc_dir_entry); +} diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 887fad44e7ec..f157a5a1dffc 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -78,6 +78,7 @@ #include <asm/irq.h> #include "fbcon.h" +#include "fb_internal.h" /* * FIXME: Locking @@ -102,8 +103,8 @@ enum { static struct fbcon_display fb_display[MAX_NR_CONSOLES]; -struct fb_info *fbcon_registered_fb[FB_MAX]; -int fbcon_num_registered_fb; +static struct fb_info *fbcon_registered_fb[FB_MAX]; +static int fbcon_num_registered_fb; #define fbcon_for_each_registered_fb(i) \ for (i = 0; WARN_CONSOLE_UNLOCKED(), i < FB_MAX; i++) \ @@ -576,7 +577,7 @@ static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info, if (scr_readw(r) != vc->vc_video_erase_char) break; if (r != q && new_rows >= rows + logo_lines) { - save = kzalloc(array3_size(logo_lines, new_cols, 2), + save = kmalloc(array3_size(logo_lines, new_cols, 2), GFP_KERNEL); if (save) { int i = min(cols, new_cols); diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c index 329d16e49a90..ee44a46a66be 100644 --- a/drivers/video/fbdev/core/fbmem.c +++ b/drivers/video/fbdev/core/fbmem.c @@ -13,20 +13,16 @@ #include <linux/module.h> -#include <linux/compat.h> #include <linux/types.h> #include <linux/errno.h> #include <linux/kernel.h> -#include <linux/major.h> #include <linux/slab.h> #include <linux/mm.h> #include <linux/mman.h> #include <linux/vt.h> #include <linux/init.h> #include <linux/linux_logo.h> -#include <linux/proc_fs.h> #include <linux/platform_device.h> -#include <linux/seq_file.h> #include <linux/console.h> #include <linux/kmod.h> #include <linux/err.h> @@ -40,14 +36,17 @@ #include <video/nomodeset.h> #include <video/vga.h> +#include "fb_internal.h" + /* * Frame buffer device initialization and setup routines */ #define FBPIXMAPSIZE (1024 * 8) -static DEFINE_MUTEX(registration_lock); +struct class *fb_class; +DEFINE_MUTEX(registration_lock); struct fb_info *registered_fb[FB_MAX] __read_mostly; int num_registered_fb __read_mostly; #define for_each_registered_fb(i) \ @@ -58,7 +57,7 @@ bool fb_center_logo __read_mostly; int fb_logo_count __read_mostly = -1; -static struct fb_info *get_fb_info(unsigned int idx) +struct fb_info *get_fb_info(unsigned int idx) { struct fb_info *fb_info; @@ -74,7 +73,7 @@ static struct fb_info *get_fb_info(unsigned int idx) return fb_info; } -static void put_fb_info(struct fb_info *fb_info) +void put_fb_info(struct fb_info *fb_info) { if (!refcount_dec_and_test(&fb_info->count)) return; @@ -703,93 +702,6 @@ int fb_show_logo(struct fb_info *info, int rotate) { return 0; } EXPORT_SYMBOL(fb_prepare_logo); EXPORT_SYMBOL(fb_show_logo); -static void *fb_seq_start(struct seq_file *m, loff_t *pos) -{ - mutex_lock(®istration_lock); - return (*pos < FB_MAX) ? pos : NULL; -} - -static void *fb_seq_next(struct seq_file *m, void *v, loff_t *pos) -{ - (*pos)++; - return (*pos < FB_MAX) ? pos : NULL; -} - -static void fb_seq_stop(struct seq_file *m, void *v) -{ - mutex_unlock(®istration_lock); -} - -static int fb_seq_show(struct seq_file *m, void *v) -{ - int i = *(loff_t *)v; - struct fb_info *fi = registered_fb[i]; - - if (fi) - seq_printf(m, "%d %s\n", fi->node, fi->fix.id); - return 0; -} - -static const struct seq_operations __maybe_unused proc_fb_seq_ops = { - .start = fb_seq_start, - .next = fb_seq_next, - .stop = fb_seq_stop, - .show = fb_seq_show, -}; - -/* - * We hold a reference to the fb_info in file->private_data, - * but if the current registered fb has changed, we don't - * actually want to use it. - * - * So look up the fb_info using the inode minor number, - * and just verify it against the reference we have. - */ -static struct fb_info *file_fb_info(struct file *file) -{ - struct inode *inode = file_inode(file); - int fbidx = iminor(inode); - struct fb_info *info = registered_fb[fbidx]; - - if (info != file->private_data) - info = NULL; - return info; -} - -static ssize_t -fb_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) -{ - struct fb_info *info = file_fb_info(file); - - if (!info) - return -ENODEV; - - if (info->state != FBINFO_STATE_RUNNING) - return -EPERM; - - if (info->fbops->fb_read) - return info->fbops->fb_read(info, buf, count, ppos); - - return fb_io_read(info, buf, count, ppos); -} - -static ssize_t -fb_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) -{ - struct fb_info *info = file_fb_info(file); - - if (!info) - return -ENODEV; - - if (info->state != FBINFO_STATE_RUNNING) - return -EPERM; - - if (info->fbops->fb_write) - return info->fbops->fb_write(info, buf, count, ppos); - - return fb_io_write(info, buf, count, ppos); -} - int fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var) { @@ -989,419 +901,6 @@ fb_blank(struct fb_info *info, int blank) } EXPORT_SYMBOL(fb_blank); -static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, - unsigned long arg) -{ - const struct fb_ops *fb; - struct fb_var_screeninfo var; - struct fb_fix_screeninfo fix; - struct fb_cmap cmap_from; - struct fb_cmap_user cmap; - void __user *argp = (void __user *)arg; - long ret = 0; - - switch (cmd) { - case FBIOGET_VSCREENINFO: - lock_fb_info(info); - var = info->var; - unlock_fb_info(info); - - ret = copy_to_user(argp, &var, sizeof(var)) ? -EFAULT : 0; - break; - case FBIOPUT_VSCREENINFO: - if (copy_from_user(&var, argp, sizeof(var))) - return -EFAULT; - /* only for kernel-internal use */ - var.activate &= ~FB_ACTIVATE_KD_TEXT; - console_lock(); - lock_fb_info(info); - ret = fbcon_modechange_possible(info, &var); - if (!ret) - ret = fb_set_var(info, &var); - if (!ret) - fbcon_update_vcs(info, var.activate & FB_ACTIVATE_ALL); - unlock_fb_info(info); - console_unlock(); - if (!ret && copy_to_user(argp, &var, sizeof(var))) - ret = -EFAULT; - break; - case FBIOGET_FSCREENINFO: - lock_fb_info(info); - memcpy(&fix, &info->fix, sizeof(fix)); - if (info->flags & FBINFO_HIDE_SMEM_START) - fix.smem_start = 0; - unlock_fb_info(info); - - ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0; - break; - case FBIOPUTCMAP: - if (copy_from_user(&cmap, argp, sizeof(cmap))) - return -EFAULT; - ret = fb_set_user_cmap(&cmap, info); - break; - case FBIOGETCMAP: - if (copy_from_user(&cmap, argp, sizeof(cmap))) - return -EFAULT; - lock_fb_info(info); - cmap_from = info->cmap; - unlock_fb_info(info); - ret = fb_cmap_to_user(&cmap_from, &cmap); - break; - case FBIOPAN_DISPLAY: - if (copy_from_user(&var, argp, sizeof(var))) - return -EFAULT; - console_lock(); - lock_fb_info(info); - ret = fb_pan_display(info, &var); - unlock_fb_info(info); - console_unlock(); - if (ret == 0 && copy_to_user(argp, &var, sizeof(var))) - return -EFAULT; - break; - case FBIO_CURSOR: - ret = -EINVAL; - break; - case FBIOGET_CON2FBMAP: - ret = fbcon_get_con2fb_map_ioctl(argp); - break; - case FBIOPUT_CON2FBMAP: - ret = fbcon_set_con2fb_map_ioctl(argp); - break; - case FBIOBLANK: - if (arg > FB_BLANK_POWERDOWN) - return -EINVAL; - console_lock(); - lock_fb_info(info); - ret = fb_blank(info, arg); - /* might again call into fb_blank */ - fbcon_fb_blanked(info, arg); - unlock_fb_info(info); - console_unlock(); - break; - default: - lock_fb_info(info); - fb = info->fbops; - if (fb->fb_ioctl) - ret = fb->fb_ioctl(info, cmd, arg); - else - ret = -ENOTTY; - unlock_fb_info(info); - } - return ret; -} - -static long fb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - struct fb_info *info = file_fb_info(file); - - if (!info) - return -ENODEV; - return do_fb_ioctl(info, cmd, arg); -} - -#ifdef CONFIG_COMPAT -struct fb_fix_screeninfo32 { - char id[16]; - compat_caddr_t smem_start; - u32 smem_len; - u32 type; - u32 type_aux; - u32 visual; - u16 xpanstep; - u16 ypanstep; - u16 ywrapstep; - u32 line_length; - compat_caddr_t mmio_start; - u32 mmio_len; - u32 accel; - u16 reserved[3]; -}; - -struct fb_cmap32 { - u32 start; - u32 len; - compat_caddr_t red; - compat_caddr_t green; - compat_caddr_t blue; - compat_caddr_t transp; -}; - -static int fb_getput_cmap(struct fb_info *info, unsigned int cmd, - unsigned long arg) -{ - struct fb_cmap32 cmap32; - struct fb_cmap cmap_from; - struct fb_cmap_user cmap; - - if (copy_from_user(&cmap32, compat_ptr(arg), sizeof(cmap32))) - return -EFAULT; - - cmap = (struct fb_cmap_user) { - .start = cmap32.start, - .len = cmap32.len, - .red = compat_ptr(cmap32.red), - .green = compat_ptr(cmap32.green), - .blue = compat_ptr(cmap32.blue), - .transp = compat_ptr(cmap32.transp), - }; - - if (cmd == FBIOPUTCMAP) - return fb_set_user_cmap(&cmap, info); - - lock_fb_info(info); - cmap_from = info->cmap; - unlock_fb_info(info); - - return fb_cmap_to_user(&cmap_from, &cmap); -} - -static int do_fscreeninfo_to_user(struct fb_fix_screeninfo *fix, - struct fb_fix_screeninfo32 __user *fix32) -{ - __u32 data; - int err; - - err = copy_to_user(&fix32->id, &fix->id, sizeof(fix32->id)); - - data = (__u32) (unsigned long) fix->smem_start; - err |= put_user(data, &fix32->smem_start); - - err |= put_user(fix->smem_len, &fix32->smem_len); - err |= put_user(fix->type, &fix32->type); - err |= put_user(fix->type_aux, &fix32->type_aux); - err |= put_user(fix->visual, &fix32->visual); - err |= put_user(fix->xpanstep, &fix32->xpanstep); - err |= put_user(fix->ypanstep, &fix32->ypanstep); - err |= put_user(fix->ywrapstep, &fix32->ywrapstep); - err |= put_user(fix->line_length, &fix32->line_length); - - data = (__u32) (unsigned long) fix->mmio_start; - err |= put_user(data, &fix32->mmio_start); - - err |= put_user(fix->mmio_len, &fix32->mmio_len); - err |= put_user(fix->accel, &fix32->accel); - err |= copy_to_user(fix32->reserved, fix->reserved, - sizeof(fix->reserved)); - - if (err) - return -EFAULT; - return 0; -} - -static int fb_get_fscreeninfo(struct fb_info *info, unsigned int cmd, - unsigned long arg) -{ - struct fb_fix_screeninfo fix; - - lock_fb_info(info); - fix = info->fix; - if (info->flags & FBINFO_HIDE_SMEM_START) - fix.smem_start = 0; - unlock_fb_info(info); - return do_fscreeninfo_to_user(&fix, compat_ptr(arg)); -} - -static long fb_compat_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - struct fb_info *info = file_fb_info(file); - const struct fb_ops *fb; - long ret = -ENOIOCTLCMD; - - if (!info) - return -ENODEV; - fb = info->fbops; - switch(cmd) { - case FBIOGET_VSCREENINFO: - case FBIOPUT_VSCREENINFO: - case FBIOPAN_DISPLAY: - case FBIOGET_CON2FBMAP: - case FBIOPUT_CON2FBMAP: - arg = (unsigned long) compat_ptr(arg); - fallthrough; - case FBIOBLANK: - ret = do_fb_ioctl(info, cmd, arg); - break; - - case FBIOGET_FSCREENINFO: - ret = fb_get_fscreeninfo(info, cmd, arg); - break; - - case FBIOGETCMAP: - case FBIOPUTCMAP: - ret = fb_getput_cmap(info, cmd, arg); - break; - - default: - if (fb->fb_compat_ioctl) - ret = fb->fb_compat_ioctl(info, cmd, arg); - break; - } - return ret; -} -#endif - -static int -fb_mmap(struct file *file, struct vm_area_struct * vma) -{ - struct fb_info *info = file_fb_info(file); - unsigned long mmio_pgoff; - unsigned long start; - u32 len; - - if (!info) - return -ENODEV; - mutex_lock(&info->mm_lock); - - if (info->fbops->fb_mmap) { - int res; - - /* - * The framebuffer needs to be accessed decrypted, be sure - * SME protection is removed ahead of the call - */ - vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot); - res = info->fbops->fb_mmap(info, vma); - mutex_unlock(&info->mm_lock); - return res; -#if IS_ENABLED(CONFIG_FB_DEFERRED_IO) - } else if (info->fbdefio) { - /* - * FB deferred I/O wants you to handle mmap in your drivers. At a - * minimum, point struct fb_ops.fb_mmap to fb_deferred_io_mmap(). - */ - dev_warn_once(info->dev, "fbdev mmap not set up for deferred I/O.\n"); - mutex_unlock(&info->mm_lock); - return -ENODEV; -#endif - } - - /* - * Ugh. This can be either the frame buffer mapping, or - * if pgoff points past it, the mmio mapping. - */ - start = info->fix.smem_start; - len = info->fix.smem_len; - mmio_pgoff = PAGE_ALIGN((start & ~PAGE_MASK) + len) >> PAGE_SHIFT; - if (vma->vm_pgoff >= mmio_pgoff) { - if (info->var.accel_flags) { - mutex_unlock(&info->mm_lock); - return -EINVAL; - } - - vma->vm_pgoff -= mmio_pgoff; - start = info->fix.mmio_start; - len = info->fix.mmio_len; - } - mutex_unlock(&info->mm_lock); - - vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); - fb_pgprotect(file, vma, start); - - return vm_iomap_memory(vma, start, len); -} - -static int -fb_open(struct inode *inode, struct file *file) -__acquires(&info->lock) -__releases(&info->lock) -{ - int fbidx = iminor(inode); - struct fb_info *info; - int res = 0; - - info = get_fb_info(fbidx); - if (!info) { - request_module("fb%d", fbidx); - info = get_fb_info(fbidx); - if (!info) - return -ENODEV; - } - if (IS_ERR(info)) - return PTR_ERR(info); - - lock_fb_info(info); - if (!try_module_get(info->fbops->owner)) { - res = -ENODEV; - goto out; - } - file->private_data = info; - if (info->fbops->fb_open) { - res = info->fbops->fb_open(info,1); - if (res) - module_put(info->fbops->owner); - } -#ifdef CONFIG_FB_DEFERRED_IO - if (info->fbdefio) - fb_deferred_io_open(info, inode, file); -#endif -out: - unlock_fb_info(info); - if (res) - put_fb_info(info); - return res; -} - -static int -fb_release(struct inode *inode, struct file *file) -__acquires(&info->lock) -__releases(&info->lock) -{ - struct fb_info * const info = file->private_data; - - lock_fb_info(info); -#if IS_ENABLED(CONFIG_FB_DEFERRED_IO) - if (info->fbdefio) - fb_deferred_io_release(info); -#endif - if (info->fbops->fb_release) - info->fbops->fb_release(info,1); - module_put(info->fbops->owner); - unlock_fb_info(info); - put_fb_info(info); - return 0; -} - -#if defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && !defined(CONFIG_MMU) -static unsigned long get_fb_unmapped_area(struct file *filp, - unsigned long addr, unsigned long len, - unsigned long pgoff, unsigned long flags) -{ - struct fb_info * const info = filp->private_data; - unsigned long fb_size = PAGE_ALIGN(info->fix.smem_len); - - if (pgoff > fb_size || len > fb_size - pgoff) - return -EINVAL; - - return (unsigned long)info->screen_base + pgoff; -} -#endif - -static const struct file_operations fb_fops = { - .owner = THIS_MODULE, - .read = fb_read, - .write = fb_write, - .unlocked_ioctl = fb_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = fb_compat_ioctl, -#endif - .mmap = fb_mmap, - .open = fb_open, - .release = fb_release, -#if defined(HAVE_ARCH_FB_UNMAPPED_AREA) || \ - (defined(CONFIG_FB_PROVIDE_GET_FB_UNMAPPED_AREA) && \ - !defined(CONFIG_MMU)) - .get_unmapped_area = get_fb_unmapped_area, -#endif -#ifdef CONFIG_FB_DEFERRED_IO - .fsync = fb_deferred_io_fsync, -#endif - .llseek = default_llseek, -}; - -struct class *fb_class; -EXPORT_SYMBOL(fb_class); - static int fb_check_foreignness(struct fb_info *fi) { const bool foreign_endian = fi->flags & FBINFO_FOREIGN_ENDIAN; @@ -1447,14 +946,7 @@ static int do_register_framebuffer(struct fb_info *fb_info) mutex_init(&fb_info->lock); mutex_init(&fb_info->mm_lock); - fb_info->dev = device_create(fb_class, fb_info->device, - MKDEV(FB_MAJOR, i), NULL, "fb%d", i); - if (IS_ERR(fb_info->dev)) { - /* Not fatal */ - printk(KERN_WARNING "Unable to create device for framebuffer %d; errno = %ld\n", i, PTR_ERR(fb_info->dev)); - fb_info->dev = NULL; - } else - fb_init_device(fb_info); + fb_device_create(fb_info); if (fb_info->pixmap.addr == NULL) { fb_info->pixmap.addr = kmalloc(FBPIXMAPSIZE, GFP_KERNEL); @@ -1478,9 +970,9 @@ static int do_register_framebuffer(struct fb_info *fb_info) INIT_LIST_HEAD(&fb_info->modelist); if (fb_info->skip_vt_switch) - pm_vt_switch_required(fb_info->dev, false); + pm_vt_switch_required(fb_info->device, false); else - pm_vt_switch_required(fb_info->dev, true); + pm_vt_switch_required(fb_info->device, true); fb_var_to_videomode(&mode, &fb_info->var); fb_add_videomode(&mode, &fb_info->modelist); @@ -1515,16 +1007,9 @@ static void unlink_framebuffer(struct fb_info *fb_info) if (WARN_ON(i < 0 || i >= FB_MAX || registered_fb[i] != fb_info)) return; - if (!fb_info->dev) - return; - - device_destroy(fb_class, MKDEV(FB_MAJOR, i)); - - pm_vt_switch_unregister(fb_info->dev); - + fb_device_destroy(fb_info); + pm_vt_switch_unregister(fb_info->device); unbind_console(fb_info); - - fb_info->dev = NULL; } static void do_unregister_framebuffer(struct fb_info *fb_info) @@ -1539,7 +1024,6 @@ static void do_unregister_framebuffer(struct fb_info *fb_info) fb_destroy_modelist(&fb_info->modelist); registered_fb[fb_info->node] = NULL; num_registered_fb--; - fb_cleanup_device(fb_info); #ifdef CONFIG_GUMSTIX_AM200EPD { struct fb_event event; @@ -1623,60 +1107,48 @@ void fb_set_suspend(struct fb_info *info, int state) } EXPORT_SYMBOL(fb_set_suspend); -/** - * fbmem_init - init frame buffer subsystem - * - * Initialize the frame buffer subsystem. - * - * NOTE: This function is _only_ to be called by drivers/char/mem.c. - * - */ - -static int __init -fbmem_init(void) +static int __init fbmem_init(void) { int ret; - if (!proc_create_seq("fb", 0, NULL, &proc_fb_seq_ops)) - return -ENOMEM; - - ret = register_chrdev(FB_MAJOR, "fb", &fb_fops); - if (ret) { - printk("unable to get major %d for fb devs\n", FB_MAJOR); - goto err_chrdev; - } - fb_class = class_create("graphics"); if (IS_ERR(fb_class)) { ret = PTR_ERR(fb_class); - pr_warn("Unable to create fb class; errno = %d\n", ret); - fb_class = NULL; - goto err_class; + pr_err("Unable to create fb class; errno = %d\n", ret); + goto err_fb_class; } + ret = fb_init_procfs(); + if (ret) + goto err_class_destroy; + + ret = fb_register_chrdev(); + if (ret) + goto err_fb_cleanup_procfs; + fb_console_init(); return 0; -err_class: - unregister_chrdev(FB_MAJOR, "fb"); -err_chrdev: - remove_proc_entry("fb", NULL); +err_fb_cleanup_procfs: + fb_cleanup_procfs(); +err_class_destroy: + class_destroy(fb_class); +err_fb_class: + fb_class = NULL; return ret; } #ifdef MODULE -module_init(fbmem_init); -static void __exit -fbmem_exit(void) +static void __exit fbmem_exit(void) { fb_console_exit(); - - remove_proc_entry("fb", NULL); + fb_unregister_chrdev(); + fb_cleanup_procfs(); class_destroy(fb_class); - unregister_chrdev(FB_MAJOR, "fb"); } +module_init(fbmem_init); module_exit(fbmem_exit); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Framebuffer base"); diff --git a/drivers/video/fbdev/core/fbsysfs.c b/drivers/video/fbdev/core/fbsysfs.c index 0c33c4adcd79..fafe574398b0 100644 --- a/drivers/video/fbdev/core/fbsysfs.c +++ b/drivers/video/fbdev/core/fbsysfs.c @@ -5,92 +5,14 @@ * Copyright (c) 2004 James Simmons <jsimmons@infradead.org> */ -/* - * Note: currently there's only stubs for framebuffer_alloc and - * framebuffer_release here. The reson for that is that until all drivers - * are converted to use it a sysfsification will open OOPSable races. - */ - -#include <linux/kernel.h> -#include <linux/slab.h> +#include <linux/console.h> #include <linux/fb.h> #include <linux/fbcon.h> -#include <linux/console.h> -#include <linux/module.h> - -#define FB_SYSFS_FLAG_ATTR 1 - -/** - * framebuffer_alloc - creates a new frame buffer info structure - * - * @size: size of driver private data, can be zero - * @dev: pointer to the device for this fb, this can be NULL - * - * Creates a new frame buffer info structure. Also reserves @size bytes - * for driver private data (info->par). info->par (if any) will be - * aligned to sizeof(long). - * - * Returns the new structure, or NULL if an error occurred. - * - */ -struct fb_info *framebuffer_alloc(size_t size, struct device *dev) -{ -#define BYTES_PER_LONG (BITS_PER_LONG/8) -#define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG)) - int fb_info_size = sizeof(struct fb_info); - struct fb_info *info; - char *p; - - if (size) - fb_info_size += PADDING; - - p = kzalloc(fb_info_size + size, GFP_KERNEL); - - if (!p) - return NULL; - - info = (struct fb_info *) p; - - if (size) - info->par = p + fb_info_size; - - info->device = dev; - info->fbcon_rotate_hint = -1; - -#if IS_ENABLED(CONFIG_FB_BACKLIGHT) - mutex_init(&info->bl_curve_mutex); -#endif - - return info; -#undef PADDING -#undef BYTES_PER_LONG -} -EXPORT_SYMBOL(framebuffer_alloc); +#include <linux/major.h> -/** - * framebuffer_release - marks the structure available for freeing - * - * @info: frame buffer info structure - * - * Drop the reference count of the device embedded in the - * framebuffer info structure. - * - */ -void framebuffer_release(struct fb_info *info) -{ - if (!info) - return; +#include "fb_internal.h" - if (WARN_ON(refcount_read(&info->count))) - return; - -#if IS_ENABLED(CONFIG_FB_BACKLIGHT) - mutex_destroy(&info->bl_curve_mutex); -#endif - - kfree(info); -} -EXPORT_SYMBOL(framebuffer_release); +#define FB_SYSFS_FLAG_ATTR 1 static int activate(struct fb_info *fb_info, struct fb_var_screeninfo *var) { @@ -516,7 +438,7 @@ static struct device_attribute device_attrs[] = { #endif }; -int fb_init_device(struct fb_info *fb_info) +static int fb_init_device(struct fb_info *fb_info) { int i, error = 0; @@ -540,7 +462,7 @@ int fb_init_device(struct fb_info *fb_info) return 0; } -void fb_cleanup_device(struct fb_info *fb_info) +static void fb_cleanup_device(struct fb_info *fb_info) { unsigned int i; @@ -552,29 +474,33 @@ void fb_cleanup_device(struct fb_info *fb_info) } } -#if IS_ENABLED(CONFIG_FB_BACKLIGHT) -/* This function generates a linear backlight curve - * - * 0: off - * 1-7: min - * 8-127: linear from min to max - */ -void fb_bl_default_curve(struct fb_info *fb_info, u8 off, u8 min, u8 max) +int fb_device_create(struct fb_info *fb_info) { - unsigned int i, flat, count, range = (max - min); - - mutex_lock(&fb_info->bl_curve_mutex); + int node = fb_info->node; + dev_t devt = MKDEV(FB_MAJOR, node); + int ret; + + fb_info->dev = device_create(fb_class, fb_info->device, devt, NULL, "fb%d", node); + if (IS_ERR(fb_info->dev)) { + /* Not fatal */ + ret = PTR_ERR(fb_info->dev); + pr_warn("Unable to create device for framebuffer %d; error %d\n", node, ret); + fb_info->dev = NULL; + } else { + fb_init_device(fb_info); + } - fb_info->bl_curve[0] = off; + return 0; +} - for (flat = 1; flat < (FB_BACKLIGHT_LEVELS / 16); ++flat) - fb_info->bl_curve[flat] = min; +void fb_device_destroy(struct fb_info *fb_info) +{ + dev_t devt = MKDEV(FB_MAJOR, fb_info->node); - count = FB_BACKLIGHT_LEVELS * 15 / 16; - for (i = 0; i < count; ++i) - fb_info->bl_curve[flat + i] = min + (range * (i + 1) / count); + if (!fb_info->dev) + return; - mutex_unlock(&fb_info->bl_curve_mutex); + fb_cleanup_device(fb_info); + device_destroy(fb_class, devt); + fb_info->dev = NULL; } -EXPORT_SYMBOL_GPL(fb_bl_default_curve); -#endif |