// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for the Conexant CX23885/7/8 PCIe bridge
*
* CX23888 Integrated Consumer Infrared Controller
*
* Copyright (C) 2009 Andy Walls <awalls@md.metrocast.net>
*/
#include "cx23885.h"
#include "cx23888-ir.h"
#include <linux/kfifo.h>
#include <linux/slab.h>
#include <media/v4l2-device.h>
#include <media/rc-core.h>
static unsigned int ir_888_debug;
module_param(ir_888_debug, int, 0644);
MODULE_PARM_DESC(ir_888_debug, "enable debug messages [CX23888 IR controller]");
#define CX23888_IR_REG_BASE 0x170000
/*
* These CX23888 register offsets have a straightforward one to one mapping
* to the CX23885 register offsets of 0x200 through 0x218
*/
#define CX23888_IR_CNTRL_REG 0x170000
#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
/* CX23888 specific control flag */
#define CNTRL_IVO 0x00008000
#define CX23888_IR_TXCLK_REG 0x170004
#define TXCLK_TCD 0x0000FFFF
#define CX23888_IR_RXCLK_REG 0x170008
#define RXCLK_RCD 0x0000FFFF
#define CX23888_IR_CDUTY_REG 0x17000C
#define CDUTY_CDC 0x0000000F
#define CX23888_IR_STATS_REG 0x170010
#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 CX23888_IR_IRQEN_REG 0x170014
#define IRQEN_RTE 0x00000001
#define IRQEN_ROE 0x00000002
#define IRQEN_RSE 0x00000010
#define IRQEN_TSE 0x00000020
#define CX23888_IR_FILTR_REG 0x170018
#define FILTR_LPF 0x0000FFFF
/* This register doesn't follow the pattern; it's 0x23C on a CX23885 */
#define CX23888_IR_FIFO_REG 0x170040
#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
/* CX23888 unique registers */
#define CX23888_IR_SEEDP_REG 0x17001C
#define CX23888_IR_TIMOL_REG 0x170020
#define CX23888_IR_WAKE0_REG 0x170024
#define CX23888_IR_WAKE1_REG 0x170028
#define CX23888_IR_WAKE2_REG 0x17002C
#define CX23888_IR_MASK0_REG 0x170030
#define CX23888_IR_MASK1_REG 0x170034
#define CX23888_IR_MAKS2_REG 0x170038
#define CX23888_IR_DPIPG_REG 0x17003C
#define CX23888_IR_LEARN_REG 0x170044
#define CX23888_VIDCLK_FREQ 108000000 /* 108 MHz, BT.656 */
#define CX23888_IR_REFCLK_FREQ (CX23888_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 cx23888_ir_fifo_rec {
u32 hw_fifo_data;
struct ir_raw_event ir_core_data;
};
#define CX23888_IR_RX_KFIFO_SIZE (256 * sizeof(union cx23888_ir_fifo_rec))
#define CX23888_IR_TX_KFIFO_SIZE (256 * sizeof(union cx23888_ir_fifo_rec))
struct cx23888_ir_state {
struct v4l2_subdev sd;
struct cx23885_dev *dev;
struct v4l2_subdev_ir_parameters rx_params;
struct mutex rx_params_lock;
atomic_t rxclk_divider;
atomic_t rx_invert;
struct kfifo rx_kfifo;
spinlock_t rx_kfifo_lock;
struct v4l2_subdev_ir_parameters tx_params;
struct mutex tx_params_lock;
atomic_t txclk_divider;
};
static inline struct cx23888_ir_state *to_state(struct v4l2_subdev *sd)
{
return v4l2_get_subdevdata(sd);
}
/*
* IR register block read and write functions
*/
static
inline int cx23888_ir_write4(struct cx23885_dev *dev, u32 addr, u32 value)
{
cx_write(addr, value);
return 0;
}
static inline u32 cx23888_ir_read4(struct cx23885_dev *dev, u32 addr)
{
return cx_read(addr);
}
static inline int cx23888_ir_and_or4(struct cx23885_dev *dev, u32 addr,
u32 and_mask, u32 or_value)
{
cx_andor(addr, ~and_mask, or_value);
return 0;
}
/*
* 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