From 5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Tue, 21 Feb 2023 18:24:12 -0800 Subject: Merge tag 'net-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next 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(). ... --- drivers/gpu/drm/mediatek/Kconfig | 39 + drivers/gpu/drm/mediatek/Makefile | 27 + drivers/gpu/drm/mediatek/mtk_cec.c | 258 +++ drivers/gpu/drm/mediatek/mtk_cec.h | 18 + drivers/gpu/drm/mediatek/mtk_disp_aal.c | 169 ++ drivers/gpu/drm/mediatek/mtk_disp_ccorr.c | 229 +++ drivers/gpu/drm/mediatek/mtk_disp_color.c | 172 ++ drivers/gpu/drm/mediatek/mtk_disp_drv.h | 125 ++ drivers/gpu/drm/mediatek/mtk_disp_gamma.c | 217 +++ drivers/gpu/drm/mediatek/mtk_disp_merge.c | 320 ++++ drivers/gpu/drm/mediatek/mtk_disp_ovl.c | 573 ++++++ drivers/gpu/drm/mediatek/mtk_disp_rdma.c | 398 ++++ drivers/gpu/drm/mediatek/mtk_dp.c | 2663 +++++++++++++++++++++++++++ drivers/gpu/drm/mediatek/mtk_dp_reg.h | 350 ++++ drivers/gpu/drm/mediatek/mtk_dpi.c | 1116 +++++++++++ drivers/gpu/drm/mediatek/mtk_dpi_regs.h | 238 +++ drivers/gpu/drm/mediatek/mtk_drm_crtc.c | 1007 ++++++++++ drivers/gpu/drm/mediatek/mtk_drm_crtc.h | 26 + drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c | 576 ++++++ drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h | 222 +++ drivers/gpu/drm/mediatek/mtk_drm_drv.c | 904 +++++++++ drivers/gpu/drm/mediatek/mtk_drm_drv.h | 66 + drivers/gpu/drm/mediatek/mtk_drm_gem.c | 267 +++ drivers/gpu/drm/mediatek/mtk_drm_gem.h | 49 + drivers/gpu/drm/mediatek/mtk_drm_plane.c | 341 ++++ drivers/gpu/drm/mediatek/mtk_drm_plane.h | 53 + drivers/gpu/drm/mediatek/mtk_dsi.c | 1230 +++++++++++++ drivers/gpu/drm/mediatek/mtk_hdmi.c | 1840 ++++++++++++++++++ drivers/gpu/drm/mediatek/mtk_hdmi.h | 14 + drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c | 351 ++++ drivers/gpu/drm/mediatek/mtk_hdmi_regs.h | 230 +++ drivers/gpu/drm/mediatek/mtk_mdp_rdma.c | 315 ++++ drivers/gpu/drm/mediatek/mtk_mdp_rdma.h | 20 + 33 files changed, 14423 insertions(+) create mode 100644 drivers/gpu/drm/mediatek/Kconfig create mode 100644 drivers/gpu/drm/mediatek/Makefile create mode 100644 drivers/gpu/drm/mediatek/mtk_cec.c create mode 100644 drivers/gpu/drm/mediatek/mtk_cec.h create mode 100644 drivers/gpu/drm/mediatek/mtk_disp_aal.c create mode 100644 drivers/gpu/drm/mediatek/mtk_disp_ccorr.c create mode 100644 drivers/gpu/drm/mediatek/mtk_disp_color.c create mode 100644 drivers/gpu/drm/mediatek/mtk_disp_drv.h create mode 100644 drivers/gpu/drm/mediatek/mtk_disp_gamma.c create mode 100644 drivers/gpu/drm/mediatek/mtk_disp_merge.c create mode 100644 drivers/gpu/drm/mediatek/mtk_disp_ovl.c create mode 100644 drivers/gpu/drm/mediatek/mtk_disp_rdma.c create mode 100644 drivers/gpu/drm/mediatek/mtk_dp.c create mode 100644 drivers/gpu/drm/mediatek/mtk_dp_reg.h create mode 100644 drivers/gpu/drm/mediatek/mtk_dpi.c create mode 100644 drivers/gpu/drm/mediatek/mtk_dpi_regs.h create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_crtc.c create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_crtc.h create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.c create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_ddp_comp.h create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_drv.c create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_drv.h create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_gem.c create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_gem.h create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_plane.c create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_plane.h create mode 100644 drivers/gpu/drm/mediatek/mtk_dsi.c create mode 100644 drivers/gpu/drm/mediatek/mtk_hdmi.c create mode 100644 drivers/gpu/drm/mediatek/mtk_hdmi.h create mode 100644 drivers/gpu/drm/mediatek/mtk_hdmi_ddc.c create mode 100644 drivers/gpu/drm/mediatek/mtk_hdmi_regs.h create mode 100644 drivers/gpu/drm/mediatek/mtk_mdp_rdma.c create mode 100644 drivers/gpu/drm/mediatek/mtk_mdp_rdma.h (limited to 'drivers/gpu/drm/mediatek') diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig new file mode 100644 index 000000000..369e495d0 --- /dev/null +++ b/drivers/gpu/drm/mediatek/Kconfig @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: GPL-2.0-only +config DRM_MEDIATEK + tristate "DRM Support for Mediatek SoCs" + depends on DRM + depends on ARCH_MEDIATEK || (ARM && COMPILE_TEST) + depends on COMMON_CLK + depends on HAVE_ARM_SMCCC + depends on OF + depends on MTK_MMSYS + select DRM_GEM_DMA_HELPER + select DRM_KMS_HELPER + select DRM_MIPI_DSI + select DRM_PANEL + select MEMORY + select MTK_SMI + select PHY_MTK_MIPI_DSI + select VIDEOMODE_HELPERS + help + Choose this option if you have a Mediatek SoCs. + The module will be called mediatek-drm + This driver provides kernel mode setting and + buffer management to userspace. + +config DRM_MEDIATEK_DP + tristate "DRM DPTX Support for MediaTek SoCs" + depends on DRM_MEDIATEK + select PHY_MTK_DP + select DRM_DISPLAY_HELPER + select DRM_DISPLAY_DP_HELPER + help + DRM/KMS Display Port driver for MediaTek SoCs. + +config DRM_MEDIATEK_HDMI + tristate "DRM HDMI Support for Mediatek SoCs" + depends on DRM_MEDIATEK + select SND_SOC_HDMI_CODEC if SND_SOC + select PHY_MTK_HDMI + help + DRM/KMS HDMI driver for Mediatek SoCs diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile new file mode 100644 index 000000000..3517d1c65 --- /dev/null +++ b/drivers/gpu/drm/mediatek/Makefile @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0 + +mediatek-drm-y := mtk_disp_aal.o \ + mtk_disp_ccorr.o \ + mtk_disp_color.o \ + mtk_disp_gamma.o \ + mtk_disp_merge.o \ + mtk_disp_ovl.o \ + mtk_disp_rdma.o \ + mtk_drm_crtc.o \ + mtk_drm_ddp_comp.o \ + mtk_drm_drv.o \ + mtk_drm_gem.o \ + mtk_drm_plane.o \ + mtk_dsi.o \ + mtk_dpi.o \ + mtk_mdp_rdma.o + +obj-$(CONFIG_DRM_MEDIATEK) += mediatek-drm.o + +mediatek-drm-hdmi-objs := mtk_cec.o \ + mtk_hdmi.o \ + mtk_hdmi_ddc.o + +obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o + +obj-$(CONFIG_DRM_MEDIATEK_DP) += mtk_dp.o diff --git a/drivers/gpu/drm/mediatek/mtk_cec.c b/drivers/gpu/drm/mediatek/mtk_cec.c new file mode 100644 index 000000000..cdfa64891 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_cec.c @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Jie Qiu + */ +#include +#include +#include +#include +#include +#include +#include + +#include "mtk_cec.h" + +#define TR_CONFIG 0x00 +#define CLEAR_CEC_IRQ BIT(15) + +#define CEC_CKGEN 0x04 +#define CEC_32K_PDN BIT(19) +#define PDN BIT(16) + +#define RX_EVENT 0x54 +#define HDMI_PORD BIT(25) +#define HDMI_HTPLG BIT(24) +#define HDMI_PORD_INT_EN BIT(9) +#define HDMI_HTPLG_INT_EN BIT(8) + +#define RX_GEN_WD 0x58 +#define HDMI_PORD_INT_32K_STATUS BIT(26) +#define RX_RISC_INT_32K_STATUS BIT(25) +#define HDMI_HTPLG_INT_32K_STATUS BIT(24) +#define HDMI_PORD_INT_32K_CLR BIT(18) +#define RX_INT_32K_CLR BIT(17) +#define HDMI_HTPLG_INT_32K_CLR BIT(16) +#define HDMI_PORD_INT_32K_STA_MASK BIT(10) +#define RX_RISC_INT_32K_STA_MASK BIT(9) +#define HDMI_HTPLG_INT_32K_STA_MASK BIT(8) +#define HDMI_PORD_INT_32K_EN BIT(2) +#define RX_INT_32K_EN BIT(1) +#define HDMI_HTPLG_INT_32K_EN BIT(0) + +#define NORMAL_INT_CTRL 0x5C +#define HDMI_HTPLG_INT_STA BIT(0) +#define HDMI_PORD_INT_STA BIT(1) +#define HDMI_HTPLG_INT_CLR BIT(16) +#define HDMI_PORD_INT_CLR BIT(17) +#define HDMI_FULL_INT_CLR BIT(20) + +struct mtk_cec { + void __iomem *regs; + struct clk *clk; + int irq; + bool hpd; + void (*hpd_event)(bool hpd, struct device *dev); + struct device *hdmi_dev; + spinlock_t lock; +}; + +static void mtk_cec_clear_bits(struct mtk_cec *cec, unsigned int offset, + unsigned int bits) +{ + void __iomem *reg = cec->regs + offset; + u32 tmp; + + tmp = readl(reg); + tmp &= ~bits; + writel(tmp, reg); +} + +static void mtk_cec_set_bits(struct mtk_cec *cec, unsigned int offset, + unsigned int bits) +{ + void __iomem *reg = cec->regs + offset; + u32 tmp; + + tmp = readl(reg); + tmp |= bits; + writel(tmp, reg); +} + +static void mtk_cec_mask(struct mtk_cec *cec, unsigned int offset, + unsigned int val, unsigned int mask) +{ + u32 tmp = readl(cec->regs + offset) & ~mask; + + tmp |= val & mask; + writel(tmp, cec->regs + offset); +} + +void mtk_cec_set_hpd_event(struct device *dev, + void (*hpd_event)(bool hpd, struct device *dev), + struct device *hdmi_dev) +{ + struct mtk_cec *cec = dev_get_drvdata(dev); + unsigned long flags; + + spin_lock_irqsave(&cec->lock, flags); + cec->hdmi_dev = hdmi_dev; + cec->hpd_event = hpd_event; + spin_unlock_irqrestore(&cec->lock, flags); +} + +bool mtk_cec_hpd_high(struct device *dev) +{ + struct mtk_cec *cec = dev_get_drvdata(dev); + unsigned int status; + + status = readl(cec->regs + RX_EVENT); + + return (status & (HDMI_PORD | HDMI_HTPLG)) == (HDMI_PORD | HDMI_HTPLG); +} + +static void mtk_cec_htplg_irq_init(struct mtk_cec *cec) +{ + mtk_cec_mask(cec, CEC_CKGEN, 0 | CEC_32K_PDN, PDN | CEC_32K_PDN); + mtk_cec_set_bits(cec, RX_GEN_WD, HDMI_PORD_INT_32K_CLR | + RX_INT_32K_CLR | HDMI_HTPLG_INT_32K_CLR); + mtk_cec_mask(cec, RX_GEN_WD, 0, HDMI_PORD_INT_32K_CLR | RX_INT_32K_CLR | + HDMI_HTPLG_INT_32K_CLR | HDMI_PORD_INT_32K_EN | + RX_INT_32K_EN | HDMI_HTPLG_INT_32K_EN); +} + +static void mtk_cec_htplg_irq_enable(struct mtk_cec *cec) +{ + mtk_cec_set_bits(cec, RX_EVENT, HDMI_PORD_INT_EN | HDMI_HTPLG_INT_EN); +} + +static void mtk_cec_htplg_irq_disable(struct mtk_cec *cec) +{ + mtk_cec_clear_bits(cec, RX_EVENT, HDMI_PORD_INT_EN | HDMI_HTPLG_INT_EN); +} + +static void mtk_cec_clear_htplg_irq(struct mtk_cec *cec) +{ + mtk_cec_set_bits(cec, TR_CONFIG, CLEAR_CEC_IRQ); + mtk_cec_set_bits(cec, NORMAL_INT_CTRL, HDMI_HTPLG_INT_CLR | + HDMI_PORD_INT_CLR | HDMI_FULL_INT_CLR); + mtk_cec_set_bits(cec, RX_GEN_WD, HDMI_PORD_INT_32K_CLR | + RX_INT_32K_CLR | HDMI_HTPLG_INT_32K_CLR); + usleep_range(5, 10); + mtk_cec_clear_bits(cec, NORMAL_INT_CTRL, HDMI_HTPLG_INT_CLR | + HDMI_PORD_INT_CLR | HDMI_FULL_INT_CLR); + mtk_cec_clear_bits(cec, TR_CONFIG, CLEAR_CEC_IRQ); + mtk_cec_clear_bits(cec, RX_GEN_WD, HDMI_PORD_INT_32K_CLR | + RX_INT_32K_CLR | HDMI_HTPLG_INT_32K_CLR); +} + +static void mtk_cec_hpd_event(struct mtk_cec *cec, bool hpd) +{ + void (*hpd_event)(bool hpd, struct device *dev); + struct device *hdmi_dev; + unsigned long flags; + + spin_lock_irqsave(&cec->lock, flags); + hpd_event = cec->hpd_event; + hdmi_dev = cec->hdmi_dev; + spin_unlock_irqrestore(&cec->lock, flags); + + if (hpd_event) + hpd_event(hpd, hdmi_dev); +} + +static irqreturn_t mtk_cec_htplg_isr_thread(int irq, void *arg) +{ + struct device *dev = arg; + struct mtk_cec *cec = dev_get_drvdata(dev); + bool hpd; + + mtk_cec_clear_htplg_irq(cec); + hpd = mtk_cec_hpd_high(dev); + + if (cec->hpd != hpd) { + dev_dbg(dev, "hotplug event! cur hpd = %d, hpd = %d\n", + cec->hpd, hpd); + cec->hpd = hpd; + mtk_cec_hpd_event(cec, hpd); + } + return IRQ_HANDLED; +} + +static int mtk_cec_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mtk_cec *cec; + struct resource *res; + int ret; + + cec = devm_kzalloc(dev, sizeof(*cec), GFP_KERNEL); + if (!cec) + return -ENOMEM; + + platform_set_drvdata(pdev, cec); + spin_lock_init(&cec->lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + cec->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(cec->regs)) { + ret = PTR_ERR(cec->regs); + dev_err(dev, "Failed to ioremap cec: %d\n", ret); + return ret; + } + + cec->clk = devm_clk_get(dev, NULL); + if (IS_ERR(cec->clk)) { + ret = PTR_ERR(cec->clk); + dev_err(dev, "Failed to get cec clock: %d\n", ret); + return ret; + } + + cec->irq = platform_get_irq(pdev, 0); + if (cec->irq < 0) + return cec->irq; + + ret = devm_request_threaded_irq(dev, cec->irq, NULL, + mtk_cec_htplg_isr_thread, + IRQF_SHARED | IRQF_TRIGGER_LOW | + IRQF_ONESHOT, "hdmi hpd", dev); + if (ret) { + dev_err(dev, "Failed to register cec irq: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(cec->clk); + if (ret) { + dev_err(dev, "Failed to enable cec clock: %d\n", ret); + return ret; + } + + mtk_cec_htplg_irq_init(cec); + mtk_cec_htplg_irq_enable(cec); + + return 0; +} + +static int mtk_cec_remove(struct platform_device *pdev) +{ + struct mtk_cec *cec = platform_get_drvdata(pdev); + + mtk_cec_htplg_irq_disable(cec); + clk_disable_unprepare(cec->clk); + return 0; +} + +static const struct of_device_id mtk_cec_of_ids[] = { + { .compatible = "mediatek,mt8173-cec", }, + {} +}; +MODULE_DEVICE_TABLE(of, mtk_cec_of_ids); + +struct platform_driver mtk_cec_driver = { + .probe = mtk_cec_probe, + .remove = mtk_cec_remove, + .driver = { + .name = "mediatek-cec", + .of_match_table = mtk_cec_of_ids, + }, +}; diff --git a/drivers/gpu/drm/mediatek/mtk_cec.h b/drivers/gpu/drm/mediatek/mtk_cec.h new file mode 100644 index 000000000..c6412dddb --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_cec.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2014 MediaTek Inc. + * Author: Jie Qiu + */ +#ifndef _MTK_CEC_H +#define _MTK_CEC_H + +#include + +struct device; + +void mtk_cec_set_hpd_event(struct device *dev, + void (*hotplug_event)(bool hpd, struct device *dev), + struct device *hdmi_dev); +bool mtk_cec_hpd_high(struct device *dev); + +#endif /* _MTK_CEC_H */ diff --git a/drivers/gpu/drm/mediatek/mtk_disp_aal.c b/drivers/gpu/drm/mediatek/mtk_disp_aal.c new file mode 100644 index 000000000..0f9d7efb6 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_disp_aal.c @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "mtk_disp_drv.h" +#include "mtk_drm_crtc.h" +#include "mtk_drm_ddp_comp.h" + +#define DISP_AAL_EN 0x0000 +#define AAL_EN BIT(0) +#define DISP_AAL_SIZE 0x0030 +#define DISP_AAL_OUTPUT_SIZE 0x04d8 + + +struct mtk_disp_aal_data { + bool has_gamma; +}; + +/** + * struct mtk_disp_aal - DISP_AAL driver structure + * @ddp_comp - structure containing type enum and hardware resources + * @crtc - associated crtc to report irq events to + */ +struct mtk_disp_aal { + struct clk *clk; + void __iomem *regs; + struct cmdq_client_reg cmdq_reg; + const struct mtk_disp_aal_data *data; +}; + +int mtk_aal_clk_enable(struct device *dev) +{ + struct mtk_disp_aal *aal = dev_get_drvdata(dev); + + return clk_prepare_enable(aal->clk); +} + +void mtk_aal_clk_disable(struct device *dev) +{ + struct mtk_disp_aal *aal = dev_get_drvdata(dev); + + clk_disable_unprepare(aal->clk); +} + +void mtk_aal_config(struct device *dev, unsigned int w, + unsigned int h, unsigned int vrefresh, + unsigned int bpc, struct cmdq_pkt *cmdq_pkt) +{ + struct mtk_disp_aal *aal = dev_get_drvdata(dev); + + mtk_ddp_write(cmdq_pkt, w << 16 | h, &aal->cmdq_reg, aal->regs, DISP_AAL_SIZE); + mtk_ddp_write(cmdq_pkt, w << 16 | h, &aal->cmdq_reg, aal->regs, DISP_AAL_OUTPUT_SIZE); +} + +void mtk_aal_gamma_set(struct device *dev, struct drm_crtc_state *state) +{ + struct mtk_disp_aal *aal = dev_get_drvdata(dev); + + if (aal->data && aal->data->has_gamma) + mtk_gamma_set_common(aal->regs, state, false); +} + +void mtk_aal_start(struct device *dev) +{ + struct mtk_disp_aal *aal = dev_get_drvdata(dev); + + writel(AAL_EN, aal->regs + DISP_AAL_EN); +} + +void mtk_aal_stop(struct device *dev) +{ + struct mtk_disp_aal *aal = dev_get_drvdata(dev); + + writel_relaxed(0x0, aal->regs + DISP_AAL_EN); +} + +static int mtk_disp_aal_bind(struct device *dev, struct device *master, + void *data) +{ + return 0; +} + +static void mtk_disp_aal_unbind(struct device *dev, struct device *master, + void *data) +{ +} + +static const struct component_ops mtk_disp_aal_component_ops = { + .bind = mtk_disp_aal_bind, + .unbind = mtk_disp_aal_unbind, +}; + +static int mtk_disp_aal_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mtk_disp_aal *priv; + struct resource *res; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + dev_err(dev, "failed to get aal clk\n"); + return PTR_ERR(priv->clk); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->regs)) { + dev_err(dev, "failed to ioremap aal\n"); + return PTR_ERR(priv->regs); + } + +#if IS_REACHABLE(CONFIG_MTK_CMDQ) + ret = cmdq_dev_get_client_reg(dev, &priv->cmdq_reg, 0); + if (ret) + dev_dbg(dev, "get mediatek,gce-client-reg fail!\n"); +#endif + + priv->data = of_device_get_match_data(dev); + platform_set_drvdata(pdev, priv); + + ret = component_add(dev, &mtk_disp_aal_component_ops); + if (ret) + dev_err(dev, "Failed to add component: %d\n", ret); + + return ret; +} + +static int mtk_disp_aal_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &mtk_disp_aal_component_ops); + + return 0; +} + +static const struct mtk_disp_aal_data mt8173_aal_driver_data = { + .has_gamma = true, +}; + +static const struct of_device_id mtk_disp_aal_driver_dt_match[] = { + { .compatible = "mediatek,mt8173-disp-aal", + .data = &mt8173_aal_driver_data}, + { .compatible = "mediatek,mt8183-disp-aal"}, + {}, +}; +MODULE_DEVICE_TABLE(of, mtk_disp_aal_driver_dt_match); + +struct platform_driver mtk_disp_aal_driver = { + .probe = mtk_disp_aal_probe, + .remove = mtk_disp_aal_remove, + .driver = { + .name = "mediatek-disp-aal", + .owner = THIS_MODULE, + .of_match_table = mtk_disp_aal_driver_dt_match, + }, +}; diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ccorr.c b/drivers/gpu/drm/mediatek/mtk_disp_ccorr.c new file mode 100644 index 000000000..3a53ebc4e --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_disp_ccorr.c @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "mtk_disp_drv.h" +#include "mtk_drm_crtc.h" +#include "mtk_drm_ddp_comp.h" + +#define DISP_CCORR_EN 0x0000 +#define CCORR_EN BIT(0) +#define DISP_CCORR_CFG 0x0020 +#define CCORR_RELAY_MODE BIT(0) +#define CCORR_ENGINE_EN BIT(1) +#define CCORR_GAMMA_OFF BIT(2) +#define CCORR_WGAMUT_SRC_CLIP BIT(3) +#define DISP_CCORR_SIZE 0x0030 +#define DISP_CCORR_COEF_0 0x0080 +#define DISP_CCORR_COEF_1 0x0084 +#define DISP_CCORR_COEF_2 0x0088 +#define DISP_CCORR_COEF_3 0x008C +#define DISP_CCORR_COEF_4 0x0090 + +struct mtk_disp_ccorr_data { + u32 matrix_bits; +}; + +/** + * struct mtk_disp_ccorr - DISP_CCORR driver structure + * @ddp_comp - structure containing type enum and hardware resources + * @crtc - associated crtc to report irq events to + */ +struct mtk_disp_ccorr { + struct clk *clk; + void __iomem *regs; + struct cmdq_client_reg cmdq_reg; + const struct mtk_disp_ccorr_data *data; +}; + +int mtk_ccorr_clk_enable(struct device *dev) +{ + struct mtk_disp_ccorr *ccorr = dev_get_drvdata(dev); + + return clk_prepare_enable(ccorr->clk); +} + +void mtk_ccorr_clk_disable(struct device *dev) +{ + struct mtk_disp_ccorr *ccorr = dev_get_drvdata(dev); + + clk_disable_unprepare(ccorr->clk); +} + +void mtk_ccorr_config(struct device *dev, unsigned int w, + unsigned int h, unsigned int vrefresh, + unsigned int bpc, struct cmdq_pkt *cmdq_pkt) +{ + struct mtk_disp_ccorr *ccorr = dev_get_drvdata(dev); + + mtk_ddp_write(cmdq_pkt, w << 16 | h, &ccorr->cmdq_reg, ccorr->regs, + DISP_CCORR_SIZE); + mtk_ddp_write(cmdq_pkt, CCORR_ENGINE_EN, &ccorr->cmdq_reg, ccorr->regs, + DISP_CCORR_CFG); +} + +void mtk_ccorr_start(struct device *dev) +{ + struct mtk_disp_ccorr *ccorr = dev_get_drvdata(dev); + + writel(CCORR_EN, ccorr->regs + DISP_CCORR_EN); +} + +void mtk_ccorr_stop(struct device *dev) +{ + struct mtk_disp_ccorr *ccorr = dev_get_drvdata(dev); + + writel_relaxed(0x0, ccorr->regs + DISP_CCORR_EN); +} + +/* Converts a DRM S31.32 value to the HW S1.n format. */ +static u16 mtk_ctm_s31_32_to_s1_n(u64 in, u32 n) +{ + u16 r; + + /* Sign bit. */ + r = in & BIT_ULL(63) ? BIT(n + 1) : 0; + + if ((in & GENMASK_ULL(62, 33)) > 0) { + /* identity value 0x100000000 -> 0x400(mt8183), */ + /* identity value 0x100000000 -> 0x800(mt8192), */ + /* if bigger this, set it to max 0x7ff. */ + r |= GENMASK(n, 0); + } else { + /* take the n+1 most important bits. */ + r |= (in >> (32 - n)) & GENMASK(n, 0); + } + + return r; +} + +void mtk_ccorr_ctm_set(struct device *dev, struct drm_crtc_state *state) +{ + struct mtk_disp_ccorr *ccorr = dev_get_drvdata(dev); + struct drm_property_blob *blob = state->ctm; + struct drm_color_ctm *ctm; + const u64 *input; + uint16_t coeffs[9] = { 0 }; + int i; + struct cmdq_pkt *cmdq_pkt = NULL; + u32 matrix_bits = ccorr->data->matrix_bits; + + if (!blob) + return; + + ctm = (struct drm_color_ctm *)blob->data; + input = ctm->matrix; + + for (i = 0; i < ARRAY_SIZE(coeffs); i++) + coeffs[i] = mtk_ctm_s31_32_to_s1_n(input[i], matrix_bits); + + mtk_ddp_write(cmdq_pkt, coeffs[0] << 16 | coeffs[1], + &ccorr->cmdq_reg, ccorr->regs, DISP_CCORR_COEF_0); + mtk_ddp_write(cmdq_pkt, coeffs[2] << 16 | coeffs[3], + &ccorr->cmdq_reg, ccorr->regs, DISP_CCORR_COEF_1); + mtk_ddp_write(cmdq_pkt, coeffs[4] << 16 | coeffs[5], + &ccorr->cmdq_reg, ccorr->regs, DISP_CCORR_COEF_2); + mtk_ddp_write(cmdq_pkt, coeffs[6] << 16 | coeffs[7], + &ccorr->cmdq_reg, ccorr->regs, DISP_CCORR_COEF_3); + mtk_ddp_write(cmdq_pkt, coeffs[8] << 16, + &ccorr->cmdq_reg, ccorr->regs, DISP_CCORR_COEF_4); +} + +static int mtk_disp_ccorr_bind(struct device *dev, struct device *master, + void *data) +{ + return 0; +} + +static void mtk_disp_ccorr_unbind(struct device *dev, struct device *master, + void *data) +{ +} + +static const struct component_ops mtk_disp_ccorr_component_ops = { + .bind = mtk_disp_ccorr_bind, + .unbind = mtk_disp_ccorr_unbind, +}; + +static int mtk_disp_ccorr_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mtk_disp_ccorr *priv; + struct resource *res; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + dev_err(dev, "failed to get ccorr clk\n"); + return PTR_ERR(priv->clk); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->regs)) { + dev_err(dev, "failed to ioremap ccorr\n"); + return PTR_ERR(priv->regs); + } + +#if IS_REACHABLE(CONFIG_MTK_CMDQ) + ret = cmdq_dev_get_client_reg(dev, &priv->cmdq_reg, 0); + if (ret) + dev_dbg(dev, "get mediatek,gce-client-reg fail!\n"); +#endif + + priv->data = of_device_get_match_data(dev); + platform_set_drvdata(pdev, priv); + + ret = component_add(dev, &mtk_disp_ccorr_component_ops); + if (ret) + dev_err(dev, "Failed to add component: %d\n", ret); + + return ret; +} + +static int mtk_disp_ccorr_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &mtk_disp_ccorr_component_ops); + + return 0; +} + +static const struct mtk_disp_ccorr_data mt8183_ccorr_driver_data = { + .matrix_bits = 10, +}; + +static const struct mtk_disp_ccorr_data mt8192_ccorr_driver_data = { + .matrix_bits = 11, +}; + +static const struct of_device_id mtk_disp_ccorr_driver_dt_match[] = { + { .compatible = "mediatek,mt8183-disp-ccorr", + .data = &mt8183_ccorr_driver_data}, + { .compatible = "mediatek,mt8192-disp-ccorr", + .data = &mt8192_ccorr_driver_data}, + {}, +}; +MODULE_DEVICE_TABLE(of, mtk_disp_ccorr_driver_dt_match); + +struct platform_driver mtk_disp_ccorr_driver = { + .probe = mtk_disp_ccorr_probe, + .remove = mtk_disp_ccorr_remove, + .driver = { + .name = "mediatek-disp-ccorr", + .owner = THIS_MODULE, + .of_match_table = mtk_disp_ccorr_driver_dt_match, + }, +}; diff --git a/drivers/gpu/drm/mediatek/mtk_disp_color.c b/drivers/gpu/drm/mediatek/mtk_disp_color.c new file mode 100644 index 000000000..473f5bb5c --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_disp_color.c @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017 MediaTek Inc. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "mtk_disp_drv.h" +#include "mtk_drm_crtc.h" +#include "mtk_drm_ddp_comp.h" + +#define DISP_COLOR_CFG_MAIN 0x0400 +#define DISP_COLOR_START_MT2701 0x0f00 +#define DISP_COLOR_START_MT8167 0x0400 +#define DISP_COLOR_START_MT8173 0x0c00 +#define DISP_COLOR_START(comp) ((comp)->data->color_offset) +#define DISP_COLOR_WIDTH(comp) (DISP_COLOR_START(comp) + 0x50) +#define DISP_COLOR_HEIGHT(comp) (DISP_COLOR_START(comp) + 0x54) + +#define COLOR_BYPASS_ALL BIT(7) +#define COLOR_SEQ_SEL BIT(13) + +struct mtk_disp_color_data { + unsigned int color_offset; +}; + +/* + * struct mtk_disp_color - DISP_COLOR driver structure + * @crtc: associated crtc to report irq events to + * @data: platform colour driver data + */ +struct mtk_disp_color { + struct drm_crtc *crtc; + struct clk *clk; + void __iomem *regs; + struct cmdq_client_reg cmdq_reg; + const struct mtk_disp_color_data *data; +}; + +int mtk_color_clk_enable(struct device *dev) +{ + struct mtk_disp_color *color = dev_get_drvdata(dev); + + return clk_prepare_enable(color->clk); +} + +void mtk_color_clk_disable(struct device *dev) +{ + struct mtk_disp_color *color = dev_get_drvdata(dev); + + clk_disable_unprepare(color->clk); +} + +void mtk_color_config(struct device *dev, unsigned int w, + unsigned int h, unsigned int vrefresh, + unsigned int bpc, struct cmdq_pkt *cmdq_pkt) +{ + struct mtk_disp_color *color = dev_get_drvdata(dev); + + mtk_ddp_write(cmdq_pkt, w, &color->cmdq_reg, color->regs, DISP_COLOR_WIDTH(color)); + mtk_ddp_write(cmdq_pkt, h, &color->cmdq_reg, color->regs, DISP_COLOR_HEIGHT(color)); +} + +void mtk_color_start(struct device *dev) +{ + struct mtk_disp_color *color = dev_get_drvdata(dev); + + writel(COLOR_BYPASS_ALL | COLOR_SEQ_SEL, + color->regs + DISP_COLOR_CFG_MAIN); + writel(0x1, color->regs + DISP_COLOR_START(color)); +} + +static int mtk_disp_color_bind(struct device *dev, struct device *master, + void *data) +{ + return 0; +} + +static void mtk_disp_color_unbind(struct device *dev, struct device *master, + void *data) +{ +} + +static const struct component_ops mtk_disp_color_component_ops = { + .bind = mtk_disp_color_bind, + .unbind = mtk_disp_color_unbind, +}; + +static int mtk_disp_color_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mtk_disp_color *priv; + struct resource *res; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + dev_err(dev, "failed to get color clk\n"); + return PTR_ERR(priv->clk); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->regs)) { + dev_err(dev, "failed to ioremap color\n"); + return PTR_ERR(priv->regs); + } +#if IS_REACHABLE(CONFIG_MTK_CMDQ) + ret = cmdq_dev_get_client_reg(dev, &priv->cmdq_reg, 0); + if (ret) + dev_dbg(dev, "get mediatek,gce-client-reg fail!\n"); +#endif + + priv->data = of_device_get_match_data(dev); + platform_set_drvdata(pdev, priv); + + ret = component_add(dev, &mtk_disp_color_component_ops); + if (ret) + dev_err(dev, "Failed to add component: %d\n", ret); + + return ret; +} + +static int mtk_disp_color_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &mtk_disp_color_component_ops); + + return 0; +} + +static const struct mtk_disp_color_data mt2701_color_driver_data = { + .color_offset = DISP_COLOR_START_MT2701, +}; + +static const struct mtk_disp_color_data mt8167_color_driver_data = { + .color_offset = DISP_COLOR_START_MT8167, +}; + +static const struct mtk_disp_color_data mt8173_color_driver_data = { + .color_offset = DISP_COLOR_START_MT8173, +}; + +static const struct of_device_id mtk_disp_color_driver_dt_match[] = { + { .compatible = "mediatek,mt2701-disp-color", + .data = &mt2701_color_driver_data}, + { .compatible = "mediatek,mt8167-disp-color", + .data = &mt8167_color_driver_data}, + { .compatible = "mediatek,mt8173-disp-color", + .data = &mt8173_color_driver_data}, + {}, +}; +MODULE_DEVICE_TABLE(of, mtk_disp_color_driver_dt_match); + +struct platform_driver mtk_disp_color_driver = { + .probe = mtk_disp_color_probe, + .remove = mtk_disp_color_remove, + .driver = { + .name = "mediatek-disp-color", + .owner = THIS_MODULE, + .of_match_table = mtk_disp_color_driver_dt_match, + }, +}; diff --git a/drivers/gpu/drm/mediatek/mtk_disp_drv.h b/drivers/gpu/drm/mediatek/mtk_disp_drv.h new file mode 100644 index 000000000..33e61a136 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_disp_drv.h @@ -0,0 +1,125 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020 MediaTek Inc. + */ + +#ifndef _MTK_DISP_DRV_H_ +#define _MTK_DISP_DRV_H_ + +#include +#include "mtk_drm_plane.h" +#include "mtk_mdp_rdma.h" + +int mtk_aal_clk_enable(struct device *dev); +void mtk_aal_clk_disable(struct device *dev); +void mtk_aal_config(struct device *dev, unsigned int w, + unsigned int h, unsigned int vrefresh, + unsigned int bpc, struct cmdq_pkt *cmdq_pkt); +void mtk_aal_gamma_set(struct device *dev, struct drm_crtc_state *state); +void mtk_aal_start(struct device *dev); +void mtk_aal_stop(struct device *dev); + +void mtk_ccorr_ctm_set(struct device *dev, struct drm_crtc_state *state); +int mtk_ccorr_clk_enable(struct device *dev); +void mtk_ccorr_clk_disable(struct device *dev); +void mtk_ccorr_config(struct device *dev, unsigned int w, + unsigned int h, unsigned int vrefresh, + unsigned int bpc, struct cmdq_pkt *cmdq_pkt); +void mtk_ccorr_start(struct device *dev); +void mtk_ccorr_stop(struct device *dev); + +void mtk_color_bypass_shadow(struct device *dev); +int mtk_color_clk_enable(struct device *dev); +void mtk_color_clk_disable(struct device *dev); +void mtk_color_config(struct device *dev, unsigned int w, + unsigned int h, unsigned int vrefresh, + unsigned int bpc, struct cmdq_pkt *cmdq_pkt); +void mtk_color_start(struct device *dev); + +void mtk_dither_set_common(void __iomem *regs, struct cmdq_client_reg *cmdq_reg, + unsigned int bpc, unsigned int cfg, + unsigned int dither_en, struct cmdq_pkt *cmdq_pkt); + +void mtk_dpi_start(struct device *dev); +void mtk_dpi_stop(struct device *dev); + +void mtk_dsi_ddp_start(struct device *dev); +void mtk_dsi_ddp_stop(struct device *dev); + +int mtk_gamma_clk_enable(struct device *dev); +void mtk_gamma_clk_disable(struct device *dev); +void mtk_gamma_config(struct device *dev, unsigned int w, + unsigned int h, unsigned int vrefresh, + unsigned int bpc, struct cmdq_pkt *cmdq_pkt); +void mtk_gamma_set(struct device *dev, struct drm_crtc_state *state); +void mtk_gamma_set_common(void __iomem *regs, struct drm_crtc_state *state, bool lut_diff); +void mtk_gamma_start(struct device *dev); +void mtk_gamma_stop(struct device *dev); + +int mtk_merge_clk_enable(struct device *dev); +void mtk_merge_clk_disable(struct device *dev); +void mtk_merge_config(struct device *dev, unsigned int width, + unsigned int height, unsigned int vrefresh, + unsigned int bpc, struct cmdq_pkt *cmdq_pkt); +void mtk_merge_start(struct device *dev); +void mtk_merge_stop(struct device *dev); +void mtk_merge_advance_config(struct device *dev, unsigned int l_w, unsigned int r_w, + unsigned int h, unsigned int vrefresh, unsigned int bpc, + struct cmdq_pkt *cmdq_pkt); +void mtk_merge_start_cmdq(struct device *dev, struct cmdq_pkt *cmdq_pkt); +void mtk_merge_stop_cmdq(struct device *dev, struct cmdq_pkt *cmdq_pkt); + +void mtk_ovl_bgclr_in_on(struct device *dev); +void mtk_ovl_bgclr_in_off(struct device *dev); +void mtk_ovl_bypass_shadow(struct device *dev); +int mtk_ovl_clk_enable(struct device *dev); +void mtk_ovl_clk_disable(struct device *dev); +void mtk_ovl_config(struct device *dev, unsigned int w, + unsigned int h, unsigned int vrefresh, + unsigned int bpc, struct cmdq_pkt *cmdq_pkt); +int mtk_ovl_layer_check(struct device *dev, unsigned int idx, + struct mtk_plane_state *mtk_state); +void mtk_ovl_layer_config(struct device *dev, unsigned int idx, + struct mtk_plane_state *state, + struct cmdq_pkt *cmdq_pkt); +unsigned int mtk_ovl_layer_nr(struct device *dev); +void mtk_ovl_layer_on(struct device *dev, unsigned int idx, + struct cmdq_pkt *cmdq_pkt); +void mtk_ovl_layer_off(struct device *dev, unsigned int idx, + struct cmdq_pkt *cmdq_pkt); +void mtk_ovl_start(struct device *dev); +void mtk_ovl_stop(struct device *dev); +unsigned int mtk_ovl_supported_rotations(struct device *dev); +void mtk_ovl_register_vblank_cb(struct device *dev, + void (*vblank_cb)(void *), + void *vblank_cb_data); +void mtk_ovl_unregister_vblank_cb(struct device *dev); +void mtk_ovl_enable_vblank(struct device *dev); +void mtk_ovl_disable_vblank(struct device *dev); + +void mtk_rdma_bypass_shadow(struct device *dev); +int mtk_rdma_clk_enable(struct device *dev); +void mtk_rdma_clk_disable(struct device *dev); +void mtk_rdma_config(struct device *dev, unsigned int width, + unsigned int height, unsigned int vrefresh, + unsigned int bpc, struct cmdq_pkt *cmdq_pkt); +unsigned int mtk_rdma_layer_nr(struct device *dev); +void mtk_rdma_layer_config(struct device *dev, unsigned int idx, + struct mtk_plane_state *state, + struct cmdq_pkt *cmdq_pkt); +void mtk_rdma_start(struct device *dev); +void mtk_rdma_stop(struct device *dev); +void mtk_rdma_register_vblank_cb(struct device *dev, + void (*vblank_cb)(void *), + void *vblank_cb_data); +void mtk_rdma_unregister_vblank_cb(struct device *dev); +void mtk_rdma_enable_vblank(struct device *dev); +void mtk_rdma_disable_vblank(struct device *dev); + +int mtk_mdp_rdma_clk_enable(struct device *dev); +void mtk_mdp_rdma_clk_disable(struct device *dev); +void mtk_mdp_rdma_start(struct device *dev, struct cmdq_pkt *cmdq_pkt); +void mtk_mdp_rdma_stop(struct device *dev, struct cmdq_pkt *cmdq_pkt); +void mtk_mdp_rdma_config(struct device *dev, struct mtk_mdp_rdma_cfg *cfg, + struct cmdq_pkt *cmdq_pkt); +#endif diff --git a/drivers/gpu/drm/mediatek/mtk_disp_gamma.c b/drivers/gpu/drm/mediatek/mtk_disp_gamma.c new file mode 100644 index 000000000..bbd558a03 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_disp_gamma.c @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "mtk_disp_drv.h" +#include "mtk_drm_crtc.h" +#include "mtk_drm_ddp_comp.h" + +#define DISP_GAMMA_EN 0x0000 +#define GAMMA_EN BIT(0) +#define DISP_GAMMA_CFG 0x0020 +#define GAMMA_LUT_EN BIT(1) +#define GAMMA_DITHERING BIT(2) +#define DISP_GAMMA_SIZE 0x0030 +#define DISP_GAMMA_LUT 0x0700 + +#define LUT_10BIT_MASK 0x03ff + +struct mtk_disp_gamma_data { + bool has_dither; + bool lut_diff; +}; + +/* + * struct mtk_disp_gamma - DISP_GAMMA driver structure + */ +struct mtk_disp_gamma { + struct clk *clk; + void __iomem *regs; + struct cmdq_client_reg cmdq_reg; + const struct mtk_disp_gamma_data *data; +}; + +int mtk_gamma_clk_enable(struct device *dev) +{ + struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); + + return clk_prepare_enable(gamma->clk); +} + +void mtk_gamma_clk_disable(struct device *dev) +{ + struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); + + clk_disable_unprepare(gamma->clk); +} + +void mtk_gamma_set_common(void __iomem *regs, struct drm_crtc_state *state, bool lut_diff) +{ + unsigned int i, reg; + struct drm_color_lut *lut; + void __iomem *lut_base; + u32 word; + u32 diff[3] = {0}; + + if (state->gamma_lut) { + reg = readl(regs + DISP_GAMMA_CFG); + reg = reg | GAMMA_LUT_EN; + writel(reg, regs + DISP_GAMMA_CFG); + lut_base = regs + DISP_GAMMA_LUT; + lut = (struct drm_color_lut *)state->gamma_lut->data; + for (i = 0; i < MTK_LUT_SIZE; i++) { + + if (!lut_diff || (i % 2 == 0)) { + word = (((lut[i].red >> 6) & LUT_10BIT_MASK) << 20) + + (((lut[i].green >> 6) & LUT_10BIT_MASK) << 10) + + ((lut[i].blue >> 6) & LUT_10BIT_MASK); + } else { + diff[0] = (lut[i].red >> 6) - (lut[i - 1].red >> 6); + diff[1] = (lut[i].green >> 6) - (lut[i - 1].green >> 6); + diff[2] = (lut[i].blue >> 6) - (lut[i - 1].blue >> 6); + + word = ((diff[0] & LUT_10BIT_MASK) << 20) + + ((diff[1] & LUT_10BIT_MASK) << 10) + + (diff[2] & LUT_10BIT_MASK); + } + writel(word, (lut_base + i * 4)); + } + } +} + +void mtk_gamma_set(struct device *dev, struct drm_crtc_state *state) +{ + struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); + bool lut_diff = false; + + if (gamma->data) + lut_diff = gamma->data->lut_diff; + + mtk_gamma_set_common(gamma->regs, state, lut_diff); +} + +void mtk_gamma_config(struct device *dev, unsigned int w, + unsigned int h, unsigned int vrefresh, + unsigned int bpc, struct cmdq_pkt *cmdq_pkt) +{ + struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); + + mtk_ddp_write(cmdq_pkt, h << 16 | w, &gamma->cmdq_reg, gamma->regs, + DISP_GAMMA_SIZE); + if (gamma->data && gamma->data->has_dither) + mtk_dither_set_common(gamma->regs, &gamma->cmdq_reg, bpc, + DISP_GAMMA_CFG, GAMMA_DITHERING, cmdq_pkt); +} + +void mtk_gamma_start(struct device *dev) +{ + struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); + + writel(GAMMA_EN, gamma->regs + DISP_GAMMA_EN); +} + +void mtk_gamma_stop(struct device *dev) +{ + struct mtk_disp_gamma *gamma = dev_get_drvdata(dev); + + writel_relaxed(0x0, gamma->regs + DISP_GAMMA_EN); +} + +static int mtk_disp_gamma_bind(struct device *dev, struct device *master, + void *data) +{ + return 0; +} + +static void mtk_disp_gamma_unbind(struct device *dev, struct device *master, + void *data) +{ +} + +static const struct component_ops mtk_disp_gamma_component_ops = { + .bind = mtk_disp_gamma_bind, + .unbind = mtk_disp_gamma_unbind, +}; + +static int mtk_disp_gamma_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mtk_disp_gamma *priv; + struct resource *res; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + dev_err(dev, "failed to get gamma clk\n"); + return PTR_ERR(priv->clk); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->regs)) { + dev_err(dev, "failed to ioremap gamma\n"); + return PTR_ERR(priv->regs); + } + +#if IS_REACHABLE(CONFIG_MTK_CMDQ) + ret = cmdq_dev_get_client_reg(dev, &priv->cmdq_reg, 0); + if (ret) + dev_dbg(dev, "get mediatek,gce-client-reg fail!\n"); +#endif + + priv->data = of_device_get_match_data(dev); + platform_set_drvdata(pdev, priv); + + ret = component_add(dev, &mtk_disp_gamma_component_ops); + if (ret) + dev_err(dev, "Failed to add component: %d\n", ret); + + return ret; +} + +static int mtk_disp_gamma_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &mtk_disp_gamma_component_ops); + + return 0; +} + +static const struct mtk_disp_gamma_data mt8173_gamma_driver_data = { + .has_dither = true, +}; + +static const struct mtk_disp_gamma_data mt8183_gamma_driver_data = { + .lut_diff = true, +}; + +static const struct of_device_id mtk_disp_gamma_driver_dt_match[] = { + { .compatible = "mediatek,mt8173-disp-gamma", + .data = &mt8173_gamma_driver_data}, + { .compatible = "mediatek,mt8183-disp-gamma", + .data = &mt8183_gamma_driver_data}, + {}, +}; +MODULE_DEVICE_TABLE(of, mtk_disp_gamma_driver_dt_match); + +struct platform_driver mtk_disp_gamma_driver = { + .probe = mtk_disp_gamma_probe, + .remove = mtk_disp_gamma_remove, + .driver = { + .name = "mediatek-disp-gamma", + .owner = THIS_MODULE, + .of_match_table = mtk_disp_gamma_driver_dt_match, + }, +}; diff --git a/drivers/gpu/drm/mediatek/mtk_disp_merge.c b/drivers/gpu/drm/mediatek/mtk_disp_merge.c new file mode 100644 index 000000000..6428b6203 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_disp_merge.c @@ -0,0 +1,320 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 MediaTek Inc. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "mtk_drm_ddp_comp.h" +#include "mtk_drm_drv.h" +#include "mtk_disp_drv.h" + +#define DISP_REG_MERGE_CTRL 0x000 +#define MERGE_EN 1 +#define DISP_REG_MERGE_CFG_0 0x010 +#define DISP_REG_MERGE_CFG_1 0x014 +#define DISP_REG_MERGE_CFG_4 0x020 +#define DISP_REG_MERGE_CFG_10 0x038 +/* no swap */ +#define SWAP_MODE 0 +#define FLD_SWAP_MODE GENMASK(4, 0) +#define DISP_REG_MERGE_CFG_12 0x040 +#define CFG_10_10_1PI_2PO_BUF_MODE 6 +#define CFG_10_10_2PI_2PO_BUF_MODE 8 +#define CFG_11_10_1PI_2PO_MERGE 18 +#define FLD_CFG_MERGE_MODE GENMASK(4, 0) +#define DISP_REG_MERGE_CFG_24 0x070 +#define DISP_REG_MERGE_CFG_25 0x074 +#define DISP_REG_MERGE_CFG_26 0x078 +#define DISP_REG_MERGE_CFG_27 0x07c +#define DISP_REG_MERGE_CFG_36 0x0a0 +#define ULTRA_EN BIT(0) +#define PREULTRA_EN BIT(4) +#define DISP_REG_MERGE_CFG_37 0x0a4 +/* 0: Off, 1: SRAM0, 2: SRAM1, 3: SRAM0 + SRAM1 */ +#define BUFFER_MODE 3 +#define FLD_BUFFER_MODE GENMASK(1, 0) +/* + * For the ultra and preultra settings, 6us ~ 9us is experience value + * and the maximum frequency of mmsys clock is 594MHz. + */ +#define DISP_REG_MERGE_CFG_40 0x0b0 +/* 6 us, 594M pixel/sec */ +#define ULTRA_TH_LOW (6 * 594) +/* 8 us, 594M pixel/sec */ +#define ULTRA_TH_HIGH (8 * 594) +#define FLD_ULTRA_TH_LOW GENMASK(15, 0) +#define FLD_ULTRA_TH_HIGH GENMASK(31, 16) +#define DISP_REG_MERGE_CFG_41 0x0b4 +/* 8 us, 594M pixel/sec */ +#define PREULTRA_TH_LOW (8 * 594) +/* 9 us, 594M pixel/sec */ +#define PREULTRA_TH_HIGH (9 * 594) +#define FLD_PREULTRA_TH_LOW GENMASK(15, 0) +#define FLD_PREULTRA_TH_HIGH GENMASK(31, 16) + +#define DISP_REG_MERGE_MUTE_0 0xf00 + +struct mtk_disp_merge { + void __iomem *regs; + struct clk *clk; + struct clk *async_clk; + struct cmdq_client_reg cmdq_reg; + bool fifo_en; + bool mute_support; + struct reset_control *reset_ctl; +}; + +void mtk_merge_start(struct device *dev) +{ + mtk_merge_start_cmdq(dev, NULL); +} + +void mtk_merge_stop(struct device *dev) +{ + mtk_merge_stop_cmdq(dev, NULL); +} + +void mtk_merge_start_cmdq(struct device *dev, struct cmdq_pkt *cmdq_pkt) +{ + struct mtk_disp_merge *priv = dev_get_drvdata(dev); + + if (priv->mute_support) + mtk_ddp_write(cmdq_pkt, 0x0, &priv->cmdq_reg, priv->regs, + DISP_REG_MERGE_MUTE_0); + + mtk_ddp_write(cmdq_pkt, 1, &priv->cmdq_reg, priv->regs, + DISP_REG_MERGE_CTRL); +} + +void mtk_merge_stop_cmdq(struct device *dev, struct cmdq_pkt *cmdq_pkt) +{ + struct mtk_disp_merge *priv = dev_get_drvdata(dev); + + if (priv->mute_support) + mtk_ddp_write(cmdq_pkt, 0x1, &priv->cmdq_reg, priv->regs, + DISP_REG_MERGE_MUTE_0); + + mtk_ddp_write(cmdq_pkt, 0, &priv->cmdq_reg, priv->regs, + DISP_REG_MERGE_CTRL); + + if (priv->async_clk) + reset_control_reset(priv->reset_ctl); +} + +static void mtk_merge_fifo_setting(struct mtk_disp_merge *priv, + struct cmdq_pkt *cmdq_pkt) +{ + mtk_ddp_write(cmdq_pkt, ULTRA_EN | PREULTRA_EN, + &priv->cmdq_reg, priv->regs, DISP_REG_MERGE_CFG_36); + + mtk_ddp_write_mask(cmdq_pkt, BUFFER_MODE, + &priv->cmdq_reg, priv->regs, DISP_REG_MERGE_CFG_37, + FLD_BUFFER_MODE); + + mtk_ddp_write_mask(cmdq_pkt, ULTRA_TH_LOW | ULTRA_TH_HIGH << 16, + &priv->cmdq_reg, priv->regs, DISP_REG_MERGE_CFG_40, + FLD_ULTRA_TH_LOW | FLD_ULTRA_TH_HIGH); + + mtk_ddp_write_mask(cmdq_pkt, PREULTRA_TH_LOW | PREULTRA_TH_HIGH << 16, + &priv->cmdq_reg, priv->regs, DISP_REG_MERGE_CFG_41, + FLD_PREULTRA_TH_LOW | FLD_PREULTRA_TH_HIGH); +} + +void mtk_merge_config(struct device *dev, unsigned int w, + unsigned int h, unsigned int vrefresh, + unsigned int bpc, struct cmdq_pkt *cmdq_pkt) +{ + mtk_merge_advance_config(dev, w, 0, h, vrefresh, bpc, cmdq_pkt); +} + +void mtk_merge_advance_config(struct device *dev, unsigned int l_w, unsigned int r_w, + unsigned int h, unsigned int vrefresh, unsigned int bpc, + struct cmdq_pkt *cmdq_pkt) +{ + struct mtk_disp_merge *priv = dev_get_drvdata(dev); + unsigned int mode = CFG_10_10_1PI_2PO_BUF_MODE; + + if (!h || !l_w) { + dev_err(dev, "%s: input width(%d) or height(%d) is invalid\n", __func__, l_w, h); + return; + } + + if (priv->fifo_en) { + mtk_merge_fifo_setting(priv, cmdq_pkt); + mode = CFG_10_10_2PI_2PO_BUF_MODE; + } + + if (r_w) + mode = CFG_11_10_1PI_2PO_MERGE; + + mtk_ddp_write(cmdq_pkt, h << 16 | l_w, &priv->cmdq_reg, priv->regs, + DISP_REG_MERGE_CFG_0); + mtk_ddp_write(cmdq_pkt, h << 16 | r_w, &priv->cmdq_reg, priv->regs, + DISP_REG_MERGE_CFG_1); + mtk_ddp_write(cmdq_pkt, h << 16 | (l_w + r_w), &priv->cmdq_reg, priv->regs, + DISP_REG_MERGE_CFG_4); + /* + * DISP_REG_MERGE_CFG_24 is merge SRAM0 w/h + * DISP_REG_MERGE_CFG_25 is merge SRAM1 w/h. + * If r_w > 0, the merge is in merge mode (input0 and input1 merge together), + * the input0 goes to SRAM0, and input1 goes to SRAM1. + * If r_w = 0, the merge is in buffer mode, the input goes through SRAM0 and + * then to SRAM1. Both SRAM0 and SRAM1 are set to the same size. + */ + mtk_ddp_write(cmdq_pkt, h << 16 | l_w, &priv->cmdq_reg, priv->regs, + DISP_REG_MERGE_CFG_24); + if (r_w) + mtk_ddp_write(cmdq_pkt, h << 16 | r_w, &priv->cmdq_reg, priv->regs, + DISP_REG_MERGE_CFG_25); + else + mtk_ddp_write(cmdq_pkt, h << 16 | l_w, &priv->cmdq_reg, priv->regs, + DISP_REG_MERGE_CFG_25); + + /* + * DISP_REG_MERGE_CFG_26 and DISP_REG_MERGE_CFG_27 is only used in LR merge. + * Only take effect when the merge is setting to merge mode. + */ + mtk_ddp_write(cmdq_pkt, h << 16 | l_w, &priv->cmdq_reg, priv->regs, + DISP_REG_MERGE_CFG_26); + mtk_ddp_write(cmdq_pkt, h << 16 | r_w, &priv->cmdq_reg, priv->regs, + DISP_REG_MERGE_CFG_27); + + mtk_ddp_write_mask(cmdq_pkt, SWAP_MODE, &priv->cmdq_reg, priv->regs, + DISP_REG_MERGE_CFG_10, FLD_SWAP_MODE); + mtk_ddp_write_mask(cmdq_pkt, mode, &priv->cmdq_reg, priv->regs, + DISP_REG_MERGE_CFG_12, FLD_CFG_MERGE_MODE); +} + +int mtk_merge_clk_enable(struct device *dev) +{ + int ret = 0; + struct mtk_disp_merge *priv = dev_get_drvdata(dev); + + ret = clk_prepare_enable(priv->clk); + if (ret) { + dev_err(dev, "merge clk prepare enable failed\n"); + return ret; + } + + ret = clk_prepare_enable(priv->async_clk); + if (ret) { + /* should clean up the state of priv->clk */ + clk_disable_unprepare(priv->clk); + + dev_err(dev, "async clk prepare enable failed\n"); + return ret; + } + + return ret; +} + +void mtk_merge_clk_disable(struct device *dev) +{ + struct mtk_disp_merge *priv = dev_get_drvdata(dev); + + clk_disable_unprepare(priv->async_clk); + clk_disable_unprepare(priv->clk); +} + +static int mtk_disp_merge_bind(struct device *dev, struct device *master, + void *data) +{ + return 0; +} + +static void mtk_disp_merge_unbind(struct device *dev, struct device *master, + void *data) +{ +} + +static const struct component_ops mtk_disp_merge_component_ops = { + .bind = mtk_disp_merge_bind, + .unbind = mtk_disp_merge_unbind, +}; + +static int mtk_disp_merge_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct mtk_disp_merge *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->regs)) { + dev_err(dev, "failed to ioremap merge\n"); + return PTR_ERR(priv->regs); + } + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + dev_err(dev, "failed to get merge clk\n"); + return PTR_ERR(priv->clk); + } + + priv->async_clk = devm_clk_get_optional(dev, "merge_async"); + if (IS_ERR(priv->async_clk)) { + dev_err(dev, "failed to get merge async clock\n"); + return PTR_ERR(priv->async_clk); + } + + if (priv->async_clk) { + priv->reset_ctl = devm_reset_control_get_optional_exclusive(dev, NULL); + if (IS_ERR(priv->reset_ctl)) + return PTR_ERR(priv->reset_ctl); + } + +#if IS_REACHABLE(CONFIG_MTK_CMDQ) + ret = cmdq_dev_get_client_reg(dev, &priv->cmdq_reg, 0); + if (ret) + dev_dbg(dev, "get mediatek,gce-client-reg fail!\n"); +#endif + + priv->fifo_en = of_property_read_bool(dev->of_node, + "mediatek,merge-fifo-en"); + + priv->mute_support = of_property_read_bool(dev->of_node, + "mediatek,merge-mute"); + platform_set_drvdata(pdev, priv); + + ret = component_add(dev, &mtk_disp_merge_component_ops); + if (ret != 0) + dev_err(dev, "Failed to add component: %d\n", ret); + + return ret; +} + +static int mtk_disp_merge_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &mtk_disp_merge_component_ops); + + return 0; +} + +static const struct of_device_id mtk_disp_merge_driver_dt_match[] = { + { .compatible = "mediatek,mt8195-disp-merge", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, mtk_disp_merge_driver_dt_match); + +struct platform_driver mtk_disp_merge_driver = { + .probe = mtk_disp_merge_probe, + .remove = mtk_disp_merge_remove, + .driver = { + .name = "mediatek-disp-merge", + .owner = THIS_MODULE, + .of_match_table = mtk_disp_merge_driver_dt_match, + }, +}; diff --git a/drivers/gpu/drm/mediatek/mtk_disp_ovl.c b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c new file mode 100644 index 000000000..84daeaffa --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_disp_ovl.c @@ -0,0 +1,573 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015 MediaTek Inc. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mtk_disp_drv.h" +#include "mtk_drm_crtc.h" +#include "mtk_drm_ddp_comp.h" + +#define DISP_REG_OVL_INTEN 0x0004 +#define OVL_FME_CPL_INT BIT(1) +#define DISP_REG_OVL_INTSTA 0x0008 +#define DISP_REG_OVL_EN 0x000c +#define DISP_REG_OVL_RST 0x0014 +#define DISP_REG_OVL_ROI_SIZE 0x0020 +#define DISP_REG_OVL_DATAPATH_CON 0x0024 +#define OVL_LAYER_SMI_ID_EN BIT(0) +#define OVL_BGCLR_SEL_IN BIT(2) +#define OVL_LAYER_AFBC_EN(n) BIT(4+n) +#define DISP_REG_OVL_ROI_BGCLR 0x0028 +#define DISP_REG_OVL_SRC_CON 0x002c +#define DISP_REG_OVL_CON(n) (0x0030 + 0x20 * (n)) +#define DISP_REG_OVL_SRC_SIZE(n) (0x0038 + 0x20 * (n)) +#define DISP_REG_OVL_OFFSET(n) (0x003c + 0x20 * (n)) +#define DISP_REG_OVL_PITCH_MSB(n) (0x0040 + 0x20 * (n)) +#define OVL_PITCH_MSB_2ND_SUBBUF BIT(16) +#define DISP_REG_OVL_PITCH(n) (0x0044 + 0x20 * (n)) +#define DISP_REG_OVL_RDMA_CTRL(n) (0x00c0 + 0x20 * (n)) +#define DISP_REG_OVL_RDMA_GMC(n) (0x00c8 + 0x20 * (n)) +#define DISP_REG_OVL_ADDR_MT2701 0x0040 +#define DISP_REG_OVL_ADDR_MT8173 0x0f40 +#define DISP_REG_OVL_ADDR(ovl, n) ((ovl)->data->addr + 0x20 * (n)) +#define DISP_REG_OVL_HDR_ADDR(ovl, n) ((ovl)->data->addr + 0x20 * (n) + 0x04) +#define DISP_REG_OVL_HDR_PITCH(ovl, n) ((ovl)->data->addr + 0x20 * (n) + 0x08) + +#define GMC_THRESHOLD_BITS 16 +#define GMC_THRESHOLD_HIGH ((1 << GMC_THRESHOLD_BITS) / 4) +#define GMC_THRESHOLD_LOW ((1 << GMC_THRESHOLD_BITS) / 8) + +#define OVL_CON_BYTE_SWAP BIT(24) +#define OVL_CON_MTX_YUV_TO_RGB (6 << 16) +#define OVL_CON_CLRFMT_RGB (1 << 12) +#define OVL_CON_CLRFMT_RGBA8888 (2 << 12) +#define OVL_CON_CLRFMT_ARGB8888 (3 << 12) +#define OVL_CON_CLRFMT_UYVY (4 << 12) +#define OVL_CON_CLRFMT_YUYV (5 << 12) +#define OVL_CON_CLRFMT_RGB565(ovl) ((ovl)->data->fmt_rgb565_is_0 ? \ + 0 : OVL_CON_CLRFMT_RGB) +#define OVL_CON_CLRFMT_RGB888(ovl) ((ovl)->data->fmt_rgb565_is_0 ? \ + OVL_CON_CLRFMT_RGB : 0) +#define OVL_CON_AEN BIT(8) +#define OVL_CON_ALPHA 0xff +#define OVL_CON_VIRT_FLIP BIT(9) +#define OVL_CON_HORZ_FLIP BIT(10) + +struct mtk_disp_ovl_data { + unsigned int addr; + unsigned int gmc_bits; + unsigned int layer_nr; + bool fmt_rgb565_is_0; + bool smi_id_en; + bool supports_afbc; +}; + +/* + * struct mtk_disp_ovl - DISP_OVL driver structure + * @crtc: associated crtc to report vblank events to + * @data: platform data + */ +struct mtk_disp_ovl { + struct drm_crtc *crtc; + struct clk *clk; + void __iomem *regs; + struct cmdq_client_reg cmdq_reg; + const struct mtk_disp_ovl_data *data; + void (*vblank_cb)(void *data); + void *vblank_cb_data; +}; + +static irqreturn_t mtk_disp_ovl_irq_handler(int irq, void *dev_id) +{ + struct mtk_disp_ovl *priv = dev_id; + + /* Clear frame completion interrupt */ + writel(0x0, priv->regs + DISP_REG_OVL_INTSTA); + + if (!priv->vblank_cb) + return IRQ_NONE; + + priv->vblank_cb(priv->vblank_cb_data); + + return IRQ_HANDLED; +} + +void mtk_ovl_register_vblank_cb(struct device *dev, + void (*vblank_cb)(void *), + void *vblank_cb_data) +{ + struct mtk_disp_ovl *ovl = dev_get_drvdata(dev); + + ovl->vblank_cb = vblank_cb; + ovl->vblank_cb_data = vblank_cb_data; +} + +void mtk_ovl_unregister_vblank_cb(struct device *dev) +{ + struct mtk_disp_ovl *ovl = dev_get_drvdata(dev); + + ovl->vblank_cb = NULL; + ovl->vblank_cb_data = NULL; +} + +void mtk_ovl_enable_vblank(struct device *dev) +{ + struct mtk_disp_ovl *ovl = dev_get_drvdata(dev); + + writel(0x0, ovl->regs + DISP_REG_OVL_INTSTA); + writel_relaxed(OVL_FME_CPL_INT, ovl->regs + DISP_REG_OVL_INTEN); +} + +void mtk_ovl_disable_vblank(struct device *dev) +{ + struct mtk_disp_ovl *ovl = dev_get_drvdata(dev); + + writel_relaxed(0x0, ovl->regs + DISP_REG_OVL_INTEN); +} + +int mtk_ovl_clk_enable(struct device *dev) +{ + struct mtk_disp_ovl *ovl = dev_get_drvdata(dev); + + return clk_prepare_enable(ovl->clk); +} + +void mtk_ovl_clk_disable(struct device *dev) +{ + struct mtk_disp_ovl *ovl = dev_get_drvdata(dev); + + clk_disable_unprepare(ovl->clk); +} + +void mtk_ovl_start(struct device *dev) +{ + struct mtk_disp_ovl *ovl = dev_get_drvdata(dev); + + if (ovl->data->smi_id_en) { + unsigned int reg; + + reg = readl(ovl->regs + DISP_REG_OVL_DATAPATH_CON); + reg = reg | OVL_LAYER_SMI_ID_EN; + writel_relaxed(reg, ovl->regs + DISP_REG_OVL_DATAPATH_CON); + } + writel_relaxed(0x1, ovl->regs + DISP_REG_OVL_EN); +} + +void mtk_ovl_stop(struct device *dev) +{ + struct mtk_disp_ovl *ovl = dev_get_drvdata(dev); + + writel_relaxed(0x0, ovl->regs + DISP_REG_OVL_EN); + if (ovl->data->smi_id_en) { + unsigned int reg; + + reg = readl(ovl->regs + DISP_REG_OVL_DATAPATH_CON); + reg = reg & ~OVL_LAYER_SMI_ID_EN; + writel_relaxed(reg, ovl->regs + DISP_REG_OVL_DATAPATH_CON); + } +} + +static void mtk_ovl_set_afbc(struct mtk_disp_ovl *ovl, struct cmdq_pkt *cmdq_pkt, + int idx, bool enabled) +{ + mtk_ddp_write_mask(cmdq_pkt, enabled ? OVL_LAYER_AFBC_EN(idx) : 0, + &ovl->cmdq_reg, ovl->regs, + DISP_REG_OVL_DATAPATH_CON, OVL_LAYER_AFBC_EN(idx)); +} + +void mtk_ovl_config(struct device *dev, unsigned int w, + unsigned int h, unsigned int vrefresh, + unsigned int bpc, struct cmdq_pkt *cmdq_pkt) +{ + struct mtk_disp_ovl *ovl = dev_get_drvdata(dev); + + if (w != 0 && h != 0) + mtk_ddp_write_relaxed(cmdq_pkt, h << 16 | w, &ovl->cmdq_reg, ovl->regs, + DISP_REG_OVL_ROI_SIZE); + mtk_ddp_write_relaxed(cmdq_pkt, 0x0, &ovl->cmdq_reg, ovl->regs, DISP_REG_OVL_ROI_BGCLR); + + mtk_ddp_write(cmdq_pkt, 0x1, &ovl->cmdq_reg, ovl->regs, DISP_REG_OVL_RST); + mtk_ddp_write(cmdq_pkt, 0x0, &ovl->cmdq_reg, ovl->regs, DISP_REG_OVL_RST); +} + +unsigned int mtk_ovl_layer_nr(struct device *dev) +{ + struct mtk_disp_ovl *ovl = dev_get_drvdata(dev); + + return ovl->data->layer_nr; +} + +unsigned int mtk_ovl_supported_rotations(struct device *dev) +{ + return DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180 | + DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y; +} + +int mtk_ovl_layer_check(struct device *dev, unsigned int idx, + struct mtk_plane_state *mtk_state) +{ + struct drm_plane_state *state = &mtk_state->base; + unsigned int rotation = 0; + + rotation = drm_rotation_simplify(state->rotation, + DRM_MODE_ROTATE_0 | + DRM_MODE_REFLECT_X | + DRM_MODE_REFLECT_Y); + rotation &= ~DRM_MODE_ROTATE_0; + + /* We can only do reflection, not rotation */ + if ((rotation & DRM_MODE_ROTATE_MASK) != 0) + return -EINVAL; + + /* + * TODO: Rotating/reflecting YUV buffers is not supported at this time. + * Only RGB[AX] variants are supported. + */ + if (state->fb->format->is_yuv && rotation != 0) + return -EINVAL; + + state->rotation = rotation; + + return 0; +} + +void mtk_ovl_layer_on(struct device *dev, unsigned int idx, + struct cmdq_pkt *cmdq_pkt) +{ + unsigned int gmc_thrshd_l; + unsigned int gmc_thrshd_h; + unsigned int gmc_value; + struct mtk_disp_ovl *ovl = dev_get_drvdata(dev); + + mtk_ddp_write(cmdq_pkt, 0x1, &ovl->cmdq_reg, ovl->regs, + DISP_REG_OVL_RDMA_CTRL(idx)); + gmc_thrshd_l = GMC_THRESHOLD_LOW >> + (GMC_THRESHOLD_BITS - ovl->data->gmc_bits); + gmc_thrshd_h = GMC_THRESHOLD_HIGH >> + (GMC_THRESHOLD_BITS - ovl->data->gmc_bits); + if (ovl->data->gmc_bits == 10) + gmc_value = gmc_thrshd_h | gmc_thrshd_h << 16; + else + gmc_value = gmc_thrshd_l | gmc_thrshd_l << 8 | + gmc_thrshd_h << 16 | gmc_thrshd_h << 24; + mtk_ddp_write(cmdq_pkt, gmc_value, + &ovl->cmdq_reg, ovl->regs, DISP_REG_OVL_RDMA_GMC(idx)); + mtk_ddp_write_mask(cmdq_pkt, BIT(idx), &ovl->cmdq_reg, ovl->regs, + DISP_REG_OVL_SRC_CON, BIT(idx)); +} + +void mtk_ovl_layer_off(struct device *dev, unsigned int idx, + struct cmdq_pkt *cmdq_pkt) +{ + struct mtk_disp_ovl *ovl = dev_get_drvdata(dev); + + mtk_ddp_write_mask(cmdq_pkt, 0, &ovl->cmdq_reg, ovl->regs, + DISP_REG_OVL_SRC_CON, BIT(idx)); + mtk_ddp_write(cmdq_pkt, 0, &ovl->cmdq_reg, ovl->regs, + DISP_REG_OVL_RDMA_CTRL(idx)); +} + +static unsigned int ovl_fmt_convert(struct mtk_disp_ovl *ovl, unsigned int fmt) +{ + /* The return value in switch "MEM_MODE_INPUT_FORMAT_XXX" + * is defined in mediatek HW data sheet. + * The alphabet order in XXX is no relation to data + * arrangement in memory. + */ + switch (fmt) { + default: + case DRM_FORMAT_RGB565: + return OVL_CON_CLRFMT_RGB565(ovl); + case DRM_FORMAT_BGR565: + return OVL_CON_CLRFMT_RGB565(ovl) | OVL_CON_BYTE_SWAP; + case DRM_FORMAT_RGB888: + return OVL_CON_CLRFMT_RGB888(ovl); + case DRM_FORMAT_BGR888: + return OVL_CON_CLRFMT_RGB888(ovl) | OVL_CON_BYTE_SWAP; + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_RGBA8888: + return OVL_CON_CLRFMT_ARGB8888; + case DRM_FORMAT_BGRX8888: + case DRM_FORMAT_BGRA8888: + return OVL_CON_CLRFMT_ARGB8888 | OVL_CON_BYTE_SWAP; + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + return OVL_CON_CLRFMT_RGBA8888; + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + return OVL_CON_CLRFMT_RGBA8888 | OVL_CON_BYTE_SWAP; + case DRM_FORMAT_UYVY: + return OVL_CON_CLRFMT_UYVY | OVL_CON_MTX_YUV_TO_RGB; + case DRM_FORMAT_YUYV: + return OVL_CON_CLRFMT_YUYV | OVL_CON_MTX_YUV_TO_RGB; + } +} + +void mtk_ovl_layer_config(struct device *dev, unsigned int idx, + struct mtk_plane_state *state, + struct cmdq_pkt *cmdq_pkt) +{ + struct mtk_disp_ovl *ovl = dev_get_drvdata(dev); + struct mtk_plane_pending_state *pending = &state->pending; + unsigned int addr = pending->addr; + unsigned int hdr_addr = pending->hdr_addr; + unsigned int pitch = pending->pitch; + unsigned int hdr_pitch = pending->hdr_pitch; + unsigned int fmt = pending->format; + unsigned int offset = (pending->y << 16) | pending->x; + unsigned int src_size = (pending->height << 16) | pending->width; + unsigned int con; + bool is_afbc = pending->modifier != DRM_FORMAT_MOD_LINEAR; + union overlay_pitch { + struct split_pitch { + u16 lsb; + u16 msb; + } split_pitch; + u32 pitch; + } overlay_pitch; + + overlay_pitch.pitch = pitch; + + if (!pending->enable) { + mtk_ovl_layer_off(dev, idx, cmdq_pkt); + return; + } + + con = ovl_fmt_convert(ovl, fmt); + if (state->base.fb && state->base.fb->format->has_alpha) + con |= OVL_CON_AEN | OVL_CON_ALPHA; + + if (pending->rotation & DRM_MODE_REFLECT_Y) { + con |= OVL_CON_VIRT_FLIP; + addr += (pending->height - 1) * pending->pitch; + } + + if (pending->rotation & DRM_MODE_REFLECT_X) { + con |= OVL_CON_HORZ_FLIP; + addr += pending->pitch - 1; + } + + if (ovl->data->supports_afbc) + mtk_ovl_set_afbc(ovl, cmdq_pkt, idx, is_afbc); + + mtk_ddp_write_relaxed(cmdq_pkt, con, &ovl->cmdq_reg, ovl->regs, + DISP_REG_OVL_CON(idx)); + mtk_ddp_write_relaxed(cmdq_pkt, overlay_pitch.split_pitch.lsb, &ovl->cmdq_reg, ovl->regs, + DISP_REG_OVL_PITCH(idx)); + mtk_ddp_write_relaxed(cmdq_pkt, src_size, &ovl->cmdq_reg, ovl->regs, + DISP_REG_OVL_SRC_SIZE(idx)); + mtk_ddp_write_relaxed(cmdq_pkt, offset, &ovl->cmdq_reg, ovl->regs, + DISP_REG_OVL_OFFSET(idx)); + mtk_ddp_write_relaxed(cmdq_pkt, addr, &ovl->cmdq_reg, ovl->regs, + DISP_REG_OVL_ADDR(ovl, idx)); + + if (is_afbc) { + mtk_ddp_write_relaxed(cmdq_pkt, hdr_addr, &ovl->cmdq_reg, ovl->regs, + DISP_REG_OVL_HDR_ADDR(ovl, idx)); + mtk_ddp_write_relaxed(cmdq_pkt, + OVL_PITCH_MSB_2ND_SUBBUF | overlay_pitch.split_pitch.msb, + &ovl->cmdq_reg, ovl->regs, DISP_REG_OVL_PITCH_MSB(idx)); + mtk_ddp_write_relaxed(cmdq_pkt, hdr_pitch, &ovl->cmdq_reg, ovl->regs, + DISP_REG_OVL_HDR_PITCH(ovl, idx)); + } else { + mtk_ddp_write_relaxed(cmdq_pkt, + overlay_pitch.split_pitch.msb, + &ovl->cmdq_reg, ovl->regs, DISP_REG_OVL_PITCH_MSB(idx)); + } + + mtk_ovl_layer_on(dev, idx, cmdq_pkt); +} + +void mtk_ovl_bgclr_in_on(struct device *dev) +{ + struct mtk_disp_ovl *ovl = dev_get_drvdata(dev); + unsigned int reg; + + reg = readl(ovl->regs + DISP_REG_OVL_DATAPATH_CON); + reg = reg | OVL_BGCLR_SEL_IN; + writel(reg, ovl->regs + DISP_REG_OVL_DATAPATH_CON); +} + +void mtk_ovl_bgclr_in_off(struct device *dev) +{ + struct mtk_disp_ovl *ovl = dev_get_drvdata(dev); + unsigned int reg; + + reg = readl(ovl->regs + DISP_REG_OVL_DATAPATH_CON); + reg = reg & ~OVL_BGCLR_SEL_IN; + writel(reg, ovl->regs + DISP_REG_OVL_DATAPATH_CON); +} + +static int mtk_disp_ovl_bind(struct device *dev, struct device *master, + void *data) +{ + return 0; +} + +static void mtk_disp_ovl_unbind(struct device *dev, struct device *master, + void *data) +{ +} + +static const struct component_ops mtk_disp_ovl_component_ops = { + .bind = mtk_disp_ovl_bind, + .unbind = mtk_disp_ovl_unbind, +}; + +static int mtk_disp_ovl_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mtk_disp_ovl *priv; + struct resource *res; + int irq; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + dev_err(dev, "failed to get ovl clk\n"); + return PTR_ERR(priv->clk); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->regs)) { + dev_err(dev, "failed to ioremap ovl\n"); + return PTR_ERR(priv->regs); + } +#if IS_REACHABLE(CONFIG_MTK_CMDQ) + ret = cmdq_dev_get_client_reg(dev, &priv->cmdq_reg, 0); + if (ret) + dev_dbg(dev, "get mediatek,gce-client-reg fail!\n"); +#endif + + priv->data = of_device_get_match_data(dev); + platform_set_drvdata(pdev, priv); + + ret = devm_request_irq(dev, irq, mtk_disp_ovl_irq_handler, + IRQF_TRIGGER_NONE, dev_name(dev), priv); + if (ret < 0) { + dev_err(dev, "Failed to request irq %d: %d\n", irq, ret); + return ret; + } + + pm_runtime_enable(dev); + + ret = component_add(dev, &mtk_disp_ovl_component_ops); + if (ret) { + pm_runtime_disable(dev); + dev_err(dev, "Failed to add component: %d\n", ret); + } + + return ret; +} + +static int mtk_disp_ovl_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &mtk_disp_ovl_component_ops); + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct mtk_disp_ovl_data mt2701_ovl_driver_data = { + .addr = DISP_REG_OVL_ADDR_MT2701, + .gmc_bits = 8, + .layer_nr = 4, + .fmt_rgb565_is_0 = false, +}; + +static const struct mtk_disp_ovl_data mt8173_ovl_driver_data = { + .addr = DISP_REG_OVL_ADDR_MT8173, + .gmc_bits = 8, + .layer_nr = 4, + .fmt_rgb565_is_0 = true, +}; + +static const struct mtk_disp_ovl_data mt8183_ovl_driver_data = { + .addr = DISP_REG_OVL_ADDR_MT8173, + .gmc_bits = 10, + .layer_nr = 4, + .fmt_rgb565_is_0 = true, +}; + +static const struct mtk_disp_ovl_data mt8183_ovl_2l_driver_data = { + .addr = DISP_REG_OVL_ADDR_MT8173, + .gmc_bits = 10, + .layer_nr = 2, + .fmt_rgb565_is_0 = true, +}; + +static const struct mtk_disp_ovl_data mt8192_ovl_driver_data = { + .addr = DISP_REG_OVL_ADDR_MT8173, + .gmc_bits = 10, + .layer_nr = 4, + .fmt_rgb565_is_0 = true, + .smi_id_en = true, +}; + +static const struct mtk_disp_ovl_data mt8192_ovl_2l_driver_data = { + .addr = DISP_REG_OVL_ADDR_MT8173, + .gmc_bits = 10, + .layer_nr = 2, + .fmt_rgb565_is_0 = true, + .smi_id_en = true, +}; + +static const struct mtk_disp_ovl_data mt8195_ovl_driver_data = { + .addr = DISP_REG_OVL_ADDR_MT8173, + .gmc_bits = 10, + .layer_nr = 4, + .fmt_rgb565_is_0 = true, + .smi_id_en = true, + .supports_afbc = true, +}; + +static const struct of_device_id mtk_disp_ovl_driver_dt_match[] = { + { .compatible = "mediatek,mt2701-disp-ovl", + .data = &mt2701_ovl_driver_data}, + { .compatible = "mediatek,mt8173-disp-ovl", + .data = &mt8173_ovl_driver_data}, + { .compatible = "mediatek,mt8183-disp-ovl", + .data = &mt8183_ovl_driver_data}, + { .compatible = "mediatek,mt8183-disp-ovl-2l", + .data = &mt8183_ovl_2l_driver_data}, + { .compatible = "mediatek,mt8192-disp-ovl", + .data = &mt8192_ovl_driver_data}, + { .compatible = "mediatek,mt8192-disp-ovl-2l", + .data = &mt8192_ovl_2l_driver_data}, + { .compatible = "mediatek,mt8195-disp-ovl", + .data = &mt8195_ovl_driver_data}, + {}, +}; +MODULE_DEVICE_TABLE(of, mtk_disp_ovl_driver_dt_match); + +struct platform_driver mtk_disp_ovl_driver = { + .probe = mtk_disp_ovl_probe, + .remove = mtk_disp_ovl_remove, + .driver = { + .name = "mediatek-disp-ovl", + .owner = THIS_MODULE, + .of_match_table = mtk_disp_ovl_driver_dt_match, + }, +}; diff --git a/drivers/gpu/drm/mediatek/mtk_disp_rdma.c b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c new file mode 100644 index 000000000..0ec2e4049 --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_disp_rdma.c @@ -0,0 +1,398 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015 MediaTek Inc. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mtk_disp_drv.h" +#include "mtk_drm_crtc.h" +#include "mtk_drm_ddp_comp.h" + +#define DISP_REG_RDMA_INT_ENABLE 0x0000 +#define DISP_REG_RDMA_INT_STATUS 0x0004 +#define RDMA_TARGET_LINE_INT BIT(5) +#define RDMA_FIFO_UNDERFLOW_INT BIT(4) +#define RDMA_EOF_ABNORMAL_INT BIT(3) +#define RDMA_FRAME_END_INT BIT(2) +#define RDMA_FRAME_START_INT BIT(1) +#define RDMA_REG_UPDATE_INT BIT(0) +#define DISP_REG_RDMA_GLOBAL_CON 0x0010 +#define RDMA_ENGINE_EN BIT(0) +#define RDMA_MODE_MEMORY BIT(1) +#define DISP_REG_RDMA_SIZE_CON_0 0x0014 +#define RDMA_MATRIX_ENABLE BIT(17) +#define RDMA_MATRIX_INT_MTX_SEL GENMASK(23, 20) +#define RDMA_MATRIX_INT_MTX_BT601_to_RGB (6 << 20) +#define DISP_REG_RDMA_SIZE_CON_1 0x0018 +#define DISP_REG_RDMA_TARGET_LINE 0x001c +#define DISP_RDMA_MEM_CON 0x0024 +#define MEM_MODE_INPUT_FORMAT_RGB565 (0x000 << 4) +#define MEM_MODE_INPUT_FORMAT_RGB888 (0x001 << 4) +#define MEM_MODE_INPUT_FORMAT_RGBA8888 (0x002 << 4) +#define MEM_MODE_INPUT_FORMAT_ARGB8888 (0x003 << 4) +#define MEM_MODE_INPUT_FORMAT_UYVY (0x004 << 4) +#define MEM_MODE_INPUT_FORMAT_YUYV (0x005 << 4) +#define MEM_MODE_INPUT_SWAP BIT(8) +#define DISP_RDMA_MEM_SRC_PITCH 0x002c +#define DISP_RDMA_MEM_GMC_SETTING_0 0x0030 +#define DISP_REG_RDMA_FIFO_CON 0x0040 +#define RDMA_FIFO_UNDERFLOW_EN BIT(31) +#define RDMA_FIFO_PSEUDO_SIZE(bytes) (((bytes) / 16) << 16) +#define RDMA_OUTPUT_VALID_FIFO_THRESHOLD(bytes) ((bytes) / 16) +#define RDMA_FIFO_SIZE(rdma) ((rdma)->data->fifo_size) +#define DISP_RDMA_MEM_START_ADDR 0x0f00 + +#define RDMA_MEM_GMC 0x40402020 + +struct mtk_disp_rdma_data { + unsigned int fifo_size; +}; + +/* + * struct mtk_disp_rdma - DISP_RDMA driver structure + * @data: local driver data + */ +struct mtk_disp_rdma { + struct clk *clk; + void __iomem *regs; + struct cmdq_client_reg cmdq_reg; + const struct mtk_disp_rdma_data *data; + void (*vblank_cb)(void *data); + void *vblank_cb_data; + u32 fifo_size; +}; + +static irqreturn_t mtk_disp_rdma_irq_handler(int irq, void *dev_id) +{ + struct mtk_disp_rdma *priv = dev_id; + + /* Clear frame completion interrupt */ + writel(0x0, priv->regs + DISP_REG_RDMA_INT_STATUS); + + if (!priv->vblank_cb) + return IRQ_NONE; + + priv->vblank_cb(priv->vblank_cb_data); + + return IRQ_HANDLED; +} + +static void rdma_update_bits(struct device *dev, unsigned int reg, + unsigned int mask, unsigned int val) +{ + struct mtk_disp_rdma *rdma = dev_get_drvdata(dev); + unsigned int tmp = readl(rdma->regs + reg); + + tmp = (tmp & ~mask) | (val & mask); + writel(tmp, rdma->regs + reg); +} + +void mtk_rdma_register_vblank_cb(struct device *dev, + void (*vblank_cb)(void *), + void *vblank_cb_data) +{ + struct mtk_disp_rdma *rdma = dev_get_drvdata(dev); + + rdma->vblank_cb = vblank_cb; + rdma->vblank_cb_data = vblank_cb_data; +} + +void mtk_rdma_unregister_vblank_cb(struct device *dev) +{ + struct mtk_disp_rdma *rdma = dev_get_drvdata(dev); + + rdma->vblank_cb = NULL; + rdma->vblank_cb_data = NULL; +} + +void mtk_rdma_enable_vblank(struct device *dev) +{ + rdma_update_bits(dev, DISP_REG_RDMA_INT_ENABLE, RDMA_FRAME_END_INT, + RDMA_FRAME_END_INT); +} + +void mtk_rdma_disable_vblank(struct device *dev) +{ + rdma_update_bits(dev, DISP_REG_RDMA_INT_ENABLE, RDMA_FRAME_END_INT, 0); +} + +int mtk_rdma_clk_enable(struct device *dev) +{ + struct mtk_disp_rdma *rdma = dev_get_drvdata(dev); + + return clk_prepare_enable(rdma->clk); +} + +void mtk_rdma_clk_disable(struct device *dev) +{ + struct mtk_disp_rdma *rdma = dev_get_drvdata(dev); + + clk_disable_unprepare(rdma->clk); +} + +void mtk_rdma_start(struct device *dev) +{ + rdma_update_bits(dev, DISP_REG_RDMA_GLOBAL_CON, RDMA_ENGINE_EN, + RDMA_ENGINE_EN); +} + +void mtk_rdma_stop(struct device *dev) +{ + rdma_update_bits(dev, DISP_REG_RDMA_GLOBAL_CON, RDMA_ENGINE_EN, 0); +} + +void mtk_rdma_config(struct device *dev, unsigned int width, + unsigned int height, unsigned int vrefresh, + unsigned int bpc, struct cmdq_pkt *cmdq_pkt) +{ + unsigned int threshold; + unsigned int reg; + struct mtk_disp_rdma *rdma = dev_get_drvdata(dev); + u32 rdma_fifo_size; + + mtk_ddp_write_mask(cmdq_pkt, width, &rdma->cmdq_reg, rdma->regs, + DISP_REG_RDMA_SIZE_CON_0, 0xfff); + mtk_ddp_write_mask(cmdq_pkt, height, &rdma->cmdq_reg, rdma->regs, + DISP_REG_RDMA_SIZE_CON_1, 0xfffff); + + if (rdma->fifo_size) + rdma_fifo_size = rdma->fifo_size; + else + rdma_fifo_size = RDMA_FIFO_SIZE(rdma); + + /* + * Enable FIFO underflow since DSI and DPI can't be blocked. + * Keep the FIFO pseudo size reset default of 8 KiB. Set the + * output threshold to 70% of max fifo size to make sure the + * threhold will not overflow + */ + threshold = rdma_fifo_size * 7 / 10; + reg = RDMA_FIFO_UNDERFLOW_EN | + RDMA_FIFO_PSEUDO_SIZE(rdma_fifo_size) | + RDMA_OUTPUT_VALID_FIFO_THRESHOLD(threshold); + mtk_ddp_write(cmdq_pkt, reg, &rdma->cmdq_reg, rdma->regs, DISP_REG_RDMA_FIFO_CON); +} + +static unsigned int rdma_fmt_convert(struct mtk_disp_rdma *rdma, + unsigned int fmt) +{ + /* The return value in switch "MEM_MODE_INPUT_FORMAT_XXX" + * is defined in mediatek HW data sheet. + * The alphabet order in XXX is no relation to data + * arrangement in memory. + */ + switch (fmt) { + default: + case DRM_FORMAT_RGB565: + return MEM_MODE_INPUT_FORMAT_RGB565; + case DRM_FORMAT_BGR565: + return MEM_MODE_INPUT_FORMAT_RGB565 | MEM_MODE_INPUT_SWAP; + case DRM_FORMAT_RGB888: + return MEM_MODE_INPUT_FORMAT_RGB888; + case DRM_FORMAT_BGR888: + return MEM_MODE_INPUT_FORMAT_RGB888 | MEM_MODE_INPUT_SWAP; + case DRM_FORMAT_RGBX8888: + case DRM_FORMAT_RGBA8888: + return MEM_MODE_INPUT_FORMAT_ARGB8888; + case DRM_FORMAT_BGRX8888: + case DRM_FORMAT_BGRA8888: + return MEM_MODE_INPUT_FORMAT_ARGB8888 | MEM_MODE_INPUT_SWAP; + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_ARGB8888: + return MEM_MODE_INPUT_FORMAT_RGBA8888; + case DRM_FORMAT_XBGR8888: + case DRM_FORMAT_ABGR8888: + return MEM_MODE_INPUT_FORMAT_RGBA8888 | MEM_MODE_INPUT_SWAP; + case DRM_FORMAT_UYVY: + return MEM_MODE_INPUT_FORMAT_UYVY; + case DRM_FORMAT_YUYV: + return MEM_MODE_INPUT_FORMAT_YUYV; + } +} + +unsigned int mtk_rdma_layer_nr(struct device *dev) +{ + return 1; +} + +void mtk_rdma_layer_config(struct device *dev, unsigned int idx, + struct mtk_plane_state *state, + struct cmdq_pkt *cmdq_pkt) +{ + struct mtk_disp_rdma *rdma = dev_get_drvdata(dev); + struct mtk_plane_pending_state *pending = &state->pending; + unsigned int addr = pending->addr; + unsigned int pitch = pending->pitch & 0xffff; + unsigned int fmt = pending->format; + unsigned int con; + + con = rdma_fmt_convert(rdma, fmt); + mtk_ddp_write_relaxed(cmdq_pkt, con, &rdma->cmdq_reg, rdma->regs, DISP_RDMA_MEM_CON); + + if (fmt == DRM_FORMAT_UYVY || fmt == DRM_FORMAT_YUYV) { + mtk_ddp_write_mask(cmdq_pkt, RDMA_MATRIX_ENABLE, &rdma->cmdq_reg, rdma->regs, + DISP_REG_RDMA_SIZE_CON_0, + RDMA_MATRIX_ENABLE); + mtk_ddp_write_mask(cmdq_pkt, RDMA_MATRIX_INT_MTX_BT601_to_RGB, + &rdma->cmdq_reg, rdma->regs, DISP_REG_RDMA_SIZE_CON_0, + RDMA_MATRIX_INT_MTX_SEL); + } else { + mtk_ddp_write_mask(cmdq_pkt, 0, &rdma->cmdq_reg, rdma->regs, + DISP_REG_RDMA_SIZE_CON_0, + RDMA_MATRIX_ENABLE); + } + mtk_ddp_write_relaxed(cmdq_pkt, addr, &rdma->cmdq_reg, rdma->regs, + DISP_RDMA_MEM_START_ADDR); + mtk_ddp_write_relaxed(cmdq_pkt, pitch, &rdma->cmdq_reg, rdma->regs, + DISP_RDMA_MEM_SRC_PITCH); + mtk_ddp_write(cmdq_pkt, RDMA_MEM_GMC, &rdma->cmdq_reg, rdma->regs, + DISP_RDMA_MEM_GMC_SETTING_0); + mtk_ddp_write_mask(cmdq_pkt, RDMA_MODE_MEMORY, &rdma->cmdq_reg, rdma->regs, + DISP_REG_RDMA_GLOBAL_CON, RDMA_MODE_MEMORY); + +} + +static int mtk_disp_rdma_bind(struct device *dev, struct device *master, + void *data) +{ + return 0; + +} + +static void mtk_disp_rdma_unbind(struct device *dev, struct device *master, + void *data) +{ +} + +static const struct component_ops mtk_disp_rdma_component_ops = { + .bind = mtk_disp_rdma_bind, + .unbind = mtk_disp_rdma_unbind, +}; + +static int mtk_disp_rdma_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mtk_disp_rdma *priv; + struct resource *res; + int irq; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + dev_err(dev, "failed to get rdma clk\n"); + return PTR_ERR(priv->clk); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->regs)) { + dev_err(dev, "failed to ioremap rdma\n"); + return PTR_ERR(priv->regs); + } +#if IS_REACHABLE(CONFIG_MTK_CMDQ) + ret = cmdq_dev_get_client_reg(dev, &priv->cmdq_reg, 0); + if (ret) + dev_dbg(dev, "get mediatek,gce-client-reg fail!\n"); +#endif + + if (of_find_property(dev->of_node, "mediatek,rdma-fifo-size", &ret)) { + ret = of_property_read_u32(dev->of_node, + "mediatek,rdma-fifo-size", + &priv->fifo_size); + if (ret) { + dev_err(dev, "Failed to get rdma fifo size\n"); + return ret; + } + } + + /* Disable and clear pending interrupts */ + writel(0x0, priv->regs + DISP_REG_RDMA_INT_ENABLE); + writel(0x0, priv->regs + DISP_REG_RDMA_INT_STATUS); + + ret = devm_request_irq(dev, irq, mtk_disp_rdma_irq_handler, + IRQF_TRIGGER_NONE, dev_name(dev), priv); + if (ret < 0) { + dev_err(dev, "Failed to request irq %d: %d\n", irq, ret); + return ret; + } + + priv->data = of_device_get_match_data(dev); + + platform_set_drvdata(pdev, priv); + + pm_runtime_enable(dev); + + ret = component_add(dev, &mtk_disp_rdma_component_ops); + if (ret) { + pm_runtime_disable(dev); + dev_err(dev, "Failed to add component: %d\n", ret); + } + + return ret; +} + +static int mtk_disp_rdma_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &mtk_disp_rdma_component_ops); + + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct mtk_disp_rdma_data mt2701_rdma_driver_data = { + .fifo_size = SZ_4K, +}; + +static const struct mtk_disp_rdma_data mt8173_rdma_driver_data = { + .fifo_size = SZ_8K, +}; + +static const struct mtk_disp_rdma_data mt8183_rdma_driver_data = { + .fifo_size = 5 * SZ_1K, +}; + +static const struct mtk_disp_rdma_data mt8195_rdma_driver_data = { + .fifo_size = 1920, +}; + +static const struct of_device_id mtk_disp_rdma_driver_dt_match[] = { + { .compatible = "mediatek,mt2701-disp-rdma", + .data = &mt2701_rdma_driver_data}, + { .compatible = "mediatek,mt8173-disp-rdma", + .data = &mt8173_rdma_driver_data}, + { .compatible = "mediatek,mt8183-disp-rdma", + .data = &mt8183_rdma_driver_data}, + { .compatible = "mediatek,mt8195-disp-rdma", + .data = &mt8195_rdma_driver_data}, + {}, +}; +MODULE_DEVICE_TABLE(of, mtk_disp_rdma_driver_dt_match); + +struct platform_driver mtk_disp_rdma_driver = { + .probe = mtk_disp_rdma_probe, + .remove = mtk_disp_rdma_remove, + .driver = { + .name = "mediatek-disp-rdma", + .owner = THIS_MODULE, + .of_match_table = mtk_disp_rdma_driver_dt_match, + }, +}; diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c new file mode 100644 index 000000000..9d085c05c --- /dev/null +++ b/drivers/gpu/drm/mediatek/mtk_dp.c @@ -0,0 +1,2663 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019-2022 MediaTek Inc. + * Copyright (c) 2022 BayLibre + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include