All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kamil Debski <k.debski@samsung.com>
To: dri-devel@lists.freedesktop.org, linux-media@vger.kernel.org
Cc: m.szyprowski@samsung.com, k.debski@samsung.com,
	mchehab@osg.samsung.com, hverkuil@xs4all.nl,
	kyungmin.park@samsung.com, thomas@tommie-lie.de, sean@mess.org,
	dmitry.torokhov@gmail.com, linux-input@vger.kernel.org,
	Hans Verkuil <hansverk@cisco.com>
Subject: [RFC v3 7/9] adv7604: add cec support.
Date: Fri, 20 Mar 2015 17:52:41 +0100	[thread overview]
Message-ID: <1426870363-18839-8-git-send-email-k.debski@samsung.com> (raw)
In-Reply-To: <1426870363-18839-1-git-send-email-k.debski@samsung.com>

From: Hans Verkuil <hansverk@cisco.com>

Add CEC support to the adv7604 driver.

Signed-off-by: Hans Verkuil <hansverk@cisco.com>
[k.debski@samsung.com: Merged changes from CEC Updates commit by Hans Verkuil]
Signed-off-by: Kamil Debski <k.debski@samsung.com>
---
 drivers/media/i2c/adv7604.c |  182 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 182 insertions(+)

diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index aaab9c9..ce9d123 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -42,6 +42,7 @@
 #include <media/v4l2-device.h>
 #include <media/v4l2-dv-timings.h>
 #include <media/v4l2-of.h>
+#include <media/cec.h>
 
 static int debug;
 module_param(debug, int, 0644);
@@ -159,6 +160,10 @@ struct adv76xx_state {
 	u16 spa_port_a[2];
 	struct v4l2_fract aspect_ratio;
 	u32 rgb_quantization_range;
+	u8   cec_addr[3];
+	u8   cec_valid_addrs;
+	bool cec_enabled_adap;
+
 	struct workqueue_struct *work_queues;
 	struct delayed_work delayed_work_enable_hotplug;
 	bool restart_stdi_once;
@@ -1862,6 +1867,176 @@ static int adv76xx_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
 	return 0;
 }
 
