1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Driver for the Conexant CX23885/7/8 PCIe bridge |
4 | * |
5 | * Infrared device support routines - non-input, non-vl42_subdev routines |
6 | * |
7 | * Copyright (C) 2009 Andy Walls <awalls@md.metrocast.net> |
8 | */ |
9 | |
10 | #include "cx23885.h" |
11 | #include "cx23885-ir.h" |
12 | #include "cx23885-input.h" |
13 | |
14 | #include <media/v4l2-device.h> |
15 | |
16 | #define CX23885_IR_RX_FIFO_SERVICE_REQ 0 |
17 | #define CX23885_IR_RX_END_OF_RX_DETECTED 1 |
18 | #define CX23885_IR_RX_HW_FIFO_OVERRUN 2 |
19 | #define CX23885_IR_RX_SW_FIFO_OVERRUN 3 |
20 | |
21 | #define CX23885_IR_TX_FIFO_SERVICE_REQ 0 |
22 | |
23 | |
24 | void cx23885_ir_rx_work_handler(struct work_struct *work) |
25 | { |
26 | struct cx23885_dev *dev = |
27 | container_of(work, struct cx23885_dev, ir_rx_work); |
28 | u32 events = 0; |
29 | unsigned long *notifications = &dev->ir_rx_notifications; |
30 | |
31 | if (test_and_clear_bit(CX23885_IR_RX_SW_FIFO_OVERRUN, addr: notifications)) |
32 | events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN; |
33 | if (test_and_clear_bit(CX23885_IR_RX_HW_FIFO_OVERRUN, addr: notifications)) |
34 | events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN; |
35 | if (test_and_clear_bit(CX23885_IR_RX_END_OF_RX_DETECTED, addr: notifications)) |
36 | events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED; |
37 | if (test_and_clear_bit(CX23885_IR_RX_FIFO_SERVICE_REQ, addr: notifications)) |
38 | events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ; |
39 | |
40 | if (events == 0) |
41 | return; |
42 | |
43 | if (dev->kernel_ir) |
44 | cx23885_input_rx_work_handler(dev, events); |
45 | } |
46 | |
47 | void cx23885_ir_tx_work_handler(struct work_struct *work) |
48 | { |
49 | struct cx23885_dev *dev = |
50 | container_of(work, struct cx23885_dev, ir_tx_work); |
51 | u32 events = 0; |
52 | unsigned long *notifications = &dev->ir_tx_notifications; |
53 | |
54 | if (test_and_clear_bit(CX23885_IR_TX_FIFO_SERVICE_REQ, addr: notifications)) |
55 | events |= V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ; |
56 | |
57 | if (events == 0) |
58 | return; |
59 | |
60 | } |
61 | |
62 | /* Possibly called in an IRQ context */ |
63 | void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events) |
64 | { |
65 | struct cx23885_dev *dev = to_cx23885(v4l2_dev: sd->v4l2_dev); |
66 | unsigned long *notifications = &dev->ir_rx_notifications; |
67 | |
68 | if (events & V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ) |
69 | set_bit(CX23885_IR_RX_FIFO_SERVICE_REQ, addr: notifications); |
70 | if (events & V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED) |
71 | set_bit(CX23885_IR_RX_END_OF_RX_DETECTED, addr: notifications); |
72 | if (events & V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN) |
73 | set_bit(CX23885_IR_RX_HW_FIFO_OVERRUN, addr: notifications); |
74 | if (events & V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN) |
75 | set_bit(CX23885_IR_RX_SW_FIFO_OVERRUN, addr: notifications); |
76 | |
77 | /* |
78 | * For the integrated AV core, we are already in a workqueue context. |
79 | * For the CX23888 integrated IR, we are in an interrupt context. |
80 | */ |
81 | if (sd == dev->sd_cx25840) |
82 | cx23885_ir_rx_work_handler(work: &dev->ir_rx_work); |
83 | else |
84 | schedule_work(work: &dev->ir_rx_work); |
85 | } |
86 | |
87 | /* Possibly called in an IRQ context */ |
88 | void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events) |
89 | { |
90 | struct cx23885_dev *dev = to_cx23885(v4l2_dev: sd->v4l2_dev); |
91 | unsigned long *notifications = &dev->ir_tx_notifications; |
92 | |
93 | if (events & V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ) |
94 | set_bit(CX23885_IR_TX_FIFO_SERVICE_REQ, addr: notifications); |
95 | |
96 | /* |
97 | * For the integrated AV core, we are already in a workqueue context. |
98 | * For the CX23888 integrated IR, we are in an interrupt context. |
99 | */ |
100 | if (sd == dev->sd_cx25840) |
101 | cx23885_ir_tx_work_handler(work: &dev->ir_tx_work); |
102 | else |
103 | schedule_work(work: &dev->ir_tx_work); |
104 | } |
105 | |