// SPDX-License-Identifier: GPL-2.0-or-later
/*
*
* handle saa7134 IR remotes via linux kernel input layer.
*/
#include "saa7134.h"
#include "saa7134-reg.h"
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#define MODULE_NAME "saa7134"
static unsigned int disable_ir;
module_param(disable_ir, int, 0444);
MODULE_PARM_DESC(disable_ir,"disable infrared remote support");
static unsigned int ir_debug;
module_param(ir_debug, int, 0644);
MODULE_PARM_DESC(ir_debug,"enable debug messages [IR]");
static int pinnacle_remote;
module_param(pinnacle_remote, int, 0644); /* Choose Pinnacle PCTV remote */
MODULE_PARM_DESC(pinnacle_remote, "Specify Pinnacle PCTV remote: 0=coloured, 1=grey (defaults to 0)");
#define input_dbg(fmt, arg...) do { \
if (ir_debug) \
printk(KERN_DEBUG pr_fmt("input: " fmt), ## arg); \
} while (0)
#define ir_dbg(ir, fmt, arg...) do { \
if (ir_debug) \
printk(KERN_DEBUG pr_fmt("ir %s: " fmt), ir->rc->device_name, \
## arg); \
} while (0)
/* Helper function for raw decoding at GPIO16 or GPIO18 */
static int saa7134_raw_decode_irq(struct saa7134_dev *dev);
/* -------------------- GPIO generic keycode builder -------------------- */
static int build_key(struct saa7134_dev *dev)
{
struct saa7134_card_ir *ir = dev->remote;
u32 gpio, data;
/* here comes the additional handshake steps for some cards */
switch (dev->board) {
case SAA7134_BOARD_GOTVIEW_7135:
saa_setb(SAA7134_GPIO_GPSTATUS1, 0x80);
saa_clearb(SAA7134_GPIO_GPSTATUS1, 0x80);
break;
}
/* rising SAA7134_GPIO_GPRESCAN reads the status */
saa_clearb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN);
saa_setb(SAA7134_GPIO_GPMODE3,SAA7134_GPIO_GPRESCAN);
gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2);
if (ir->polling) {
if (ir->last_gpio == gpio)
return 0;
ir->last_gpio = gpio;
}
data = ir_extract_bits(gpio, ir->mask_keycode);
input_dbg("build_key gpio=0x%x mask=0x%x data=%d\n",
gpio, ir->mask_keycode, data);
switch (dev->board) {
case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG:
if (data == ir->mask_keycode)
rc_keyup(ir->dev);
else
rc_keydown_notimeout(ir->dev, RC_PROTO_UNKNOWN, data,
0);
return 0;
}
if (ir->polling) {
if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) ||
(ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) {
rc_keydown_notimeout(ir->dev, RC_PROTO_UNKNOWN, data,
0);
} else {
rc_keyup(ir->dev);
}
}
else { /* IRQ driven mode - handle key press and release in one go */
if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) ||
(ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) {
rc_keydown_notimeout(ir->dev, RC_PROTO_UNKNOWN, data,
0);
rc_keyup(ir->dev);
}
}
return 0;
}
/* --------------------- Chip specific I2C key builders ----------------- */
static int get_key_flydvb_trio(struct IR_i2c *ir, enum rc_proto *protocol,
u32 *scancode, u8 *toggle)
{
int gpio, rc;
int attempt = 0;
unsigned char b;
/* We need this to access GPI Used by the saa_readl macro. */
struct saa7134_dev *dev = ir->c->adapter->algo_data;
if (dev == NULL) {
ir_dbg(ir, "get_key_flydvb_trio: ir->c->adapter->algo_data is NULL!\n");
return -EIO;
}
/* rising SAA7134_GPIGPRESCAN reads the status */
saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2);
if (0x40000 & ~gpio)
return 0; /* No button press */
/* poll IR chip */
/* weak up the IR chip */
b = 0;
while (1 != i2c_master_send(ir->c, &b, 1)) {
if ((attempt++) < 10) {
/*
* wait a bit for next attempt -
* I don't know how make it better
*/
msleep(10);
continue;
}
ir_dbg(ir, "send wake up byte to pic16C505 (IR chip)failed %dx\n",
attempt);
return -EIO;
}
rc = i2c_master_recv(ir->c, &b, 1);
if (rc != 1) {
ir_dbg(ir, "read error\n");
if (rc < 0)
return rc;
return -EIO;
}
*protocol = RC_PROTO_UNKNOWN;
*scancode = b;
*toggle = 0;
return 1;
}
static int get_key_msi_tvanywhere_plus(struct IR_i2c *ir,
enum rc_proto *protocol,
u32 *scancode, u8 *toggle)
{
unsigned char b;
int gpio, rc;
/* <dev> is needed to access GPIO. Used by the saa_readl macro. */
struct saa7134_dev *dev = ir->c->adapter->algo_data;
if (dev == NULL) {
ir_dbg(ir, "get_key_msi_tvanywhere_plus: ir->c->adapter->algo_data is NULL!\n");
return -EIO;
}
/* rising SAA7134_GPIO_GPRESCAN reads the status */
saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
gpio = saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2);
/* GPIO&0x40 is pulsed low when a button is pressed. Don't do
I2C receive if gpio&0x40 is not low. */
if (gpio & 0x40)
return 0; /* No button press */