+static void adv7604_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status)
+{
+	if ((cec_read(sd, 0x11) & 0x01) == 0) {
+		v4l2_dbg(1, debug, sd, "%s: tx raw: tx disabled\n", __func__);
+		return;
+	}
+
+	if (tx_raw_status & 0x02) {
+		v4l2_dbg(1, debug, sd, "%s: tx raw: arbitration lost\n", __func__);
+		v4l2_subdev_notify(sd, V4L2_SUBDEV_CEC_TX_DONE, (void *)CEC_TX_STATUS_ARB_LOST);
+		return;
+	}
+	if (tx_raw_status & 0x04) {
+		v4l2_dbg(1, debug, sd, "%s: tx raw: retry failed\n", __func__);
+		v4l2_subdev_notify(sd, V4L2_SUBDEV_CEC_TX_DONE, (void *)CEC_TX_STATUS_RETRY_TIMEOUT);
+		return;
+	}
+	if (tx_raw_status & 0x01) {
+		v4l2_dbg(1, debug, sd, "%s: tx raw: ready ok\n", __func__);
+		v4l2_subdev_notify(sd, V4L2_SUBDEV_CEC_TX_DONE, (void *)CEC_TX_STATUS_OK);
+		return;
+	}
+}
+
+static void adv7604_cec_isr(struct v4l2_subdev *sd, bool *handled)
+{
+	struct cec_msg msg;
+	u8 cec_irq;
+
+	/* cec controller */
+	cec_irq = io_read(sd, 0x4d) & 0x0f;
+	if (!cec_irq)
+		return;
+
+	v4l2_dbg(1, debug, sd, "%s: cec: irq 0x%x\n", __func__, cec_irq);
+	adv7604_cec_tx_raw_status(sd, cec_irq);
+	if (cec_irq & 0x08) {
+		msg.len = cec_read(sd, 0x25) & 0x1f;
+		if (msg.len > 16)
+			msg.len = 16;
+
+		if (msg.len) {
+			u8 i;
+
+			for (i = 0; i < msg.len; i++)
+				msg.msg[i] = cec_read(sd, i + 0x15);
+			cec_write(sd, 0x26, 0x01); /* re-enable rx */
+			v4l2_subdev_notify(sd, V4L2_SUBDEV_CEC_RX_MSG, &msg);
+		}
+	}
+
+	/* note: the bit order is swapped between 0x4d and 0x4e */
+	cec_irq = ((cec_irq & 0x08) >> 3) | ((cec_irq & 0x04) >> 1) |
+		  ((cec_irq & 0x02) << 1) | ((cec_irq & 0x01) << 3);
+	io_write(sd, 0x4e, cec_irq);
+
+	if (handled)
+		*handled = true;
+}
+
+static int adv7604_cec_enable(struct v4l2_subdev *sd, bool enable)
+{
+	struct adv7604_state *state = to_state(sd);
+	
+	if (!state->cec_enabled_adap && enable) {
+		cec_write_and_or(sd, 0x2a, 0xfe, 0x01);	/* power up cec */
+		cec_write(sd, 0x2c, 0x01);	/* cec soft reset */
+		cec_write_and_or(sd, 0x11, 0xfe, 0);  /* initially disable tx */
+		/* enabled irqs: */
+		/* tx: ready */
+		/* tx: arbitration lost */
+		/* tx: retry timeout */
+		/* rx: ready */
+		io_write_and_or(sd, 0x50, 0xf0, 0x0f);
+		cec_write(sd, 0x26, 0x01);            /* enable rx */
+	} else if (state->cec_enabled_adap && !enable) {
+		io_write_and_or(sd, 0x50, 0xf0, 0x00);  /* disable cec interrupts */
+		cec_write_and_or(sd, 0x27, 0x8f, 0x70); /* disable address mask 1-3 */
+		cec_write_and_or(sd, 0x2a, 0xfe, 0x00); /* power down cec section */
+		state->cec_valid_addrs = 0;
+	}
+	state->cec_enabled_adap = enable;
+	return 0;
+}
+
+#define ADV7604_MAX_ADDRS (3)
+
+static int adv7604_cec_log_addr(struct v4l2_subdev *sd, u8 addr)
+{
+	struct adv7604_state *state = to_state(sd);
+	unsigned i, free_idx = ADV7604_MAX_ADDRS;
+	
+	if (!state->cec_enabled_adap)
+		return -EIO;
+
+	for (i = 0; i < ADV7604_MAX_ADDRS; i++) {
+		bool is_valid = state->cec_valid_addrs & (1 << i);
+
+		if (free_idx == ADV7604_MAX_ADDRS && !is_valid)
+			free_idx = i;
+		if (is_valid && state->cec_addr[i] == addr)
+			return 0;
+	}
+	if (i == ADV7604_MAX_ADDRS) {
+		i = free_idx;
+		if (i == ADV7604_MAX_ADDRS)
+			return -ENXIO;
+	}
+	state->cec_addr[i] = addr;
+	state->cec_valid_addrs |= 1 << i;
+
+	switch (i) {
+	case 0:
+		/* enable address mask 0 */
+		cec_write_and_or(sd, 0x27, 0xef, 0x10);
+		/* set address for mask 0 */
+		cec_write_and_or(sd, 0x28, 0xf0, addr);
+		break;
+	case 1:
+		/* enable address mask 1 */
+		cec_write_and_or(sd, 0x27, 0xdf, 0x20);
+		/* set address for mask 1 */
+		cec_write_and_or(sd, 0x28, 0x0f, addr << 4);
+		break;
+	case 2:
+		/* enable address mask 2 */
+		cec_write_and_or(sd, 0x27, 0xbf, 0x40);
+		/* set address for mask 1 */
+		cec_write_and_or(sd, 0x29, 0xf0, addr);
+		break;
+	}
+	return 0;
+}
+
+static int adv7604_cec_transmit(struct v4l2_subdev *sd, struct cec_msg *msg)
+{
+	u8 len = msg->len;
+	unsigned i;
+
+	if (len == 1)
+		cec_write_and_or(sd, 0x12, 0xf8, 1);  /* allow for one retry for polling */
+	else
+		cec_write_and_or(sd, 0x12, 0xf8, 3);  /* allow for three retries */
+
+	if (len > 16) {
+		v4l2_err(sd, "%s: len exceeded 16 (%d)\n", __func__, len);
+		return -EINVAL;
+	}
+
+	/* write data */
+	for (i = 0; i < len; i++)
+		cec_write(sd, i, msg->msg[i]);
+
+	/* set length (data + header) */
+	cec_write(sd, 0x10, len);
+	/* start transmit, enable tx */
+	cec_write(sd, 0x11, 0x01);
+	/* For some reason sometimes the
+	 * transmit won't start.
+	 * Doing it twice seems to help ?
+	*/
+	cec_write(sd, 0x11, 0x01);
+	return 0;
+}
+
+static void adv7604_cec_transmit_timed_out(struct v4l2_subdev *sd)
+{
+	cec_write_and_or(sd, 0x11, 0xfe, 0);  /* disable tx */
+}
+
 static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
 {
 	struct adv76xx_state *state = to_state(sd);
@@ -1907,6 +2082,9 @@ static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
 			*handled = true;
 	}
 
