162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci/*
462306a36Sopenharmony_ci * Hauppauge HD PVR USB driver
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Copyright (C) 2008      Janne Grunau (j@jannau.net)
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * IR device registration code is
962306a36Sopenharmony_ci * Copyright (C) 2010	Andy Walls <awalls@md.metrocast.net>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_I2C)
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/i2c.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci#include <linux/export.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "hdpvr.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define CTRL_READ_REQUEST	0xb8
2162306a36Sopenharmony_ci#define CTRL_WRITE_REQUEST	0x38
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#define REQTYPE_I2C_READ	0xb1
2462306a36Sopenharmony_ci#define REQTYPE_I2C_WRITE	0xb0
2562306a36Sopenharmony_ci#define REQTYPE_I2C_WRITE_STATT	0xd0
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define Z8F0811_IR_TX_I2C_ADDR	0x70
2862306a36Sopenharmony_ci#define Z8F0811_IR_RX_I2C_ADDR	0x71
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistruct i2c_client *hdpvr_register_ir_i2c(struct hdpvr_device *dev)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	struct IR_i2c_init_data *init_data = &dev->ir_i2c_init_data;
3462306a36Sopenharmony_ci	struct i2c_board_info info = {
3562306a36Sopenharmony_ci		I2C_BOARD_INFO("ir_z8f0811_hdpvr", Z8F0811_IR_RX_I2C_ADDR),
3662306a36Sopenharmony_ci	};
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	/* Our default information for ir-kbd-i2c.c to use */
3962306a36Sopenharmony_ci	init_data->ir_codes = RC_MAP_HAUPPAUGE;
4062306a36Sopenharmony_ci	init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR;
4162306a36Sopenharmony_ci	init_data->type = RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RC6_MCE |
4262306a36Sopenharmony_ci			  RC_PROTO_BIT_RC6_6A_32;
4362306a36Sopenharmony_ci	init_data->name = "HD-PVR";
4462306a36Sopenharmony_ci	init_data->polling_interval = 405; /* ms, duplicated from Windows */
4562306a36Sopenharmony_ci	info.platform_data = init_data;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	return i2c_new_client_device(&dev->i2c_adapter, &info);
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic int hdpvr_i2c_read(struct hdpvr_device *dev, int bus,
5162306a36Sopenharmony_ci			  unsigned char addr, char *wdata, int wlen,
5262306a36Sopenharmony_ci			  char *data, int len)
5362306a36Sopenharmony_ci{
5462306a36Sopenharmony_ci	int ret;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	if ((len > sizeof(dev->i2c_buf)) || (wlen > sizeof(dev->i2c_buf)))
5762306a36Sopenharmony_ci		return -EINVAL;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	if (wlen) {
6062306a36Sopenharmony_ci		memcpy(dev->i2c_buf, wdata, wlen);
6162306a36Sopenharmony_ci		ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
6262306a36Sopenharmony_ci				      REQTYPE_I2C_WRITE, CTRL_WRITE_REQUEST,
6362306a36Sopenharmony_ci				      (bus << 8) | addr, 0, dev->i2c_buf,
6462306a36Sopenharmony_ci				      wlen, 1000);
6562306a36Sopenharmony_ci		if (ret < 0)
6662306a36Sopenharmony_ci			return ret;
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
7062306a36Sopenharmony_ci			      REQTYPE_I2C_READ, CTRL_READ_REQUEST,
7162306a36Sopenharmony_ci			      (bus << 8) | addr, 0, dev->i2c_buf, len, 1000);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	if (ret == len) {
7462306a36Sopenharmony_ci		memcpy(data, dev->i2c_buf, len);
7562306a36Sopenharmony_ci		ret = 0;
7662306a36Sopenharmony_ci	} else if (ret >= 0)
7762306a36Sopenharmony_ci		ret = -EIO;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	return ret;
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic int hdpvr_i2c_write(struct hdpvr_device *dev, int bus,
8362306a36Sopenharmony_ci			   unsigned char addr, char *data, int len)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	int ret;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	if (len > sizeof(dev->i2c_buf))
8862306a36Sopenharmony_ci		return -EINVAL;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	memcpy(dev->i2c_buf, data, len);
9162306a36Sopenharmony_ci	ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
9262306a36Sopenharmony_ci			      REQTYPE_I2C_WRITE, CTRL_WRITE_REQUEST,
9362306a36Sopenharmony_ci			      (bus << 8) | addr, 0, dev->i2c_buf, len, 1000);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	if (ret < 0)
9662306a36Sopenharmony_ci		return ret;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
9962306a36Sopenharmony_ci			      REQTYPE_I2C_WRITE_STATT, CTRL_READ_REQUEST,
10062306a36Sopenharmony_ci			      0, 0, dev->i2c_buf, 2, 1000);
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	if ((ret == 2) && (dev->i2c_buf[1] == (len - 1)))
10362306a36Sopenharmony_ci		ret = 0;
10462306a36Sopenharmony_ci	else if (ret >= 0)
10562306a36Sopenharmony_ci		ret = -EIO;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	return ret;
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic int hdpvr_transfer(struct i2c_adapter *i2c_adapter, struct i2c_msg *msgs,
11162306a36Sopenharmony_ci			  int num)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	struct hdpvr_device *dev = i2c_get_adapdata(i2c_adapter);
11462306a36Sopenharmony_ci	int retval = 0, addr;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	mutex_lock(&dev->i2c_mutex);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	addr = msgs[0].addr << 1;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	if (num == 1) {
12162306a36Sopenharmony_ci		if (msgs[0].flags & I2C_M_RD)
12262306a36Sopenharmony_ci			retval = hdpvr_i2c_read(dev, 1, addr, NULL, 0,
12362306a36Sopenharmony_ci						msgs[0].buf, msgs[0].len);
12462306a36Sopenharmony_ci		else
12562306a36Sopenharmony_ci			retval = hdpvr_i2c_write(dev, 1, addr, msgs[0].buf,
12662306a36Sopenharmony_ci						 msgs[0].len);
12762306a36Sopenharmony_ci	} else if (num == 2) {
12862306a36Sopenharmony_ci		if (msgs[0].addr != msgs[1].addr) {
12962306a36Sopenharmony_ci			v4l2_warn(&dev->v4l2_dev, "refusing 2-phase i2c xfer with conflicting target addresses\n");
13062306a36Sopenharmony_ci			retval = -EINVAL;
13162306a36Sopenharmony_ci			goto out;
13262306a36Sopenharmony_ci		}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci		if ((msgs[0].flags & I2C_M_RD) || !(msgs[1].flags & I2C_M_RD)) {
13562306a36Sopenharmony_ci			v4l2_warn(&dev->v4l2_dev, "refusing complex xfer with r0=%d, r1=%d\n",
13662306a36Sopenharmony_ci				  msgs[0].flags & I2C_M_RD,
13762306a36Sopenharmony_ci				  msgs[1].flags & I2C_M_RD);
13862306a36Sopenharmony_ci			retval = -EINVAL;
13962306a36Sopenharmony_ci			goto out;
14062306a36Sopenharmony_ci		}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci		/*
14362306a36Sopenharmony_ci		 * Write followed by atomic read is the only complex xfer that
14462306a36Sopenharmony_ci		 * we actually support here.
14562306a36Sopenharmony_ci		 */
14662306a36Sopenharmony_ci		retval = hdpvr_i2c_read(dev, 1, addr, msgs[0].buf, msgs[0].len,
14762306a36Sopenharmony_ci					msgs[1].buf, msgs[1].len);
14862306a36Sopenharmony_ci	} else {
14962306a36Sopenharmony_ci		v4l2_warn(&dev->v4l2_dev, "refusing %d-phase i2c xfer\n", num);
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ciout:
15362306a36Sopenharmony_ci	mutex_unlock(&dev->i2c_mutex);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	return retval ? retval : num;
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic u32 hdpvr_functionality(struct i2c_adapter *adapter)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic const struct i2c_algorithm hdpvr_algo = {
16462306a36Sopenharmony_ci	.master_xfer   = hdpvr_transfer,
16562306a36Sopenharmony_ci	.functionality = hdpvr_functionality,
16662306a36Sopenharmony_ci};
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic const struct i2c_adapter hdpvr_i2c_adapter_template = {
16962306a36Sopenharmony_ci	.name   = "Hauppauge HD PVR I2C",
17062306a36Sopenharmony_ci	.owner  = THIS_MODULE,
17162306a36Sopenharmony_ci	.algo   = &hdpvr_algo,
17262306a36Sopenharmony_ci};
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic int hdpvr_activate_ir(struct hdpvr_device *dev)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	char buffer[2];
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	mutex_lock(&dev->i2c_mutex);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	hdpvr_i2c_read(dev, 0, 0x54, NULL, 0, buffer, 1);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	buffer[0] = 0;
18362306a36Sopenharmony_ci	buffer[1] = 0x8;
18462306a36Sopenharmony_ci	hdpvr_i2c_write(dev, 1, 0x54, buffer, 2);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	buffer[1] = 0x18;
18762306a36Sopenharmony_ci	hdpvr_i2c_write(dev, 1, 0x54, buffer, 2);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	mutex_unlock(&dev->i2c_mutex);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	return 0;
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ciint hdpvr_register_i2c_adapter(struct hdpvr_device *dev)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	hdpvr_activate_ir(dev);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	dev->i2c_adapter = hdpvr_i2c_adapter_template;
19962306a36Sopenharmony_ci	dev->i2c_adapter.dev.parent = &dev->udev->dev;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	i2c_set_adapdata(&dev->i2c_adapter, dev);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	return i2c_add_adapter(&dev->i2c_adapter);
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci#endif
207