162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Driver for the Conexant CX23885/7/8 PCIe bridge
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Infrared device support routines - non-input, non-vl42_subdev routines
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  Copyright (C) 2009  Andy Walls <awalls@md.metrocast.net>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "cx23885.h"
1162306a36Sopenharmony_ci#include "cx23885-ir.h"
1262306a36Sopenharmony_ci#include "cx23885-input.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <media/v4l2-device.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define CX23885_IR_RX_FIFO_SERVICE_REQ		0
1762306a36Sopenharmony_ci#define CX23885_IR_RX_END_OF_RX_DETECTED	1
1862306a36Sopenharmony_ci#define CX23885_IR_RX_HW_FIFO_OVERRUN		2
1962306a36Sopenharmony_ci#define CX23885_IR_RX_SW_FIFO_OVERRUN		3
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define CX23885_IR_TX_FIFO_SERVICE_REQ		0
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_civoid cx23885_ir_rx_work_handler(struct work_struct *work)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	struct cx23885_dev *dev =
2762306a36Sopenharmony_ci			     container_of(work, struct cx23885_dev, ir_rx_work);
2862306a36Sopenharmony_ci	u32 events = 0;
2962306a36Sopenharmony_ci	unsigned long *notifications = &dev->ir_rx_notifications;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	if (test_and_clear_bit(CX23885_IR_RX_SW_FIFO_OVERRUN, notifications))
3262306a36Sopenharmony_ci		events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN;
3362306a36Sopenharmony_ci	if (test_and_clear_bit(CX23885_IR_RX_HW_FIFO_OVERRUN, notifications))
3462306a36Sopenharmony_ci		events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN;
3562306a36Sopenharmony_ci	if (test_and_clear_bit(CX23885_IR_RX_END_OF_RX_DETECTED, notifications))
3662306a36Sopenharmony_ci		events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED;
3762306a36Sopenharmony_ci	if (test_and_clear_bit(CX23885_IR_RX_FIFO_SERVICE_REQ, notifications))
3862306a36Sopenharmony_ci		events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	if (events == 0)
4162306a36Sopenharmony_ci		return;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	if (dev->kernel_ir)
4462306a36Sopenharmony_ci		cx23885_input_rx_work_handler(dev, events);
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_civoid cx23885_ir_tx_work_handler(struct work_struct *work)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	struct cx23885_dev *dev =
5062306a36Sopenharmony_ci			     container_of(work, struct cx23885_dev, ir_tx_work);
5162306a36Sopenharmony_ci	u32 events = 0;
5262306a36Sopenharmony_ci	unsigned long *notifications = &dev->ir_tx_notifications;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	if (test_and_clear_bit(CX23885_IR_TX_FIFO_SERVICE_REQ, notifications))
5562306a36Sopenharmony_ci		events |= V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	if (events == 0)
5862306a36Sopenharmony_ci		return;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/* Possibly called in an IRQ context */
6362306a36Sopenharmony_civoid cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev);
6662306a36Sopenharmony_ci	unsigned long *notifications = &dev->ir_rx_notifications;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	if (events & V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ)
6962306a36Sopenharmony_ci		set_bit(CX23885_IR_RX_FIFO_SERVICE_REQ, notifications);
7062306a36Sopenharmony_ci	if (events & V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED)
7162306a36Sopenharmony_ci		set_bit(CX23885_IR_RX_END_OF_RX_DETECTED, notifications);
7262306a36Sopenharmony_ci	if (events & V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN)
7362306a36Sopenharmony_ci		set_bit(CX23885_IR_RX_HW_FIFO_OVERRUN, notifications);
7462306a36Sopenharmony_ci	if (events & V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN)
7562306a36Sopenharmony_ci		set_bit(CX23885_IR_RX_SW_FIFO_OVERRUN, notifications);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	/*
7862306a36Sopenharmony_ci	 * For the integrated AV core, we are already in a workqueue context.
7962306a36Sopenharmony_ci	 * For the CX23888 integrated IR, we are in an interrupt context.
8062306a36Sopenharmony_ci	 */
8162306a36Sopenharmony_ci	if (sd == dev->sd_cx25840)
8262306a36Sopenharmony_ci		cx23885_ir_rx_work_handler(&dev->ir_rx_work);
8362306a36Sopenharmony_ci	else
8462306a36Sopenharmony_ci		schedule_work(&dev->ir_rx_work);
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci/* Possibly called in an IRQ context */
8862306a36Sopenharmony_civoid cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev);
9162306a36Sopenharmony_ci	unsigned long *notifications = &dev->ir_tx_notifications;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (events & V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ)
9462306a36Sopenharmony_ci		set_bit(CX23885_IR_TX_FIFO_SERVICE_REQ, notifications);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	/*
9762306a36Sopenharmony_ci	 * For the integrated AV core, we are already in a workqueue context.
9862306a36Sopenharmony_ci	 * For the CX23888 integrated IR, we are in an interrupt context.
9962306a36Sopenharmony_ci	 */
10062306a36Sopenharmony_ci	if (sd == dev->sd_cx25840)
10162306a36Sopenharmony_ci		cx23885_ir_tx_work_handler(&dev->ir_tx_work);
10262306a36Sopenharmony_ci	else
10362306a36Sopenharmony_ci		schedule_work(&dev->ir_tx_work);
10462306a36Sopenharmony_ci}
105