summaryrefslogtreecommitdiff
path: root/drivers/media/platform/seco-cec/seco-cec.c
diff options
context:
space:
mode:
authorEttore Chimenti <ek5.chimenti@gmail.com>2018-10-21 12:58:20 -0400
committerMauro Carvalho Chehab <mchehab+samsung@kernel.org>2018-11-22 13:17:10 -0500
commitdaef95769b3a1d60afc31fa97578824a2ff39915 (patch)
treeb38b83051fdbd4933bc423b6850179c3f5381ac0 /drivers/media/platform/seco-cec/seco-cec.c
parentb03c2fb97adcc65d3c4098c4aa41fbaa6623ebf2 (diff)
media: seco-cec: add Consumer-IR support
Introduce support for Consumer-IR into seco-cec driver, as it shares the same interrupt for receiving messages. The device decodes RC5 signals only, defaults to hauppauge mapping. It will spawn an input interface using the RC framework (like CEC device). Signed-off-by: Ettore Chimenti <ek5.chimenti@gmail.com> Reviewed-by: Sean Young <sean@mess.org> Signed-off-by: Hans Verkuil <hansverk@cisco.com> Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
Diffstat (limited to 'drivers/media/platform/seco-cec/seco-cec.c')
-rw-r--r--drivers/media/platform/seco-cec/seco-cec.c125
1 files changed, 124 insertions, 1 deletions
diff --git a/drivers/media/platform/seco-cec/seco-cec.c b/drivers/media/platform/seco-cec/seco-cec.c
index 85ef161742d8..8308873c53ab 100644
--- a/drivers/media/platform/seco-cec/seco-cec.c
+++ b/drivers/media/platform/seco-cec/seco-cec.c
@@ -26,6 +26,8 @@ struct secocec_data {
struct platform_device *pdev;
struct cec_adapter *cec_adap;
struct cec_notifier *notifier;
+ struct rc_dev *ir;
+ char ir_input_phys[32];
int irq;
};
@@ -340,6 +342,114 @@ struct cec_adap_ops secocec_cec_adap_ops = {
.adap_transmit = secocec_adap_transmit,
};
+#ifdef CONFIG_VIDEO_SECO_RC
+static int secocec_ir_probe(void *priv)
+{
+ struct secocec_data *cec = priv;
+ struct device *dev = cec->dev;
+ int status;
+ u16 val;
+
+ /* Prepare the RC input device */
+ cec->ir = devm_rc_allocate_device(dev, RC_DRIVER_SCANCODE);
+ if (!cec->ir)
+ return -ENOMEM;
+
+ snprintf(cec->ir_input_phys, sizeof(cec->ir_input_phys),
+ "%s/input0", dev_name(dev));
+
+ cec->ir->device_name = dev_name(dev);
+ cec->ir->input_phys = cec->ir_input_phys;
+ cec->ir->input_id.bustype = BUS_HOST;
+ cec->ir->input_id.vendor = 0;
+ cec->ir->input_id.product = 0;
+ cec->ir->input_id.version = 1;
+ cec->ir->driver_name = SECOCEC_DEV_NAME;
+ cec->ir->allowed_protocols = RC_PROTO_BIT_RC5;
+ cec->ir->priv = cec;
+ cec->ir->map_name = RC_MAP_HAUPPAUGE;
+ cec->ir->timeout = MS_TO_NS(100);
+
+ /* Clear the status register */
+ status = smb_rd16(SECOCEC_STATUS_REG_1, &val);
+ if (status != 0)
+ goto err;
+
+ status = smb_wr16(SECOCEC_STATUS_REG_1, val);
+ if (status != 0)
+ goto err;
+
+ /* Enable the interrupts */
+ status = smb_rd16(SECOCEC_ENABLE_REG_1, &val);
+ if (status != 0)
+ goto err;
+
+ status = smb_wr16(SECOCEC_ENABLE_REG_1,
+ val | SECOCEC_ENABLE_REG_1_IR);
+ if (status != 0)
+ goto err;
+
+ dev_dbg(dev, "IR enabled");
+
+ status = devm_rc_register_device(dev, cec->ir);
+
+ if (status) {
+ dev_err(dev, "Failed to prepare input device");
+ cec->ir = NULL;
+ goto err;
+ }
+
+ return 0;
+
+err:
+ smb_rd16(SECOCEC_ENABLE_REG_1, &val);
+
+ smb_wr16(SECOCEC_ENABLE_REG_1,
+ val & ~SECOCEC_ENABLE_REG_1_IR);
+
+ dev_dbg(dev, "IR disabled");
+ return status;
+}
+
+static int secocec_ir_rx(struct secocec_data *priv)
+{
+ struct secocec_data *cec = priv;
+ struct device *dev = cec->dev;
+ u16 val, status, key, addr, toggle;
+
+ if (!cec->ir)
+ return -ENODEV;
+
+ status = smb_rd16(SECOCEC_IR_READ_DATA, &val);
+ if (status != 0)
+ goto err;
+
+ key = val & SECOCEC_IR_COMMAND_MASK;
+ addr = (val & SECOCEC_IR_ADDRESS_MASK) >> SECOCEC_IR_ADDRESS_SHL;
+ toggle = (val & SECOCEC_IR_TOGGLE_MASK) >> SECOCEC_IR_TOGGLE_SHL;
+
+ rc_keydown(cec->ir, RC_PROTO_RC5, RC_SCANCODE_RC5(addr, key), toggle);
+
+ dev_dbg(dev, "IR key pressed: 0x%02x addr 0x%02x toggle 0x%02x", key,
+ addr, toggle);
+
+ return 0;
+
+err:
+ dev_err(dev, "IR Receive message failed (%d)", status);
+ return -EIO;
+}
+#else
+static void secocec_ir_rx(struct secocec_data *priv)
+{
+}
+
+static int secocec_ir_probe(void *priv)
+{
+ return 0;
+}
+#endif
+
static irqreturn_t secocec_irq_handler(int irq, void *priv)
{
struct secocec_data *cec = priv;
@@ -374,7 +484,8 @@ static irqreturn_t secocec_irq_handler(int irq, void *priv)
if (status_val & SECOCEC_STATUS_REG_1_IR) {
val |= SECOCEC_STATUS_REG_1_IR;
- /* TODO IRDA RX */
+
+ secocec_ir_rx(cec);
}
/* Reset status register */
@@ -542,6 +653,10 @@ static int secocec_probe(struct platform_device *pdev)
if (secocec->notifier)
cec_register_cec_notifier(secocec->cec_adap, secocec->notifier);
+ ret = secocec_ir_probe(secocec);
+ if (ret)
+ goto err_delete_adapter;
+
platform_set_drvdata(pdev, secocec);
dev_dbg(dev, "Device registered");
@@ -559,7 +674,15 @@ err:
static int secocec_remove(struct platform_device *pdev)
{
struct secocec_data *secocec = platform_get_drvdata(pdev);
+ u16 val;
+
+ if (secocec->ir) {
+ smb_rd16(SECOCEC_ENABLE_REG_1, &val);
+ smb_wr16(SECOCEC_ENABLE_REG_1, val & ~SECOCEC_ENABLE_REG_1_IR);
+
+ dev_dbg(&pdev->dev, "IR disabled");
+ }
cec_unregister_adapter(secocec->cec_adap);
if (secocec->notifier)