/*
* 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>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#include <linux/slab.h>
#include <linux/kfifo.h>
#include <linux/module.h>
#include <media/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 (