dri-devel.lists.freedesktop.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/4] tegra-cec: add Tegra HDMI CEC support
@ 2017-07-15 12:47 Hans Verkuil
       [not found] ` <20170715124753.43714-1-hverkuil-qWit8jRvyhVmR6Xm/wNWPw@public.gmane.org>
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Hans Verkuil @ 2017-07-15 12:47 UTC (permalink / raw)
  To: linux-media-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	devicetree-u79uwXL29TY76Z2rM5mHXA

From: Hans Verkuil <hans.verkuil-FYB4Gu1CFyUAvxtiuMwx3w@public.gmane.org>

This patch series adds support for the Tegra CEC functionality.

It has two prerequisites:

this cec-notifier patch: https://patchwork.linuxtv.org/patch/42521/

and this workaround: http://www.spinics.net/lists/dri-devel/msg147038.html

A proper fix needs to be found for that workaround, but it is good
enough for testing this patch series.

The first patch documents the CEC bindings, the second adds support
for this to tegra124.dtsi and enables it for the Jetson TK1.

The third patch adds the CEC driver itself and the final patch adds
the cec notifier support to the drm/tegra driver in order to notify
the CEC driver whenever the physical address changes.

I expect that the dts changes apply as well to the Tegra X1/X2 and possibly
other Tegra SoCs, but I can only test this with my Jetson TK1 board.

The dt-bindings and the tegra-cec driver would go in through the media
subsystem, the drm/tegra part through the drm subsystem and the dts
changes through (I guess) the linux-tegra developers. Luckily they are
all independent of one another.

To test this you need the CEC utilities from git://linuxtv.org/v4l-utils.git.

To build this:

git clone git://linuxtv.org/v4l-utils.git
cd v4l-utils
./bootstrap.sh; ./configure
make
sudo make install	# optional, you really only need utils/cec*

To test:

cec-ctl --playback	# configure as playback device
cec-ctl -S		# detect all connected CEC devices

See here for the public CEC API:

https://hverkuil.home.xs4all.nl/spec/uapi/cec/cec-api.html

Regards,

	Hans

Hans Verkuil (4):
  dt-bindings: document the tegra CEC bindings
  ARM: tegra: add CEC support to tegra124.dtsi
  tegra-cec: add Tegra HDMI CEC driver
  drm/tegra: add cec-notifier support

 .../devicetree/bindings/media/tegra-cec.txt        |  26 ++
 MAINTAINERS                                        |   8 +
 arch/arm/boot/dts/tegra124-jetson-tk1.dts          |   4 +
 arch/arm/boot/dts/tegra124.dtsi                    |  12 +-
 drivers/gpu/drm/tegra/drm.h                        |   3 +
 drivers/gpu/drm/tegra/hdmi.c                       |   9 +
 drivers/gpu/drm/tegra/output.c                     |   6 +
 drivers/media/platform/Kconfig                     |  11 +
 drivers/media/platform/Makefile                    |   2 +
 drivers/media/platform/tegra-cec/Makefile          |   1 +
 drivers/media/platform/tegra-cec/tegra_cec.c       | 506 +++++++++++++++++++++
 drivers/media/platform/tegra-cec/tegra_cec.h       | 127 ++++++
 12 files changed, 714 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/media/tegra-cec.txt
 create mode 100644 drivers/media/platform/tegra-cec/Makefile
 create mode 100644 drivers/media/platform/tegra-cec/tegra_cec.c
 create mode 100644 drivers/media/platform/tegra-cec/tegra_cec.h

-- 
2.11.0

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/4] dt-bindings: document the tegra CEC bindings
       [not found] ` <20170715124753.43714-1-hverkuil-qWit8jRvyhVmR6Xm/wNWPw@public.gmane.org>
