From patchwork Tue Dec 23 14:32:19 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kamil Debski X-Patchwork-Id: 5533791 Return-Path: X-Original-To: patchwork-linux-media@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 7D6E39F357 for ; Tue, 23 Dec 2014 14:33:16 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 0FAF2201BC for ; Tue, 23 Dec 2014 14:33:13 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 3D03B2018E for ; Tue, 23 Dec 2014 14:33:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755921AbaLWOdJ (ORCPT ); Tue, 23 Dec 2014 09:33:09 -0500 Received: from mailout4.samsung.com ([203.254.224.34]:12917 "EHLO mailout4.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750983AbaLWOdI (ORCPT ); Tue, 23 Dec 2014 09:33:08 -0500 Received: from epcpsbgm2.samsung.com (epcpsbgm2 [203.254.230.27]) by mailout4.samsung.com (Oracle Communications Messaging Server 7u4-24.01(7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTP id <0NH1007LIHR5XAA0@mailout4.samsung.com> for linux-media@vger.kernel.org; Tue, 23 Dec 2014 23:33:05 +0900 (KST) X-AuditID: cbfee61b-f79d76d0000024d6-25-54997d21acd6 Received: from epmmp1.local.host ( [203.254.227.16]) by epcpsbgm2.samsung.com (EPCPMTA) with SMTP id 65.8B.09430.12D79945; Tue, 23 Dec 2014 23:33:05 +0900 (KST) Received: from AMDC1342.digital.local ([106.116.147.39]) by mmp1.samsung.com (Oracle Communications Messaging Server 7u4-24.01 (7.0.4.24.0) 64bit (built Nov 17 2011)) with ESMTPA id <0NH1008NBHQA9B00@mmp1.samsung.com>; Tue, 23 Dec 2014 23:33:05 +0900 (KST) From: Kamil Debski 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, Hans Verkuil Subject: [RFC 3/6] adv7604: add cec support. Date: Tue, 23 Dec 2014 15:32:19 +0100 Message-id: <1419345142-3364-4-git-send-email-k.debski@samsung.com> X-Mailer: git-send-email 1.7.9.5 In-reply-to: <1419345142-3364-1-git-send-email-k.debski@samsung.com> References: <1419345142-3364-1-git-send-email-k.debski@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFprNLMWRmVeSWpSXmKPExsVy+t9jAV3F2pkhBu+/qFtc+fqezeLDxJmM FqcmP2Oy+PH6ApvF2aY37BY9G7ayWqw9cpfdYvWzCgcOjym/N7J63O8+zuSxpf8uu0ffllWM Hp83yXmc+vqZPYAtissmJTUnsyy1SN8ugSvj7PaZjAU7zCq+zD7D3MC4W7uLkZNDQsBE4tz2 KawQtpjEhXvr2boYuTiEBBYxSnzdvJEFwulgkljx8i1TFyMHB5uApsSqex4gDSICDhLTNv5g AqlhFljIKHHp/yx2kISwgI7E3+7JzCA2i4CqxNGWb8wgvbwCzhL7VtuDmBICChJzJtmAVHAK uEjM+f2aEcQWAqrY2feZbQIj7wJGhlWMoqkFyQXFSem5RnrFibnFpXnpesn5uZsYwUH2THoH 46oGi0OMAhyMSjy8C87MCBFiTSwrrsw9xCjBwawkwiufPjNEiDclsbIqtSg/vqg0J7X4EKM0 B4uSOK+SfVuIkEB6YklqdmpqQWoRTJaJg1OqgVFQsPzoa5vpf9nED/2ev9c3/Hfo/Nqid/PE +z9ek5c7UfJc+uGMjv5qvszHn4TiBZeGbrlRnrNj/SuhmcpdV5Q3SzyrfcD3IGz1ooVbjJ/c 5jc34PnFFGwfwfAxcVtpgG7rjujoxd+37Ip+rup6YOLyBpGLR/222B0Vcytf8/+Zt8O5qRoS WzSUWIozEg21mIuKEwE+9KpsLgIAAA== Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Hans Verkuil Add CEC support ot the adv7604 driver. Signed-off-by: Hans Verkuil [k.debski@samsung.com: Merged changes from CEC Updates commit by Hans Verkuil] Signed-off-by: Kamil Debski --- 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 e43dd2e..f0ea929 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -42,6 +42,7 @@ #include #include #include +#include static int debug; module_param(debug, int, 0644); @@ -158,6 +159,10 @@ struct adv7604_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; @@ -1935,6 +1940,176 @@ static int adv7604_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 adv7604_isr(struct v4l2_subdev *sd, u32 status, bool *handled) { struct adv7604_state *state = to_state(sd); @@ -1980,6 +2155,9 @@ static int adv7604_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) { @@ -2374,6 +2552,10 @@ static const struct v4l2_subdev_video_ops adv7604_video_ops = { .s_dv_timings = adv7604_s_dv_timings, .g_dv_timings = adv7604_g_dv_timings, .query_dv_timings = adv7604_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 adv7604_pad_ops = {