diff options
Diffstat (limited to 'drivers/media/rc/nuvoton-cir.c')
| -rw-r--r-- | drivers/media/rc/nuvoton-cir.c | 127 |
1 files changed, 127 insertions, 0 deletions
diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index 85af7a869167..baeb5971fd52 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c | |||
| @@ -526,6 +526,130 @@ static int nvt_set_tx_carrier(struct rc_dev *dev, u32 carrier) | |||
| 526 | return 0; | 526 | return 0; |
| 527 | } | 527 | } |
| 528 | 528 | ||
| 529 | static int nvt_write_wakeup_codes(struct rc_dev *dev, | ||
| 530 | const u8 *wakeup_sample_buf, int count) | ||
| 531 | { | ||
| 532 | int i = 0; | ||
| 533 | u8 reg, reg_learn_mode; | ||
| 534 | unsigned long flags; | ||
| 535 | struct nvt_dev *nvt = dev->priv; | ||
| 536 | |||
| 537 | nvt_dbg_wake("writing wakeup samples"); | ||
| 538 | |||
| 539 | reg = nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRCON); | ||
| 540 | reg_learn_mode = reg & ~CIR_WAKE_IRCON_MODE0; | ||
| 541 | reg_learn_mode |= CIR_WAKE_IRCON_MODE1; | ||
| 542 | |||
| 543 | /* Lock the learn area to prevent racing with wake-isr */ | ||
| 544 | spin_lock_irqsave(&nvt->nvt_lock, flags); | ||
| 545 | |||
| 546 | /* Enable fifo writes */ | ||
| 547 | nvt_cir_wake_reg_write(nvt, reg_learn_mode, CIR_WAKE_IRCON); | ||
| 548 | |||
| 549 | /* Clear cir wake rx fifo */ | ||
| 550 | nvt_clear_cir_wake_fifo(nvt); | ||
| 551 | |||
| 552 | if (count > WAKE_FIFO_LEN) { | ||
| 553 | nvt_dbg_wake("HW FIFO too small for all wake samples"); | ||
| 554 | count = WAKE_FIFO_LEN; | ||
| 555 | } | ||
| 556 | |||
| 557 | if (count) | ||
| 558 | pr_info("Wake samples (%d) =", count); | ||
| 559 | else | ||
| 560 | pr_info("Wake sample fifo cleared"); | ||
| 561 | |||
| 562 | /* Write wake samples to fifo */ | ||
| 563 | for (i = 0; i < count; i++) { | ||
| 564 | pr_cont(" %02x", wakeup_sample_buf[i]); | ||
| 565 | nvt_cir_wake_reg_write(nvt, wakeup_sample_buf[i], | ||
| 566 | CIR_WAKE_WR_FIFO_DATA); | ||
| 567 | } | ||
| 568 | pr_cont("\n"); | ||
| 569 | |||
| 570 | /* Switch cir to wakeup mode and disable fifo writing */ | ||
| 571 | nvt_cir_wake_reg_write(nvt, reg, CIR_WAKE_IRCON); | ||
| 572 | |||
| 573 | /* Set number of bytes needed for wake */ | ||
| 574 | nvt_cir_wake_reg_write(nvt, count ? count : | ||
| 575 | CIR_WAKE_FIFO_CMP_BYTES, | ||
| 576 | CIR_WAKE_FIFO_CMP_DEEP); | ||
| 577 | |||
| 578 | spin_unlock_irqrestore(&nvt->nvt_lock, flags); | ||
| 579 | |||
| 580 | return 0; | ||
| 581 | } | ||
| 582 | |||
| 583 | static int nvt_ir_raw_set_wakeup_filter(struct rc_dev *dev, | ||
| 584 | struct rc_scancode_filter *sc_filter) | ||
| 585 | { | ||
| 586 | u8 *reg_buf; | ||
| 587 | u8 buf_val; | ||
| 588 | int i, ret, count; | ||
| 589 | unsigned int val; | ||
| 590 | struct ir_raw_event *raw; | ||
| 591 | bool complete; | ||
| 592 | |||
| 593 | /* Require both mask and data to be set before actually committing */ | ||
| 594 | if (!sc_filter->mask || !sc_filter->data) | ||
| 595 | return 0; | ||
| 596 | |||
| 597 | raw = kmalloc_array(WAKE_FIFO_LEN, sizeof(*raw), GFP_KERNEL); | ||
| 598 | if (!raw) | ||
| 599 | return -ENOMEM; | ||
| 600 | |||
| 601 | ret = ir_raw_encode_scancode(dev->enabled_wakeup_protocols, sc_filter, | ||
| 602 | raw, WAKE_FIFO_LEN); | ||
| 603 | complete = (ret != -ENOBUFS); | ||
| 604 | if (!complete) | ||
| 605 | ret = WAKE_FIFO_LEN; | ||
| 606 | else if (ret < 0) | ||
| 607 | goto out_raw; | ||
| 608 | |||
| 609 | reg_buf = kmalloc_array(WAKE_FIFO_LEN, sizeof(*reg_buf), GFP_KERNEL); | ||
| 610 | if (!reg_buf) { | ||
| 611 | ret = -ENOMEM; | ||
| 612 | goto out_raw; | ||
| 613 | } | ||
| 614 | |||
| 615 | /* Inspect the ir samples */ | ||
| 616 | for (i = 0, count = 0; i < ret && count < WAKE_FIFO_LEN; ++i) { | ||
| 617 | val = NS_TO_US((raw[i]).duration) / SAMPLE_PERIOD; | ||
| 618 | |||
| 619 | /* Split too large values into several smaller ones */ | ||
| 620 | while (val > 0 && count < WAKE_FIFO_LEN) { | ||
| 621 | |||
| 622 | /* Skip last value for better comparison tolerance */ | ||
| 623 | if (complete && i == ret - 1 && val < BUF_LEN_MASK) | ||
| 624 | break; | ||
| 625 | |||
| 626 | /* Clamp values to BUF_LEN_MASK at most */ | ||
| 627 | buf_val = (val > BUF_LEN_MASK) ? BUF_LEN_MASK : val; | ||
| 628 | |||
| 629 | reg_buf[count] = buf_val; | ||
| 630 | val -= buf_val; | ||
| 631 | if ((raw[i]).pulse) | ||
| 632 | reg_buf[count] |= BUF_PULSE_BIT; | ||
| 633 | count++; | ||
| 634 | } | ||
| 635 | } | ||
| 636 | |||
| 637 | ret = nvt_write_wakeup_codes(dev, reg_buf, count); | ||
| 638 | |||
| 639 | kfree(reg_buf); | ||
| 640 | out_raw: | ||
| 641 | kfree(raw); | ||
| 642 | |||
| 643 | return ret; | ||
| 644 | } | ||
| 645 | |||
| 646 | /* Dummy implementation. nuvoton is agnostic to the protocol used */ | ||
| 647 | static int nvt_ir_raw_change_wakeup_protocol(struct rc_dev *dev, | ||
| 648 | u64 *rc_type) | ||
| 649 | { | ||
| 650 | return 0; | ||
| 651 | } | ||
| 652 | |||
| 529 | /* | 653 | /* |
| 530 | * nvt_tx_ir | 654 | * nvt_tx_ir |
| 531 | * | 655 | * |
| @@ -1043,11 +1167,14 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) | |||
| 1043 | /* Set up the rc device */ | 1167 | /* Set up the rc device */ |
| 1044 | rdev->priv = nvt; | 1168 | rdev->priv = nvt; |
| 1045 | rdev->driver_type = RC_DRIVER_IR_RAW; | 1169 | rdev->driver_type = RC_DRIVER_IR_RAW; |
| 1170 | rdev->encode_wakeup = true; | ||
| 1046 | rdev->allowed_protocols = RC_BIT_ALL; | 1171 | rdev->allowed_protocols = RC_BIT_ALL; |
| 1047 | rdev->open = nvt_open; | 1172 | rdev->open = nvt_open; |
| 1048 | rdev->close = nvt_close; | 1173 | rdev->close = nvt_close; |
| 1049 | rdev->tx_ir = nvt_tx_ir; | 1174 | rdev->tx_ir = nvt_tx_ir; |
| 1050 | rdev->s_tx_carrier = nvt_set_tx_carrier; | 1175 | rdev->s_tx_carrier = nvt_set_tx_carrier; |
| 1176 | rdev->s_wakeup_filter = nvt_ir_raw_set_wakeup_filter; | ||
| 1177 | rdev->change_wakeup_protocol = nvt_ir_raw_change_wakeup_protocol; | ||
| 1051 | rdev->input_name = "Nuvoton w836x7hg Infrared Remote Transceiver"; | 1178 | rdev->input_name = "Nuvoton w836x7hg Infrared Remote Transceiver"; |
| 1052 | rdev->input_phys = "nuvoton/cir0"; | 1179 | rdev->input_phys = "nuvoton/cir0"; |
| 1053 | rdev->input_id.bustype = BUS_HOST; | 1180 | rdev->input_id.bustype = BUS_HOST; |