@ 2017-07-15 12:47   ` Hans Verkuil
       [not found]     ` <20170715124753.43714-2-hverkuil-qWit8jRvyhVmR6Xm/wNWPw@public.gmane.org>
  2017-07-15 12:47   ` [PATCH 2/4] ARM: tegra: add CEC support to tegra124.dtsi Hans Verkuil
  1 sibling, 1 reply; 7+ messages in thread
From: Hans Verkuil @ 2017-07-15 12:47 UTC (permalink / raw)
  To: linux-media-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Hans Verkuil

From: Hans Verkuil <hans.verkuil-FYB4Gu1CFyUAvxtiuMwx3w@public.gmane.org>

This documents the binding for the Tegra CEC module.

Signed-off-by: Hans Verkuil <hans.verkuil-FYB4Gu1CFyUAvxtiuMwx3w@public.gmane.org>
---
 .../devicetree/bindings/media/tegra-cec.txt        | 26 ++++++++++++++++++++++
 1 file changed, 26 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/media/tegra-cec.txt

diff --git a/Documentation/devicetree/bindings/media/tegra-cec.txt b/Documentation/devicetree/bindings/media/tegra-cec.txt
new file mode 100644
index 000000000000..ba0b6071acaa
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/tegra-cec.txt
@@ -0,0 +1,26 @@
+* Tegra HDMI CEC driver
+
+The HDMI CEC module is present in Tegra SoCs and its purpose is to
+handle communication between HDMI connected devices over the CEC bus.
+
+Required properties:
+  - compatible : value should be one of the following:
+	"nvidia,tegra114-cec"
+	"nvidia,tegra124-cec"
+	"nvidia,tegra210-cec"
+  - reg : Physical base address of the IP registers and length of memory
+	  mapped region.
+  - interrupts : HDMI CEC interrupt number to the CPU.
+  - clocks : from common clock binding: handle to HDMI CEC clock.
+  - clock-names : from common clock binding: must contain "cec",
+		  corresponding to ithe entry in the clocks property.
+  - hdmi-phandle : phandle to the HDMI controller, see also cec.txt.
+
+Example:
+
+tegra_cec {
+	compatible = "nvidia,tegra124-cec";
+	reg = <0x0 0x70015000 0x0 0x00001000>;
+	interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
+	clocks = <&tegra_car TEGRA124_CLK_CEC>;
+	clock-names = "cec";
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/4] ARM: tegra: add CEC support to tegra124.dtsi
       [not found] ` <20170715124753.43714-1-hverkuil-qWit8jRvyhVmR6Xm/wNWPw@public.gmane.org>
  2017-07-15 12:47   ` [PATCH 1/4] dt-bindings: document the tegra CEC bindings Hans Verkuil
@ 2017-07-15 12:47   ` Hans Verkuil
  1 sibling, 0 replies; 7+ messages in thread
From: Hans Verkuil @ 2017-07-15 12:47 UTC (permalink / raw)
  To: linux-media-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Hans Verkuil

From: Hans Verkuil <hans.verkuil-FYB4Gu1CFyUAvxtiuMwx3w@public.gmane.org>

Add support for the Tegra CEC IP to tegra124.dtsi and enable it on the
Jetson TK1.

Signed-off-by: Hans Verkuil <hans.verkuil-FYB4Gu1CFyUAvxtiuMwx3w@public.gmane.org>
---
 arch/arm/boot/dts/tegra124-jetson-tk1.dts |  4 ++++
 arch/arm/boot/dts/tegra124.dtsi           | 12 +++++++++++-
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/tegra124-jetson-tk1.dts b/arch/arm/boot/dts/tegra124-jetson-tk1.dts
index 7bacb2954f58..c22c0e6dc3d9 100644
--- a/arch/arm/boot/dts/tegra124-jetson-tk1.dts
+++ b/arch/arm/boot/dts/tegra124-jetson-tk1.dts
@@ -67,6 +67,10 @@
 		};
 	};
 