+ 	/* cec */
+ 	adv7604_cec_isr(sd, handled);
+
 	/* tx 5v detect */
 	tx_5v = io_read(sd, 0x70) & info->cable_det_mask;
 	if (tx_5v) {
@@ -2301,6 +2479,10 @@ static const struct v4l2_subdev_video_ops adv76xx_video_ops = {
 	.s_dv_timings = adv76xx_s_dv_timings,
 	.g_dv_timings = adv76xx_g_dv_timings,
 	.query_dv_timings = adv76xx_query_dv_timings,
+	.cec_enable = adv7604_cec_enable,
+	.cec_log_addr = adv7604_cec_log_addr,
+	.cec_transmit = adv7604_cec_transmit,
+	.cec_transmit_timed_out = adv7604_cec_transmit_timed_out,
 };
 
 static const struct v4l2_subdev_pad_ops adv76xx_pad_ops = {
-- 
1.7.9.5


WARNING: multiple messages have this Message-ID (diff)
From: Kamil Debski <k.debski@samsung.com>
To: dri-devel@lists.freedesktop.org, linux-media@vger.kernel.org
Cc: Hans Verkuil <hansverk@cisco.com>,
	sean@mess.org, mchehab@osg.samsung.com,
	dmitry.torokhov@gmail.com, kyungmin.park@samsung.com,
	thomas@tommie-lie.de, linux-input@vger.kernel.org,
	m.szyprowski@samsung.com
Subject: [RFC v3 7/9] adv7604: add cec support.
Date: Fri, 20 Mar 2015 17:52:41 +0100	[thread overview]
Message-ID: <1426870363-18839-8-git-send-email-k.debski@samsung.com> (raw)
In-Reply-To: <1426870363-18839-1-git-send-email-k.debski@samsung.com>

From: Hans Verkuil <hansverk@cisco.com>

Add CEC support to the adv7604 driver.

Signed-off-by: Hans Verkuil <hansverk@cisco.com>
[k.debski@samsung.com: Merged changes from CEC Updates commit by Hans Verkuil]
Signed-off-by: Kamil Debski <k.debski@samsung.com>
---
 drivers/media/i2c/adv7604.c |  182 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 182 insertions(+)

diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c
index aaab9c9..ce9d123 100644
--- a/drivers/media/i2c/adv7604.c
+++ b/drivers/media/i2c/adv7604.c
@@ -42,6 +42,7 @@
 #include <media/v4l2-device.h>
 #include <media/v4l2-dv-timings.h>
 #include <media/v4l2-of.h>
+#include <media/cec.h>
 
 static int debug;
 module_param(debug, int, 0644);
@@ -159,6 +160,10 @@ struct adv76xx_state {
 	u16 spa_port_a[2];
 	struct v4l2_fract aspect_ratio;
 	u32 rgb_quantization_range;
+	u8   cec_addr[3];
+	u8   cec_valid_addrs;
+	bool cec_enabled_adap;
+
 	struct workqueue_struct *work_queues;
 	struct delayed_work delayed_work_enable_hotplug;
 	bool restart_stdi_once;
@@ -1862,6 +1867,176 @@ static int adv76xx_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh,
 	return 0;
 }
 
+static void adv7604_cec_tx_raw_status(struct v4l2_subdev *sd, u8 tx_raw_status)
+{
+	if ((cec_read(sd, 0x11) & 0x01) == 0) {
+		v4l2_dbg(1, debug, sd, "%s: tx raw: tx disabled\n", __func__);
+		return;
+	}
+
+	if (tx_raw_status & 0x02) {
+		v4l2_dbg(1, debug, sd, "%s: tx raw: arbitration lost\n", __func__);
+		v4l2_subdev_notify(sd, V4L2_SUBDEV_CEC_TX_DONE, (void *)CEC_TX_STATUS_ARB_LOST);
+		return;
+	}
+	if (tx_raw_status & 0x04) {
+		v4l2_dbg(1, debug, sd, "%s: tx raw: retry failed\n", __func__);
+		v4l2_subdev_notify(sd, V4L2_SUBDEV_CEC_TX_DONE, (void *)CEC_TX_STATUS_RETRY_TIMEOUT);
+		return;
+	}
+	if (tx_raw_status & 0x01) {
+		v4l2_dbg(1, debug, sd, "%s: tx raw: ready ok\n", __func__);
+		v4l2_subdev_notify(sd, V4L2_SUBDEV_CEC_TX_DONE, (void *)CEC_TX_STATUS_OK);
+		return;
+	}
+}
+
+static void adv7604_cec_isr(struct v4l2_subdev *sd, bool *handled)
+{
+	struct cec_msg msg;
+	u8 cec_irq;
+
+	/* cec controller */
+	cec_irq = io_read(sd, 0x4d) & 0x0f;
+	if (!cec_irq)
+		return;
+
+	v4l2_dbg(1, debug, sd, "%s: cec: irq 0x%x\n", __func__, cec_irq);
+	adv7604_cec_tx_raw_status(sd, cec_irq);
+	if (cec_irq & 0x08) {
+		msg.len = cec_read(sd, 0x25) & 0x1f;
+		if (msg.len > 16)
+			msg.len = 16;
+
+		if (msg.len) {
+			u8 i;
+
+			for (i = 0; i < msg.len; i++)
+				msg.msg[i] = cec_read(sd, i + 0x15);
+			cec_write(sd, 0x26, 0x01); /* re-enable rx */
+			v4l2_subdev_notify(sd, V4L2_SUBDEV_CEC_RX_MSG, &msg);
+		}
+	}
+
+	/* note: the bit order is swapped between 0x4d and 0x4e */
+	cec_irq = ((cec_irq & 0x08) >> 3) | ((cec_irq & 0x04) >> 1) |
+		  ((cec_irq & 0x02) << 1) | ((cec_irq & 0x01) << 3);
+	io_write(sd, 0x4e, cec_irq);
+
+	if (handled)
+		*handled = true;
+}
+
+static int adv7604_cec_enable(struct v4l2_subdev *sd, bool enable)
+{
+	struct adv7604_state *state = to_state(sd);
+	
+	if (!state->cec_enabled_adap && enable) {
+		cec_write_and_or(sd, 0x2a, 0xfe, 0x01);	/* power up cec */
+		cec_write(sd, 0x2c, 0x01);	/* cec soft reset */
+		cec_write_and_or(sd, 0x11, 0xfe, 0);  /* initially disable tx */
+		/* enabled irqs: */
+		/* tx: ready */
+		/* tx: arbitration lost */
+		/* tx: retry timeout */
+		/* rx: ready */
+		io_write_and_or(sd, 0x50, 0xf0, 0x0f);
+		cec_write(sd, 0x26, 0x01);            /* enable rx */
+	} else if (state->cec_enabled_adap && !enable) {
+		io_write_and_or(sd, 0x50, 0xf0, 0x00);  /* disable cec interrupts */
+		cec_write_and_or(sd, 0x27, 0x8f, 0x70); /* disable address mask 1-3 */
+		cec_write_and_or(sd, 0x2a, 0xfe, 0x00); /* power down cec section */
+		state->cec_valid_addrs = 0;
+	}
+	state->cec_enabled_adap = enable;
+	return 0;
+}
+
+#define ADV7604_MAX_ADDRS (3)
+
+static int adv7604_cec_log_addr(struct v4l2_subdev *sd, u8 addr)
+{
+	struct adv7604_state *state = to_state(sd);
+	unsigned i, free_idx = ADV7604_MAX_ADDRS;
+	
+	if (!state->cec_enabled_adap)
+		return -EIO;
+
+	for (i = 0; i < ADV7604_MAX_ADDRS; i++) {
+		bool is_valid = state->cec_valid_addrs & (1 << i);
+
+		if (free_idx == ADV7604_MAX_ADDRS && !is_valid)
+			free_idx = i;
+		if (is_valid && state->cec_addr[i] == addr)
+			return 0;
+	}
+	if (i == ADV7604_MAX_ADDRS) {
+		i = free_idx;
+		if (i == ADV7604_MAX_ADDRS)
+			return -ENXIO;
+	}
+	state->cec_addr[i] = addr;
+	state->cec_valid_addrs |= 1 << i;
+
+	switch (i) {
+	case 0:
+		/* enable address mask 0 */
+		cec_write_and_or(sd, 0x27, 0xef, 0x10);
+		/* set address for mask 0 */
+		cec_write_and_or(sd, 0x28, 0xf0, addr);
+		break;
+	case 1:
+		/* enable address mask 1 */
+		cec_write_and_or(sd, 0x27, 0xdf, 0x20);
+		/* set address for mask 1 */
+		cec_write_and_or(sd, 0x28, 0x0f, addr << 4);
+		break;
+	case 2:
+		/* enable address mask 2 */
+		cec_write_and_or(sd, 0x27, 0xbf, 0x40);
+		/* set address for mask 1 */
+		cec_write_and_or(sd, 0x29, 0xf0, addr);
+		break;
+	}
+	return 0;
+}
+
+static int adv7604_cec_transmit(struct v4l2_subdev *sd, struct cec_msg *msg)
+{
+	u8 len = msg->len;
+	unsigned i;
+
+	if (len == 1)
+		cec_write_and_or(sd, 0x12, 0xf8, 1);  /* allow for one retry for polling */
+	else
+		cec_write_and_or(sd, 0x12, 0xf8, 3);  /* allow for three retries */
+
+	if (len > 16) {
+		v4l2_err(sd, "%s: len exceeded 16 (%d)\n", __func__, len);
+		return -EINVAL;
+	}
+
+	/* write data */
+	for (i = 0; i < len; i++)
+		cec_write(sd, i, msg->msg[i]);
+
+	/* set length (data + header) */
+	cec_write(sd, 0x10, len);
+	/* start transmit, enable tx */
+	cec_write(sd, 0x11, 0x01);
+	/* For some reason sometimes the
+	 * transmit won't start.
+	 * Doing it twice seems to help ?
+	*/
+	cec_write(sd, 0x11, 0x01);
+	return 0;
+}
+
+static void adv7604_cec_transmit_timed_out(struct v4l2_subdev *sd)
+{
+	cec_write_and_or(sd, 0x11, 0xfe, 0);  /* disable tx */
+}
+
 static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
 {
 	struct adv76xx_state *state = to_state(sd);
@@ -1907,6 +2082,9 @@ static int adv76xx_isr(struct v4l2_subdev *sd, u32 status, bool *handled)
 			*handled = true;
 	}
 
+ 	/* cec */
+ 	adv7604_cec_isr(sd, handled);
+
 	/* tx 5v detect */
 	tx_5v = io_read(sd, 0x70) & info->cable_det_mask;
 	if (tx_5v) {
@@ -2301,6 +2479,10 @@ static const struct v4l2_subdev_video_ops adv76xx_video_ops = {
 	.s_dv_timings = adv76xx_s_dv_timings,
 	.g_dv_timings = adv76xx_g_dv_timings,
 	.query_dv_timings = adv76xx_query_dv_timings,
+	.cec_enable = adv7604_cec_enable,
+	.cec_log_addr = adv7604_cec_log_addr,
+	.cec_transmit = adv7604_cec_transmit,
+	.cec_transmit_timed_out = adv7604_cec_transmit_timed_out,
 };
 
 static const struct v4l2_subdev_pad_ops adv76xx_pad_ops = {
-- 
1.7.9.5

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

  parent reply	other threads:[~2015-03-20 16:54 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-03-20 16:52 [RFC v3 0/9] HDMI CEC framework Kamil Debski
2015-03-20 16:52 ` Kamil Debski
2015-03-20 16:52 ` [RFC v3 1/9] dts: add hdmi-cec to to pinctrl definitions Kamil Debski
2015-03-20 16:52   ` Kamil Debski
2015-03-20 16:52 ` [RFC v3 2/9] dts: add s5p-cec to exynos4412-odroidu3 Kamil Debski
2015-03-20 16:52   ` Kamil Debski
2015-03-20 16:52 ` [RFC v3 3/9] Input: add key codes specific to the HDMI CEC bus Kamil Debski
2015-03-20 16:52   ` Kamil Debski
2015-03-20 16:52 ` [RFC v3 4/9] rc: add a map for devices communicating over " Kamil Debski
2015-03-20 16:52   ` Kamil Debski
2015-03-20 16:52 ` [RFC v3 5/9] cec: add new driver for cec support Kamil Debski
2015-03-20 16:52   ` Kamil Debski
2015-03-20 18:08   ` Hans Verkuil
2015-04-13 13:19     ` Kamil Debski
2015-04-17 12:16       ` Hans Verkuil
2015-04-20 13:10         ` Kamil Debski
2015-03-20 16:52 ` [RFC v3 6/9] v4l2-subdev: add cec ops Kamil Debski
2015-03-20 16:52 ` Kamil Debski [this message]
2015-03-20 16:52   ` [RFC v3 7/9] adv7604: add cec support Kamil Debski
2015-03-20 16:52 ` [RFC v3 8/9] adv7511: " Kamil Debski
2015-03-20 16:52 ` [RFC v3 9/9] s5p-cec: Add s5p-cec driver Kamil Debski

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1426870363-18839-8-git-send-email-k.debski@samsung.com \
    --to=k.debski@samsung.com \
    --cc=dmitry.torokhov@gmail.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=hansverk@cisco.com \
    --cc=hverkuil@xs4all.nl \
    --cc=kyungmin.park@samsung.com \
    --cc=linux-input@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=m.szyprowski@samsung.com \
    --cc=mchehab@osg.samsung.com \
    --cc=sean@mess.org \
    --cc=thomas@tommie-lie.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.