diff options
| author | 2023-02-21 18:24:12 -0800 | |
|---|---|---|
| committer | 2023-02-21 18:24:12 -0800 | |
| commit | 5b7c4cabbb65f5c469464da6c5f614cbd7f730f2 (patch) | |
| tree | cc5c2d0a898769fd59549594fedb3ee6f84e59a0 /drivers/media/cec/platform/tegra | |
| 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/media/cec/platform/tegra')
| -rw-r--r-- | drivers/media/cec/platform/tegra/Makefile | 2 | ||||
| -rw-r--r-- | drivers/media/cec/platform/tegra/tegra_cec.c | 483 | ||||
| -rw-r--r-- | drivers/media/cec/platform/tegra/tegra_cec.h | 116 |
3 files changed, 601 insertions, 0 deletions
diff --git a/drivers/media/cec/platform/tegra/Makefile b/drivers/media/cec/platform/tegra/Makefile new file mode 100644 index 000000000..275d1c019 --- /dev/null +++ b/drivers/media/cec/platform/tegra/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_CEC_TEGRA) += tegra_cec.o diff --git a/drivers/media/cec/platform/tegra/tegra_cec.c b/drivers/media/cec/platform/tegra/tegra_cec.c new file mode 100644 index 000000000..5e907395c --- /dev/null +++ b/drivers/media/cec/platform/tegra/tegra_cec.c @@ -0,0 +1,483 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Tegra CEC implementation + * + * The original 3.10 CEC driver using a custom API: + * + * Copyright (c) 2012-2015, NVIDIA CORPORATION. All rights reserved. + * + * Conversion to the CEC framework and to the mainline kernel: + * + * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/clk/tegra.h> + +#include <media/cec-notifier.h> + +#include "tegra_cec.h" + +#define TEGRA_CEC_NAME "tegra-cec" + +struct tegra_cec { + struct cec_adapter *adap; + struct device *dev; + struct clk *clk; + void __iomem *cec_base; + struct cec_notifier *notifier; + int tegra_cec_irq; + bool rx_done; + bool tx_done; + int tx_status; + u8 rx_buf[CEC_MAX_MSG_SIZE]; + u8 rx_buf_cnt; + u32 tx_buf[CEC_MAX_MSG_SIZE]; + u8 tx_buf_cur; + u8 tx_buf_cnt; +}; + +static inline u32 cec_read(struct tegra_cec *cec, u32 reg) +{ + return readl(cec->cec_base + reg); +} + +static inline void cec_write(struct tegra_cec *cec, u32 reg, u32 val) +{ + writel(val, cec->cec_base + reg); +} + +static void tegra_cec_error_recovery(struct tegra_cec *cec) +{ + u32 hw_ctrl; + + hw_ctrl = cec_read(cec, TEGRA_CEC_HW_CONTROL); + cec_write(cec, TEGRA_CEC_HW_CONTROL, 0); + cec_write(cec, TEGRA_CEC_INT_STAT, 0xffffffff); + cec_write(cec, TEGRA_CEC_HW_CONTROL, hw_ctrl); +} + +static irqreturn_t tegra_cec_irq_thread_handler(int irq, void *data) +{ + struct device *dev = data; + struct tegra_cec *cec = dev_get_drvdata(dev); + + if (cec->tx_done) { + cec_transmit_attempt_done(cec->adap, cec->tx_status); + cec->tx_done = false; + } + if (cec->rx_done) { + struct cec_msg msg = {}; + + msg.len = cec->rx_buf_cnt; + memcpy(msg.msg, cec->rx_buf, msg.len); + cec_received_msg(cec->adap, &msg); + cec->rx_done = false; + cec->rx_buf_cnt = 0; + } + return IRQ_HANDLED; +} + +static irqreturn_t tegra_cec_irq_handler(int irq, void *data) +{ + struct device *dev = data; + struct tegra_cec *cec = dev_get_drvdata(dev); + u32 status, mask; + + status = cec_read(cec, TEGRA_CEC_INT_STAT); + mask = cec_read(cec, TEGRA_CEC_INT_MASK); + + status &= mask; + + if (!status) + return IRQ_HANDLED; + + if (status & TEGRA_CEC_INT_STAT_TX_REGISTER_UNDERRUN) { + dev_err(dev, "TX underrun, interrupt timing issue!\n"); + + tegra_cec_error_recovery(cec); + cec_write(cec, TEGRA_CEC_INT_MASK, + mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY); + + cec->tx_done = true; + cec->tx_status = CEC_TX_STATUS_ERROR; + return IRQ_WAKE_THREAD; + } + + if ((status & TEGRA_CEC_INT_STAT_TX_ARBITRATION_FAILED) || + (status & TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED)) { + tegra_cec_error_recovery(cec); + cec_write(cec, TEGRA_CEC_INT_MASK, + mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY); + + cec->tx_done = true; + if (status & TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED) + cec->tx_status = CEC_TX_STATUS_LOW_DRIVE; + else + cec->tx_status = CEC_TX_STATUS_ARB_LOST; + return IRQ_WAKE_THREAD; + } + + if (status & TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED) { + cec_write(cec, TEGRA_CEC_INT_STAT, + TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED); + + if (status & TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD) { + tegra_cec_error_recovery(cec); + + cec->tx_done = true; + cec->tx_status = CEC_TX_STATUS_NACK; + } else { + cec->tx_done = true; + cec->tx_status = CEC_TX_STATUS_OK; + } + return IRQ_WAKE_THREAD; + } + + if (status & TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD) + dev_warn(dev, "TX NAKed on the fly!\n"); + + if (status & TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY) { + if (cec->tx_buf_cur == cec->tx_buf_cnt) { + cec_write(cec, TEGRA_CEC_INT_MASK, + mask & ~TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY); + } else { + cec_write(cec, TEGRA_CEC_TX_REGISTER, + cec->tx_buf[cec->tx_buf_cur++]); + cec_write(cec, TEGRA_CEC_INT_STAT, + TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY); + } + } + + if (status & TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED) { + cec_write(cec, TEGRA_CEC_INT_STAT, + TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED); + cec->rx_done = false; + cec->rx_buf_cnt = 0; + } + if (status & TEGRA_CEC_INT_STAT_RX_REGISTER_FULL) { + u32 v; + + cec_write(cec, TEGRA_CEC_INT_STAT, + TEGRA_CEC_INT_STAT_RX_REGISTER_FULL); + v = cec_read(cec, TEGRA_CEC_RX_REGISTER); + if (cec->rx_buf_cnt < CEC_MAX_MSG_SIZE) + cec->rx_buf[cec->rx_buf_cnt++] = v & 0xff; + if (v & TEGRA_CEC_RX_REGISTER_EOM) { + cec->rx_done = true; + return IRQ_WAKE_THREAD; + } + } + + return IRQ_HANDLED; +} + +static int tegra_cec_adap_enable(struct cec_adapter *adap, bool enable) +{ + struct tegra_cec *cec = adap->priv; + + cec->rx_buf_cnt = 0; + cec->tx_buf_cnt = 0; + cec->tx_buf_cur = 0; + + cec_write(cec, TEGRA_CEC_HW_CONTROL, 0); + cec_write(cec, TEGRA_CEC_INT_MASK, 0); + cec_write(cec, TEGRA_CEC_INT_STAT, 0xffffffff); + cec_write(cec, TEGRA_CEC_SW_CONTROL, 0); + + if (!enable) + return 0; + + cec_write(cec, TEGRA_CEC_INPUT_FILTER, (1U << 31) | 0x20); + + cec_write(cec, TEGRA_CEC_RX_TIMING_0, + (0x7a << TEGRA_CEC_RX_TIM0_START_BIT_MAX_LO_TIME_SHIFT) | + (0x6d << TEGRA_CEC_RX_TIM0_START_BIT_MIN_LO_TIME_SHIFT) | + (0x93 << TEGRA_CEC_RX_TIM0_START_BIT_MAX_DURATION_SHIFT) | + (0x86 << TEGRA_CEC_RX_TIM0_START_BIT_MIN_DURATION_SHIFT)); + + cec_write(cec, TEGRA_CEC_RX_TIMING_1, + (0x35 << TEGRA_CEC_RX_TIM1_DATA_BIT_MAX_LO_TIME_SHIFT) | + (0x21 << TEGRA_CEC_RX_TIM1_DATA_BIT_SAMPLE_TIME_SHIFT) | + (0x56 << TEGRA_CEC_RX_TIM1_DATA_BIT_MAX_DURATION_SHIFT) | + (0x40 << TEGRA_CEC_RX_TIM1_DATA_BIT_MIN_DURATION_SHIFT)); + + cec_write(cec, TEGRA_CEC_RX_TIMING_2, + (0x50 << TEGRA_CEC_RX_TIM2_END_OF_BLOCK_TIME_SHIFT)); + + cec_write(cec, TEGRA_CEC_TX_TIMING_0, + (0x74 << TEGRA_CEC_TX_TIM0_START_BIT_LO_TIME_SHIFT) | + (0x8d << TEGRA_CEC_TX_TIM0_START_BIT_DURATION_SHIFT) | + (0x08 << TEGRA_CEC_TX_TIM0_BUS_XITION_TIME_SHIFT) | + (0x71 << TEGRA_CEC_TX_TIM0_BUS_ERROR_LO_TIME_SHIFT)); + + cec_write(cec, TEGRA_CEC_TX_TIMING_1, + (0x2f << TEGRA_CEC_TX_TIM1_LO_DATA_BIT_LO_TIME_SHIFT) | + (0x13 << TEGRA_CEC_TX_TIM1_HI_DATA_BIT_LO_TIME_SHIFT) | + (0x4b << TEGRA_CEC_TX_TIM1_DATA_BIT_DURATION_SHIFT) | + (0x21 << TEGRA_CEC_TX_TIM1_ACK_NAK_BIT_SAMPLE_TIME_SHIFT)); + + cec_write(cec, TEGRA_CEC_TX_TIMING_2, + (0x07 << TEGRA_CEC_TX_TIM2_BUS_IDLE_TIME_ADDITIONAL_FRAME_SHIFT) | + (0x05 << TEGRA_CEC_TX_TIM2_BUS_IDLE_TIME_NEW_FRAME_SHIFT) | + (0x03 << TEGRA_CEC_TX_TIM2_BUS_IDLE_TIME_RETRY_FRAME_SHIFT)); + + cec_write(cec, TEGRA_CEC_INT_MASK, + TEGRA_CEC_INT_MASK_TX_REGISTER_UNDERRUN | + TEGRA_CEC_INT_MASK_TX_FRAME_OR_BLOCK_NAKD | + TEGRA_CEC_INT_MASK_TX_ARBITRATION_FAILED | + TEGRA_CEC_INT_MASK_TX_BUS_ANOMALY_DETECTED | + TEGRA_CEC_INT_MASK_TX_FRAME_TRANSMITTED | + TEGRA_CEC_INT_MASK_RX_REGISTER_FULL | + TEGRA_CEC_INT_MASK_RX_START_BIT_DETECTED); + + cec_write(cec, TEGRA_CEC_HW_CONTROL, TEGRA_CEC_HWCTRL_TX_RX_MODE); + return 0; +} + +static int tegra_cec_adap_log_addr(struct cec_adapter *adap, u8 logical_addr) +{ + struct tegra_cec *cec = adap->priv; + u32 state = cec_read(cec, TEGRA_CEC_HW_CONTROL); + + if (logical_addr == CEC_LOG_ADDR_INVALID) + state &= ~TEGRA_CEC_HWCTRL_RX_LADDR_MASK; + else + state |= TEGRA_CEC_HWCTRL_RX_LADDR((1 << logical_addr)); + + cec_write(cec, TEGRA_CEC_HW_CONTROL, state); + return 0; +} + +static int tegra_cec_adap_monitor_all_enable(struct cec_adapter *adap, + bool enable) +{ + struct tegra_cec *cec = adap->priv; + u32 reg = cec_read(cec, TEGRA_CEC_HW_CONTROL); + + if (enable) + reg |= TEGRA_CEC_HWCTRL_RX_SNOOP; + else + reg &= ~TEGRA_CEC_HWCTRL_RX_SNOOP; + cec_write(cec, TEGRA_CEC_HW_CONTROL, reg); + return 0; +} + +static int tegra_cec_adap_transmit(struct cec_adapter *adap, u8 attempts, + u32 signal_free_time_ms, struct cec_msg *msg) +{ + bool retry_xfer = signal_free_time_ms == CEC_SIGNAL_FREE_TIME_RETRY; + struct tegra_cec *cec = adap->priv; + unsigned int i; + u32 mode = 0; + u32 mask; + + if (cec_msg_is_broadcast(msg)) + mode = TEGRA_CEC_TX_REG_BCAST; + + cec->tx_buf_cur = 0; + cec->tx_buf_cnt = msg->len; + + for (i = 0; i < msg->len; i++) { + cec->tx_buf[i] = mode | msg->msg[i]; + if (i == 0) + cec->tx_buf[i] |= TEGRA_CEC_TX_REG_START_BIT; + if (i == msg->len - 1) + cec->tx_buf[i] |= TEGRA_CEC_TX_REG_EOM; + if (i == 0 && retry_xfer) + cec->tx_buf[i] |= TEGRA_CEC_TX_REG_RETRY; + } + + mask = cec_read(cec, TEGRA_CEC_INT_MASK); + cec_write(cec, TEGRA_CEC_INT_MASK, + mask | TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY); + + return 0; +} + +static const struct cec_adap_ops tegra_cec_ops = { + .adap_enable = tegra_cec_adap_enable, + .adap_log_addr = tegra_cec_adap_log_addr, + .adap_transmit = tegra_cec_adap_transmit, + .adap_monitor_all_enable = tegra_cec_adap_monitor_all_enable, +}; + +static int tegra_cec_probe(struct platform_device *pdev) +{ + struct device *hdmi_dev; + struct tegra_cec *cec; + struct resource *res; + int ret = 0; + + hdmi_dev = cec_notifier_parse_hdmi_phandle(&pdev->dev); + + if (IS_ERR(hdmi_dev)) + return PTR_ERR(hdmi_dev); + + cec = devm_kzalloc(&pdev->dev, sizeof(struct tegra_cec), GFP_KERNEL); + + if (!cec) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (!res) { + dev_err(&pdev->dev, + "Unable to allocate resources for device\n"); + return -EBUSY; + } + + if (!devm_request_mem_region(&pdev->dev, res->start, resource_size(res), + pdev->name)) { + dev_err(&pdev->dev, + "Unable to request mem region for device\n"); + return -EBUSY; + } + + cec->tegra_cec_irq = platform_get_irq(pdev, 0); + + if (cec->tegra_cec_irq <= 0) + return -EBUSY; + + cec->cec_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + + if (!cec->cec_base) { + dev_err(&pdev->dev, "Unable to grab IOs for device\n"); + return -EBUSY; + } + + cec->clk = devm_clk_get(&pdev->dev, "cec"); + + if (IS_ERR_OR_NULL(cec->clk)) { + dev_err(&pdev->dev, "Can't get clock for CEC\n"); + return -ENOENT; + } + + ret = clk_prepare_enable(cec->clk); + if (ret) { + dev_err(&pdev->dev, "Unable to prepare clock for CEC\n"); + return ret; + } + + /* set context info. */ + cec->dev = &pdev->dev; + + platform_set_drvdata(pdev, cec); + + ret = devm_request_threaded_irq(&pdev->dev, cec->tegra_cec_irq, + tegra_cec_irq_handler, tegra_cec_irq_thread_handler, + 0, "cec_irq", &pdev->dev); + + if (ret) { + dev_err(&pdev->dev, + "Unable to request interrupt for device\n"); + goto err_clk; + } + + cec->adap = cec_allocate_adapter(&tegra_cec_ops, cec, TEGRA_CEC_NAME, + CEC_CAP_DEFAULTS | CEC_CAP_MONITOR_ALL | + CEC_CAP_CONNECTOR_INFO, + CEC_MAX_LOG_ADDRS); + if (IS_ERR(cec->adap)) { + ret = -ENOMEM; + dev_err(&pdev->dev, "Couldn't create cec adapter\n"); + goto err_clk; + } + + cec->notifier = cec_notifier_cec_adap_register(hdmi_dev, NULL, + cec->adap); + if (!cec->notifier) { + ret = -ENOMEM; + goto err_adapter; + } + + ret = cec_register_adapter(cec->adap, &pdev->dev); + if (ret) { + dev_err(&pdev->dev, "Couldn't register device\n"); + goto err_notifier; + } + + return 0; + +err_notifier: + cec_notifier_cec_adap_unregister(cec->notifier, cec->adap); +err_adapter: + cec_delete_adapter(cec->adap); +err_clk: + clk_disable_unprepare(cec->clk); + return ret; +} + +static int tegra_cec_remove(struct platform_device *pdev) +{ + struct tegra_cec *cec = platform_get_drvdata(pdev); + + clk_disable_unprepare(cec->clk); + + cec_notifier_cec_adap_unregister(cec->notifier, cec->adap); + cec_unregister_adapter(cec->adap); + + return 0; +} + +#ifdef CONFIG_PM +static int tegra_cec_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct tegra_cec *cec = platform_get_drvdata(pdev); + + clk_disable_unprepare(cec->clk); + + dev_notice(&pdev->dev, "suspended\n"); + return 0; +} + +static int tegra_cec_resume(struct platform_device *pdev) +{ + struct tegra_cec *cec = platform_get_drvdata(pdev); + + dev_notice(&pdev->dev, "Resuming\n"); + + return clk_prepare_enable(cec->clk); +} +#endif + +static const struct of_device_id tegra_cec_of_match[] = { + { .compatible = "nvidia,tegra114-cec", }, + { .compatible = "nvidia,tegra124-cec", }, + { .compatible = "nvidia,tegra210-cec", }, + {}, +}; + +static struct platform_driver tegra_cec_driver = { + .driver = { + .name = TEGRA_CEC_NAME, + .of_match_table = of_match_ptr(tegra_cec_of_match), + }, + .probe = tegra_cec_probe, + .remove = tegra_cec_remove, + +#ifdef CONFIG_PM + .suspend = tegra_cec_suspend, + .resume = tegra_cec_resume, +#endif +}; + +module_platform_driver(tegra_cec_driver); + +MODULE_DESCRIPTION("Tegra HDMI CEC driver"); +MODULE_AUTHOR("NVIDIA CORPORATION"); +MODULE_AUTHOR("Cisco Systems, Inc. and/or its affiliates"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/cec/platform/tegra/tegra_cec.h b/drivers/media/cec/platform/tegra/tegra_cec.h new file mode 100644 index 000000000..8c370be38 --- /dev/null +++ b/drivers/media/cec/platform/tegra/tegra_cec.h @@ -0,0 +1,116 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Tegra CEC register definitions + * + * The original 3.10 CEC driver using a custom API: + * + * Copyright (c) 2012-2015, NVIDIA CORPORATION. All rights reserved. + * + * Conversion to the CEC framework and to the mainline kernel: + * + * Copyright 2016-2017 Cisco Systems, Inc. and/or its affiliates. All rights reserved. + */ + +#ifndef TEGRA_CEC_H +#define TEGRA_CEC_H + +/* CEC registers */ +#define TEGRA_CEC_SW_CONTROL 0x000 +#define TEGRA_CEC_HW_CONTROL 0x004 +#define TEGRA_CEC_INPUT_FILTER 0x008 +#define TEGRA_CEC_TX_REGISTER 0x010 +#define TEGRA_CEC_RX_REGISTER 0x014 +#define TEGRA_CEC_RX_TIMING_0 0x018 +#define TEGRA_CEC_RX_TIMING_1 0x01c +#define TEGRA_CEC_RX_TIMING_2 0x020 +#define TEGRA_CEC_TX_TIMING_0 0x024 +#define TEGRA_CEC_TX_TIMING_1 0x028 +#define TEGRA_CEC_TX_TIMING_2 0x02c +#define TEGRA_CEC_INT_STAT 0x030 +#define TEGRA_CEC_INT_MASK 0x034 +#define TEGRA_CEC_HW_DEBUG_RX 0x038 +#define TEGRA_CEC_HW_DEBUG_TX 0x03c + +#define TEGRA_CEC_HWCTRL_RX_LADDR_MASK 0x7fff +#define TEGRA_CEC_HWCTRL_RX_LADDR(x) \ + ((x) & TEGRA_CEC_HWCTRL_RX_LADDR_MASK) +#define TEGRA_CEC_HWCTRL_RX_SNOOP BIT(15) +#define TEGRA_CEC_HWCTRL_RX_NAK_MODE BIT(16) +#define TEGRA_CEC_HWCTRL_TX_NAK_MODE BIT(24) +#define TEGRA_CEC_HWCTRL_FAST_SIM_MODE BIT(30) +#define TEGRA_CEC_HWCTRL_TX_RX_MODE BIT(31) + +#define TEGRA_CEC_INPUT_FILTER_MODE BIT(31) +#define TEGRA_CEC_INPUT_FILTER_FIFO_LENGTH_SHIFT 0 + +#define TEGRA_CEC_TX_REG_DATA_SHIFT 0 +#define TEGRA_CEC_TX_REG_EOM BIT(8) +#define TEGRA_CEC_TX_REG_BCAST BIT(12) +#define TEGRA_CEC_TX_REG_START_BIT BIT(16) +#define TEGRA_CEC_TX_REG_RETRY BIT(17) + +#define TEGRA_CEC_RX_REGISTER_SHIFT 0 +#define TEGRA_CEC_RX_REGISTER_EOM BIT(8) +#define TEGRA_CEC_RX_REGISTER_ACK BIT(9) + +#define TEGRA_CEC_RX_TIM0_START_BIT_MAX_LO_TIME_SHIFT 0 +#define TEGRA_CEC_RX_TIM0_START_BIT_MIN_LO_TIME_SHIFT 8 +#define TEGRA_CEC_RX_TIM0_START_BIT_MAX_DURATION_SHIFT 16 +#define TEGRA_CEC_RX_TIM0_START_BIT_MIN_DURATION_SHIFT 24 + +#define TEGRA_CEC_RX_TIM1_DATA_BIT_MAX_LO_TIME_SHIFT 0 +#define TEGRA_CEC_RX_TIM1_DATA_BIT_SAMPLE_TIME_SHIFT 8 +#define TEGRA_CEC_RX_TIM1_DATA_BIT_MAX_DURATION_SHIFT 16 +#define TEGRA_CEC_RX_TIM1_DATA_BIT_MIN_DURATION_SHIFT 24 + +#define TEGRA_CEC_RX_TIM2_END_OF_BLOCK_TIME_SHIFT 0 + +#define TEGRA_CEC_TX_TIM0_START_BIT_LO_TIME_SHIFT 0 +#define TEGRA_CEC_TX_TIM0_START_BIT_DURATION_SHIFT 8 +#define TEGRA_CEC_TX_TIM0_BUS_XITION_TIME_SHIFT 16 +#define TEGRA_CEC_TX_TIM0_BUS_ERROR_LO_TIME_SHIFT 24 + +#define TEGRA_CEC_TX_TIM1_LO_DATA_BIT_LO_TIME_SHIFT 0 +#define TEGRA_CEC_TX_TIM1_HI_DATA_BIT_LO_TIME_SHIFT 8 +#define TEGRA_CEC_TX_TIM1_DATA_BIT_DURATION_SHIFT 16 +#define TEGRA_CEC_TX_TIM1_ACK_NAK_BIT_SAMPLE_TIME_SHIFT 24 + +#define TEGRA_CEC_TX_TIM2_BUS_IDLE_TIME_ADDITIONAL_FRAME_SHIFT 0 +#define TEGRA_CEC_TX_TIM2_BUS_IDLE_TIME_NEW_FRAME_SHIFT 4 +#define TEGRA_CEC_TX_TIM2_BUS_IDLE_TIME_RETRY_FRAME_SHIFT 8 + +#define TEGRA_CEC_INT_STAT_TX_REGISTER_EMPTY BIT(0) +#define TEGRA_CEC_INT_STAT_TX_REGISTER_UNDERRUN BIT(1) +#define TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD BIT(2) +#define TEGRA_CEC_INT_STAT_TX_ARBITRATION_FAILED BIT(3) +#define TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED BIT(4) +#define TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED BIT(5) +#define TEGRA_CEC_INT_STAT_RX_REGISTER_FULL BIT(8) +#define TEGRA_CEC_INT_STAT_RX_REGISTER_OVERRUN BIT(9) +#define TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED BIT(10) +#define TEGRA_CEC_INT_STAT_RX_BUS_ANOMALY_DETECTED BIT(11) +#define TEGRA_CEC_INT_STAT_RX_BUS_ERROR_DETECTED BIT(12) +#define TEGRA_CEC_INT_STAT_FILTERED_RX_DATA_PIN_TRANSITION_H2L BIT(13) +#define TEGRA_CEC_INT_STAT_FILTERED_RX_DATA_PIN_TRANSITION_L2H BIT(14) + +#define TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY BIT(0) +#define TEGRA_CEC_INT_MASK_TX_REGISTER_UNDERRUN BIT(1) +#define TEGRA_CEC_INT_MASK_TX_FRAME_OR_BLOCK_NAKD BIT(2) +#define TEGRA_CEC_INT_MASK_TX_ARBITRATION_FAILED BIT(3) +#define TEGRA_CEC_INT_MASK_TX_BUS_ANOMALY_DETECTED BIT(4) +#define TEGRA_CEC_INT_MASK_TX_FRAME_TRANSMITTED BIT(5) +#define TEGRA_CEC_INT_MASK_RX_REGISTER_FULL BIT(8) +#define TEGRA_CEC_INT_MASK_RX_REGISTER_OVERRUN BIT(9) +#define TEGRA_CEC_INT_MASK_RX_START_BIT_DETECTED BIT(10) +#define TEGRA_CEC_INT_MASK_RX_BUS_ANOMALY_DETECTED BIT(11) +#define TEGRA_CEC_INT_MASK_RX_BUS_ERROR_DETECTED BIT(12) +#define TEGRA_CEC_INT_MASK_FILTERED_RX_DATA_PIN_TRANSITION_H2L BIT(13) +#define TEGRA_CEC_INT_MASK_FILTERED_RX_DATA_PIN_TRANSITION_L2H BIT(14) + +#define TEGRA_CEC_HW_DEBUG_TX_DURATION_COUNT_SHIFT 0 +#define TEGRA_CEC_HW_DEBUG_TX_TXBIT_COUNT_SHIFT 17 +#define TEGRA_CEC_HW_DEBUG_TX_STATE_SHIFT 21 +#define TEGRA_CEC_HW_DEBUG_TX_FORCELOOUT BIT(25) +#define TEGRA_CEC_HW_DEBUG_TX_TXDATABIT_SAMPLE_TIMER BIT(26) + +#endif /* TEGRA_CEC_H */ |