+	tegra_cec {
+		status = "okay";
+	};
+
 	gpu@0,57000000 {
 		/*
 		 * Node left disabled on purpose - the bootloader will enable
diff --git a/arch/arm/boot/dts/tegra124.dtsi b/arch/arm/boot/dts/tegra124.dtsi
index 1b10b14a6abd..df7e9e2925f5 100644
--- a/arch/arm/boot/dts/tegra124.dtsi
+++ b/arch/arm/boot/dts/tegra124.dtsi
@@ -123,7 +123,7 @@
 			nvidia,head = <1>;
 		};
 
-		hdmi@54280000 {
+		hdmi: hdmi@54280000 {
 			compatible = "nvidia,tegra124-hdmi";
 			reg = <0x0 0x54280000 0x0 0x00040000>;
 			interrupts = <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;
@@ -851,6 +851,16 @@
 		status = "disabled";
 	};
 
+	tegra_cec {
+		compatible = "nvidia,tegra124-cec";
+		reg = <0x0 0x70015000 0x0 0x00001000>;
+		interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&tegra_car TEGRA124_CLK_CEC>;
+		clock-names = "cec";
+		hdmi-phandle = <&hdmi>;
+		status = "disabled";
+	};
+
 	soctherm: thermal-sensor@700e2000 {
 		compatible = "nvidia,tegra124-soctherm";
 		reg = <0x0 0x700e2000 0x0 0x600 /* SOC_THERM reg_base */
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 3/4] tegra-cec: add Tegra HDMI CEC driver
  2017-07-15 12:47 [PATCH 0/4] tegra-cec: add Tegra HDMI CEC support Hans Verkuil
       [not found] ` <20170715124753.43714-1-hverkuil-qWit8jRvyhVmR6Xm/wNWPw@public.gmane.org>
@ 2017-07-15 12:47 ` Hans Verkuil
  2017-07-15 12:47 ` [PATCH 4/4] drm/tegra: add cec-notifier support Hans Verkuil
  2 siblings, 0 replies; 7+ messages in thread
From: Hans Verkuil @ 2017-07-15 12:47 UTC (permalink / raw)
  To: linux-media; +Cc: linux-tegra, dri-devel, devicetree, Hans Verkuil

From: Hans Verkuil <hans.verkuil@cisco.com>

This driver adds support for the Tegra CEC IP. It is based on the
NVIDIA drivers/misc/tegra-cec driver in their 3.10 kernel.

This has been converted to the CEC framework and cleaned up.

Tested with my Jetson TK1 board. It has also been tested with the
Tegra X1 in an embedded product.

Note of warning for the Tegra X2: this SoC supports two HDMI outputs,
but only one CEC adapter and the CEC bus is shared between the
two outputs. This is a design mistake and the CEC adapter can
control only one HDMI output. Never hook up both HDMI outputs
to the CEC bus in a hardware design: this is illegal as per the
CEC specification.

The CEC bus can be shared between multiple inputs and zero or one
outputs, but not between multiple outputs.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 MAINTAINERS                                  |   8 +
 drivers/media/platform/Kconfig               |  11 +
 drivers/media/platform/Makefile              |   2 +
 drivers/media/platform/tegra-cec/Makefile    |   1 +
 drivers/media/platform/tegra-cec/tegra_cec.c | 506 +++++++++++++++++++++++++++
 drivers/media/platform/tegra-cec/tegra_cec.h | 127 +++++++
 6 files changed, 655 insertions(+)
 create mode 100644 drivers/media/platform/tegra-cec/Makefile
 create mode 100644 drivers/media/platform/tegra-cec/tegra_cec.c
 create mode 100644 drivers/media/platform/tegra-cec/tegra_cec.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 7d9bd4a041af..35b393feac52 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1917,6 +1917,14 @@ M:	Lennert Buytenhek <kernel@wantstofly.org>
 L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 S:	Maintained
 
+ARM/TEGRA HDMI CEC SUBSYSTEM SUPPORT
+M:	Hans Verkuil <hans.verkuil@cisco.com>
+L:	linux-tegra@vger.kernel.org
+L:	linux-media@vger.kernel.org
+S:	Maintained
+F:	drivers/media/platform/tegra-cec/
+F:	Documentation/devicetree/bindings/media/tegra-cec.txt
+
 ARM/TETON BGA MACHINE SUPPORT
 M:	"Mark F. Brown" <mark.brown314@gmail.com>
 L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index 1313cd533436..31f54cbdf2e2 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -570,6 +570,17 @@ config VIDEO_STM32_HDMI_CEC
          CEC bus is present in the HDMI connector and enables communication
          between compatible devices.
 
+config VIDEO_TEGRA_HDMI_CEC
+       tristate "Tegra HDMI CEC driver"
+       depends on ARCH_TEGRA || COMPILE_TEST
+       select CEC_CORE
+       select CEC_NOTIFIER
+       ---help---
+         This is a driver for the Tegra HDMI CEC interface. It uses the
+         generic CEC framework interface.
+         The CEC bus is present in the HDMI connector and enables communication
+         between compatible devices.
+
 endif #CEC_PLATFORM_DRIVERS
 
 menuconfig SDR_PLATFORM_DRIVERS
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 9beadc760467..9da73532e556 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -46,6 +46,8 @@ obj-$(CONFIG_VIDEO_STI_HDMI_CEC) 	+= sti/cec/
 
 obj-$(CONFIG_VIDEO_STI_DELTA)		+= sti/delta/
 
+obj-$(CONFIG_VIDEO_TEGRA_HDMI_CEC)	+= tegra-cec/
+
 obj-y 					+= stm32/
 
 obj-y                                   += blackfin/
diff --git a/drivers/media/platform/tegra-cec/Makefile b/drivers/media/platform/tegra-cec/Makefile
new file mode 100644
index 000000000000..f3d81127589f
--- /dev/null
+++ b/drivers/media/platform/tegra-cec/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_VIDEO_TEGRA_HDMI_CEC)	+= tegra_cec.o
diff --git a/drivers/media/platform/tegra-cec/tegra_cec.c b/drivers/media/platform/tegra-cec/tegra_cec.c
new file mode 100644
index 000000000000..346586c3ad6d
--- /dev/null
+++ b/drivers/media/platform/tegra-cec/tegra_cec.c
@@ -0,0 +1,506 @@
+/*
+ * 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.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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_REGISTER_OVERRUN |
+		      TEGRA_CEC_INT_STAT_RX_BUS_ANOMALY_DETECTED |
+		      TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED |
+		      TEGRA_CEC_INT_STAT_RX_BUS_ERROR_DETECTED)) {
+		cec_write(cec, TEGRA_CEC_INT_STAT,
+			  (TEGRA_CEC_INT_STAT_RX_REGISTER_OVERRUN |
+			   TEGRA_CEC_INT_STAT_RX_BUS_ANOMALY_DETECTED |
+			   TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED |
+			   TEGRA_CEC_INT_STAT_RX_BUS_ERROR_DETECTED));
+	} else 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_REGISTER_OVERRUN);
+
+	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 platform_device *hdmi_dev;
+	struct device_node *np;
+	struct tegra_cec *cec;
+	struct resource *res;
+	int ret = 0;
+
+	np = of_parse_phandle(pdev->dev.of_node, "hdmi-phandle", 0);
+
+	if (!np) {
+		dev_err(&pdev->dev, "Failed to find hdmi node in device tree\n");
+		return -ENODEV;
+	}
+	hdmi_dev = of_find_device_by_node(np);
+	if (hdmi_dev == NULL)
+		return -EPROBE_DEFER;
+
+	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");
+		ret = -EBUSY;
+		goto cec_error;
+	}
+
+	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");
+		ret = -EBUSY;
+		goto cec_error;
+	}
+
+	cec->tegra_cec_irq = platform_get_irq(pdev, 0);
+
+	if (cec->tegra_cec_irq <= 0) {
+		ret = -EBUSY;
+		goto cec_error;
+	}
+
+	cec->cec_base = devm_ioremap_nocache(&pdev->dev, res->start,
+		resource_size(res));
+
+	if (!cec->cec_base) {
+		dev_err(&pdev->dev, "Unable to grab IOs for device\n");
+		ret = -EBUSY;
+		goto cec_error;
+	}
+
+	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");
+		ret = -ENOENT;
+		goto clk_error;
+	}
+
+	clk_prepare_enable(cec->clk);
+
+	/* set context info. */
+	cec->dev = &pdev->dev;
+
+	platform_set_drvdata(pdev, cec);
+	/* clear out the hardware. */
+
+	device_init_wakeup(&pdev->dev, 1);
+
+	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 cec_error;
+	}
+
+	cec->notifier = cec_notifier_get(&hdmi_dev->dev);
+	if (!cec->notifier) {
+		ret = -ENOMEM;
+		goto cec_error;
+	}
+
+	cec->adap = cec_allocate_adapter(&tegra_cec_ops, cec, TEGRA_CEC_NAME,
+			CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT |
+			CEC_CAP_MONITOR_ALL | CEC_CAP_PASSTHROUGH | CEC_CAP_RC,
+			CEC_MAX_LOG_ADDRS);
+	if (IS_ERR_OR_NULL(cec->adap)) {
+		ret = -ENOMEM;
+		dev_err(&pdev->dev, "Couldn't create cec adapter\n");
+		goto cec_error;
+	}
+	ret = cec_register_adapter(cec->adap, &pdev->dev);
+	if (ret) {
+		dev_err(&pdev->dev, "Couldn't register device\n");
+		goto cec_error;
+	}
+
+	cec_register_cec_notifier(cec->adap, cec->notifier);
+
+	return 0;
+
+cec_error:
+	if (cec->notifier)
+		cec_notifier_put(cec->notifier);
+	if (!IS_ERR_OR_NULL(cec->adap))
+		cec_delete_adapter(cec->adap);
+	clk_disable_unprepare(cec->clk);
+clk_error:
+	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_unregister_adapter(cec->adap);
+	cec_notifier_put(cec->notifier);
+
+	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");
+
+	clk_prepare_enable(cec->clk);
+
+	return 0;
+}
+#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);
diff --git a/drivers/media/platform/tegra-cec/tegra_cec.h b/drivers/media/platform/tegra-cec/tegra_cec.h
new file mode 100644
index 000000000000..e301513daa87
--- /dev/null
+++ b/drivers/media/platform/tegra-cec/tegra_cec.h
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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				(1 << 15)
+#define TEGRA_CEC_HWCTRL_RX_NAK_MODE				(1 << 16)
+#define TEGRA_CEC_HWCTRL_TX_NAK_MODE				(1 << 24)
+#define TEGRA_CEC_HWCTRL_FAST_SIM_MODE				(1 << 30)
+#define TEGRA_CEC_HWCTRL_TX_RX_MODE				(1 << 31)
+
+#define TEGRA_CEC_INPUT_FILTER_MODE				(1 << 31)
+#define TEGRA_CEC_INPUT_FILTER_FIFO_LENGTH_SHIFT		0
+
+#define TEGRA_CEC_TX_REG_DATA_SHIFT				0
+#define TEGRA_CEC_TX_REG_EOM					(1 << 8)
+#define TEGRA_CEC_TX_REG_BCAST					(1 << 12)
+#define TEGRA_CEC_TX_REG_START_BIT				(1 << 16)
+#define TEGRA_CEC_TX_REG_RETRY					(1 << 17)
+
+#define TEGRA_CEC_RX_REGISTER_SHIFT				0
+#define TEGRA_CEC_RX_REGISTER_EOM				(1 << 8)
+#define TEGRA_CEC_RX_REGISTER_ACK				(1 << 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			(1 << 0)
+#define TEGRA_CEC_INT_STAT_TX_REGISTER_UNDERRUN			(1 << 1)
+#define TEGRA_CEC_INT_STAT_TX_FRAME_OR_BLOCK_NAKD		(1 << 2)
+#define TEGRA_CEC_INT_STAT_TX_ARBITRATION_FAILED		(1 << 3)
+#define TEGRA_CEC_INT_STAT_TX_BUS_ANOMALY_DETECTED		(1 << 4)
+#define TEGRA_CEC_INT_STAT_TX_FRAME_TRANSMITTED			(1 << 5)
+#define TEGRA_CEC_INT_STAT_RX_REGISTER_FULL			(1 << 8)
+#define TEGRA_CEC_INT_STAT_RX_REGISTER_OVERRUN			(1 << 9)
+#define TEGRA_CEC_INT_STAT_RX_START_BIT_DETECTED		(1 << 10)
+#define TEGRA_CEC_INT_STAT_RX_BUS_ANOMALY_DETECTED		(1 << 11)
+#define TEGRA_CEC_INT_STAT_RX_BUS_ERROR_DETECTED		(1 << 12)
+#define TEGRA_CEC_INT_STAT_FILTERED_RX_DATA_PIN_TRANSITION_H2L	(1 << 13)
+#define TEGRA_CEC_INT_STAT_FILTERED_RX_DATA_PIN_TRANSITION_L2H	(1 << 14)
+
+#define TEGRA_CEC_INT_MASK_TX_REGISTER_EMPTY			(1 << 0)
+#define TEGRA_CEC_INT_MASK_TX_REGISTER_UNDERRUN			(1 << 1)
+#define TEGRA_CEC_INT_MASK_TX_FRAME_OR_BLOCK_NAKD		(1 << 2)
+#define TEGRA_CEC_INT_MASK_TX_ARBITRATION_FAILED		(1 << 3)
+#define TEGRA_CEC_INT_MASK_TX_BUS_ANOMALY_DETECTED		(1 << 4)
+#define TEGRA_CEC_INT_MASK_TX_FRAME_TRANSMITTED			(1 << 5)
+#define TEGRA_CEC_INT_MASK_RX_REGISTER_FULL			(1 << 8)
+#define TEGRA_CEC_INT_MASK_RX_REGISTER_OVERRUN			(1 << 9)
+#define TEGRA_CEC_INT_MASK_RX_START_BIT_DETECTED		(1 << 10)
+#define TEGRA_CEC_INT_MASK_RX_BUS_ANOMALY_DETECTED		(1 << 11)
+#define TEGRA_CEC_INT_MASK_RX_BUS_ERROR_DETECTED		(1 << 12)
+#define TEGRA_CEC_INT_MASK_FILTERED_RX_DATA_PIN_TRANSITION_H2L	(1 << 13)
+#define TEGRA_CEC_INT_MASK_FILTERED_RX_DATA_PIN_TRANSITION_L2H	(1 << 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			(1 << 25)
+#define TEGRA_CEC_HW_DEBUG_TX_TXDATABIT_SAMPLE_TIMER		(1 << 26)
+
+#endif /* TEGRA_CEC_H */
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 4/4] drm/tegra: add cec-notifier support
  2017-07-15 12:47 [PATCH 0/4] tegra-cec: add Tegra HDMI CEC support Hans Verkuil
       [not found] ` <20170715124753.43714-1-hverkuil-qWit8jRvyhVmR6Xm/wNWPw@public.gmane.org>
  2017-07-15 12:47 ` [PATCH 3/4] tegra-cec: add Tegra HDMI CEC driver Hans Verkuil
@ 2017-07-15 12:47 ` Hans Verkuil
       [not found]   ` <20170715124753.43714-5-hverkuil-qWit8jRvyhVmR6Xm/wNWPw@public.gmane.org>
  2 siblings, 1 reply; 7+ messages in thread
From: Hans Verkuil @ 2017-07-15 12:47 UTC (permalink / raw)
  To: linux-media; +Cc: linux-tegra, devicetree, Hans Verkuil, dri-devel

From: Hans Verkuil <hans.verkuil@cisco.com>

In order to support CEC the HDMI driver has to inform the CEC driver
whenever the physical address changes. So when the EDID is read the
CEC driver has to be informed and whenever the hotplug detect goes
away.

This is done through the cec-notifier framework.

The link between the HDMI driver and the CEC driver is done through
the hdmi_phandle in the tegra-cec node in the device tree.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/gpu/drm/tegra/drm.h    | 3 +++
 drivers/gpu/drm/tegra/hdmi.c   | 9 +++++++++
 drivers/gpu/drm/tegra/output.c | 6 ++++++
 3 files changed, 18 insertions(+)

diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index 6d6da01282f3..c0a18b60caf1 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -212,6 +212,8 @@ int tegra_dc_state_setup_clock(struct tegra_dc *dc,
 			       struct clk *clk, unsigned long pclk,
 			       unsigned int div);
 
+struct cec_notifier;
+
 struct tegra_output {
 	struct device_node *of_node;
 	struct device *dev;
@@ -219,6 +221,7 @@ struct tegra_output {
 	struct drm_panel *panel;
 	struct i2c_adapter *ddc;
 	const struct edid *edid;
+	struct cec_notifier *notifier;
 	unsigned int hpd_irq;
 	int hpd_gpio;
 	enum of_gpio_flags hpd_gpio_flags;
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index cda0491ed6bf..fbf14e1efd0e 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -21,6 +21,8 @@
 
 #include <sound/hda_verbs.h>
 
+#include <media/cec-notifier.h>
+
 #include "hdmi.h"
 #include "drm.h"
 #include "dc.h"
@@ -1720,6 +1722,10 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
 		return PTR_ERR(hdmi->vdd);
 	}
 
+	hdmi->output.notifier = cec_notifier_get(&pdev->dev);
+	if (hdmi->output.notifier == NULL)
+		return -ENOMEM;
+
 	hdmi->output.dev = &pdev->dev;
 
 	err = tegra_output_probe(&hdmi->output);
@@ -1778,6 +1784,9 @@ static int tegra_hdmi_remove(struct platform_device *pdev)
 
 	tegra_output_remove(&hdmi->output);
 
+	if (hdmi->output.notifier)
+		cec_notifier_put(hdmi->output.notifier);
+
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index 595d1ec3e02e..57c052521a44 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -11,6 +11,8 @@
 #include <drm/drm_panel.h>
 #include "drm.h"
 
+#include <media/cec-notifier.h>
+
 int tegra_output_connector_get_modes(struct drm_connector *connector)
 {
 	struct tegra_output *output = connector_to_output(connector);
@@ -33,6 +35,7 @@ int tegra_output_connector_get_modes(struct drm_connector *connector)
 		edid = drm_get_edid(connector, output->ddc);
 
 	drm_mode_connector_update_edid_property(connector, edid);
+	cec_notifier_set_phys_addr_from_edid(output->notifier, edid);
 
 	if (edid) {
 		err = drm_add_edid_modes(connector, edid);
@@ -68,6 +71,9 @@ tegra_output_connector_detect(struct drm_connector *connector, bool force)
 			status = connector_status_connected;
 	}
 
+	if (status != connector_status_connected)
+		cec_notifier_phys_addr_invalidate(output->notifier);
+
 	return status;
 }
 
-- 
2.11.0

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/4] dt-bindings: document the tegra CEC bindings
       [not found]     ` <20170715124753.43714-2-hverkuil-qWit8jRvyhVmR6Xm/wNWPw@public.gmane.org>
