18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Driver for the Conexant CX2584x Audio/Video decoder chip and related cores
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Integrated Consumer Infrared Controller
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *  Copyright (C) 2010  Andy Walls <awalls@md.metrocast.net>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci#include <linux/kfifo.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <media/drv-intf/cx25840.h>
148c2ecf20Sopenharmony_ci#include <media/rc-core.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "cx25840-core.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic unsigned int ir_debug;
198c2ecf20Sopenharmony_cimodule_param(ir_debug, int, 0644);
208c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ir_debug, "enable integrated IR debug messages");
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define CX25840_IR_REG_BASE	0x200
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define CX25840_IR_CNTRL_REG	0x200
258c2ecf20Sopenharmony_ci#define CNTRL_WIN_3_3	0x00000000
268c2ecf20Sopenharmony_ci#define CNTRL_WIN_4_3	0x00000001
278c2ecf20Sopenharmony_ci#define CNTRL_WIN_3_4	0x00000002
288c2ecf20Sopenharmony_ci#define CNTRL_WIN_4_4	0x00000003
298c2ecf20Sopenharmony_ci#define CNTRL_WIN	0x00000003
308c2ecf20Sopenharmony_ci#define CNTRL_EDG_NONE	0x00000000
318c2ecf20Sopenharmony_ci#define CNTRL_EDG_FALL	0x00000004
328c2ecf20Sopenharmony_ci#define CNTRL_EDG_RISE	0x00000008
338c2ecf20Sopenharmony_ci#define CNTRL_EDG_BOTH	0x0000000C
348c2ecf20Sopenharmony_ci#define CNTRL_EDG	0x0000000C
358c2ecf20Sopenharmony_ci#define CNTRL_DMD	0x00000010
368c2ecf20Sopenharmony_ci#define CNTRL_MOD	0x00000020
378c2ecf20Sopenharmony_ci#define CNTRL_RFE	0x00000040
388c2ecf20Sopenharmony_ci#define CNTRL_TFE	0x00000080
398c2ecf20Sopenharmony_ci#define CNTRL_RXE	0x00000100
408c2ecf20Sopenharmony_ci#define CNTRL_TXE	0x00000200
418c2ecf20Sopenharmony_ci#define CNTRL_RIC	0x00000400
428c2ecf20Sopenharmony_ci#define CNTRL_TIC	0x00000800
438c2ecf20Sopenharmony_ci#define CNTRL_CPL	0x00001000
448c2ecf20Sopenharmony_ci#define CNTRL_LBM	0x00002000
458c2ecf20Sopenharmony_ci#define CNTRL_R		0x00004000
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#define CX25840_IR_TXCLK_REG	0x204
488c2ecf20Sopenharmony_ci#define TXCLK_TCD	0x0000FFFF
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci#define CX25840_IR_RXCLK_REG	0x208
518c2ecf20Sopenharmony_ci#define RXCLK_RCD	0x0000FFFF
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci#define CX25840_IR_CDUTY_REG	0x20C
548c2ecf20Sopenharmony_ci#define CDUTY_CDC	0x0000000F
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci#define CX25840_IR_STATS_REG	0x210
578c2ecf20Sopenharmony_ci#define STATS_RTO	0x00000001
588c2ecf20Sopenharmony_ci#define STATS_ROR	0x00000002
598c2ecf20Sopenharmony_ci#define STATS_RBY	0x00000004
608c2ecf20Sopenharmony_ci#define STATS_TBY	0x00000008
618c2ecf20Sopenharmony_ci#define STATS_RSR	0x00000010
628c2ecf20Sopenharmony_ci#define STATS_TSR	0x00000020
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci#define CX25840_IR_IRQEN_REG	0x214
658c2ecf20Sopenharmony_ci#define IRQEN_RTE	0x00000001
668c2ecf20Sopenharmony_ci#define IRQEN_ROE	0x00000002
678c2ecf20Sopenharmony_ci#define IRQEN_RSE	0x00000010
688c2ecf20Sopenharmony_ci#define IRQEN_TSE	0x00000020
698c2ecf20Sopenharmony_ci#define IRQEN_MSK	0x00000033
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci#define CX25840_IR_FILTR_REG	0x218
728c2ecf20Sopenharmony_ci#define FILTR_LPF	0x0000FFFF
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci#define CX25840_IR_FIFO_REG	0x23C
758c2ecf20Sopenharmony_ci#define FIFO_RXTX	0x0000FFFF
768c2ecf20Sopenharmony_ci#define FIFO_RXTX_LVL	0x00010000
778c2ecf20Sopenharmony_ci#define FIFO_RXTX_RTO	0x0001FFFF
788c2ecf20Sopenharmony_ci#define FIFO_RX_NDV	0x00020000
798c2ecf20Sopenharmony_ci#define FIFO_RX_DEPTH	8
808c2ecf20Sopenharmony_ci#define FIFO_TX_DEPTH	8
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci#define CX25840_VIDCLK_FREQ	108000000 /* 108 MHz, BT.656 */
838c2ecf20Sopenharmony_ci#define CX25840_IR_REFCLK_FREQ	(CX25840_VIDCLK_FREQ / 2)
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci/*
868c2ecf20Sopenharmony_ci * We use this union internally for convenience, but callers to tx_write
878c2ecf20Sopenharmony_ci * and rx_read will be expecting records of type struct ir_raw_event.
888c2ecf20Sopenharmony_ci * Always ensure the size of this union is dictated by struct ir_raw_event.
898c2ecf20Sopenharmony_ci */
908c2ecf20Sopenharmony_ciunion cx25840_ir_fifo_rec {
918c2ecf20Sopenharmony_ci	u32 hw_fifo_data;
928c2ecf20Sopenharmony_ci	struct ir_raw_event ir_core_data;
938c2ecf20Sopenharmony_ci};
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci#define CX25840_IR_RX_KFIFO_SIZE    (256 * sizeof(union cx25840_ir_fifo_rec))
968c2ecf20Sopenharmony_ci#define CX25840_IR_TX_KFIFO_SIZE    (256 * sizeof(union cx25840_ir_fifo_rec))
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistruct cx25840_ir_state {
998c2ecf20Sopenharmony_ci	struct i2c_client *c;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	struct v4l2_subdev_ir_parameters rx_params;
1028c2ecf20Sopenharmony_ci	struct mutex rx_params_lock; /* protects Rx parameter settings cache */
1038c2ecf20Sopenharmony_ci	atomic_t rxclk_divider;
1048c2ecf20Sopenharmony_ci	atomic_t rx_invert;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	struct kfifo rx_kfifo;
1078c2ecf20Sopenharmony_ci	spinlock_t rx_kfifo_lock; /* protect Rx data kfifo */
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	struct v4l2_subdev_ir_parameters tx_params;
1108c2ecf20Sopenharmony_ci	struct mutex tx_params_lock; /* protects Tx parameter settings cache */
1118c2ecf20Sopenharmony_ci	atomic_t txclk_divider;
1128c2ecf20Sopenharmony_ci};
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic inline struct cx25840_ir_state *to_ir_state(struct v4l2_subdev *sd)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	struct cx25840_state *state = to_state(sd);
1178c2ecf20Sopenharmony_ci	return state ? state->ir_state : NULL;
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci/*
1228c2ecf20Sopenharmony_ci * Rx and Tx Clock Divider register computations
1238c2ecf20Sopenharmony_ci *
1248c2ecf20Sopenharmony_ci * Note the largest clock divider value of 0xffff corresponds to:
1258c2ecf20Sopenharmony_ci *	(0xffff + 1) * 1000 / 108/2 MHz = 1,213,629.629... ns
1268c2ecf20Sopenharmony_ci * which fits in 21 bits, so we'll use unsigned int for time arguments.
1278c2ecf20Sopenharmony_ci */
1288c2ecf20Sopenharmony_cistatic inline u16 count_to_clock_divider(unsigned int d)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	if (d > RXCLK_RCD + 1)
1318c2ecf20Sopenharmony_ci		d = RXCLK_RCD;
1328c2ecf20Sopenharmony_ci	else if (d < 2)
1338c2ecf20Sopenharmony_ci		d = 1;
1348c2ecf20Sopenharmony_ci	else
1358c2ecf20Sopenharmony_ci		d--;
1368c2ecf20Sopenharmony_ci	return (u16) d;
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic inline u16 ns_to_clock_divider(unsigned int ns)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	return count_to_clock_divider(
1428c2ecf20Sopenharmony_ci		DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ / 1000000 * ns, 1000));
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic inline unsigned int clock_divider_to_ns(unsigned int divider)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	/* Period of the Rx or Tx clock in ns */
1488c2ecf20Sopenharmony_ci	return DIV_ROUND_CLOSEST((divider + 1) * 1000,
1498c2ecf20Sopenharmony_ci				 CX25840_IR_REFCLK_FREQ / 1000000);
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic inline u16 carrier_freq_to_clock_divider(unsigned int freq)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	return count_to_clock_divider(
1558c2ecf20Sopenharmony_ci			  DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, freq * 16));
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic inline unsigned int clock_divider_to_carrier_freq(unsigned int divider)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	return DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, (divider + 1) * 16);
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic inline u16 freq_to_clock_divider(unsigned int freq,
1648c2ecf20Sopenharmony_ci					unsigned int rollovers)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	return count_to_clock_divider(
1678c2ecf20Sopenharmony_ci		   DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ, freq * rollovers));
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic inline unsigned int clock_divider_to_freq(unsigned int divider,
1718c2ecf20Sopenharmony_ci						 unsigned int rollovers)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	return DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ,
1748c2ecf20Sopenharmony_ci				 (divider + 1) * rollovers);
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci/*
1788c2ecf20Sopenharmony_ci * Low Pass Filter register calculations
1798c2ecf20Sopenharmony_ci *
1808c2ecf20Sopenharmony_ci * Note the largest count value of 0xffff corresponds to:
1818c2ecf20Sopenharmony_ci *	0xffff * 1000 / 108/2 MHz = 1,213,611.11... ns
1828c2ecf20Sopenharmony_ci * which fits in 21 bits, so we'll use unsigned int for time arguments.
1838c2ecf20Sopenharmony_ci */
1848c2ecf20Sopenharmony_cistatic inline u16 count_to_lpf_count(unsigned int d)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	if (d > FILTR_LPF)
1878c2ecf20Sopenharmony_ci		d = FILTR_LPF;
1888c2ecf20Sopenharmony_ci	else if (d < 4)
1898c2ecf20Sopenharmony_ci		d = 0;
1908c2ecf20Sopenharmony_ci	return (u16) d;
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic inline u16 ns_to_lpf_count(unsigned int ns)
1948c2ecf20Sopenharmony_ci{
1958c2ecf20Sopenharmony_ci	return count_to_lpf_count(
1968c2ecf20Sopenharmony_ci		DIV_ROUND_CLOSEST(CX25840_IR_REFCLK_FREQ / 1000000 * ns, 1000));
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic inline unsigned int lpf_count_to_ns(unsigned int count)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	/* Duration of the Low Pass Filter rejection window in ns */
2028c2ecf20Sopenharmony_ci	return DIV_ROUND_CLOSEST(count * 1000,
2038c2ecf20Sopenharmony_ci				 CX25840_IR_REFCLK_FREQ / 1000000);
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic inline unsigned int lpf_count_to_us(unsigned int count)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	/* Duration of the Low Pass Filter rejection window in us */
2098c2ecf20Sopenharmony_ci	return DIV_ROUND_CLOSEST(count, CX25840_IR_REFCLK_FREQ / 1000000);
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci/*
2138c2ecf20Sopenharmony_ci * FIFO register pulse width count computations
2148c2ecf20Sopenharmony_ci */
2158c2ecf20Sopenharmony_cistatic u32 clock_divider_to_resolution(u16 divider)
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	/*
2188c2ecf20Sopenharmony_ci	 * Resolution is the duration of 1 tick of the readable portion of
2198c2ecf20Sopenharmony_ci	 * of the pulse width counter as read from the FIFO.  The two lsb's are
2208c2ecf20Sopenharmony_ci	 * not readable, hence the << 2.  This function returns ns.
2218c2ecf20Sopenharmony_ci	 */
2228c2ecf20Sopenharmony_ci	return DIV_ROUND_CLOSEST((1 << 2)  * ((u32) divider + 1) * 1000,
2238c2ecf20Sopenharmony_ci				 CX25840_IR_REFCLK_FREQ / 1000000);
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic u64 pulse_width_count_to_ns(u16 count, u16 divider)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	u64 n;
2298c2ecf20Sopenharmony_ci	u32 rem;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	/*
2328c2ecf20Sopenharmony_ci	 * The 2 lsb's of the pulse width timer count are not readable, hence
2338c2ecf20Sopenharmony_ci	 * the (count << 2) | 0x3
2348c2ecf20Sopenharmony_ci	 */
2358c2ecf20Sopenharmony_ci	n = (((u64) count << 2) | 0x3) * (divider + 1) * 1000; /* millicycles */
2368c2ecf20Sopenharmony_ci	rem = do_div(n, CX25840_IR_REFCLK_FREQ / 1000000);     /* / MHz => ns */
2378c2ecf20Sopenharmony_ci	if (rem >= CX25840_IR_REFCLK_FREQ / 1000000 / 2)
2388c2ecf20Sopenharmony_ci		n++;
2398c2ecf20Sopenharmony_ci	return n;
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci#if 0
2438c2ecf20Sopenharmony_ci/* Keep as we will need this for Transmit functionality */
2448c2ecf20Sopenharmony_cistatic u16 ns_to_pulse_width_count(u32 ns, u16 divider)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	u64 n;
2478c2ecf20Sopenharmony_ci	u32 d;
2488c2ecf20Sopenharmony_ci	u32 rem;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	/*
2518c2ecf20Sopenharmony_ci	 * The 2 lsb's of the pulse width timer count are not accessible, hence
2528c2ecf20Sopenharmony_ci	 * the (1 << 2)
2538c2ecf20Sopenharmony_ci	 */
2548c2ecf20Sopenharmony_ci	n = ((u64) ns) * CX25840_IR_REFCLK_FREQ / 1000000; /* millicycles */
2558c2ecf20Sopenharmony_ci	d = (1 << 2) * ((u32) divider + 1) * 1000; /* millicycles/count */
2568c2ecf20Sopenharmony_ci	rem = do_div(n, d);
2578c2ecf20Sopenharmony_ci	if (rem >= d / 2)
2588c2ecf20Sopenharmony_ci		n++;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	if (n > FIFO_RXTX)
2618c2ecf20Sopenharmony_ci		n = FIFO_RXTX;
2628c2ecf20Sopenharmony_ci	else if (n == 0)
2638c2ecf20Sopenharmony_ci		n = 1;
2648c2ecf20Sopenharmony_ci	return (u16) n;
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci#endif
2688c2ecf20Sopenharmony_cistatic unsigned int pulse_width_count_to_us(u16 count, u16 divider)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	u64 n;
2718c2ecf20Sopenharmony_ci	u32 rem;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	/*
2748c2ecf20Sopenharmony_ci	 * The 2 lsb's of the pulse width timer count are not readable, hence
2758c2ecf20Sopenharmony_ci	 * the (count << 2) | 0x3
2768c2ecf20Sopenharmony_ci	 */
2778c2ecf20Sopenharmony_ci	n = (((u64) count << 2) | 0x3) * (divider + 1);    /* cycles      */
2788c2ecf20Sopenharmony_ci	rem = do_div(n, CX25840_IR_REFCLK_FREQ / 1000000); /* / MHz => us */
2798c2ecf20Sopenharmony_ci	if (rem >= CX25840_IR_REFCLK_FREQ / 1000000 / 2)
2808c2ecf20Sopenharmony_ci		n++;
2818c2ecf20Sopenharmony_ci	return (unsigned int) n;
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci/*
2858c2ecf20Sopenharmony_ci * Pulse Clocks computations: Combined Pulse Width Count & Rx Clock Counts
2868c2ecf20Sopenharmony_ci *
2878c2ecf20Sopenharmony_ci * The total pulse clock count is an 18 bit pulse width timer count as the most
2888c2ecf20Sopenharmony_ci * significant part and (up to) 16 bit clock divider count as a modulus.
2898c2ecf20Sopenharmony_ci * When the Rx clock divider ticks down to 0, it increments the 18 bit pulse
2908c2ecf20Sopenharmony_ci * width timer count's least significant bit.
2918c2ecf20Sopenharmony_ci */
2928c2ecf20Sopenharmony_cistatic u64 ns_to_pulse_clocks(u32 ns)
2938c2ecf20Sopenharmony_ci{
2948c2ecf20Sopenharmony_ci	u64 clocks;
2958c2ecf20Sopenharmony_ci	u32 rem;
2968c2ecf20Sopenharmony_ci	clocks = CX25840_IR_REFCLK_FREQ / 1000000 * (u64) ns; /* millicycles  */
2978c2ecf20Sopenharmony_ci	rem = do_div(clocks, 1000);                         /* /1000 = cycles */
2988c2ecf20Sopenharmony_ci	if (rem >= 1000 / 2)
2998c2ecf20Sopenharmony_ci		clocks++;
3008c2ecf20Sopenharmony_ci	return clocks;
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_cistatic u16 pulse_clocks_to_clock_divider(u64 count)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	do_div(count, (FIFO_RXTX << 2) | 0x3);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	/* net result needs to be rounded down and decremented by 1 */
3088c2ecf20Sopenharmony_ci	if (count > RXCLK_RCD + 1)
3098c2ecf20Sopenharmony_ci		count = RXCLK_RCD;
3108c2ecf20Sopenharmony_ci	else if (count < 2)
3118c2ecf20Sopenharmony_ci		count = 1;
3128c2ecf20Sopenharmony_ci	else
3138c2ecf20Sopenharmony_ci		count--;
3148c2ecf20Sopenharmony_ci	return (u16) count;
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci/*
3188c2ecf20Sopenharmony_ci * IR Control Register helpers
3198c2ecf20Sopenharmony_ci */
3208c2ecf20Sopenharmony_cienum tx_fifo_watermark {
3218c2ecf20Sopenharmony_ci	TX_FIFO_HALF_EMPTY = 0,
3228c2ecf20Sopenharmony_ci	TX_FIFO_EMPTY      = CNTRL_TIC,
3238c2ecf20Sopenharmony_ci};
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cienum rx_fifo_watermark {
3268c2ecf20Sopenharmony_ci	RX_FIFO_HALF_FULL = 0,
3278c2ecf20Sopenharmony_ci	RX_FIFO_NOT_EMPTY = CNTRL_RIC,
3288c2ecf20Sopenharmony_ci};
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic inline void control_tx_irq_watermark(struct i2c_client *c,
3318c2ecf20Sopenharmony_ci					    enum tx_fifo_watermark level)
3328c2ecf20Sopenharmony_ci{
3338c2ecf20Sopenharmony_ci	cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_TIC, level);
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic inline void control_rx_irq_watermark(struct i2c_client *c,
3378c2ecf20Sopenharmony_ci					    enum rx_fifo_watermark level)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_RIC, level);
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_cistatic inline void control_tx_enable(struct i2c_client *c, bool enable)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~(CNTRL_TXE | CNTRL_TFE),
3458c2ecf20Sopenharmony_ci			enable ? (CNTRL_TXE | CNTRL_TFE) : 0);
3468c2ecf20Sopenharmony_ci}
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_cistatic inline void control_rx_enable(struct i2c_client *c, bool enable)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~(CNTRL_RXE | CNTRL_RFE),
3518c2ecf20Sopenharmony_ci			enable ? (CNTRL_RXE | CNTRL_RFE) : 0);
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic inline void control_tx_modulation_enable(struct i2c_client *c,
3558c2ecf20Sopenharmony_ci						bool enable)
3568c2ecf20Sopenharmony_ci{
3578c2ecf20Sopenharmony_ci	cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_MOD,
3588c2ecf20Sopenharmony_ci			enable ? CNTRL_MOD : 0);
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_cistatic inline void control_rx_demodulation_enable(struct i2c_client *c,
3628c2ecf20Sopenharmony_ci						  bool enable)
3638c2ecf20Sopenharmony_ci{
3648c2ecf20Sopenharmony_ci	cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_DMD,
3658c2ecf20Sopenharmony_ci			enable ? CNTRL_DMD : 0);
3668c2ecf20Sopenharmony_ci}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_cistatic inline void control_rx_s_edge_detection(struct i2c_client *c,
3698c2ecf20Sopenharmony_ci					       u32 edge_types)
3708c2ecf20Sopenharmony_ci{
3718c2ecf20Sopenharmony_ci	cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_EDG_BOTH,
3728c2ecf20Sopenharmony_ci			edge_types & CNTRL_EDG_BOTH);
3738c2ecf20Sopenharmony_ci}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_cistatic void control_rx_s_carrier_window(struct i2c_client *c,
3768c2ecf20Sopenharmony_ci					unsigned int carrier,
3778c2ecf20Sopenharmony_ci					unsigned int *carrier_range_low,
3788c2ecf20Sopenharmony_ci					unsigned int *carrier_range_high)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci	u32 v;
3818c2ecf20Sopenharmony_ci	unsigned int c16 = carrier * 16;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	if (*carrier_range_low < DIV_ROUND_CLOSEST(c16, 16 + 3)) {
3848c2ecf20Sopenharmony_ci		v = CNTRL_WIN_3_4;
3858c2ecf20Sopenharmony_ci		*carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 4);
3868c2ecf20Sopenharmony_ci	} else {
3878c2ecf20Sopenharmony_ci		v = CNTRL_WIN_3_3;
3888c2ecf20Sopenharmony_ci		*carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 3);
3898c2ecf20Sopenharmony_ci	}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	if (*carrier_range_high > DIV_ROUND_CLOSEST(c16, 16 - 3)) {
3928c2ecf20Sopenharmony_ci		v |= CNTRL_WIN_4_3;
3938c2ecf20Sopenharmony_ci		*carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 4);
3948c2ecf20Sopenharmony_ci	} else {
3958c2ecf20Sopenharmony_ci		v |= CNTRL_WIN_3_3;
3968c2ecf20Sopenharmony_ci		*carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 3);
3978c2ecf20Sopenharmony_ci	}
3988c2ecf20Sopenharmony_ci	cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_WIN, v);
3998c2ecf20Sopenharmony_ci}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_cistatic inline void control_tx_polarity_invert(struct i2c_client *c,
4028c2ecf20Sopenharmony_ci					      bool invert)
4038c2ecf20Sopenharmony_ci{
4048c2ecf20Sopenharmony_ci	cx25840_and_or4(c, CX25840_IR_CNTRL_REG, ~CNTRL_CPL,
4058c2ecf20Sopenharmony_ci			invert ? CNTRL_CPL : 0);
4068c2ecf20Sopenharmony_ci}
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci/*
4098c2ecf20Sopenharmony_ci * IR Rx & Tx Clock Register helpers
4108c2ecf20Sopenharmony_ci */
4118c2ecf20Sopenharmony_cistatic unsigned int txclk_tx_s_carrier(struct i2c_client *c,
4128c2ecf20Sopenharmony_ci				       unsigned int freq,
4138c2ecf20Sopenharmony_ci				       u16 *divider)
4148c2ecf20Sopenharmony_ci{
4158c2ecf20Sopenharmony_ci	*divider = carrier_freq_to_clock_divider(freq);
4168c2ecf20Sopenharmony_ci	cx25840_write4(c, CX25840_IR_TXCLK_REG, *divider);
4178c2ecf20Sopenharmony_ci	return clock_divider_to_carrier_freq(*divider);
4188c2ecf20Sopenharmony_ci}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_cistatic unsigned int rxclk_rx_s_carrier(struct i2c_client *c,
4218c2ecf20Sopenharmony_ci				       unsigned int freq,
4228c2ecf20Sopenharmony_ci				       u16 *divider)
4238c2ecf20Sopenharmony_ci{
4248c2ecf20Sopenharmony_ci	*divider = carrier_freq_to_clock_divider(freq);
4258c2ecf20Sopenharmony_ci	cx25840_write4(c, CX25840_IR_RXCLK_REG, *divider);
4268c2ecf20Sopenharmony_ci	return clock_divider_to_carrier_freq(*divider);
4278c2ecf20Sopenharmony_ci}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_cistatic u32 txclk_tx_s_max_pulse_width(struct i2c_client *c, u32 ns,
4308c2ecf20Sopenharmony_ci				      u16 *divider)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	u64 pulse_clocks;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	if (ns > IR_MAX_DURATION)
4358c2ecf20Sopenharmony_ci		ns = IR_MAX_DURATION;
4368c2ecf20Sopenharmony_ci	pulse_clocks = ns_to_pulse_clocks(ns);
4378c2ecf20Sopenharmony_ci	*divider = pulse_clocks_to_clock_divider(pulse_clocks);
4388c2ecf20Sopenharmony_ci	cx25840_write4(c, CX25840_IR_TXCLK_REG, *divider);
4398c2ecf20Sopenharmony_ci	return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider);
4408c2ecf20Sopenharmony_ci}
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_cistatic u32 rxclk_rx_s_max_pulse_width(struct i2c_client *c, u32 ns,
4438c2ecf20Sopenharmony_ci				      u16 *divider)
4448c2ecf20Sopenharmony_ci{
4458c2ecf20Sopenharmony_ci	u64 pulse_clocks;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	if (ns > IR_MAX_DURATION)
4488c2ecf20Sopenharmony_ci		ns = IR_MAX_DURATION;
4498c2ecf20Sopenharmony_ci	pulse_clocks = ns_to_pulse_clocks(ns);
4508c2ecf20Sopenharmony_ci	*divider = pulse_clocks_to_clock_divider(pulse_clocks);
4518c2ecf20Sopenharmony_ci	cx25840_write4(c, CX25840_IR_RXCLK_REG, *divider);
4528c2ecf20Sopenharmony_ci	return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider);
4538c2ecf20Sopenharmony_ci}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci/*
4568c2ecf20Sopenharmony_ci * IR Tx Carrier Duty Cycle register helpers
4578c2ecf20Sopenharmony_ci */
4588c2ecf20Sopenharmony_cistatic unsigned int cduty_tx_s_duty_cycle(struct i2c_client *c,
4598c2ecf20Sopenharmony_ci					  unsigned int duty_cycle)
4608c2ecf20Sopenharmony_ci{
4618c2ecf20Sopenharmony_ci	u32 n;
4628c2ecf20Sopenharmony_ci	n = DIV_ROUND_CLOSEST(duty_cycle * 100, 625); /* 16ths of 100% */
4638c2ecf20Sopenharmony_ci	if (n != 0)
4648c2ecf20Sopenharmony_ci		n--;
4658c2ecf20Sopenharmony_ci	if (n > 15)
4668c2ecf20Sopenharmony_ci		n = 15;
4678c2ecf20Sopenharmony_ci	cx25840_write4(c, CX25840_IR_CDUTY_REG, n);
4688c2ecf20Sopenharmony_ci	return DIV_ROUND_CLOSEST((n + 1) * 100, 16);
4698c2ecf20Sopenharmony_ci}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci/*
4728c2ecf20Sopenharmony_ci * IR Filter Register helpers
4738c2ecf20Sopenharmony_ci */
4748c2ecf20Sopenharmony_cistatic u32 filter_rx_s_min_width(struct i2c_client *c, u32 min_width_ns)
4758c2ecf20Sopenharmony_ci{
4768c2ecf20Sopenharmony_ci	u32 count = ns_to_lpf_count(min_width_ns);
4778c2ecf20Sopenharmony_ci	cx25840_write4(c, CX25840_IR_FILTR_REG, count);
4788c2ecf20Sopenharmony_ci	return lpf_count_to_ns(count);
4798c2ecf20Sopenharmony_ci}
4808c2ecf20Sopenharmony_ci
4818c2ecf20Sopenharmony_ci/*
4828c2ecf20Sopenharmony_ci * IR IRQ Enable Register helpers
4838c2ecf20Sopenharmony_ci */
4848c2ecf20Sopenharmony_cistatic inline void irqenable_rx(struct v4l2_subdev *sd, u32 mask)
4858c2ecf20Sopenharmony_ci{
4868c2ecf20Sopenharmony_ci	struct cx25840_state *state = to_state(sd);
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	if (is_cx23885(state) || is_cx23887(state))
4898c2ecf20Sopenharmony_ci		mask ^= IRQEN_MSK;
4908c2ecf20Sopenharmony_ci	mask &= (IRQEN_RTE | IRQEN_ROE | IRQEN_RSE);
4918c2ecf20Sopenharmony_ci	cx25840_and_or4(state->c, CX25840_IR_IRQEN_REG,
4928c2ecf20Sopenharmony_ci			~(IRQEN_RTE | IRQEN_ROE | IRQEN_RSE), mask);
4938c2ecf20Sopenharmony_ci}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_cistatic inline void irqenable_tx(struct v4l2_subdev *sd, u32 mask)
4968c2ecf20Sopenharmony_ci{
4978c2ecf20Sopenharmony_ci	struct cx25840_state *state = to_state(sd);
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	if (is_cx23885(state) || is_cx23887(state))
5008c2ecf20Sopenharmony_ci		mask ^= IRQEN_MSK;
5018c2ecf20Sopenharmony_ci	mask &= IRQEN_TSE;
5028c2ecf20Sopenharmony_ci	cx25840_and_or4(state->c, CX25840_IR_IRQEN_REG, ~IRQEN_TSE, mask);
5038c2ecf20Sopenharmony_ci}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci/*
5068c2ecf20Sopenharmony_ci * V4L2 Subdevice IR Ops
5078c2ecf20Sopenharmony_ci */
5088c2ecf20Sopenharmony_ciint cx25840_ir_irq_handler(struct v4l2_subdev *sd, u32 status, bool *handled)
5098c2ecf20Sopenharmony_ci{
5108c2ecf20Sopenharmony_ci	struct cx25840_state *state = to_state(sd);
5118c2ecf20Sopenharmony_ci	struct cx25840_ir_state *ir_state = to_ir_state(sd);
5128c2ecf20Sopenharmony_ci	struct i2c_client *c = NULL;
5138c2ecf20Sopenharmony_ci	unsigned long flags;
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	union cx25840_ir_fifo_rec rx_data[FIFO_RX_DEPTH];
5168c2ecf20Sopenharmony_ci	unsigned int i, j, k;
5178c2ecf20Sopenharmony_ci	u32 events, v;
5188c2ecf20Sopenharmony_ci	int tsr, rsr, rto, ror, tse, rse, rte, roe, kror;
5198c2ecf20Sopenharmony_ci	u32 cntrl, irqen, stats;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	*handled = false;
5228c2ecf20Sopenharmony_ci	if (ir_state == NULL)
5238c2ecf20Sopenharmony_ci		return -ENODEV;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	c = ir_state->c;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	/* Only support the IR controller for the CX2388[57] AV Core for now */
5288c2ecf20Sopenharmony_ci	if (!(is_cx23885(state) || is_cx23887(state)))
5298c2ecf20Sopenharmony_ci		return -ENODEV;
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	cntrl = cx25840_read4(c, CX25840_IR_CNTRL_REG);
5328c2ecf20Sopenharmony_ci	irqen = cx25840_read4(c, CX25840_IR_IRQEN_REG);
5338c2ecf20Sopenharmony_ci	if (is_cx23885(state) || is_cx23887(state))
5348c2ecf20Sopenharmony_ci		irqen ^= IRQEN_MSK;
5358c2ecf20Sopenharmony_ci	stats = cx25840_read4(c, CX25840_IR_STATS_REG);
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	tsr = stats & STATS_TSR; /* Tx FIFO Service Request */
5388c2ecf20Sopenharmony_ci	rsr = stats & STATS_RSR; /* Rx FIFO Service Request */
5398c2ecf20Sopenharmony_ci	rto = stats & STATS_RTO; /* Rx Pulse Width Timer Time Out */
5408c2ecf20Sopenharmony_ci	ror = stats & STATS_ROR; /* Rx FIFO Over Run */
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	tse = irqen & IRQEN_TSE; /* Tx FIFO Service Request IRQ Enable */
5438c2ecf20Sopenharmony_ci	rse = irqen & IRQEN_RSE; /* Rx FIFO Service Request IRQ Enable */
5448c2ecf20Sopenharmony_ci	rte = irqen & IRQEN_RTE; /* Rx Pulse Width Timer Time Out IRQ Enable */
5458c2ecf20Sopenharmony_ci	roe = irqen & IRQEN_ROE; /* Rx FIFO Over Run IRQ Enable */
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	v4l2_dbg(2, ir_debug, sd, "IR IRQ Status:  %s %s %s %s %s %s\n",
5488c2ecf20Sopenharmony_ci		 tsr ? "tsr" : "   ", rsr ? "rsr" : "   ",
5498c2ecf20Sopenharmony_ci		 rto ? "rto" : "   ", ror ? "ror" : "   ",
5508c2ecf20Sopenharmony_ci		 stats & STATS_TBY ? "tby" : "   ",
5518c2ecf20Sopenharmony_ci		 stats & STATS_RBY ? "rby" : "   ");
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	v4l2_dbg(2, ir_debug, sd, "IR IRQ Enables: %s %s %s %s\n",
5548c2ecf20Sopenharmony_ci		 tse ? "tse" : "   ", rse ? "rse" : "   ",
5558c2ecf20Sopenharmony_ci		 rte ? "rte" : "   ", roe ? "roe" : "   ");
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	/*
5588c2ecf20Sopenharmony_ci	 * Transmitter interrupt service
5598c2ecf20Sopenharmony_ci	 */
5608c2ecf20Sopenharmony_ci	if (tse && tsr) {
5618c2ecf20Sopenharmony_ci		/*
5628c2ecf20Sopenharmony_ci		 * TODO:
5638c2ecf20Sopenharmony_ci		 * Check the watermark threshold setting
5648c2ecf20Sopenharmony_ci		 * Pull FIFO_TX_DEPTH or FIFO_TX_DEPTH/2 entries from tx_kfifo
5658c2ecf20Sopenharmony_ci		 * Push the data to the hardware FIFO.
5668c2ecf20Sopenharmony_ci		 * If there was nothing more to send in the tx_kfifo, disable
5678c2ecf20Sopenharmony_ci		 *	the TSR IRQ and notify the v4l2_device.
5688c2ecf20Sopenharmony_ci		 * If there was something in the tx_kfifo, check the tx_kfifo
5698c2ecf20Sopenharmony_ci		 *      level and notify the v4l2_device, if it is low.
5708c2ecf20Sopenharmony_ci		 */
5718c2ecf20Sopenharmony_ci		/* For now, inhibit TSR interrupt until Tx is implemented */
5728c2ecf20Sopenharmony_ci		irqenable_tx(sd, 0);
5738c2ecf20Sopenharmony_ci		events = V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ;
5748c2ecf20Sopenharmony_ci		v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_TX_NOTIFY, &events);
5758c2ecf20Sopenharmony_ci		*handled = true;
5768c2ecf20Sopenharmony_ci	}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	/*
5798c2ecf20Sopenharmony_ci	 * Receiver interrupt service
5808c2ecf20Sopenharmony_ci	 */
5818c2ecf20Sopenharmony_ci	kror = 0;
5828c2ecf20Sopenharmony_ci	if ((rse && rsr) || (rte && rto)) {
5838c2ecf20Sopenharmony_ci		/*
5848c2ecf20Sopenharmony_ci		 * Receive data on RSR to clear the STATS_RSR.
5858c2ecf20Sopenharmony_ci		 * Receive data on RTO, since we may not have yet hit the RSR
5868c2ecf20Sopenharmony_ci		 * watermark when we receive the RTO.
5878c2ecf20Sopenharmony_ci		 */
5888c2ecf20Sopenharmony_ci		for (i = 0, v = FIFO_RX_NDV;
5898c2ecf20Sopenharmony_ci		     (v & FIFO_RX_NDV) && !kror; i = 0) {
5908c2ecf20Sopenharmony_ci			for (j = 0;
5918c2ecf20Sopenharmony_ci			     (v & FIFO_RX_NDV) && j < FIFO_RX_DEPTH; j++) {
5928c2ecf20Sopenharmony_ci				v = cx25840_read4(c, CX25840_IR_FIFO_REG);
5938c2ecf20Sopenharmony_ci				rx_data[i].hw_fifo_data = v & ~FIFO_RX_NDV;
5948c2ecf20Sopenharmony_ci				i++;
5958c2ecf20Sopenharmony_ci			}
5968c2ecf20Sopenharmony_ci			if (i == 0)
5978c2ecf20Sopenharmony_ci				break;
5988c2ecf20Sopenharmony_ci			j = i * sizeof(union cx25840_ir_fifo_rec);
5998c2ecf20Sopenharmony_ci			k = kfifo_in_locked(&ir_state->rx_kfifo,
6008c2ecf20Sopenharmony_ci					    (unsigned char *) rx_data, j,
6018c2ecf20Sopenharmony_ci					    &ir_state->rx_kfifo_lock);
6028c2ecf20Sopenharmony_ci			if (k != j)
6038c2ecf20Sopenharmony_ci				kror++; /* rx_kfifo over run */
6048c2ecf20Sopenharmony_ci		}
6058c2ecf20Sopenharmony_ci		*handled = true;
6068c2ecf20Sopenharmony_ci	}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	events = 0;
6098c2ecf20Sopenharmony_ci	v = 0;
6108c2ecf20Sopenharmony_ci	if (kror) {
6118c2ecf20Sopenharmony_ci		events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN;
6128c2ecf20Sopenharmony_ci		v4l2_err(sd, "IR receiver software FIFO overrun\n");
6138c2ecf20Sopenharmony_ci	}
6148c2ecf20Sopenharmony_ci	if (roe && ror) {
6158c2ecf20Sopenharmony_ci		/*
6168c2ecf20Sopenharmony_ci		 * The RX FIFO Enable (CNTRL_RFE) must be toggled to clear
6178c2ecf20Sopenharmony_ci		 * the Rx FIFO Over Run status (STATS_ROR)
6188c2ecf20Sopenharmony_ci		 */
6198c2ecf20Sopenharmony_ci		v |= CNTRL_RFE;
6208c2ecf20Sopenharmony_ci		events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN;
6218c2ecf20Sopenharmony_ci		v4l2_err(sd, "IR receiver hardware FIFO overrun\n");
6228c2ecf20Sopenharmony_ci	}
6238c2ecf20Sopenharmony_ci	if (rte && rto) {
6248c2ecf20Sopenharmony_ci		/*
6258c2ecf20Sopenharmony_ci		 * The IR Receiver Enable (CNTRL_RXE) must be toggled to clear
6268c2ecf20Sopenharmony_ci		 * the Rx Pulse Width Timer Time Out (STATS_RTO)
6278c2ecf20Sopenharmony_ci		 */
6288c2ecf20Sopenharmony_ci		v |= CNTRL_RXE;
6298c2ecf20Sopenharmony_ci		events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED;
6308c2ecf20Sopenharmony_ci	}
6318c2ecf20Sopenharmony_ci	if (v) {
6328c2ecf20Sopenharmony_ci		/* Clear STATS_ROR & STATS_RTO as needed by resetting hardware */
6338c2ecf20Sopenharmony_ci		cx25840_write4(c, CX25840_IR_CNTRL_REG, cntrl & ~v);
6348c2ecf20Sopenharmony_ci		cx25840_write4(c, CX25840_IR_CNTRL_REG, cntrl);
6358c2ecf20Sopenharmony_ci		*handled = true;
6368c2ecf20Sopenharmony_ci	}
6378c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ir_state->rx_kfifo_lock, flags);
6388c2ecf20Sopenharmony_ci	if (kfifo_len(&ir_state->rx_kfifo) >= CX25840_IR_RX_KFIFO_SIZE / 2)
6398c2ecf20Sopenharmony_ci		events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ;
6408c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ir_state->rx_kfifo_lock, flags);
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	if (events)
6438c2ecf20Sopenharmony_ci		v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_RX_NOTIFY, &events);
6448c2ecf20Sopenharmony_ci	return 0;
6458c2ecf20Sopenharmony_ci}
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci/* Receiver */
6488c2ecf20Sopenharmony_cistatic int cx25840_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count,
6498c2ecf20Sopenharmony_ci			      ssize_t *num)
6508c2ecf20Sopenharmony_ci{
6518c2ecf20Sopenharmony_ci	struct cx25840_ir_state *ir_state = to_ir_state(sd);
6528c2ecf20Sopenharmony_ci	bool invert;
6538c2ecf20Sopenharmony_ci	u16 divider;
6548c2ecf20Sopenharmony_ci	unsigned int i, n;
6558c2ecf20Sopenharmony_ci	union cx25840_ir_fifo_rec *p;
6568c2ecf20Sopenharmony_ci	unsigned u, v, w;
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci	if (ir_state == NULL)
6598c2ecf20Sopenharmony_ci		return -ENODEV;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	invert = (bool) atomic_read(&ir_state->rx_invert);
6628c2ecf20Sopenharmony_ci	divider = (u16) atomic_read(&ir_state->rxclk_divider);
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	n = count / sizeof(union cx25840_ir_fifo_rec)
6658c2ecf20Sopenharmony_ci		* sizeof(union cx25840_ir_fifo_rec);
6668c2ecf20Sopenharmony_ci	if (n == 0) {
6678c2ecf20Sopenharmony_ci		*num = 0;
6688c2ecf20Sopenharmony_ci		return 0;
6698c2ecf20Sopenharmony_ci	}
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	n = kfifo_out_locked(&ir_state->rx_kfifo, buf, n,
6728c2ecf20Sopenharmony_ci			     &ir_state->rx_kfifo_lock);
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	n /= sizeof(union cx25840_ir_fifo_rec);
6758c2ecf20Sopenharmony_ci	*num = n * sizeof(union cx25840_ir_fifo_rec);
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci	for (p = (union cx25840_ir_fifo_rec *) buf, i = 0; i < n; p++, i++) {
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ci		if ((p->hw_fifo_data & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) {
6808c2ecf20Sopenharmony_ci			/* Assume RTO was because of no IR light input */
6818c2ecf20Sopenharmony_ci			u = 0;
6828c2ecf20Sopenharmony_ci			w = 1;
6838c2ecf20Sopenharmony_ci		} else {
6848c2ecf20Sopenharmony_ci			u = (p->hw_fifo_data & FIFO_RXTX_LVL) ? 1 : 0;
6858c2ecf20Sopenharmony_ci			if (invert)
6868c2ecf20Sopenharmony_ci				u = u ? 0 : 1;
6878c2ecf20Sopenharmony_ci			w = 0;
6888c2ecf20Sopenharmony_ci		}
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci		v = (unsigned) pulse_width_count_to_ns(
6918c2ecf20Sopenharmony_ci				  (u16)(p->hw_fifo_data & FIFO_RXTX), divider) / 1000;
6928c2ecf20Sopenharmony_ci		if (v > IR_MAX_DURATION)
6938c2ecf20Sopenharmony_ci			v = IR_MAX_DURATION;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci		p->ir_core_data = (struct ir_raw_event)
6968c2ecf20Sopenharmony_ci			{ .pulse = u, .duration = v, .timeout = w };
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci		v4l2_dbg(2, ir_debug, sd, "rx read: %10u ns  %s  %s\n",
6998c2ecf20Sopenharmony_ci			 v, u ? "mark" : "space", w ? "(timed out)" : "");
7008c2ecf20Sopenharmony_ci		if (w)
7018c2ecf20Sopenharmony_ci			v4l2_dbg(2, ir_debug, sd, "rx read: end of rx\n");
7028c2ecf20Sopenharmony_ci	}
7038c2ecf20Sopenharmony_ci	return 0;
7048c2ecf20Sopenharmony_ci}
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_cistatic int cx25840_ir_rx_g_parameters(struct v4l2_subdev *sd,
7078c2ecf20Sopenharmony_ci				      struct v4l2_subdev_ir_parameters *p)
7088c2ecf20Sopenharmony_ci{
7098c2ecf20Sopenharmony_ci	struct cx25840_ir_state *ir_state = to_ir_state(sd);
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	if (ir_state == NULL)
7128c2ecf20Sopenharmony_ci		return -ENODEV;
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	mutex_lock(&ir_state->rx_params_lock);
7158c2ecf20Sopenharmony_ci	memcpy(p, &ir_state->rx_params,
7168c2ecf20Sopenharmony_ci				      sizeof(struct v4l2_subdev_ir_parameters));
7178c2ecf20Sopenharmony_ci	mutex_unlock(&ir_state->rx_params_lock);
7188c2ecf20Sopenharmony_ci	return 0;
7198c2ecf20Sopenharmony_ci}
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_cistatic int cx25840_ir_rx_shutdown(struct v4l2_subdev *sd)
7228c2ecf20Sopenharmony_ci{
7238c2ecf20Sopenharmony_ci	struct cx25840_ir_state *ir_state = to_ir_state(sd);
7248c2ecf20Sopenharmony_ci	struct i2c_client *c;
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	if (ir_state == NULL)
7278c2ecf20Sopenharmony_ci		return -ENODEV;
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	c = ir_state->c;
7308c2ecf20Sopenharmony_ci	mutex_lock(&ir_state->rx_params_lock);
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	/* Disable or slow down all IR Rx circuits and counters */
7338c2ecf20Sopenharmony_ci	irqenable_rx(sd, 0);
7348c2ecf20Sopenharmony_ci	control_rx_enable(c, false);
7358c2ecf20Sopenharmony_ci	control_rx_demodulation_enable(c, false);
7368c2ecf20Sopenharmony_ci	control_rx_s_edge_detection(c, CNTRL_EDG_NONE);
7378c2ecf20Sopenharmony_ci	filter_rx_s_min_width(c, 0);
7388c2ecf20Sopenharmony_ci	cx25840_write4(c, CX25840_IR_RXCLK_REG, RXCLK_RCD);
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	ir_state->rx_params.shutdown = true;
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	mutex_unlock(&ir_state->rx_params_lock);
7438c2ecf20Sopenharmony_ci	return 0;
7448c2ecf20Sopenharmony_ci}
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_cistatic int cx25840_ir_rx_s_parameters(struct v4l2_subdev *sd,
7478c2ecf20Sopenharmony_ci				      struct v4l2_subdev_ir_parameters *p)
7488c2ecf20Sopenharmony_ci{
7498c2ecf20Sopenharmony_ci	struct cx25840_ir_state *ir_state = to_ir_state(sd);
7508c2ecf20Sopenharmony_ci	struct i2c_client *c;
7518c2ecf20Sopenharmony_ci	struct v4l2_subdev_ir_parameters *o;
7528c2ecf20Sopenharmony_ci	u16 rxclk_divider;
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	if (ir_state == NULL)
7558c2ecf20Sopenharmony_ci		return -ENODEV;
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	if (p->shutdown)
7588c2ecf20Sopenharmony_ci		return cx25840_ir_rx_shutdown(sd);
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH)
7618c2ecf20Sopenharmony_ci		return -ENOSYS;
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	c = ir_state->c;
7648c2ecf20Sopenharmony_ci	o = &ir_state->rx_params;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	mutex_lock(&ir_state->rx_params_lock);
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	o->shutdown = p->shutdown;
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
7718c2ecf20Sopenharmony_ci	o->mode = p->mode;
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	p->bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec);
7748c2ecf20Sopenharmony_ci	o->bytes_per_data_element = p->bytes_per_data_element;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	/* Before we tweak the hardware, we have to disable the receiver */
7778c2ecf20Sopenharmony_ci	irqenable_rx(sd, 0);
7788c2ecf20Sopenharmony_ci	control_rx_enable(c, false);
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	control_rx_demodulation_enable(c, p->modulation);
7818c2ecf20Sopenharmony_ci	o->modulation = p->modulation;
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	if (p->modulation) {
7848c2ecf20Sopenharmony_ci		p->carrier_freq = rxclk_rx_s_carrier(c, p->carrier_freq,
7858c2ecf20Sopenharmony_ci						     &rxclk_divider);
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci		o->carrier_freq = p->carrier_freq;
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci		p->duty_cycle = 50;
7908c2ecf20Sopenharmony_ci		o->duty_cycle = p->duty_cycle;
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci		control_rx_s_carrier_window(c, p->carrier_freq,
7938c2ecf20Sopenharmony_ci					    &p->carrier_range_lower,
7948c2ecf20Sopenharmony_ci					    &p->carrier_range_upper);
7958c2ecf20Sopenharmony_ci		o->carrier_range_lower = p->carrier_range_lower;
7968c2ecf20Sopenharmony_ci		o->carrier_range_upper = p->carrier_range_upper;
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci		p->max_pulse_width =
7998c2ecf20Sopenharmony_ci			(u32) pulse_width_count_to_ns(FIFO_RXTX, rxclk_divider);
8008c2ecf20Sopenharmony_ci	} else {
8018c2ecf20Sopenharmony_ci		p->max_pulse_width =
8028c2ecf20Sopenharmony_ci			    rxclk_rx_s_max_pulse_width(c, p->max_pulse_width,
8038c2ecf20Sopenharmony_ci						       &rxclk_divider);
8048c2ecf20Sopenharmony_ci	}
8058c2ecf20Sopenharmony_ci	o->max_pulse_width = p->max_pulse_width;
8068c2ecf20Sopenharmony_ci	atomic_set(&ir_state->rxclk_divider, rxclk_divider);
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	p->noise_filter_min_width =
8098c2ecf20Sopenharmony_ci			    filter_rx_s_min_width(c, p->noise_filter_min_width);
8108c2ecf20Sopenharmony_ci	o->noise_filter_min_width = p->noise_filter_min_width;
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	p->resolution = clock_divider_to_resolution(rxclk_divider);
8138c2ecf20Sopenharmony_ci	o->resolution = p->resolution;
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	/* FIXME - make this dependent on resolution for better performance */
8168c2ecf20Sopenharmony_ci	control_rx_irq_watermark(c, RX_FIFO_HALF_FULL);
8178c2ecf20Sopenharmony_ci
8188c2ecf20Sopenharmony_ci	control_rx_s_edge_detection(c, CNTRL_EDG_BOTH);
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci	o->invert_level = p->invert_level;
8218c2ecf20Sopenharmony_ci	atomic_set(&ir_state->rx_invert, p->invert_level);
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_ci	o->interrupt_enable = p->interrupt_enable;
8248c2ecf20Sopenharmony_ci	o->enable = p->enable;
8258c2ecf20Sopenharmony_ci	if (p->enable) {
8268c2ecf20Sopenharmony_ci		unsigned long flags;
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci		spin_lock_irqsave(&ir_state->rx_kfifo_lock, flags);
8298c2ecf20Sopenharmony_ci		kfifo_reset(&ir_state->rx_kfifo);
8308c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ir_state->rx_kfifo_lock, flags);
8318c2ecf20Sopenharmony_ci		if (p->interrupt_enable)
8328c2ecf20Sopenharmony_ci			irqenable_rx(sd, IRQEN_RSE | IRQEN_RTE | IRQEN_ROE);
8338c2ecf20Sopenharmony_ci		control_rx_enable(c, p->enable);
8348c2ecf20Sopenharmony_ci	}
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	mutex_unlock(&ir_state->rx_params_lock);
8378c2ecf20Sopenharmony_ci	return 0;
8388c2ecf20Sopenharmony_ci}
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci/* Transmitter */
8418c2ecf20Sopenharmony_cistatic int cx25840_ir_tx_write(struct v4l2_subdev *sd, u8 *buf, size_t count,
8428c2ecf20Sopenharmony_ci			       ssize_t *num)
8438c2ecf20Sopenharmony_ci{
8448c2ecf20Sopenharmony_ci	struct cx25840_ir_state *ir_state = to_ir_state(sd);
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	if (ir_state == NULL)
8478c2ecf20Sopenharmony_ci		return -ENODEV;
8488c2ecf20Sopenharmony_ci
8498c2ecf20Sopenharmony_ci#if 0
8508c2ecf20Sopenharmony_ci	/*
8518c2ecf20Sopenharmony_ci	 * FIXME - the code below is an incomplete and untested sketch of what
8528c2ecf20Sopenharmony_ci	 * may need to be done.  The critical part is to get 4 (or 8) pulses
8538c2ecf20Sopenharmony_ci	 * from the tx_kfifo, or converted from ns to the proper units from the
8548c2ecf20Sopenharmony_ci	 * input, and push them off to the hardware Tx FIFO right away, if the
8558c2ecf20Sopenharmony_ci	 * HW TX fifo needs service.  The rest can be pushed to the tx_kfifo in
8568c2ecf20Sopenharmony_ci	 * a less critical timeframe.  Also watch out for overruning the
8578c2ecf20Sopenharmony_ci	 * tx_kfifo - don't let it happen and let the caller know not all his
8588c2ecf20Sopenharmony_ci	 * pulses were written.
8598c2ecf20Sopenharmony_ci	 */
8608c2ecf20Sopenharmony_ci	u32 *ns_pulse = (u32 *) buf;
8618c2ecf20Sopenharmony_ci	unsigned int n;
8628c2ecf20Sopenharmony_ci	u32 fifo_pulse[FIFO_TX_DEPTH];
8638c2ecf20Sopenharmony_ci	u32 mark;
8648c2ecf20Sopenharmony_ci
8658c2ecf20Sopenharmony_ci	/* Compute how much we can fit in the tx kfifo */
8668c2ecf20Sopenharmony_ci	n = CX25840_IR_TX_KFIFO_SIZE - kfifo_len(ir_state->tx_kfifo);
8678c2ecf20Sopenharmony_ci	n = min(n, (unsigned int) count);
8688c2ecf20Sopenharmony_ci	n /= sizeof(u32);
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	/* FIXME - turn on Tx Fifo service interrupt
8718c2ecf20Sopenharmony_ci	 * check hardware fifo level, and other stuff
8728c2ecf20Sopenharmony_ci	 */
8738c2ecf20Sopenharmony_ci	for (i = 0; i < n; ) {
8748c2ecf20Sopenharmony_ci		for (j = 0; j < FIFO_TX_DEPTH / 2 && i < n; j++) {
8758c2ecf20Sopenharmony_ci			mark = ns_pulse[i] & LEVEL_MASK;
8768c2ecf20Sopenharmony_ci			fifo_pulse[j] = ns_to_pulse_width_count(
8778c2ecf20Sopenharmony_ci					 ns_pulse[i] &
8788c2ecf20Sopenharmony_ci					       ~LEVEL_MASK,
8798c2ecf20Sopenharmony_ci					 ir_state->txclk_divider);
8808c2ecf20Sopenharmony_ci			if (mark)
8818c2ecf20Sopenharmony_ci				fifo_pulse[j] &= FIFO_RXTX_LVL;
8828c2ecf20Sopenharmony_ci			i++;
8838c2ecf20Sopenharmony_ci		}
8848c2ecf20Sopenharmony_ci		kfifo_put(ir_state->tx_kfifo, (u8 *) fifo_pulse,
8858c2ecf20Sopenharmony_ci							       j * sizeof(u32));
8868c2ecf20Sopenharmony_ci	}
8878c2ecf20Sopenharmony_ci	*num = n * sizeof(u32);
8888c2ecf20Sopenharmony_ci#else
8898c2ecf20Sopenharmony_ci	/* For now enable the Tx FIFO Service interrupt & pretend we did work */
8908c2ecf20Sopenharmony_ci	irqenable_tx(sd, IRQEN_TSE);
8918c2ecf20Sopenharmony_ci	*num = count;
8928c2ecf20Sopenharmony_ci#endif
8938c2ecf20Sopenharmony_ci	return 0;
8948c2ecf20Sopenharmony_ci}
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_cistatic int cx25840_ir_tx_g_parameters(struct v4l2_subdev *sd,
8978c2ecf20Sopenharmony_ci				      struct v4l2_subdev_ir_parameters *p)
8988c2ecf20Sopenharmony_ci{
8998c2ecf20Sopenharmony_ci	struct cx25840_ir_state *ir_state = to_ir_state(sd);
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci	if (ir_state == NULL)
9028c2ecf20Sopenharmony_ci		return -ENODEV;
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	mutex_lock(&ir_state->tx_params_lock);
9058c2ecf20Sopenharmony_ci	memcpy(p, &ir_state->tx_params,
9068c2ecf20Sopenharmony_ci				      sizeof(struct v4l2_subdev_ir_parameters));
9078c2ecf20Sopenharmony_ci	mutex_unlock(&ir_state->tx_params_lock);
9088c2ecf20Sopenharmony_ci	return 0;
9098c2ecf20Sopenharmony_ci}
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_cistatic int cx25840_ir_tx_shutdown(struct v4l2_subdev *sd)
9128c2ecf20Sopenharmony_ci{
9138c2ecf20Sopenharmony_ci	struct cx25840_ir_state *ir_state = to_ir_state(sd);
9148c2ecf20Sopenharmony_ci	struct i2c_client *c;
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	if (ir_state == NULL)
9178c2ecf20Sopenharmony_ci		return -ENODEV;
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci	c = ir_state->c;
9208c2ecf20Sopenharmony_ci	mutex_lock(&ir_state->tx_params_lock);
9218c2ecf20Sopenharmony_ci
9228c2ecf20Sopenharmony_ci	/* Disable or slow down all IR Tx circuits and counters */
9238c2ecf20Sopenharmony_ci	irqenable_tx(sd, 0);
9248c2ecf20Sopenharmony_ci	control_tx_enable(c, false);
9258c2ecf20Sopenharmony_ci	control_tx_modulation_enable(c, false);
9268c2ecf20Sopenharmony_ci	cx25840_write4(c, CX25840_IR_TXCLK_REG, TXCLK_TCD);
9278c2ecf20Sopenharmony_ci
9288c2ecf20Sopenharmony_ci	ir_state->tx_params.shutdown = true;
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci	mutex_unlock(&ir_state->tx_params_lock);
9318c2ecf20Sopenharmony_ci	return 0;
9328c2ecf20Sopenharmony_ci}
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_cistatic int cx25840_ir_tx_s_parameters(struct v4l2_subdev *sd,
9358c2ecf20Sopenharmony_ci				      struct v4l2_subdev_ir_parameters *p)
9368c2ecf20Sopenharmony_ci{
9378c2ecf20Sopenharmony_ci	struct cx25840_ir_state *ir_state = to_ir_state(sd);
9388c2ecf20Sopenharmony_ci	struct i2c_client *c;
9398c2ecf20Sopenharmony_ci	struct v4l2_subdev_ir_parameters *o;
9408c2ecf20Sopenharmony_ci	u16 txclk_divider;
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	if (ir_state == NULL)
9438c2ecf20Sopenharmony_ci		return -ENODEV;
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci	if (p->shutdown)
9468c2ecf20Sopenharmony_ci		return cx25840_ir_tx_shutdown(sd);
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_ci	if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH)
9498c2ecf20Sopenharmony_ci		return -ENOSYS;
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci	c = ir_state->c;
9528c2ecf20Sopenharmony_ci	o = &ir_state->tx_params;
9538c2ecf20Sopenharmony_ci	mutex_lock(&ir_state->tx_params_lock);
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	o->shutdown = p->shutdown;
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci	p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH;
9588c2ecf20Sopenharmony_ci	o->mode = p->mode;
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_ci	p->bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec);
9618c2ecf20Sopenharmony_ci	o->bytes_per_data_element = p->bytes_per_data_element;
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci	/* Before we tweak the hardware, we have to disable the transmitter */
9648c2ecf20Sopenharmony_ci	irqenable_tx(sd, 0);
9658c2ecf20Sopenharmony_ci	control_tx_enable(c, false);
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	control_tx_modulation_enable(c, p->modulation);
9688c2ecf20Sopenharmony_ci	o->modulation = p->modulation;
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	if (p->modulation) {
9718c2ecf20Sopenharmony_ci		p->carrier_freq = txclk_tx_s_carrier(c, p->carrier_freq,
9728c2ecf20Sopenharmony_ci						     &txclk_divider);
9738c2ecf20Sopenharmony_ci		o->carrier_freq = p->carrier_freq;
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci		p->duty_cycle = cduty_tx_s_duty_cycle(c, p->duty_cycle);
9768c2ecf20Sopenharmony_ci		o->duty_cycle = p->duty_cycle;
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci		p->max_pulse_width =
9798c2ecf20Sopenharmony_ci			(u32) pulse_width_count_to_ns(FIFO_RXTX, txclk_divider);
9808c2ecf20Sopenharmony_ci	} else {
9818c2ecf20Sopenharmony_ci		p->max_pulse_width =
9828c2ecf20Sopenharmony_ci			    txclk_tx_s_max_pulse_width(c, p->max_pulse_width,
9838c2ecf20Sopenharmony_ci						       &txclk_divider);
9848c2ecf20Sopenharmony_ci	}
9858c2ecf20Sopenharmony_ci	o->max_pulse_width = p->max_pulse_width;
9868c2ecf20Sopenharmony_ci	atomic_set(&ir_state->txclk_divider, txclk_divider);
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci	p->resolution = clock_divider_to_resolution(txclk_divider);
9898c2ecf20Sopenharmony_ci	o->resolution = p->resolution;
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	/* FIXME - make this dependent on resolution for better performance */
9928c2ecf20Sopenharmony_ci	control_tx_irq_watermark(c, TX_FIFO_HALF_EMPTY);
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	control_tx_polarity_invert(c, p->invert_carrier_sense);
9958c2ecf20Sopenharmony_ci	o->invert_carrier_sense = p->invert_carrier_sense;
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	/*
9988c2ecf20Sopenharmony_ci	 * FIXME: we don't have hardware help for IO pin level inversion
9998c2ecf20Sopenharmony_ci	 * here like we have on the CX23888.
10008c2ecf20Sopenharmony_ci	 * Act on this with some mix of logical inversion of data levels,
10018c2ecf20Sopenharmony_ci	 * carrier polarity, and carrier duty cycle.
10028c2ecf20Sopenharmony_ci	 */
10038c2ecf20Sopenharmony_ci	o->invert_level = p->invert_level;
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci	o->interrupt_enable = p->interrupt_enable;
10068c2ecf20Sopenharmony_ci	o->enable = p->enable;
10078c2ecf20Sopenharmony_ci	if (p->enable) {
10088c2ecf20Sopenharmony_ci		/* reset tx_fifo here */
10098c2ecf20Sopenharmony_ci		if (p->interrupt_enable)
10108c2ecf20Sopenharmony_ci			irqenable_tx(sd, IRQEN_TSE);
10118c2ecf20Sopenharmony_ci		control_tx_enable(c, p->enable);
10128c2ecf20Sopenharmony_ci	}
10138c2ecf20Sopenharmony_ci
10148c2ecf20Sopenharmony_ci	mutex_unlock(&ir_state->tx_params_lock);
10158c2ecf20Sopenharmony_ci	return 0;
10168c2ecf20Sopenharmony_ci}
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci
10198c2ecf20Sopenharmony_ci/*
10208c2ecf20Sopenharmony_ci * V4L2 Subdevice Core Ops support
10218c2ecf20Sopenharmony_ci */
10228c2ecf20Sopenharmony_ciint cx25840_ir_log_status(struct v4l2_subdev *sd)
10238c2ecf20Sopenharmony_ci{
10248c2ecf20Sopenharmony_ci	struct cx25840_state *state = to_state(sd);
10258c2ecf20Sopenharmony_ci	struct i2c_client *c = state->c;
10268c2ecf20Sopenharmony_ci	char *s;
10278c2ecf20Sopenharmony_ci	int i, j;
10288c2ecf20Sopenharmony_ci	u32 cntrl, txclk, rxclk, cduty, stats, irqen, filtr;
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	/* The CX23888 chip doesn't have an IR controller on the A/V core */
10318c2ecf20Sopenharmony_ci	if (is_cx23888(state))
10328c2ecf20Sopenharmony_ci		return 0;
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci	cntrl = cx25840_read4(c, CX25840_IR_CNTRL_REG);
10358c2ecf20Sopenharmony_ci	txclk = cx25840_read4(c, CX25840_IR_TXCLK_REG) & TXCLK_TCD;
10368c2ecf20Sopenharmony_ci	rxclk = cx25840_read4(c, CX25840_IR_RXCLK_REG) & RXCLK_RCD;
10378c2ecf20Sopenharmony_ci	cduty = cx25840_read4(c, CX25840_IR_CDUTY_REG) & CDUTY_CDC;
10388c2ecf20Sopenharmony_ci	stats = cx25840_read4(c, CX25840_IR_STATS_REG);
10398c2ecf20Sopenharmony_ci	irqen = cx25840_read4(c, CX25840_IR_IRQEN_REG);
10408c2ecf20Sopenharmony_ci	if (is_cx23885(state) || is_cx23887(state))
10418c2ecf20Sopenharmony_ci		irqen ^= IRQEN_MSK;
10428c2ecf20Sopenharmony_ci	filtr = cx25840_read4(c, CX25840_IR_FILTR_REG) & FILTR_LPF;
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_ci	v4l2_info(sd, "IR Receiver:\n");
10458c2ecf20Sopenharmony_ci	v4l2_info(sd, "\tEnabled:                           %s\n",
10468c2ecf20Sopenharmony_ci		  cntrl & CNTRL_RXE ? "yes" : "no");
10478c2ecf20Sopenharmony_ci	v4l2_info(sd, "\tDemodulation from a carrier:       %s\n",
10488c2ecf20Sopenharmony_ci		  cntrl & CNTRL_DMD ? "enabled" : "disabled");
10498c2ecf20Sopenharmony_ci	v4l2_info(sd, "\tFIFO:                              %s\n",
10508c2ecf20Sopenharmony_ci		  cntrl & CNTRL_RFE ? "enabled" : "disabled");
10518c2ecf20Sopenharmony_ci	switch (cntrl & CNTRL_EDG) {
10528c2ecf20Sopenharmony_ci	case CNTRL_EDG_NONE:
10538c2ecf20Sopenharmony_ci		s = "disabled";
10548c2ecf20Sopenharmony_ci		break;
10558c2ecf20Sopenharmony_ci	case CNTRL_EDG_FALL:
10568c2ecf20Sopenharmony_ci		s = "falling edge";
10578c2ecf20Sopenharmony_ci		break;
10588c2ecf20Sopenharmony_ci	case CNTRL_EDG_RISE:
10598c2ecf20Sopenharmony_ci		s = "rising edge";
10608c2ecf20Sopenharmony_ci		break;
10618c2ecf20Sopenharmony_ci	case CNTRL_EDG_BOTH:
10628c2ecf20Sopenharmony_ci		s = "rising & falling edges";
10638c2ecf20Sopenharmony_ci		break;
10648c2ecf20Sopenharmony_ci	default:
10658c2ecf20Sopenharmony_ci		s = "??? edge";
10668c2ecf20Sopenharmony_ci		break;
10678c2ecf20Sopenharmony_ci	}
10688c2ecf20Sopenharmony_ci	v4l2_info(sd, "\tPulse timers' start/stop trigger:  %s\n", s);
10698c2ecf20Sopenharmony_ci	v4l2_info(sd, "\tFIFO data on pulse timer overflow: %s\n",
10708c2ecf20Sopenharmony_ci		  cntrl & CNTRL_R ? "not loaded" : "overflow marker");
10718c2ecf20Sopenharmony_ci	v4l2_info(sd, "\tFIFO interrupt watermark:          %s\n",
10728c2ecf20Sopenharmony_ci		  cntrl & CNTRL_RIC ? "not empty" : "half full or greater");
10738c2ecf20Sopenharmony_ci	v4l2_info(sd, "\tLoopback mode:                     %s\n",
10748c2ecf20Sopenharmony_ci		  cntrl & CNTRL_LBM ? "loopback active" : "normal receive");
10758c2ecf20Sopenharmony_ci	if (cntrl & CNTRL_DMD) {
10768c2ecf20Sopenharmony_ci		v4l2_info(sd, "\tExpected carrier (16 clocks):      %u Hz\n",
10778c2ecf20Sopenharmony_ci			  clock_divider_to_carrier_freq(rxclk));
10788c2ecf20Sopenharmony_ci		switch (cntrl & CNTRL_WIN) {
10798c2ecf20Sopenharmony_ci		case CNTRL_WIN_3_3:
10808c2ecf20Sopenharmony_ci			i = 3;
10818c2ecf20Sopenharmony_ci			j = 3;
10828c2ecf20Sopenharmony_ci			break;
10838c2ecf20Sopenharmony_ci		case CNTRL_WIN_4_3:
10848c2ecf20Sopenharmony_ci			i = 4;
10858c2ecf20Sopenharmony_ci			j = 3;
10868c2ecf20Sopenharmony_ci			break;
10878c2ecf20Sopenharmony_ci		case CNTRL_WIN_3_4:
10888c2ecf20Sopenharmony_ci			i = 3;
10898c2ecf20Sopenharmony_ci			j = 4;
10908c2ecf20Sopenharmony_ci			break;
10918c2ecf20Sopenharmony_ci		case CNTRL_WIN_4_4:
10928c2ecf20Sopenharmony_ci			i = 4;
10938c2ecf20Sopenharmony_ci			j = 4;
10948c2ecf20Sopenharmony_ci			break;
10958c2ecf20Sopenharmony_ci		default:
10968c2ecf20Sopenharmony_ci			i = 0;
10978c2ecf20Sopenharmony_ci			j = 0;
10988c2ecf20Sopenharmony_ci			break;
10998c2ecf20Sopenharmony_ci		}
11008c2ecf20Sopenharmony_ci		v4l2_info(sd, "\tNext carrier edge window:	    16 clocks -%1d/+%1d, %u to %u Hz\n",
11018c2ecf20Sopenharmony_ci			  i, j,
11028c2ecf20Sopenharmony_ci			  clock_divider_to_freq(rxclk, 16 + j),
11038c2ecf20Sopenharmony_ci			  clock_divider_to_freq(rxclk, 16 - i));
11048c2ecf20Sopenharmony_ci	}
11058c2ecf20Sopenharmony_ci	v4l2_info(sd, "\tMax measurable pulse width:        %u us, %llu ns\n",
11068c2ecf20Sopenharmony_ci		  pulse_width_count_to_us(FIFO_RXTX, rxclk),
11078c2ecf20Sopenharmony_ci		  pulse_width_count_to_ns(FIFO_RXTX, rxclk));
11088c2ecf20Sopenharmony_ci	v4l2_info(sd, "\tLow pass filter:                   %s\n",
11098c2ecf20Sopenharmony_ci		  filtr ? "enabled" : "disabled");
11108c2ecf20Sopenharmony_ci	if (filtr)
11118c2ecf20Sopenharmony_ci		v4l2_info(sd, "\tMin acceptable pulse width (LPF):  %u us, %u ns\n",
11128c2ecf20Sopenharmony_ci			  lpf_count_to_us(filtr),
11138c2ecf20Sopenharmony_ci			  lpf_count_to_ns(filtr));
11148c2ecf20Sopenharmony_ci	v4l2_info(sd, "\tPulse width timer timed-out:       %s\n",
11158c2ecf20Sopenharmony_ci		  stats & STATS_RTO ? "yes" : "no");
11168c2ecf20Sopenharmony_ci	v4l2_info(sd, "\tPulse width timer time-out intr:   %s\n",
11178c2ecf20Sopenharmony_ci		  irqen & IRQEN_RTE ? "enabled" : "disabled");
11188c2ecf20Sopenharmony_ci	v4l2_info(sd, "\tFIFO overrun:                      %s\n",
11198c2ecf20Sopenharmony_ci		  stats & STATS_ROR ? "yes" : "no");
11208c2ecf20Sopenharmony_ci	v4l2_info(sd, "\tFIFO overrun interrupt:            %s\n",
11218c2ecf20Sopenharmony_ci		  irqen & IRQEN_ROE ? "enabled" : "disabled");
11228c2ecf20Sopenharmony_ci	v4l2_info(sd, "\tBusy:                              %s\n",
11238c2ecf20Sopenharmony_ci		  stats & STATS_RBY ? "yes" : "no");
11248c2ecf20Sopenharmony_ci	v4l2_info(sd, "\tFIFO service requested:            %s\n",
11258c2ecf20Sopenharmony_ci		  stats & STATS_RSR ? "yes" : "no");
11268c2ecf20Sopenharmony_ci	v4l2_info(sd, "\tFIFO service request interrupt:    %s\n",
11278c2ecf20Sopenharmony_ci		  irqen & IRQEN_RSE ? "enabled" : "disabled");
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci	v4l2_info(sd, "IR Transmitter:\n");
11308c2ecf20Sopenharmony_ci	v4l2_info(sd, "\tEnabled:                           %s\n",
11318c2ecf20Sopenharmony_ci		  cntrl & CNTRL_TXE ? "yes" : "no");
11328c2ecf20Sopenharmony_ci	v4l2_info(sd, "\tModulation onto a carrier:         %s\n",
11338c2ecf20Sopenharmony_ci		  cntrl & CNTRL_MOD ? "enabled" : "disabled");
11348c2ecf20Sopenharmony_ci	v4l2_info(sd, "\tFIFO:                              %s\n",
11358c2ecf20Sopenharmony_ci		  cntrl & CNTRL_TFE ? "enabled" : "disabled");
11368c2ecf20Sopenharmony_ci	v4l2_info(sd, "\tFIFO interrupt watermark:          %s\n",
11378c2ecf20Sopenharmony_ci		  cntrl & CNTRL_TIC ? "not empty" : "half full or less");
11388c2ecf20Sopenharmony_ci	v4l2_info(sd, "\tCarrier polarity:                  %s\n",
11398c2ecf20Sopenharmony_ci		  cntrl & CNTRL_CPL ? "space:burst mark:noburst"
11408c2ecf20Sopenharmony_ci				    : "space:noburst mark:burst");
11418c2ecf20Sopenharmony_ci	if (cntrl & CNTRL_MOD) {
11428c2ecf20Sopenharmony_ci		v4l2_info(sd, "\tCarrier (16 clocks):               %u Hz\n",
11438c2ecf20Sopenharmony_ci			  clock_divider_to_carrier_freq(txclk));
11448c2ecf20Sopenharmony_ci		v4l2_info(sd, "\tCarrier duty cycle:                %2u/16\n",
11458c2ecf20Sopenharmony_ci			  cduty + 1);
11468c2ecf20Sopenharmony_ci	}
11478c2ecf20Sopenharmony_ci	v4l2_info(sd, "\tMax pulse width:                   %u us, %llu ns\n",
11488c2ecf20Sopenharmony_ci		  pulse_width_count_to_us(FIFO_RXTX, txclk),
11498c2ecf20Sopenharmony_ci		  pulse_width_count_to_ns(FIFO_RXTX, txclk));
11508c2ecf20Sopenharmony_ci	v4l2_info(sd, "\tBusy:                              %s\n",
11518c2ecf20Sopenharmony_ci		  stats & STATS_TBY ? "yes" : "no");
11528c2ecf20Sopenharmony_ci	v4l2_info(sd, "\tFIFO service requested:            %s\n",
11538c2ecf20Sopenharmony_ci		  stats & STATS_TSR ? "yes" : "no");
11548c2ecf20Sopenharmony_ci	v4l2_info(sd, "\tFIFO service request interrupt:    %s\n",
11558c2ecf20Sopenharmony_ci		  irqen & IRQEN_TSE ? "enabled" : "disabled");
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_ci	return 0;
11588c2ecf20Sopenharmony_ci}
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci
11618c2ecf20Sopenharmony_ciconst struct v4l2_subdev_ir_ops cx25840_ir_ops = {
11628c2ecf20Sopenharmony_ci	.rx_read = cx25840_ir_rx_read,
11638c2ecf20Sopenharmony_ci	.rx_g_parameters = cx25840_ir_rx_g_parameters,
11648c2ecf20Sopenharmony_ci	.rx_s_parameters = cx25840_ir_rx_s_parameters,
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci	.tx_write = cx25840_ir_tx_write,
11678c2ecf20Sopenharmony_ci	.tx_g_parameters = cx25840_ir_tx_g_parameters,
11688c2ecf20Sopenharmony_ci	.tx_s_parameters = cx25840_ir_tx_s_parameters,
11698c2ecf20Sopenharmony_ci};
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci
11728c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ir_parameters default_rx_params = {
11738c2ecf20Sopenharmony_ci	.bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec),
11748c2ecf20Sopenharmony_ci	.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci	.enable = false,
11778c2ecf20Sopenharmony_ci	.interrupt_enable = false,
11788c2ecf20Sopenharmony_ci	.shutdown = true,
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci	.modulation = true,
11818c2ecf20Sopenharmony_ci	.carrier_freq = 36000, /* 36 kHz - RC-5, and RC-6 carrier */
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci	/* RC-5: 666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */
11848c2ecf20Sopenharmony_ci	/* RC-6: 333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */
11858c2ecf20Sopenharmony_ci	.noise_filter_min_width = 333333, /* ns */
11868c2ecf20Sopenharmony_ci	.carrier_range_lower = 35000,
11878c2ecf20Sopenharmony_ci	.carrier_range_upper = 37000,
11888c2ecf20Sopenharmony_ci	.invert_level = false,
11898c2ecf20Sopenharmony_ci};
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ir_parameters default_tx_params = {
11928c2ecf20Sopenharmony_ci	.bytes_per_data_element = sizeof(union cx25840_ir_fifo_rec),
11938c2ecf20Sopenharmony_ci	.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH,
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci	.enable = false,
11968c2ecf20Sopenharmony_ci	.interrupt_enable = false,
11978c2ecf20Sopenharmony_ci	.shutdown = true,
11988c2ecf20Sopenharmony_ci
11998c2ecf20Sopenharmony_ci	.modulation = true,
12008c2ecf20Sopenharmony_ci	.carrier_freq = 36000, /* 36 kHz - RC-5 carrier */
12018c2ecf20Sopenharmony_ci	.duty_cycle = 25,      /* 25 %   - RC-5 carrier */
12028c2ecf20Sopenharmony_ci	.invert_level = false,
12038c2ecf20Sopenharmony_ci	.invert_carrier_sense = false,
12048c2ecf20Sopenharmony_ci};
12058c2ecf20Sopenharmony_ci
12068c2ecf20Sopenharmony_ciint cx25840_ir_probe(struct v4l2_subdev *sd)
12078c2ecf20Sopenharmony_ci{
12088c2ecf20Sopenharmony_ci	struct cx25840_state *state = to_state(sd);
12098c2ecf20Sopenharmony_ci	struct cx25840_ir_state *ir_state;
12108c2ecf20Sopenharmony_ci	struct v4l2_subdev_ir_parameters default_params;
12118c2ecf20Sopenharmony_ci
12128c2ecf20Sopenharmony_ci	/* Only init the IR controller for the CX2388[57] AV Core for now */
12138c2ecf20Sopenharmony_ci	if (!(is_cx23885(state) || is_cx23887(state)))
12148c2ecf20Sopenharmony_ci		return 0;
12158c2ecf20Sopenharmony_ci
12168c2ecf20Sopenharmony_ci	ir_state = devm_kzalloc(&state->c->dev, sizeof(*ir_state), GFP_KERNEL);
12178c2ecf20Sopenharmony_ci	if (ir_state == NULL)
12188c2ecf20Sopenharmony_ci		return -ENOMEM;
12198c2ecf20Sopenharmony_ci
12208c2ecf20Sopenharmony_ci	spin_lock_init(&ir_state->rx_kfifo_lock);
12218c2ecf20Sopenharmony_ci	if (kfifo_alloc(&ir_state->rx_kfifo,
12228c2ecf20Sopenharmony_ci			CX25840_IR_RX_KFIFO_SIZE, GFP_KERNEL))
12238c2ecf20Sopenharmony_ci		return -ENOMEM;
12248c2ecf20Sopenharmony_ci
12258c2ecf20Sopenharmony_ci	ir_state->c = state->c;
12268c2ecf20Sopenharmony_ci	state->ir_state = ir_state;
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci	/* Ensure no interrupts arrive yet */
12298c2ecf20Sopenharmony_ci	if (is_cx23885(state) || is_cx23887(state))
12308c2ecf20Sopenharmony_ci		cx25840_write4(ir_state->c, CX25840_IR_IRQEN_REG, IRQEN_MSK);
12318c2ecf20Sopenharmony_ci	else
12328c2ecf20Sopenharmony_ci		cx25840_write4(ir_state->c, CX25840_IR_IRQEN_REG, 0);
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_ci	mutex_init(&ir_state->rx_params_lock);
12358c2ecf20Sopenharmony_ci	default_params = default_rx_params;
12368c2ecf20Sopenharmony_ci	v4l2_subdev_call(sd, ir, rx_s_parameters, &default_params);
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci	mutex_init(&ir_state->tx_params_lock);
12398c2ecf20Sopenharmony_ci	default_params = default_tx_params;
12408c2ecf20Sopenharmony_ci	v4l2_subdev_call(sd, ir, tx_s_parameters, &default_params);
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	return 0;
12438c2ecf20Sopenharmony_ci}
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ciint cx25840_ir_remove(struct v4l2_subdev *sd)
12468c2ecf20Sopenharmony_ci{
12478c2ecf20Sopenharmony_ci	struct cx25840_state *state = to_state(sd);
12488c2ecf20Sopenharmony_ci	struct cx25840_ir_state *ir_state = to_ir_state(sd);
12498c2ecf20Sopenharmony_ci
12508c2ecf20Sopenharmony_ci	if (ir_state == NULL)
12518c2ecf20Sopenharmony_ci		return -ENODEV;
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_ci	cx25840_ir_rx_shutdown(sd);
12548c2ecf20Sopenharmony_ci	cx25840_ir_tx_shutdown(sd);
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci	kfifo_free(&ir_state->rx_kfifo);
12578c2ecf20Sopenharmony_ci	state->ir_state = NULL;
12588c2ecf20Sopenharmony_ci	return 0;
12598c2ecf20Sopenharmony_ci}
1260