diff options
author | 2023-02-21 18:24:12 -0800 | |
---|---|---|
committer | 2023-02-21 18:24:12 -0800 | |
commit | 5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 (patch) | |
tree | cc5c2d0a898769fd59549594fedb3ee6f84e59a0 /drivers/video/fbdev/ep93xx-fb.c | |
download | linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.tar.gz linux-5b7c4cabbb65f5c469464da6c5f614cbd7f730f2.zip |
Merge tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-nextgrafted
Pull networking updates from Jakub Kicinski:
"Core:
- Add dedicated kmem_cache for typical/small skb->head, avoid having
to access struct page at kfree time, and improve memory use.
- Introduce sysctl to set default RPS configuration for new netdevs.
- Define Netlink protocol specification format which can be used to
describe messages used by each family and auto-generate parsers.
Add tools for generating kernel data structures and uAPI headers.
- Expose all net/core sysctls inside netns.
- Remove 4s sleep in netpoll if carrier is instantly detected on
boot.
- Add configurable limit of MDB entries per port, and port-vlan.
- Continue populating drop reasons throughout the stack.
- Retire a handful of legacy Qdiscs and classifiers.
Protocols:
- Support IPv4 big TCP (TSO frames larger than 64kB).
- Add IP_LOCAL_PORT_RANGE socket option, to control local port range
on socket by socket basis.
- Track and report in procfs number of MPTCP sockets used.
- Support mixing IPv4 and IPv6 flows in the in-kernel MPTCP path
manager.
- IPv6: don't check net.ipv6.route.max_size and rely on garbage
collection to free memory (similarly to IPv4).
- Support Penultimate Segment Pop (PSP) flavor in SRv6 (RFC8986).
- ICMP: add per-rate limit counters.
- Add support for user scanning requests in ieee802154.
- Remove static WEP support.
- Support minimal Wi-Fi 7 Extremely High Throughput (EHT) rate
reporting.
- WiFi 7 EHT channel puncturing support (client & AP).
BPF:
- Add a rbtree data structure following the "next-gen data structure"
precedent set by recently added linked list, that is, by using
kfunc + kptr instead of adding a new BPF map type.
- Expose XDP hints via kfuncs with initial support for RX hash and
timestamp metadata.
- Add BPF_F_NO_TUNNEL_KEY extension to bpf_skb_set_tunnel_key to
better support decap on GRE tunnel devices not operating in collect
metadata.
- Improve x86 JIT's codegen for PROBE_MEM runtime error checks.
- Remove the need for trace_printk_lock for bpf_trace_printk and
bpf_trace_vprintk helpers.
- Extend libbpf's bpf_tracing.h support for tracing arguments of
kprobes/uprobes and syscall as a special case.
- Significantly reduce the search time for module symbols by
livepatch and BPF.
- Enable cpumasks to be used as kptrs, which is useful for tracing
programs tracking which tasks end up running on which CPUs in
different time intervals.
- Add support for BPF trampoline on s390x and riscv64.
- Add capability to export the XDP features supported by the NIC.
- Add __bpf_kfunc tag for marking kernel functions as kfuncs.
- Add cgroup.memory=nobpf kernel parameter option to disable BPF
memory accounting for container environments.
Netfilter:
- Remove the CLUSTERIP target. It has been marked as obsolete for
years, and we still have WARN splats wrt races of the out-of-band
/proc interface installed by this target.
- Add 'destroy' commands to nf_tables. They are identical to the
existing 'delete' commands, but do not return an error if the
referenced object (set, chain, rule...) did not exist.
Driver API:
- Improve cpumask_local_spread() locality to help NICs set the right
IRQ affinity on AMD platforms.
- Separate C22 and C45 MDIO bus transactions more clearly.
- Introduce new DCB table to control DSCP rewrite on egress.
- Support configuration of Physical Layer Collision Avoidance (PLCA)
Reconciliation Sublayer (RS) (802.3cg-2019). Modern version of
shared medium Ethernet.
- Support for MAC Merge layer (IEEE 802.3-2018 clause 99). Allowing
preemption of low priority frames by high priority frames.
- Add support for controlling MACSec offload using netlink SET.
- Rework devlink instance refcounts to allow registration and
de-registration under the instance lock. Split the code into
multiple files, drop some of the unnecessarily granular locks and
factor out common parts of netlink operation handling.
- Add TX frame aggregation parameters (for USB drivers).
- Add a new attr TCA_EXT_WARN_MSG to report TC (offload) warning
messages with notifications for debug.
- Allow offloading of UDP NEW connections via act_ct.
- Add support for per action HW stats in TC.
- Support hardware miss to TC action (continue processing in SW from
a specific point in the action chain).
- Warn if old Wireless Extension user space interface is used with
modern cfg80211/mac80211 drivers. Do not support Wireless
Extensions for Wi-Fi 7 devices at all. Everyone should switch to
using nl80211 interface instead.
- Improve the CAN bit timing configuration. Use extack to return
error messages directly to user space, update the SJW handling,
including the definition of a new default value that will benefit
CAN-FD controllers, by increasing their oscillator tolerance.
New hardware / drivers:
- Ethernet:
- nVidia BlueField-3 support (control traffic driver)
- Ethernet support for imx93 SoCs
- Motorcomm yt8531 gigabit Ethernet PHY
- onsemi NCN26000 10BASE-T1S PHY (with support for PLCA)
- Microchip LAN8841 PHY (incl. cable diagnostics and PTP)
- Amlogic gxl MDIO mux
- WiFi:
- RealTek RTL8188EU (rtl8xxxu)
- Qualcomm Wi-Fi 7 devices (ath12k)
- CAN:
- Renesas R-Car V4H
Drivers:
- Bluetooth:
- Set Per Platform Antenna Gain (PPAG) for Intel controllers.
- Ethernet NICs:
- Intel (1G, igc):
- support TSN / Qbv / packet scheduling features of i226 model
- Intel (100G, ice):
- use GNSS subsystem instead of TTY
- multi-buffer XDP support
- extend support for GPIO pins to E823 devices
- nVidia/Mellanox:
- update the shared buffer configuration on PFC commands
- implement PTP adjphase function for HW offset control
- TC support for Geneve and GRE with VF tunnel offload
- more efficient crypto key management method
- multi-port eswitch support
- Netronome/Corigine:
- add DCB IEEE support
- support IPsec offloading for NFP3800
- Freescale/NXP (enetc):
- support XDP_REDIRECT for XDP non-linear buffers
- improve reconfig, avoid link flap and waiting for idle
- support MAC Merge layer
- Other NICs:
- sfc/ef100: add basic devlink support for ef100
- ionic: rx_push mode operation (writing descriptors via MMIO)
- bnxt: use the auxiliary bus abstraction for RDMA
- r8169: disable ASPM and reset bus in case of tx timeout
- cpsw: support QSGMII mode for J721e CPSW9G
- cpts: support pulse-per-second output
- ngbe: add an mdio bus driver
- usbnet: optimize usbnet_bh() by avoiding unnecessary queuing
- r8152: handle devices with FW with NCM support
- amd-xgbe: support 10Mbps, 2.5GbE speeds and rx-adaptation
- virtio-net: support multi buffer XDP
- virtio/vsock: replace virtio_vsock_pkt with sk_buff
- tsnep: XDP support
- Ethernet high-speed switches:
- nVidia/Mellanox (mlxsw):
- add support for latency TLV (in FW control messages)
- Microchip (sparx5):
- separate explicit and implicit traffic forwarding rules, make
the implicit rules always active
- add support for egress DSCP rewrite
- IS0 VCAP support (Ingress Classification)
- IS2 VCAP filters (protos, L3 addrs, L4 ports, flags, ToS
etc.)
- ES2 VCAP support (Egress Access Control)
- support for Per-Stream Filtering and Policing (802.1Q,
8.6.5.1)
- Ethernet embedded switches:
- Marvell (mv88e6xxx):
- add MAB (port auth) offload support
- enable PTP receive for mv88e6390
- NXP (ocelot):
- support MAC Merge layer
- support for the the vsc7512 internal copper phys
- Microchip:
- lan9303: convert to PHYLINK
- lan966x: support TC flower filter statistics
- lan937x: PTP support for KSZ9563/KSZ8563 and LAN937x
- lan937x: support Credit Based Shaper configuration
- ksz9477: support Energy Efficient Ethernet
- other:
- qca8k: convert to regmap read/write API, use bulk operations
- rswitch: Improve TX timestamp accuracy
- Intel WiFi (iwlwifi):
- EHT (Wi-Fi 7) rate reporting
- STEP equalizer support: transfer some STEP (connection to radio
on platforms with integrated wifi) related parameters from the
BIOS to the firmware.
- Qualcomm 802.11ax WiFi (ath11k):
- IPQ5018 support
- Fine Timing Measurement (FTM) responder role support
- channel 177 support
- MediaTek WiFi (mt76):
- per-PHY LED support
- mt7996: EHT (Wi-Fi 7) support
- Wireless Ethernet Dispatch (WED) reset support
- switch to using page pool allocator
- RealTek WiFi (rtw89):
- support new version of Bluetooth co-existance
- Mobile:
- rmnet: support TX aggregation"
* tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next: (1872 commits)
page_pool: add a comment explaining the fragment counter usage
net: ethtool: fix __ethtool_dev_mm_supported() implementation
ethtool: pse-pd: Fix double word in comments
xsk: add linux/vmalloc.h to xsk.c
sefltests: netdevsim: wait for devlink instance after netns removal
selftest: fib_tests: Always cleanup before exit
net/mlx5e: Align IPsec ASO result memory to be as required by hardware
net/mlx5e: TC, Set CT miss to the specific ct action instance
net/mlx5e: Rename CHAIN_TO_REG to MAPPED_OBJ_TO_REG
net/mlx5: Refactor tc miss handling to a single function
net/mlx5: Kconfig: Make tc offload depend on tc skb extension
net/sched: flower: Support hardware miss to tc action
net/sched: flower: Move filter handle initialization earlier
net/sched: cls_api: Support hardware miss to tc action
net/sched: Rename user cookie and act cookie
sfc: fix builds without CONFIG_RTC_LIB
sfc: clean up some inconsistent indentings
net/mlx4_en: Introduce flexible array to silence overflow warning
net: lan966x: Fix possible deadlock inside PTP
net/ulp: Remove redundant ->clone() test in inet_clone_ulp().
...
Diffstat (limited to 'drivers/video/fbdev/ep93xx-fb.c')
-rw-r--r-- | drivers/video/fbdev/ep93xx-fb.c | 607 |
1 files changed, 607 insertions, 0 deletions
diff --git a/drivers/video/fbdev/ep93xx-fb.c b/drivers/video/fbdev/ep93xx-fb.c new file mode 100644 index 000000000..305f1587b --- /dev/null +++ b/drivers/video/fbdev/ep93xx-fb.c @@ -0,0 +1,607 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * linux/drivers/video/ep93xx-fb.c + * + * Framebuffer support for the EP93xx series. + * + * Copyright (C) 2007 Bluewater Systems Ltd + * Author: Ryan Mallon + * + * Copyright (c) 2009 H Hartley Sweeten <hsweeten@visionengravers.com> + * + * Based on the Cirrus Logic ep93xxfb driver, and various other ep93xxfb + * drivers. + */ + +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/dma-mapping.h> +#include <linux/slab.h> +#include <linux/clk.h> +#include <linux/fb.h> +#include <linux/io.h> + +#include <linux/platform_data/video-ep93xx.h> + +/* Vertical Frame Timing Registers */ +#define EP93XXFB_VLINES_TOTAL 0x0000 /* SW locked */ +#define EP93XXFB_VSYNC 0x0004 /* SW locked */ +#define EP93XXFB_VACTIVE 0x0008 /* SW locked */ +#define EP93XXFB_VBLANK 0x0228 /* SW locked */ +#define EP93XXFB_VCLK 0x000c /* SW locked */ + +/* Horizontal Frame Timing Registers */ +#define EP93XXFB_HCLKS_TOTAL 0x0010 /* SW locked */ +#define EP93XXFB_HSYNC 0x0014 /* SW locked */ +#define EP93XXFB_HACTIVE 0x0018 /* SW locked */ +#define EP93XXFB_HBLANK 0x022c /* SW locked */ +#define EP93XXFB_HCLK 0x001c /* SW locked */ + +/* Frame Buffer Memory Configuration Registers */ +#define EP93XXFB_SCREEN_PAGE 0x0028 +#define EP93XXFB_SCREEN_HPAGE 0x002c +#define EP93XXFB_SCREEN_LINES 0x0030 +#define EP93XXFB_LINE_LENGTH 0x0034 +#define EP93XXFB_VLINE_STEP 0x0038 +#define EP93XXFB_LINE_CARRY 0x003c /* SW locked */ +#define EP93XXFB_EOL_OFFSET 0x0230 + +/* Other Video Registers */ +#define EP93XXFB_BRIGHTNESS 0x0020 +#define EP93XXFB_ATTRIBS 0x0024 /* SW locked */ +#define EP93XXFB_SWLOCK 0x007c /* SW locked */ +#define EP93XXFB_AC_RATE 0x0214 +#define EP93XXFB_FIFO_LEVEL 0x0234 +#define EP93XXFB_PIXELMODE 0x0054 +#define EP93XXFB_PIXELMODE_32BPP (0x7 << 0) +#define EP93XXFB_PIXELMODE_24BPP (0x6 << 0) +#define EP93XXFB_PIXELMODE_16BPP (0x4 << 0) +#define EP93XXFB_PIXELMODE_8BPP (0x2 << 0) +#define EP93XXFB_PIXELMODE_SHIFT_1P_24B (0x0 << 3) +#define EP93XXFB_PIXELMODE_SHIFT_1P_18B (0x1 << 3) +#define EP93XXFB_PIXELMODE_COLOR_LUT (0x0 << 10) +#define EP93XXFB_PIXELMODE_COLOR_888 (0x4 << 10) +#define EP93XXFB_PIXELMODE_COLOR_555 (0x5 << 10) +#define EP93XXFB_PARL_IF_OUT 0x0058 +#define EP93XXFB_PARL_IF_IN 0x005c + +/* Blink Control Registers */ +#define EP93XXFB_BLINK_RATE 0x0040 +#define EP93XXFB_BLINK_MASK 0x0044 +#define EP93XXFB_BLINK_PATTRN 0x0048 +#define EP93XXFB_PATTRN_MASK 0x004c +#define EP93XXFB_BKGRND_OFFSET 0x0050 + +/* Hardware Cursor Registers */ +#define EP93XXFB_CURSOR_ADR_START 0x0060 +#define EP93XXFB_CURSOR_ADR_RESET 0x0064 +#define EP93XXFB_CURSOR_SIZE 0x0068 +#define EP93XXFB_CURSOR_COLOR1 0x006c +#define EP93XXFB_CURSOR_COLOR2 0x0070 +#define EP93XXFB_CURSOR_BLINK_COLOR1 0x021c +#define EP93XXFB_CURSOR_BLINK_COLOR2 0x0220 +#define EP93XXFB_CURSOR_XY_LOC 0x0074 +#define EP93XXFB_CURSOR_DSCAN_HY_LOC 0x0078 +#define EP93XXFB_CURSOR_BLINK_RATE_CTRL 0x0224 + +/* LUT Registers */ +#define EP93XXFB_GRY_SCL_LUTR 0x0080 +#define EP93XXFB_GRY_SCL_LUTG 0x0280 +#define EP93XXFB_GRY_SCL_LUTB 0x0300 +#define EP93XXFB_LUT_SW_CONTROL 0x0218 +#define EP93XXFB_LUT_SW_CONTROL_SWTCH (1 << 0) +#define EP93XXFB_LUT_SW_CONTROL_SSTAT (1 << 1) +#define EP93XXFB_COLOR_LUT 0x0400 + +/* Video Signature Registers */ +#define EP93XXFB_VID_SIG_RSLT_VAL 0x0200 +#define EP93XXFB_VID_SIG_CTRL 0x0204 +#define EP93XXFB_VSIG 0x0208 +#define EP93XXFB_HSIG 0x020c +#define EP93XXFB_SIG_CLR_STR 0x0210 + +/* Minimum / Maximum resolutions supported */ +#define EP93XXFB_MIN_XRES 64 +#define EP93XXFB_MIN_YRES 64 +#define EP93XXFB_MAX_XRES 1024 +#define EP93XXFB_MAX_YRES 768 + +struct ep93xx_fbi { + struct ep93xxfb_mach_info *mach_info; + struct clk *clk; + struct resource *res; + void __iomem *mmio_base; + unsigned int pseudo_palette[256]; +}; + +static int check_screenpage_bug = 1; +module_param(check_screenpage_bug, int, 0644); +MODULE_PARM_DESC(check_screenpage_bug, + "Check for bit 27 screen page bug. Default = 1"); + +static inline unsigned int ep93xxfb_readl(struct ep93xx_fbi *fbi, + unsigned int off) +{ + return __raw_readl(fbi->mmio_base + off); +} + +static inline void ep93xxfb_writel(struct ep93xx_fbi *fbi, + unsigned int val, unsigned int off) +{ + __raw_writel(val, fbi->mmio_base + off); +} + +/* + * Write to one of the locked raster registers. + */ +static inline void ep93xxfb_out_locked(struct ep93xx_fbi *fbi, + unsigned int val, unsigned int reg) +{ + /* + * We don't need a lock or delay here since the raster register + * block will remain unlocked until the next access. + */ + ep93xxfb_writel(fbi, 0xaa, EP93XXFB_SWLOCK); + ep93xxfb_writel(fbi, val, reg); +} + +static void ep93xxfb_set_video_attribs(struct fb_info *info) +{ + struct ep93xx_fbi *fbi = info->par; + unsigned int attribs; + + attribs = EP93XXFB_ENABLE; + attribs |= fbi->mach_info->flags; + ep93xxfb_out_locked(fbi, attribs, EP93XXFB_ATTRIBS); +} + +static int ep93xxfb_set_pixelmode(struct fb_info *info) +{ + struct ep93xx_fbi *fbi = info->par; + unsigned int val; + + info->var.transp.offset = 0; + info->var.transp.length = 0; + + switch (info->var.bits_per_pixel) { + case 8: + val = EP93XXFB_PIXELMODE_8BPP | EP93XXFB_PIXELMODE_COLOR_LUT | + EP93XXFB_PIXELMODE_SHIFT_1P_18B; + + info->var.red.offset = 0; + info->var.red.length = 8; + info->var.green.offset = 0; + info->var.green.length = 8; + info->var.blue.offset = 0; + info->var.blue.length = 8; + info->fix.visual = FB_VISUAL_PSEUDOCOLOR; + break; + + case 16: + val = EP93XXFB_PIXELMODE_16BPP | EP93XXFB_PIXELMODE_COLOR_555 | + EP93XXFB_PIXELMODE_SHIFT_1P_18B; + + info->var.red.offset = 11; + info->var.red.length = 5; + info->var.green.offset = 5; + info->var.green.length = 6; + info->var.blue.offset = 0; + info->var.blue.length = 5; + info->fix.visual = FB_VISUAL_TRUECOLOR; + break; + + case 24: + val = EP93XXFB_PIXELMODE_24BPP | EP93XXFB_PIXELMODE_COLOR_888 | + EP93XXFB_PIXELMODE_SHIFT_1P_24B; + + info->var.red.offset = 16; + info->var.red.length = 8; + info->var.green.offset = 8; + info->var.green.length = 8; + info->var.blue.offset = 0; + info->var.blue.length = 8; + info->fix.visual = FB_VISUAL_TRUECOLOR; + break; + + case 32: + val = EP93XXFB_PIXELMODE_32BPP | EP93XXFB_PIXELMODE_COLOR_888 | + EP93XXFB_PIXELMODE_SHIFT_1P_24B; + + info->var.red.offset = 16; + info->var.red.length = 8; + info->var.green.offset = 8; + info->var.green.length = 8; + info->var.blue.offset = 0; + info->var.blue.length = 8; + info->fix.visual = FB_VISUAL_TRUECOLOR; + break; + + default: + return -EINVAL; + } + + ep93xxfb_writel(fbi, val, EP93XXFB_PIXELMODE); + return 0; +} + +static void ep93xxfb_set_timing(struct fb_info *info) +{ + struct ep93xx_fbi *fbi = info->par; + unsigned int vlines_total, hclks_total, start, stop; + + vlines_total = info->var.yres + info->var.upper_margin + + info->var.lower_margin + info->var.vsync_len - 1; + + hclks_total = info->var.xres + info->var.left_margin + + info->var.right_margin + info->var.hsync_len - 1; + + ep93xxfb_out_locked(fbi, vlines_total, EP93XXFB_VLINES_TOTAL); + ep93xxfb_out_locked(fbi, hclks_total, EP93XXFB_HCLKS_TOTAL); + + start = vlines_total; + stop = vlines_total - info->var.vsync_len; + ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VSYNC); + + start = vlines_total - info->var.vsync_len - info->var.upper_margin; + stop = info->var.lower_margin - 1; + ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VBLANK); + ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VACTIVE); + + start = vlines_total; + stop = vlines_total + 1; + ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_VCLK); + + start = hclks_total; + stop = hclks_total - info->var.hsync_len; + ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HSYNC); + + start = hclks_total - info->var.hsync_len - info->var.left_margin; + stop = info->var.right_margin - 1; + ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HBLANK); + ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HACTIVE); + + start = hclks_total; + stop = hclks_total; + ep93xxfb_out_locked(fbi, start | (stop << 16), EP93XXFB_HCLK); + + ep93xxfb_out_locked(fbi, 0x0, EP93XXFB_LINE_CARRY); +} + +static int ep93xxfb_set_par(struct fb_info *info) +{ + struct ep93xx_fbi *fbi = info->par; + + clk_set_rate(fbi->clk, 1000 * PICOS2KHZ(info->var.pixclock)); + + ep93xxfb_set_timing(info); + + info->fix.line_length = info->var.xres_virtual * + info->var.bits_per_pixel / 8; + + ep93xxfb_writel(fbi, info->fix.smem_start, EP93XXFB_SCREEN_PAGE); + ep93xxfb_writel(fbi, info->var.yres - 1, EP93XXFB_SCREEN_LINES); + ep93xxfb_writel(fbi, ((info->var.xres * info->var.bits_per_pixel) + / 32) - 1, EP93XXFB_LINE_LENGTH); + ep93xxfb_writel(fbi, info->fix.line_length / 4, EP93XXFB_VLINE_STEP); + ep93xxfb_set_video_attribs(info); + return 0; +} + +static int ep93xxfb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + int err; + + err = ep93xxfb_set_pixelmode(info); + if (err) + return err; + + var->xres = max_t(unsigned int, var->xres, EP93XXFB_MIN_XRES); + var->xres = min_t(unsigned int, var->xres, EP93XXFB_MAX_XRES); + var->xres_virtual = max(var->xres_virtual, var->xres); + + var->yres = max_t(unsigned int, var->yres, EP93XXFB_MIN_YRES); + var->yres = min_t(unsigned int, var->yres, EP93XXFB_MAX_YRES); + var->yres_virtual = max(var->yres_virtual, var->yres); + + return 0; +} + +static int ep93xxfb_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + unsigned int offset = vma->vm_pgoff << PAGE_SHIFT; + + if (offset < info->fix.smem_len) { + return dma_mmap_wc(info->dev, vma, info->screen_base, + info->fix.smem_start, info->fix.smem_len); + } + + return -EINVAL; +} + +static int ep93xxfb_blank(int blank_mode, struct fb_info *info) +{ + struct ep93xx_fbi *fbi = info->par; + unsigned int attribs = ep93xxfb_readl(fbi, EP93XXFB_ATTRIBS); + + if (blank_mode) { + if (fbi->mach_info->blank) + fbi->mach_info->blank(blank_mode, info); + ep93xxfb_out_locked(fbi, attribs & ~EP93XXFB_ENABLE, + EP93XXFB_ATTRIBS); + clk_disable(fbi->clk); + } else { + clk_enable(fbi->clk); + ep93xxfb_out_locked(fbi, attribs | EP93XXFB_ENABLE, + EP93XXFB_ATTRIBS); + if (fbi->mach_info->blank) + fbi->mach_info->blank(blank_mode, info); + } + + return 0; +} + +static inline int ep93xxfb_convert_color(int val, int width) +{ + return ((val << width) + 0x7fff - val) >> 16; +} + +static int ep93xxfb_setcolreg(unsigned int regno, unsigned int red, + unsigned int green, unsigned int blue, + unsigned int transp, struct fb_info *info) +{ + struct ep93xx_fbi *fbi = info->par; + unsigned int *pal = info->pseudo_palette; + unsigned int ctrl, i, rgb, lut_current, lut_stat; + + switch (info->fix.visual) { + case FB_VISUAL_PSEUDOCOLOR: + if (regno > 255) + return 1; + rgb = ((red & 0xff00) << 8) | (green & 0xff00) | + ((blue & 0xff00) >> 8); + + pal[regno] = rgb; + ep93xxfb_writel(fbi, rgb, (EP93XXFB_COLOR_LUT + (regno << 2))); + ctrl = ep93xxfb_readl(fbi, EP93XXFB_LUT_SW_CONTROL); + lut_stat = !!(ctrl & EP93XXFB_LUT_SW_CONTROL_SSTAT); + lut_current = !!(ctrl & EP93XXFB_LUT_SW_CONTROL_SWTCH); + + if (lut_stat == lut_current) { + for (i = 0; i < 256; i++) { + ep93xxfb_writel(fbi, pal[i], + EP93XXFB_COLOR_LUT + (i << 2)); + } + + ep93xxfb_writel(fbi, + ctrl ^ EP93XXFB_LUT_SW_CONTROL_SWTCH, + EP93XXFB_LUT_SW_CONTROL); + } + break; + + case FB_VISUAL_TRUECOLOR: + if (regno > 16) + return 1; + + red = ep93xxfb_convert_color(red, info->var.red.length); + green = ep93xxfb_convert_color(green, info->var.green.length); + blue = ep93xxfb_convert_color(blue, info->var.blue.length); + transp = ep93xxfb_convert_color(transp, + info->var.transp.length); + + pal[regno] = (red << info->var.red.offset) | + (green << info->var.green.offset) | + (blue << info->var.blue.offset) | + (transp << info->var.transp.offset); + break; + + default: + return 1; + } + + return 0; +} + +static const struct fb_ops ep93xxfb_ops = { + .owner = THIS_MODULE, + .fb_check_var = ep93xxfb_check_var, + .fb_set_par = ep93xxfb_set_par, + .fb_blank = ep93xxfb_blank, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_setcolreg = ep93xxfb_setcolreg, + .fb_mmap = ep93xxfb_mmap, +}; + +static int ep93xxfb_alloc_videomem(struct fb_info *info) +{ + char __iomem *virt_addr; + dma_addr_t phys_addr; + unsigned int fb_size; + + /* Maximum 16bpp -> used memory is maximum x*y*2 bytes */ + fb_size = EP93XXFB_MAX_XRES * EP93XXFB_MAX_YRES * 2; + + virt_addr = dma_alloc_wc(info->dev, fb_size, &phys_addr, GFP_KERNEL); + if (!virt_addr) + return -ENOMEM; + + /* + * There is a bug in the ep93xx framebuffer which causes problems + * if bit 27 of the physical address is set. + * See: https://marc.info/?l=linux-arm-kernel&m=110061245502000&w=2 + * There does not seem to be any official errata for this, but I + * have confirmed the problem exists on my hardware (ep9315) at + * least. + */ + if (check_screenpage_bug && phys_addr & (1 << 27)) { + dev_err(info->dev, "ep93xx framebuffer bug. phys addr (0x%x) " + "has bit 27 set: cannot init framebuffer\n", + phys_addr); + + dma_free_coherent(info->dev, fb_size, virt_addr, phys_addr); + return -ENOMEM; + } + + info->fix.smem_start = phys_addr; + info->fix.smem_len = fb_size; + info->screen_base = virt_addr; + + return 0; +} + +static void ep93xxfb_dealloc_videomem(struct fb_info *info) +{ + if (info->screen_base) + dma_free_coherent(info->dev, info->fix.smem_len, + info->screen_base, info->fix.smem_start); +} + +static int ep93xxfb_probe(struct platform_device *pdev) +{ + struct ep93xxfb_mach_info *mach_info = dev_get_platdata(&pdev->dev); + struct fb_info *info; + struct ep93xx_fbi *fbi; + struct resource *res; + char *video_mode; + int err; + + if (!mach_info) + return -EINVAL; + + info = framebuffer_alloc(sizeof(struct ep93xx_fbi), &pdev->dev); + if (!info) + return -ENOMEM; + + info->dev = &pdev->dev; + platform_set_drvdata(pdev, info); + fbi = info->par; + fbi->mach_info = mach_info; + + err = fb_alloc_cmap(&info->cmap, 256, 0); + if (err) + goto failed_cmap; + + err = ep93xxfb_alloc_videomem(info); + if (err) + goto failed_videomem; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + err = -ENXIO; + goto failed_resource; + } + + /* + * FIXME - We don't do a request_mem_region here because we are + * sharing the register space with the backlight driver (see + * drivers/video/backlight/ep93xx_bl.c) and doing so will cause + * the second loaded driver to return -EBUSY. + * + * NOTE: No locking is required; the backlight does not touch + * any of the framebuffer registers. + */ + fbi->res = res; + fbi->mmio_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + if (!fbi->mmio_base) { + err = -ENXIO; + goto failed_resource; + } + + strcpy(info->fix.id, pdev->name); + info->fbops = &ep93xxfb_ops; + info->fix.type = FB_TYPE_PACKED_PIXELS; + info->fix.accel = FB_ACCEL_NONE; + info->var.activate = FB_ACTIVATE_NOW; + info->var.vmode = FB_VMODE_NONINTERLACED; + info->flags = FBINFO_DEFAULT; + info->node = -1; + info->state = FBINFO_STATE_RUNNING; + info->pseudo_palette = &fbi->pseudo_palette; + + fb_get_options("ep93xx-fb", &video_mode); + err = fb_find_mode(&info->var, info, video_mode, + NULL, 0, NULL, 16); + if (err == 0) { + dev_err(info->dev, "No suitable video mode found\n"); + err = -EINVAL; + goto failed_resource; + } + + if (mach_info->setup) { + err = mach_info->setup(pdev); + if (err) + goto failed_resource; + } + + err = ep93xxfb_check_var(&info->var, info); + if (err) + goto failed_check; + + fbi->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(fbi->clk)) { + err = PTR_ERR(fbi->clk); + fbi->clk = NULL; + goto failed_check; + } + + ep93xxfb_set_par(info); + clk_prepare_enable(fbi->clk); + + err = register_framebuffer(info); + if (err) + goto failed_framebuffer; + + dev_info(info->dev, "registered. Mode = %dx%d-%d\n", + info->var.xres, info->var.yres, info->var.bits_per_pixel); + return 0; + +failed_framebuffer: + clk_disable_unprepare(fbi->clk); +failed_check: + if (fbi->mach_info->teardown) + fbi->mach_info->teardown(pdev); +failed_resource: + ep93xxfb_dealloc_videomem(info); +failed_videomem: + fb_dealloc_cmap(&info->cmap); +failed_cmap: + kfree(info); + + return err; +} + +static int ep93xxfb_remove(struct platform_device *pdev) +{ + struct fb_info *info = platform_get_drvdata(pdev); + struct ep93xx_fbi *fbi = info->par; + + unregister_framebuffer(info); + clk_disable_unprepare(fbi->clk); + ep93xxfb_dealloc_videomem(info); + fb_dealloc_cmap(&info->cmap); + + if (fbi->mach_info->teardown) + fbi->mach_info->teardown(pdev); + + kfree(info); + + return 0; +} + +static struct platform_driver ep93xxfb_driver = { + .probe = ep93xxfb_probe, + .remove = ep93xxfb_remove, + .driver = { + .name = "ep93xx-fb", + }, +}; +module_platform_driver(ep93xxfb_driver); + +MODULE_DESCRIPTION("EP93XX Framebuffer Driver"); +MODULE_ALIAS("platform:ep93xx-fb"); +MODULE_AUTHOR("Ryan Mallon, " + "H Hartley Sweeten <hsweeten@visionengravers.com"); +MODULE_LICENSE("GPL"); |