@ 2017-07-17 19:37       ` Rob Herring
  0 siblings, 0 replies; 7+ messages in thread
From: Rob Herring @ 2017-07-17 19:37 UTC (permalink / raw)
  To: Hans Verkuil
  Cc: linux-media-u79uwXL29TY76Z2rM5mHXA,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Hans Verkuil,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW

On Sat, Jul 15, 2017 at 02:47:50PM +0200, Hans Verkuil wrote:
> From: Hans Verkuil <hans.verkuil-FYB4Gu1CFyUAvxtiuMwx3w@public.gmane.org>
> 
> This documents the binding for the Tegra CEC module.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil-FYB4Gu1CFyUAvxtiuMwx3w@public.gmane.org>
> ---
>  .../devicetree/bindings/media/tegra-cec.txt        | 26 ++++++++++++++++++++++
>  1 file changed, 26 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/media/tegra-cec.txt
> 
> diff --git a/Documentation/devicetree/bindings/media/tegra-cec.txt b/Documentation/devicetree/bindings/media/tegra-cec.txt
> new file mode 100644
> index 000000000000..ba0b6071acaa
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/tegra-cec.txt
> @@ -0,0 +1,26 @@
> +* Tegra HDMI CEC driver

Bindings are for h/w, not drivers...

> +The HDMI CEC module is present in Tegra SoCs and its purpose is to
> +handle communication between HDMI connected devices over the CEC bus.
> +
> +Required properties:
> +  - compatible : value should be one of the following:
> +	"nvidia,tegra114-cec"
> +	"nvidia,tegra124-cec"
> +	"nvidia,tegra210-cec"
> +  - reg : Physical base address of the IP registers and length of memory
> +	  mapped region.
> +  - interrupts : HDMI CEC interrupt number to the CPU.
> +  - clocks : from common clock binding: handle to HDMI CEC clock.
> +  - clock-names : from common clock binding: must contain "cec",
> +		  corresponding to ithe entry in the clocks property.

s/ithe/the/

> +  - hdmi-phandle : phandle to the HDMI controller, see also cec.txt.
> +
> +Example:
> +
> +tegra_cec {

cec@70015000

> +	compatible = "nvidia,tegra124-cec";
> +	reg = <0x0 0x70015000 0x0 0x00001000>;
> +	interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
> +	clocks = <&tegra_car TEGRA124_CLK_CEC>;
> +	clock-names = "cec";
> -- 
> 2.11.0
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW@public.gmane.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCHv2 4/4] drm/tegra: add cec-notifier support
       [not found]   ` <20170715124753.43714-5-hverkuil-qWit8jRvyhVmR6Xm/wNWPw@public.gmane.org>
