// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for the Conexant CX2584x Audio/Video decoder chip and related cores
*
* Integrated Consumer Infrared Controller
*
* Copyright (C) 2010 Andy Walls <awalls@md.metrocast.net>
*/
#include <linux/slab.h>
#include <linux/kfifo.h>
#include <linux/module.h>
#include <media/drv-intf/cx25840.h>
#include <media/rc-core.h>
#include "cx25840-core.h"
static unsigned int ir_debug;
module_param(ir_debug, int, 0644);
MODULE_PARM_DESC(ir_debug, "enable integrated IR debug messages");
#define CX25840_IR_REG_BASE 0x200
#define CX25840_IR_CNTRL_REG 0x200
#define CNTRL_WIN_3_3 0x00000000
#define CNTRL_WIN_4_3 0x00000001
#define CNTRL_WIN_3_4 0x00000002
#define CNTRL_WIN_4_4 0x00000003
#define CNTRL_WIN 0x00000003
#define CNTRL_EDG_NONE 0x00000000
#define CNTRL_EDG_FALL 0x00000004
#define CNTRL_EDG_RISE 0x00000008
#define CNTRL_EDG_BOTH 0x0000000C
#define CNTRL_EDG 0x0000000C
#define CNTRL_DMD 0x00000010
#define CNTRL_MOD 0x00000020
#define CNTRL_RFE 0x00000040
#define CNTRL_TFE 0x00000080
#define CNTRL_RXE 0x00000100
#define CNTRL_TXE 0x00000200
#define CNTRL_RIC 0x00000400
#define CNTRL_TIC 0x00000800
#define CNTRL_CPL 0x00001000
#define CNTRL_LBM 0x00002000
#define CNTRL_R 0x00004000
#define CX25840_IR_TXCLK_REG 0x204
#define TXCLK_TCD 0x0000FFFF
#define CX25840_IR_RXCLK_REG 0x208
#define RXCLK_RCD 0x0000FFFF
#define CX25840_IR_CDUTY_REG 0x20C
#define CDUTY_CDC 0x0000000F
#define CX25840_IR_STATS_REG 0x210
#define STATS_RTO 0x00000001
#define STATS_ROR 0x00000002
#define STATS_RBY 0x00000004
#define STATS_TBY 0x00000008
#define STATS_RSR 0x00000010
#define STATS_TSR 0x00000020
#define CX25840_IR_IRQEN_REG 0x214
#define IRQEN_RTE 0x00000001
#define IRQEN_ROE 0x00000002
#define IRQEN_RSE 0x00000010
#define IRQEN_TSE 0x00000020
#define IRQEN_MSK 0x00000033
#define CX25840_IR_FILTR_REG 0x218
#define FILTR_LPF 0x0000FFFF
#define CX25840_IR_FIFO_REG 0x23C
#define FIFO_RXTX 0x0000FFFF
#define FIFO_RXTX_LVL 0x00010000
#define FIFO_RXTX_RTO 0x0001FFFF
#define FIFO_RX_NDV 0x00020000
#define FIFO_RX_DEPTH 8
#define FIFO_TX_DEPTH 8
#define CX25840_VIDCLK_FREQ 108000000 /* 108 MHz, BT.656 */
#define CX25840_IR_REFCLK_FREQ (CX25840_VIDCLK_FREQ / 2)
/*
* We use this union internally for convenience, but callers to tx_write
* and rx_read will be expecting records of type struct ir_raw_event.
* Always ensure the size of this union is dictated by struct ir_raw_event.
*/
union cx25840_ir_fifo_rec {
u32 hw_fifo_data;
struct ir_raw_event ir_core_data;
};
#define CX25840_IR_RX_KFIFO_SIZE (256 * sizeof(union cx25840_ir_fifo_rec))
#define CX25840_IR_TX_KFIFO_SIZE (256 * sizeof(union cx25840_ir_fifo_rec))
struct cx25840_ir_state {
struct i2c_client *c;
struct v4l2_subdev_ir_parameters rx_params;
struct mutex rx_params_lock; /* protects Rx parameter settings cache */
atomic_t rxclk_divider;
atomic_t rx_invert;
struct kfifo rx_kfifo;
spinlock_t rx_kfifo_lock; /* protect Rx data kfifo */
struct v4l2_subdev_ir_parameters tx_params;
struct mutex tx_params_lock; /* protects Tx parameter settings cache */
atomic_t txclk_divider;
};
static inline struct cx25840_ir_state *to_ir_state(struct v4l2_subdev *sd)
{
struct cx25840_state *state = to_state(sd);
return state ? state->ir_state : NULL;
}
/*
* Rx and Tx Clock Divider register computations
*
* Note the largest clock divider value of 0xffff corresponds to:
* (0xffff + 1) * 1000 / 108/2 MHz = 1,213,629.629... ns
* which fits in 21 bits, so we'll use unsigned int for time arguments.
*/
static inline u16 count_to_clock_divider(unsigned int d)
{
if (d > RXCLK_RCD + 1)
d = RXCLK_RCD;
else if (d < 2)
d = 1;
else
d--;
return (u16) d;
}
static inline u16 ns_to_clock_divider(unsigned int ns)
{
return count_to_clock_divider(
DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ / 1000000 * ns, 1000));
}
static inline unsigned int clock_divider_to_ns(unsigned int divider)
{
/* Period of the Rx or Tx clock in ns */
return DIV_ROUND_CLOSEST((divider + 1) * 1000,
CX25840_IR_REFCLK_FREQ / 1000000);
}
static