diff options
Diffstat (limited to 'drivers/media/platform/seco-cec/seco-cec.c')
-rw-r--r-- | drivers/media/platform/seco-cec/seco-cec.c | 125 |
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) |