@ 2017-07-30 11:08     ` Hans Verkuil
  0 siblings, 0 replies; 7+ messages in thread
From: Hans Verkuil @ 2017-07-30 11:08 UTC (permalink / raw)
  To: linux-media-u79uwXL29TY76Z2rM5mHXA
  Cc: linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Hans Verkuil,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW

In order to support CEC the HDMI driver has to inform the CEC driver
whenever the physical address changes. So when the EDID is read the
CEC driver has to be informed and whenever the hotplug detect goes
away.

This is done through the cec-notifier framework.

The link between the HDMI driver and the CEC driver is done through
the hdmi_phandle in the tegra-cec node in the device tree.

Signed-off-by: Hans Verkuil <hans.verkuil-FYB4Gu1CFyUAvxtiuMwx3w@public.gmane.org>
---
Changes since v1:

Add 'select CEC_CORE if CEC_NOTIFIER' to Kconfig. If the tegra-cec driver
is a module and the drm tegra driver is built-in, then the CEC core
will be a module also, causing the CEC notifier to fail (will be
compiled as empty functions). This select forces the correct dependency.
---
 drivers/gpu/drm/tegra/Kconfig  | 1 +
 drivers/gpu/drm/tegra/drm.h    | 3 +++
 drivers/gpu/drm/tegra/hdmi.c   | 9 +++++++++
 drivers/gpu/drm/tegra/output.c | 6 ++++++
 4 files changed, 19 insertions(+)

diff --git a/drivers/gpu/drm/tegra/Kconfig b/drivers/gpu/drm/tegra/Kconfig
index 2db29d67193d..c882918c2024 100644
--- a/drivers/gpu/drm/tegra/Kconfig
+++ b/drivers/gpu/drm/tegra/Kconfig
@@ -8,6 +8,7 @@ config DRM_TEGRA
 	select DRM_PANEL
 	select TEGRA_HOST1X
 	select IOMMU_IOVA if IOMMU_SUPPORT
+	select CEC_CORE if CEC_NOTIFIER
 	help
 	  Choose this option if you have an NVIDIA Tegra SoC.

diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index 6d6da01282f3..c0a18b60caf1 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -212,6 +212,8 @@ int tegra_dc_state_setup_clock(struct tegra_dc *dc,
 			       struct clk *clk, unsigned long pclk,
 			       unsigned int div);

+struct cec_notifier;
+
 struct tegra_output {
 	struct device_node *of_node;
 	struct device *dev;
@@ -219,6 +221,7 @@ struct tegra_output {
 	struct drm_panel *panel;
 	struct i2c_adapter *ddc;
 	const struct edid *edid;
+	struct cec_notifier *notifier;
 	unsigned int hpd_irq;
 	int hpd_gpio;
 	enum of_gpio_flags hpd_gpio_flags;
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c
index cda0491ed6bf..fbf14e1efd0e 100644
--- a/drivers/gpu/drm/tegra/hdmi.c
+++ b/drivers/gpu/drm/tegra/hdmi.c
@@ -21,6 +21,8 @@

 #include <sound/hda_verbs.h>

+#include <media/cec-notifier.h>
+
 #include "hdmi.h"
 #include "drm.h"
 #include "dc.h"
@@ -1720,6 +1722,10 @@ static int tegra_hdmi_probe(struct platform_device *pdev)
 		return PTR_ERR(hdmi->vdd);
 	}

+	hdmi->output.notifier = cec_notifier_get(&pdev->dev);
+	if (hdmi->output.notifier == NULL)
+		return -ENOMEM;
+
 	hdmi->output.dev = &pdev->dev;

 	err = tegra_output_probe(&hdmi->output);
@@ -1778,6 +1784,9 @@ static int tegra_hdmi_remove(struct platform_device *pdev)

 	tegra_output_remove(&hdmi->output);

+	if (hdmi->output.notifier)
+		cec_notifier_put(hdmi->output.notifier);
+
 	return 0;
 }

diff --git a/drivers/gpu/drm/tegra/output.c b/drivers/gpu/drm/tegra/output.c
index 595d1ec3e02e..57c052521a44 100644
--- a/drivers/gpu/drm/tegra/output.c
+++ b/drivers/gpu/drm/tegra/output.c
@@ -11,6 +11,8 @@
 #include <drm/drm_panel.h>
 #include "drm.h"

+#include <media/cec-notifier.h>
+
 int tegra_output_connector_get_modes(struct drm_connector *connector)
 {
 	struct tegra_output *output = connector_to_output(connector);
@@ -33,6 +35,7 @@ int tegra_output_connector_get_modes(struct drm_connector *connector)
 		edid = drm_get_edid(connector, output->ddc);

 	drm_mode_connector_update_edid_property(connector, edid);
+	cec_notifier_set_phys_addr_from_edid(output->notifier, edid);

 	if (edid) {
 		err = drm_add_edid_modes(connector, edid);
@@ -68,6 +71,9 @@ tegra_output_connector_detect(struct drm_connector *connector, bool force)
 			status = connector_status_connected;
 	}

+	if (status != connector_status_connected)
+		cec_notifier_phys_addr_invalidate(output->notifier);
+
 	return status;
 }

-- 
2.13.1


--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2017-07-30 11:08 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-07-15 12:47 [PATCH 0/4] tegra-cec: add Tegra HDMI CEC support Hans Verkuil
     [not found] ` <20170715124753.43714-1-hverkuil-qWit8jRvyhVmR6Xm/wNWPw@public.gmane.org>
2017-07-15 12:47   ` [PATCH 1/4] dt-bindings: document the tegra CEC bindings Hans Verkuil
     [not found]     ` <20170715124753.43714-2-hverkuil-qWit8jRvyhVmR6Xm/wNWPw@public.gmane.org>
2017-07-17 19:37       ` Rob Herring
2017-07-15 12:47   ` [PATCH 2/4] ARM: tegra: add CEC support to tegra124.dtsi Hans Verkuil
2017-07-15 12:47 ` [PATCH 3/4] tegra-cec: add Tegra HDMI CEC driver Hans Verkuil
2017-07-15 12:47 ` [PATCH 4/4] drm/tegra: add cec-notifier support Hans Verkuil
     [not found]   ` <20170715124753.43714-5-hverkuil-qWit8jRvyhVmR6Xm/wNWPw@public.gmane.org>
2017-07-30 11:08     ` [PATCHv2 " Hans Verkuil

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).