diff options
author | 2023-02-21 18:24:12 -0800 | |
---|---|---|
committer | 2023-02-21 18:24:12 -0800 | |
commit | 5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 (patch) | |
tree | cc5c2d0a898769fd59549594fedb3ee6f84e59a0 /drivers/gpu/drm/sun4i/sun8i_mixer.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/gpu/drm/sun4i/sun8i_mixer.c')
-rw-r--r-- | drivers/gpu/drm/sun4i/sun8i_mixer.c | 724 |
1 files changed, 724 insertions, 0 deletions
diff --git a/drivers/gpu/drm/sun4i/sun8i_mixer.c b/drivers/gpu/drm/sun4i/sun8i_mixer.c new file mode 100644 index 000000000..bafee05f6 --- /dev/null +++ b/drivers/gpu/drm/sun4i/sun8i_mixer.c @@ -0,0 +1,724 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2017 Icenowy Zheng <icenowy@aosc.io> + * + * Based on sun4i_backend.c, which is: + * Copyright (C) 2015 Free Electrons + * Copyright (C) 2015 NextThing Co + */ + +#include <linux/component.h> +#include <linux/dma-mapping.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of_graph.h> +#include <linux/reset.h> + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_gem_dma_helper.h> +#include <drm/drm_probe_helper.h> + +#include "sun4i_drv.h" +#include "sun8i_mixer.h" +#include "sun8i_ui_layer.h" +#include "sun8i_vi_layer.h" +#include "sunxi_engine.h" + +struct de2_fmt_info { + u32 drm_fmt; + u32 de2_fmt; +}; + +static const struct de2_fmt_info de2_formats[] = { + { + .drm_fmt = DRM_FORMAT_ARGB8888, + .de2_fmt = SUN8I_MIXER_FBFMT_ARGB8888, + }, + { + .drm_fmt = DRM_FORMAT_ABGR8888, + .de2_fmt = SUN8I_MIXER_FBFMT_ABGR8888, + }, + { + .drm_fmt = DRM_FORMAT_RGBA8888, + .de2_fmt = SUN8I_MIXER_FBFMT_RGBA8888, + }, + { + .drm_fmt = DRM_FORMAT_BGRA8888, + .de2_fmt = SUN8I_MIXER_FBFMT_BGRA8888, + }, + { + .drm_fmt = DRM_FORMAT_XRGB8888, + .de2_fmt = SUN8I_MIXER_FBFMT_XRGB8888, + }, + { + .drm_fmt = DRM_FORMAT_XBGR8888, + .de2_fmt = SUN8I_MIXER_FBFMT_XBGR8888, + }, + { + .drm_fmt = DRM_FORMAT_RGBX8888, + .de2_fmt = SUN8I_MIXER_FBFMT_RGBX8888, + }, + { + .drm_fmt = DRM_FORMAT_BGRX8888, + .de2_fmt = SUN8I_MIXER_FBFMT_BGRX8888, + }, + { + .drm_fmt = DRM_FORMAT_RGB888, + .de2_fmt = SUN8I_MIXER_FBFMT_RGB888, + }, + { + .drm_fmt = DRM_FORMAT_BGR888, + .de2_fmt = SUN8I_MIXER_FBFMT_BGR888, + }, + { + .drm_fmt = DRM_FORMAT_RGB565, + .de2_fmt = SUN8I_MIXER_FBFMT_RGB565, + }, + { + .drm_fmt = DRM_FORMAT_BGR565, + .de2_fmt = SUN8I_MIXER_FBFMT_BGR565, + }, + { + .drm_fmt = DRM_FORMAT_ARGB4444, + .de2_fmt = SUN8I_MIXER_FBFMT_ARGB4444, + }, + { + /* for DE2 VI layer which ignores alpha */ + .drm_fmt = DRM_FORMAT_XRGB4444, + .de2_fmt = SUN8I_MIXER_FBFMT_ARGB4444, + }, + { + .drm_fmt = DRM_FORMAT_ABGR4444, + .de2_fmt = SUN8I_MIXER_FBFMT_ABGR4444, + }, + { + /* for DE2 VI layer which ignores alpha */ + .drm_fmt = DRM_FORMAT_XBGR4444, + .de2_fmt = SUN8I_MIXER_FBFMT_ABGR4444, + }, + { + .drm_fmt = DRM_FORMAT_RGBA4444, + .de2_fmt = SUN8I_MIXER_FBFMT_RGBA4444, + }, + { + /* for DE2 VI layer which ignores alpha */ + .drm_fmt = DRM_FORMAT_RGBX4444, + .de2_fmt = SUN8I_MIXER_FBFMT_RGBA4444, + }, + { + .drm_fmt = DRM_FORMAT_BGRA4444, + .de2_fmt = SUN8I_MIXER_FBFMT_BGRA4444, + }, + { + /* for DE2 VI layer which ignores alpha */ + .drm_fmt = DRM_FORMAT_BGRX4444, + .de2_fmt = SUN8I_MIXER_FBFMT_BGRA4444, + }, + { + .drm_fmt = DRM_FORMAT_ARGB1555, + .de2_fmt = SUN8I_MIXER_FBFMT_ARGB1555, + }, + { + /* for DE2 VI layer which ignores alpha */ + .drm_fmt = DRM_FORMAT_XRGB1555, + .de2_fmt = SUN8I_MIXER_FBFMT_ARGB1555, + }, + { + .drm_fmt = DRM_FORMAT_ABGR1555, + .de2_fmt = SUN8I_MIXER_FBFMT_ABGR1555, + }, + { + /* for DE2 VI layer which ignores alpha */ + .drm_fmt = DRM_FORMAT_XBGR1555, + .de2_fmt = SUN8I_MIXER_FBFMT_ABGR1555, + }, + { + .drm_fmt = DRM_FORMAT_RGBA5551, + .de2_fmt = SUN8I_MIXER_FBFMT_RGBA5551, + }, + { + /* for DE2 VI layer which ignores alpha */ + .drm_fmt = DRM_FORMAT_RGBX5551, + .de2_fmt = SUN8I_MIXER_FBFMT_RGBA5551, + }, + { + .drm_fmt = DRM_FORMAT_BGRA5551, + .de2_fmt = SUN8I_MIXER_FBFMT_BGRA5551, + }, + { + /* for DE2 VI layer which ignores alpha */ + .drm_fmt = DRM_FORMAT_BGRX5551, + .de2_fmt = SUN8I_MIXER_FBFMT_BGRA5551, + }, + { + .drm_fmt = DRM_FORMAT_ARGB2101010, + .de2_fmt = SUN8I_MIXER_FBFMT_ARGB2101010, + }, + { + .drm_fmt = DRM_FORMAT_ABGR2101010, + .de2_fmt = SUN8I_MIXER_FBFMT_ABGR2101010, + }, + { + .drm_fmt = DRM_FORMAT_RGBA1010102, + .de2_fmt = SUN8I_MIXER_FBFMT_RGBA1010102, + }, + { + .drm_fmt = DRM_FORMAT_BGRA1010102, + .de2_fmt = SUN8I_MIXER_FBFMT_BGRA1010102, + }, + { + .drm_fmt = DRM_FORMAT_UYVY, + .de2_fmt = SUN8I_MIXER_FBFMT_UYVY, + }, + { + .drm_fmt = DRM_FORMAT_VYUY, + .de2_fmt = SUN8I_MIXER_FBFMT_VYUY, + }, + { + .drm_fmt = DRM_FORMAT_YUYV, + .de2_fmt = SUN8I_MIXER_FBFMT_YUYV, + }, + { + .drm_fmt = DRM_FORMAT_YVYU, + .de2_fmt = SUN8I_MIXER_FBFMT_YVYU, + }, + { + .drm_fmt = DRM_FORMAT_NV16, + .de2_fmt = SUN8I_MIXER_FBFMT_NV16, + }, + { + .drm_fmt = DRM_FORMAT_NV61, + .de2_fmt = SUN8I_MIXER_FBFMT_NV61, + }, + { + .drm_fmt = DRM_FORMAT_NV12, + .de2_fmt = SUN8I_MIXER_FBFMT_NV12, + }, + { + .drm_fmt = DRM_FORMAT_NV21, + .de2_fmt = SUN8I_MIXER_FBFMT_NV21, + }, + { + .drm_fmt = DRM_FORMAT_YUV422, + .de2_fmt = SUN8I_MIXER_FBFMT_YUV422, + }, + { + .drm_fmt = DRM_FORMAT_YUV420, + .de2_fmt = SUN8I_MIXER_FBFMT_YUV420, + }, + { + .drm_fmt = DRM_FORMAT_YUV411, + .de2_fmt = SUN8I_MIXER_FBFMT_YUV411, + }, + { + .drm_fmt = DRM_FORMAT_YVU422, + .de2_fmt = SUN8I_MIXER_FBFMT_YUV422, + }, + { + .drm_fmt = DRM_FORMAT_YVU420, + .de2_fmt = SUN8I_MIXER_FBFMT_YUV420, + }, + { + .drm_fmt = DRM_FORMAT_YVU411, + .de2_fmt = SUN8I_MIXER_FBFMT_YUV411, + }, + { + .drm_fmt = DRM_FORMAT_P010, + .de2_fmt = SUN8I_MIXER_FBFMT_P010_YUV, + }, + { + .drm_fmt = DRM_FORMAT_P210, + .de2_fmt = SUN8I_MIXER_FBFMT_P210_YUV, + }, +}; + +int sun8i_mixer_drm_format_to_hw(u32 format, u32 *hw_format) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(de2_formats); ++i) + if (de2_formats[i].drm_fmt == format) { + *hw_format = de2_formats[i].de2_fmt; + return 0; + } + + return -EINVAL; +} + +static void sun8i_mixer_commit(struct sunxi_engine *engine) +{ + DRM_DEBUG_DRIVER("Committing changes\n"); + + regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_DBUFF, + SUN8I_MIXER_GLOBAL_DBUFF_ENABLE); +} + +static struct drm_plane **sun8i_layers_init(struct drm_device *drm, + struct sunxi_engine *engine) +{ + struct drm_plane **planes; + struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine); + int i; + + planes = devm_kcalloc(drm->dev, + mixer->cfg->vi_num + mixer->cfg->ui_num + 1, + sizeof(*planes), GFP_KERNEL); + if (!planes) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < mixer->cfg->vi_num; i++) { + struct sun8i_vi_layer *layer; + + layer = sun8i_vi_layer_init_one(drm, mixer, i); + if (IS_ERR(layer)) { + dev_err(drm->dev, + "Couldn't initialize overlay plane\n"); + return ERR_CAST(layer); + } + + planes[i] = &layer->plane; + } + + for (i = 0; i < mixer->cfg->ui_num; i++) { + struct sun8i_ui_layer *layer; + + layer = sun8i_ui_layer_init_one(drm, mixer, i); + if (IS_ERR(layer)) { + dev_err(drm->dev, "Couldn't initialize %s plane\n", + i ? "overlay" : "primary"); + return ERR_CAST(layer); + } + + planes[mixer->cfg->vi_num + i] = &layer->plane; + } + + return planes; +} + +static void sun8i_mixer_mode_set(struct sunxi_engine *engine, + const struct drm_display_mode *mode) +{ + struct sun8i_mixer *mixer = engine_to_sun8i_mixer(engine); + u32 bld_base, size, val; + bool interlaced; + + bld_base = sun8i_blender_base(mixer); + interlaced = !!(mode->flags & DRM_MODE_FLAG_INTERLACE); + size = SUN8I_MIXER_SIZE(mode->hdisplay, mode->vdisplay); + + DRM_DEBUG_DRIVER("Updating global size W: %u H: %u\n", + mode->hdisplay, mode->vdisplay); + + regmap_write(engine->regs, SUN8I_MIXER_GLOBAL_SIZE, size); + regmap_write(engine->regs, SUN8I_MIXER_BLEND_OUTSIZE(bld_base), size); + + if (interlaced) + val = SUN8I_MIXER_BLEND_OUTCTL_INTERLACED; + else + val = 0; + + regmap_update_bits(engine->regs, SUN8I_MIXER_BLEND_OUTCTL(bld_base), + SUN8I_MIXER_BLEND_OUTCTL_INTERLACED, val); + + DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n", + interlaced ? "on" : "off"); +} + +static const struct sunxi_engine_ops sun8i_engine_ops = { + .commit = sun8i_mixer_commit, + .layers_init = sun8i_layers_init, + .mode_set = sun8i_mixer_mode_set, +}; + +static const struct regmap_config sun8i_mixer_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0xffffc, /* guessed */ +}; + +static int sun8i_mixer_of_get_id(struct device_node *node) +{ + struct device_node *ep, *remote; + struct of_endpoint of_ep; + + /* Output port is 1, and we want the first endpoint. */ + ep = of_graph_get_endpoint_by_regs(node, 1, -1); + if (!ep) + return -EINVAL; + + remote = of_graph_get_remote_endpoint(ep); + of_node_put(ep); + if (!remote) + return -EINVAL; + + of_graph_parse_endpoint(remote, &of_ep); + of_node_put(remote); + return of_ep.id; +} + +static int sun8i_mixer_bind(struct device *dev, struct device *master, + void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct drm_device *drm = data; + struct sun4i_drv *drv = drm->dev_private; + struct sun8i_mixer *mixer; + void __iomem *regs; + unsigned int base; + int plane_cnt; + int i, ret; + + /* + * The mixer uses single 32-bit register to store memory + * addresses, so that it cannot deal with 64-bit memory + * addresses. + * Restrict the DMA mask so that the mixer won't be + * allocated some memory that is too high. + */ + ret = dma_set_mask(dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(dev, "Cannot do 32-bit DMA.\n"); + return ret; + } + + mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL); + if (!mixer) + return -ENOMEM; + dev_set_drvdata(dev, mixer); + mixer->engine.ops = &sun8i_engine_ops; + mixer->engine.node = dev->of_node; + + if (of_find_property(dev->of_node, "iommus", NULL)) { + /* + * This assume we have the same DMA constraints for + * all our the mixers in our pipeline. This sounds + * bad, but it has always been the case for us, and + * DRM doesn't do per-device allocation either, so we + * would need to fix DRM first... + */ + ret = of_dma_configure(drm->dev, dev->of_node, true); + if (ret) + return ret; + } + + /* + * While this function can fail, we shouldn't do anything + * if this happens. Some early DE2 DT entries don't provide + * mixer id but work nevertheless because matching between + * TCON and mixer is done by comparing node pointers (old + * way) instead comparing ids. If this function fails and + * id is needed, it will fail during id matching anyway. + */ + mixer->engine.id = sun8i_mixer_of_get_id(dev->of_node); + + mixer->cfg = of_device_get_match_data(dev); + if (!mixer->cfg) + return -EINVAL; + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + mixer->engine.regs = devm_regmap_init_mmio(dev, regs, + &sun8i_mixer_regmap_config); + if (IS_ERR(mixer->engine.regs)) { + dev_err(dev, "Couldn't create the mixer regmap\n"); + return PTR_ERR(mixer->engine.regs); + } + + mixer->reset = devm_reset_control_get(dev, NULL); + if (IS_ERR(mixer->reset)) { + dev_err(dev, "Couldn't get our reset line\n"); + return PTR_ERR(mixer->reset); + } + + ret = reset_control_deassert(mixer->reset); + if (ret) { + dev_err(dev, "Couldn't deassert our reset line\n"); + return ret; + } + + mixer->bus_clk = devm_clk_get(dev, "bus"); + if (IS_ERR(mixer->bus_clk)) { + dev_err(dev, "Couldn't get the mixer bus clock\n"); + ret = PTR_ERR(mixer->bus_clk); + goto err_assert_reset; + } + clk_prepare_enable(mixer->bus_clk); + + mixer->mod_clk = devm_clk_get(dev, "mod"); + if (IS_ERR(mixer->mod_clk)) { + dev_err(dev, "Couldn't get the mixer module clock\n"); + ret = PTR_ERR(mixer->mod_clk); + goto err_disable_bus_clk; + } + + /* + * It seems that we need to enforce that rate for whatever + * reason for the mixer to be functional. Make sure it's the + * case. + */ + if (mixer->cfg->mod_rate) + clk_set_rate(mixer->mod_clk, mixer->cfg->mod_rate); + + clk_prepare_enable(mixer->mod_clk); + + list_add_tail(&mixer->engine.list, &drv->engine_list); + + base = sun8i_blender_base(mixer); + + /* Reset registers and disable unused sub-engines */ + if (mixer->cfg->is_de3) { + for (i = 0; i < DE3_MIXER_UNIT_SIZE; i += 4) + regmap_write(mixer->engine.regs, i, 0); + + regmap_write(mixer->engine.regs, SUN50I_MIXER_FCE_EN, 0); + regmap_write(mixer->engine.regs, SUN50I_MIXER_PEAK_EN, 0); + regmap_write(mixer->engine.regs, SUN50I_MIXER_LCTI_EN, 0); + regmap_write(mixer->engine.regs, SUN50I_MIXER_BLS_EN, 0); + regmap_write(mixer->engine.regs, SUN50I_MIXER_FCC_EN, 0); + regmap_write(mixer->engine.regs, SUN50I_MIXER_DNS_EN, 0); + regmap_write(mixer->engine.regs, SUN50I_MIXER_DRC_EN, 0); + regmap_write(mixer->engine.regs, SUN50I_MIXER_FMT_EN, 0); + regmap_write(mixer->engine.regs, SUN50I_MIXER_CDC0_EN, 0); + regmap_write(mixer->engine.regs, SUN50I_MIXER_CDC1_EN, 0); + } else { + for (i = 0; i < DE2_MIXER_UNIT_SIZE; i += 4) + regmap_write(mixer->engine.regs, i, 0); + + regmap_write(mixer->engine.regs, SUN8I_MIXER_FCE_EN, 0); + regmap_write(mixer->engine.regs, SUN8I_MIXER_BWS_EN, 0); + regmap_write(mixer->engine.regs, SUN8I_MIXER_LTI_EN, 0); + regmap_write(mixer->engine.regs, SUN8I_MIXER_PEAK_EN, 0); + regmap_write(mixer->engine.regs, SUN8I_MIXER_ASE_EN, 0); + regmap_write(mixer->engine.regs, SUN8I_MIXER_FCC_EN, 0); + regmap_write(mixer->engine.regs, SUN8I_MIXER_DCSC_EN, 0); + } + + /* Enable the mixer */ + regmap_write(mixer->engine.regs, SUN8I_MIXER_GLOBAL_CTL, + SUN8I_MIXER_GLOBAL_CTL_RT_EN); + + /* Set background color to black */ + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_BKCOLOR(base), + SUN8I_MIXER_BLEND_COLOR_BLACK); + + /* + * Set fill color of bottom plane to black. Generally not needed + * except when VI plane is at bottom (zpos = 0) and enabled. + */ + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(base), + SUN8I_MIXER_BLEND_PIPE_CTL_FC_EN(0)); + regmap_write(mixer->engine.regs, SUN8I_MIXER_BLEND_ATTR_FCOLOR(base, 0), + SUN8I_MIXER_BLEND_COLOR_BLACK); + + plane_cnt = mixer->cfg->vi_num + mixer->cfg->ui_num; + for (i = 0; i < plane_cnt; i++) + regmap_write(mixer->engine.regs, + SUN8I_MIXER_BLEND_MODE(base, i), + SUN8I_MIXER_BLEND_MODE_DEF); + + regmap_update_bits(mixer->engine.regs, SUN8I_MIXER_BLEND_PIPE_CTL(base), + SUN8I_MIXER_BLEND_PIPE_CTL_EN_MSK, 0); + + return 0; + +err_disable_bus_clk: + clk_disable_unprepare(mixer->bus_clk); +err_assert_reset: + reset_control_assert(mixer->reset); + return ret; +} + +static void sun8i_mixer_unbind(struct device *dev, struct device *master, + void *data) +{ + struct sun8i_mixer *mixer = dev_get_drvdata(dev); + + list_del(&mixer->engine.list); + + clk_disable_unprepare(mixer->mod_clk); + clk_disable_unprepare(mixer->bus_clk); + reset_control_assert(mixer->reset); +} + +static const struct component_ops sun8i_mixer_ops = { + .bind = sun8i_mixer_bind, + .unbind = sun8i_mixer_unbind, +}; + +static int sun8i_mixer_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &sun8i_mixer_ops); +} + +static int sun8i_mixer_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &sun8i_mixer_ops); + + return 0; +} + +static const struct sun8i_mixer_cfg sun8i_a83t_mixer0_cfg = { + .ccsc = CCSC_MIXER0_LAYOUT, + .scaler_mask = 0xf, + .scanline_yuv = 2048, + .ui_num = 3, + .vi_num = 1, +}; + +static const struct sun8i_mixer_cfg sun8i_a83t_mixer1_cfg = { + .ccsc = CCSC_MIXER1_LAYOUT, + .scaler_mask = 0x3, + .scanline_yuv = 2048, + .ui_num = 1, + .vi_num = 1, +}; + +static const struct sun8i_mixer_cfg sun8i_h3_mixer0_cfg = { + .ccsc = CCSC_MIXER0_LAYOUT, + .mod_rate = 432000000, + .scaler_mask = 0xf, + .scanline_yuv = 2048, + .ui_num = 3, + .vi_num = 1, +}; + +static const struct sun8i_mixer_cfg sun8i_r40_mixer0_cfg = { + .ccsc = CCSC_MIXER0_LAYOUT, + .mod_rate = 297000000, + .scaler_mask = 0xf, + .scanline_yuv = 2048, + .ui_num = 3, + .vi_num = 1, +}; + +static const struct sun8i_mixer_cfg sun8i_r40_mixer1_cfg = { + .ccsc = CCSC_MIXER1_LAYOUT, + .mod_rate = 297000000, + .scaler_mask = 0x3, + .scanline_yuv = 2048, + .ui_num = 1, + .vi_num = 1, +}; + +static const struct sun8i_mixer_cfg sun8i_v3s_mixer_cfg = { + .vi_num = 2, + .ui_num = 1, + .scaler_mask = 0x3, + .scanline_yuv = 2048, + .ccsc = CCSC_MIXER0_LAYOUT, + .mod_rate = 150000000, +}; + +static const struct sun8i_mixer_cfg sun20i_d1_mixer0_cfg = { + .ccsc = CCSC_D1_MIXER0_LAYOUT, + .mod_rate = 297000000, + .scaler_mask = 0x3, + .scanline_yuv = 2048, + .ui_num = 1, + .vi_num = 1, +}; + +static const struct sun8i_mixer_cfg sun20i_d1_mixer1_cfg = { + .ccsc = CCSC_MIXER1_LAYOUT, + .mod_rate = 297000000, + .scaler_mask = 0x1, + .scanline_yuv = 1024, + .ui_num = 0, + .vi_num = 1, +}; + +static const struct sun8i_mixer_cfg sun50i_a64_mixer0_cfg = { + .ccsc = CCSC_MIXER0_LAYOUT, + .mod_rate = 297000000, + .scaler_mask = 0xf, + .scanline_yuv = 4096, + .ui_num = 3, + .vi_num = 1, +}; + +static const struct sun8i_mixer_cfg sun50i_a64_mixer1_cfg = { + .ccsc = CCSC_MIXER1_LAYOUT, + .mod_rate = 297000000, + .scaler_mask = 0x3, + .scanline_yuv = 2048, + .ui_num = 1, + .vi_num = 1, +}; + +static const struct sun8i_mixer_cfg sun50i_h6_mixer0_cfg = { + .ccsc = CCSC_MIXER0_LAYOUT, + .is_de3 = true, + .mod_rate = 600000000, + .scaler_mask = 0xf, + .scanline_yuv = 4096, + .ui_num = 3, + .vi_num = 1, +}; + +static const struct of_device_id sun8i_mixer_of_table[] = { + { + .compatible = "allwinner,sun8i-a83t-de2-mixer-0", + .data = &sun8i_a83t_mixer0_cfg, + }, + { + .compatible = "allwinner,sun8i-a83t-de2-mixer-1", + .data = &sun8i_a83t_mixer1_cfg, + }, + { + .compatible = "allwinner,sun8i-h3-de2-mixer-0", + .data = &sun8i_h3_mixer0_cfg, + }, + { + .compatible = "allwinner,sun8i-r40-de2-mixer-0", + .data = &sun8i_r40_mixer0_cfg, + }, + { + .compatible = "allwinner,sun8i-r40-de2-mixer-1", + .data = &sun8i_r40_mixer1_cfg, + }, + { + .compatible = "allwinner,sun8i-v3s-de2-mixer", + .data = &sun8i_v3s_mixer_cfg, + }, + { + .compatible = "allwinner,sun20i-d1-de2-mixer-0", + .data = &sun20i_d1_mixer0_cfg, + }, + { + .compatible = "allwinner,sun20i-d1-de2-mixer-1", + .data = &sun20i_d1_mixer1_cfg, + }, + { + .compatible = "allwinner,sun50i-a64-de2-mixer-0", + .data = &sun50i_a64_mixer0_cfg, + }, + { + .compatible = "allwinner,sun50i-a64-de2-mixer-1", + .data = &sun50i_a64_mixer1_cfg, + }, + { + .compatible = "allwinner,sun50i-h6-de3-mixer-0", + .data = &sun50i_h6_mixer0_cfg, + }, + { } +}; +MODULE_DEVICE_TABLE(of, sun8i_mixer_of_table); + +static struct platform_driver sun8i_mixer_platform_driver = { + .probe = sun8i_mixer_probe, + .remove = sun8i_mixer_remove, + .driver = { + .name = "sun8i-mixer", + .of_match_table = sun8i_mixer_of_table, + }, +}; +module_platform_driver(sun8i_mixer_platform_driver); + +MODULE_AUTHOR("Icenowy Zheng <icenowy@aosc.io>"); +MODULE_DESCRIPTION("Allwinner DE2 Mixer driver"); +MODULE_LICENSE("GPL"); |