162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2008 Sensoray Company Inc.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include <linux/usb.h>
862306a36Sopenharmony_ci#include <linux/i2c.h>
962306a36Sopenharmony_ci#include <linux/videodev2.h>
1062306a36Sopenharmony_ci#include <linux/slab.h>
1162306a36Sopenharmony_ci#include <media/v4l2-device.h>
1262306a36Sopenharmony_ci#include <media/v4l2-common.h>
1362306a36Sopenharmony_ci#include <media/v4l2-subdev.h>
1462306a36Sopenharmony_ci#include "go7007-priv.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ciMODULE_DESCRIPTION("Sensoray 2250/2251 i2c v4l2 subdev driver");
1762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/*
2062306a36Sopenharmony_ci * Note: this board has two i2c devices: a vpx3226f and a tlv320aic23b.
2162306a36Sopenharmony_ci * Due to the unusual way these are accessed on this device we do not
2262306a36Sopenharmony_ci * reuse the i2c drivers, but instead they are implemented in this
2362306a36Sopenharmony_ci * driver. It would be nice to improve on this, though.
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define TLV320_ADDRESS      0x34
2762306a36Sopenharmony_ci#define VPX322_ADDR_ANALOGCONTROL1	0x02
2862306a36Sopenharmony_ci#define VPX322_ADDR_BRIGHTNESS0		0x0127
2962306a36Sopenharmony_ci#define VPX322_ADDR_BRIGHTNESS1		0x0131
3062306a36Sopenharmony_ci#define VPX322_ADDR_CONTRAST0		0x0128
3162306a36Sopenharmony_ci#define VPX322_ADDR_CONTRAST1		0x0132
3262306a36Sopenharmony_ci#define VPX322_ADDR_HUE			0x00dc
3362306a36Sopenharmony_ci#define VPX322_ADDR_SAT			0x0030
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_cistruct go7007_usb_board {
3662306a36Sopenharmony_ci	unsigned int flags;
3762306a36Sopenharmony_ci	struct go7007_board_info main_info;
3862306a36Sopenharmony_ci};
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistruct go7007_usb {
4162306a36Sopenharmony_ci	struct go7007_usb_board *board;
4262306a36Sopenharmony_ci	struct mutex i2c_lock;
4362306a36Sopenharmony_ci	struct usb_device *usbdev;
4462306a36Sopenharmony_ci	struct urb *video_urbs[8];
4562306a36Sopenharmony_ci	struct urb *audio_urbs[8];
4662306a36Sopenharmony_ci	struct urb *intr_urb;
4762306a36Sopenharmony_ci};
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic unsigned char aud_regs[] = {
5062306a36Sopenharmony_ci	0x1e, 0x00,
5162306a36Sopenharmony_ci	0x00, 0x17,
5262306a36Sopenharmony_ci	0x02, 0x17,
5362306a36Sopenharmony_ci	0x04, 0xf9,
5462306a36Sopenharmony_ci	0x06, 0xf9,
5562306a36Sopenharmony_ci	0x08, 0x02,
5662306a36Sopenharmony_ci	0x0a, 0x00,
5762306a36Sopenharmony_ci	0x0c, 0x00,
5862306a36Sopenharmony_ci	0x0a, 0x00,
5962306a36Sopenharmony_ci	0x0c, 0x00,
6062306a36Sopenharmony_ci	0x0e, 0x02,
6162306a36Sopenharmony_ci	0x10, 0x00,
6262306a36Sopenharmony_ci	0x12, 0x01,
6362306a36Sopenharmony_ci	0x00, 0x00,
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic unsigned char vid_regs[] = {
6862306a36Sopenharmony_ci	0xF2, 0x0f,
6962306a36Sopenharmony_ci	0xAA, 0x00,
7062306a36Sopenharmony_ci	0xF8, 0xff,
7162306a36Sopenharmony_ci	0x00, 0x00,
7262306a36Sopenharmony_ci};
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic u16 vid_regs_fp[] = {
7562306a36Sopenharmony_ci	0x028, 0x067,
7662306a36Sopenharmony_ci	0x120, 0x016,
7762306a36Sopenharmony_ci	0x121, 0xcF2,
7862306a36Sopenharmony_ci	0x122, 0x0F2,
7962306a36Sopenharmony_ci	0x123, 0x00c,
8062306a36Sopenharmony_ci	0x124, 0x2d0,
8162306a36Sopenharmony_ci	0x125, 0x2e0,
8262306a36Sopenharmony_ci	0x126, 0x004,
8362306a36Sopenharmony_ci	0x128, 0x1E0,
8462306a36Sopenharmony_ci	0x12A, 0x016,
8562306a36Sopenharmony_ci	0x12B, 0x0F2,
8662306a36Sopenharmony_ci	0x12C, 0x0F2,
8762306a36Sopenharmony_ci	0x12D, 0x00c,
8862306a36Sopenharmony_ci	0x12E, 0x2d0,
8962306a36Sopenharmony_ci	0x12F, 0x2e0,
9062306a36Sopenharmony_ci	0x130, 0x004,
9162306a36Sopenharmony_ci	0x132, 0x1E0,
9262306a36Sopenharmony_ci	0x140, 0x060,
9362306a36Sopenharmony_ci	0x153, 0x00C,
9462306a36Sopenharmony_ci	0x154, 0x200,
9562306a36Sopenharmony_ci	0x150, 0x801,
9662306a36Sopenharmony_ci	0x000, 0x000
9762306a36Sopenharmony_ci};
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci/* PAL specific values */
10062306a36Sopenharmony_cistatic u16 vid_regs_fp_pal[] = {
10162306a36Sopenharmony_ci	0x120, 0x017,
10262306a36Sopenharmony_ci	0x121, 0xd22,
10362306a36Sopenharmony_ci	0x122, 0x122,
10462306a36Sopenharmony_ci	0x12A, 0x017,
10562306a36Sopenharmony_ci	0x12B, 0x122,
10662306a36Sopenharmony_ci	0x12C, 0x122,
10762306a36Sopenharmony_ci	0x140, 0x060,
10862306a36Sopenharmony_ci	0x000, 0x000,
10962306a36Sopenharmony_ci};
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistruct s2250 {
11262306a36Sopenharmony_ci	struct v4l2_subdev sd;
11362306a36Sopenharmony_ci	struct v4l2_ctrl_handler hdl;
11462306a36Sopenharmony_ci	v4l2_std_id std;
11562306a36Sopenharmony_ci	int input;
11662306a36Sopenharmony_ci	int brightness;
11762306a36Sopenharmony_ci	int contrast;
11862306a36Sopenharmony_ci	int saturation;
11962306a36Sopenharmony_ci	int hue;
12062306a36Sopenharmony_ci	int reg12b_val;
12162306a36Sopenharmony_ci	int audio_input;
12262306a36Sopenharmony_ci	struct i2c_client *audio;
12362306a36Sopenharmony_ci};
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic inline struct s2250 *to_state(struct v4l2_subdev *sd)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	return container_of(sd, struct s2250, sd);
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci/* from go7007-usb.c which is Copyright (C) 2005-2006 Micronas USA Inc.*/
13162306a36Sopenharmony_cistatic int go7007_usb_vendor_request(struct go7007 *go, u16 request,
13262306a36Sopenharmony_ci	u16 value, u16 index, void *transfer_buffer, int length, int in)
13362306a36Sopenharmony_ci{
13462306a36Sopenharmony_ci	struct go7007_usb *usb = go->hpi_context;
13562306a36Sopenharmony_ci	int timeout = 5000;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	if (in) {
13862306a36Sopenharmony_ci		return usb_control_msg(usb->usbdev,
13962306a36Sopenharmony_ci				usb_rcvctrlpipe(usb->usbdev, 0), request,
14062306a36Sopenharmony_ci				USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
14162306a36Sopenharmony_ci				value, index, transfer_buffer, length, timeout);
14262306a36Sopenharmony_ci	} else {
14362306a36Sopenharmony_ci		return usb_control_msg(usb->usbdev,
14462306a36Sopenharmony_ci				usb_sndctrlpipe(usb->usbdev, 0), request,
14562306a36Sopenharmony_ci				USB_TYPE_VENDOR | USB_RECIP_DEVICE,
14662306a36Sopenharmony_ci				value, index, transfer_buffer, length, timeout);
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci/* end from go7007-usb.c which is Copyright (C) 2005-2006 Micronas USA Inc.*/
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic int write_reg(struct i2c_client *client, u8 reg, u8 value)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	struct go7007 *go = i2c_get_adapdata(client->adapter);
15462306a36Sopenharmony_ci	struct go7007_usb *usb;
15562306a36Sopenharmony_ci	int rc;
15662306a36Sopenharmony_ci	int dev_addr = client->addr << 1;  /* firmware wants 8-bit address */
15762306a36Sopenharmony_ci	u8 *buf;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	if (go == NULL)
16062306a36Sopenharmony_ci		return -ENODEV;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	if (go->status == STATUS_SHUTDOWN)
16362306a36Sopenharmony_ci		return -EBUSY;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	buf = kzalloc(16, GFP_KERNEL);
16662306a36Sopenharmony_ci	if (buf == NULL)
16762306a36Sopenharmony_ci		return -ENOMEM;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	usb = go->hpi_context;
17062306a36Sopenharmony_ci	if (mutex_lock_interruptible(&usb->i2c_lock) != 0) {
17162306a36Sopenharmony_ci		dev_info(&client->dev, "i2c lock failed\n");
17262306a36Sopenharmony_ci		kfree(buf);
17362306a36Sopenharmony_ci		return -EINTR;
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci	rc = go7007_usb_vendor_request(go, 0x55, dev_addr,
17662306a36Sopenharmony_ci				       (reg<<8 | value),
17762306a36Sopenharmony_ci				       buf,
17862306a36Sopenharmony_ci				       16, 1);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	mutex_unlock(&usb->i2c_lock);
18162306a36Sopenharmony_ci	kfree(buf);
18262306a36Sopenharmony_ci	return rc;
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic int write_reg_fp(struct i2c_client *client, u16 addr, u16 val)
18662306a36Sopenharmony_ci{
18762306a36Sopenharmony_ci	struct go7007 *go = i2c_get_adapdata(client->adapter);
18862306a36Sopenharmony_ci	struct go7007_usb *usb;
18962306a36Sopenharmony_ci	int rc;
19062306a36Sopenharmony_ci	u8 *buf;
19162306a36Sopenharmony_ci	struct s2250 *dec = i2c_get_clientdata(client);
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	if (go == NULL)
19462306a36Sopenharmony_ci		return -ENODEV;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	if (go->status == STATUS_SHUTDOWN)
19762306a36Sopenharmony_ci		return -EBUSY;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	buf = kzalloc(16, GFP_KERNEL);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	if (buf == NULL)
20262306a36Sopenharmony_ci		return -ENOMEM;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	memset(buf, 0xcd, 6);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	usb = go->hpi_context;
20962306a36Sopenharmony_ci	if (mutex_lock_interruptible(&usb->i2c_lock) != 0) {
21062306a36Sopenharmony_ci		dev_info(&client->dev, "i2c lock failed\n");
21162306a36Sopenharmony_ci		kfree(buf);
21262306a36Sopenharmony_ci		return -EINTR;
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci	rc = go7007_usb_vendor_request(go, 0x57, addr, val, buf, 16, 1);
21562306a36Sopenharmony_ci	mutex_unlock(&usb->i2c_lock);
21662306a36Sopenharmony_ci	if (rc < 0) {
21762306a36Sopenharmony_ci		kfree(buf);
21862306a36Sopenharmony_ci		return rc;
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	if (buf[0] == 0) {
22262306a36Sopenharmony_ci		unsigned int subaddr, val_read;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci		subaddr = (buf[4] << 8) + buf[5];
22562306a36Sopenharmony_ci		val_read = (buf[2] << 8) + buf[3];
22662306a36Sopenharmony_ci		kfree(buf);
22762306a36Sopenharmony_ci		if (val_read != val) {
22862306a36Sopenharmony_ci			dev_info(&client->dev, "invalid fp write %x %x\n",
22962306a36Sopenharmony_ci				 val_read, val);
23062306a36Sopenharmony_ci			return -EFAULT;
23162306a36Sopenharmony_ci		}
23262306a36Sopenharmony_ci		if (subaddr != addr) {
23362306a36Sopenharmony_ci			dev_info(&client->dev, "invalid fp write addr %x %x\n",
23462306a36Sopenharmony_ci				 subaddr, addr);
23562306a36Sopenharmony_ci			return -EFAULT;
23662306a36Sopenharmony_ci		}
23762306a36Sopenharmony_ci	} else {
23862306a36Sopenharmony_ci		kfree(buf);
23962306a36Sopenharmony_ci		return -EFAULT;
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	/* save last 12b value */
24362306a36Sopenharmony_ci	if (addr == 0x12b)
24462306a36Sopenharmony_ci		dec->reg12b_val = val;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	return 0;
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic int read_reg_fp(struct i2c_client *client, u16 addr, u16 *val)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	struct go7007 *go = i2c_get_adapdata(client->adapter);
25262306a36Sopenharmony_ci	struct go7007_usb *usb;
25362306a36Sopenharmony_ci	int rc;
25462306a36Sopenharmony_ci	u8 *buf;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	if (go == NULL)
25762306a36Sopenharmony_ci		return -ENODEV;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	if (go->status == STATUS_SHUTDOWN)
26062306a36Sopenharmony_ci		return -EBUSY;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	buf = kzalloc(16, GFP_KERNEL);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	if (buf == NULL)
26562306a36Sopenharmony_ci		return -ENOMEM;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	memset(buf, 0xcd, 6);
27062306a36Sopenharmony_ci	usb = go->hpi_context;
27162306a36Sopenharmony_ci	if (mutex_lock_interruptible(&usb->i2c_lock) != 0) {
27262306a36Sopenharmony_ci		dev_info(&client->dev, "i2c lock failed\n");
27362306a36Sopenharmony_ci		kfree(buf);
27462306a36Sopenharmony_ci		return -EINTR;
27562306a36Sopenharmony_ci	}
27662306a36Sopenharmony_ci	rc = go7007_usb_vendor_request(go, 0x58, addr, 0, buf, 16, 1);
27762306a36Sopenharmony_ci	mutex_unlock(&usb->i2c_lock);
27862306a36Sopenharmony_ci	if (rc < 0) {
27962306a36Sopenharmony_ci		kfree(buf);
28062306a36Sopenharmony_ci		return rc;
28162306a36Sopenharmony_ci	}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	*val = (buf[0] << 8) | buf[1];
28462306a36Sopenharmony_ci	kfree(buf);
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	return 0;
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic int write_regs(struct i2c_client *client, u8 *regs)
29162306a36Sopenharmony_ci{
29262306a36Sopenharmony_ci	int i;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) {
29562306a36Sopenharmony_ci		if (write_reg(client, regs[i], regs[i+1]) < 0) {
29662306a36Sopenharmony_ci			dev_info(&client->dev, "failed\n");
29762306a36Sopenharmony_ci			return -1;
29862306a36Sopenharmony_ci		}
29962306a36Sopenharmony_ci	}
30062306a36Sopenharmony_ci	return 0;
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistatic int write_regs_fp(struct i2c_client *client, u16 *regs)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	int i;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) {
30862306a36Sopenharmony_ci		if (write_reg_fp(client, regs[i], regs[i+1]) < 0) {
30962306a36Sopenharmony_ci			dev_info(&client->dev, "failed fp\n");
31062306a36Sopenharmony_ci			return -1;
31162306a36Sopenharmony_ci		}
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci	return 0;
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci/* ------------------------------------------------------------------------- */
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic int s2250_s_video_routing(struct v4l2_subdev *sd, u32 input, u32 output,
32062306a36Sopenharmony_ci				 u32 config)
32162306a36Sopenharmony_ci{
32262306a36Sopenharmony_ci	struct s2250 *state = to_state(sd);
32362306a36Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(sd);
32462306a36Sopenharmony_ci	int vidsys;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	vidsys = (state->std == V4L2_STD_NTSC) ? 0x01 : 0x00;
32762306a36Sopenharmony_ci	if (input == 0) {
32862306a36Sopenharmony_ci		/* composite */
32962306a36Sopenharmony_ci		write_reg_fp(client, 0x20, 0x020 | vidsys);
33062306a36Sopenharmony_ci		write_reg_fp(client, 0x21, 0x662);
33162306a36Sopenharmony_ci		write_reg_fp(client, 0x140, 0x060);
33262306a36Sopenharmony_ci	} else if (input == 1) {
33362306a36Sopenharmony_ci		/* S-Video */
33462306a36Sopenharmony_ci		write_reg_fp(client, 0x20, 0x040 | vidsys);
33562306a36Sopenharmony_ci		write_reg_fp(client, 0x21, 0x666);
33662306a36Sopenharmony_ci		write_reg_fp(client, 0x140, 0x060);
33762306a36Sopenharmony_ci	} else {
33862306a36Sopenharmony_ci		return -EINVAL;
33962306a36Sopenharmony_ci	}
34062306a36Sopenharmony_ci	state->input = input;
34162306a36Sopenharmony_ci	return 0;
34262306a36Sopenharmony_ci}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_cistatic int s2250_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	struct s2250 *state = to_state(sd);
34762306a36Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(sd);
34862306a36Sopenharmony_ci	u16 vidsource;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	vidsource = (state->input == 1) ? 0x040 : 0x020;
35162306a36Sopenharmony_ci	if (norm & V4L2_STD_625_50) {
35262306a36Sopenharmony_ci		write_regs_fp(client, vid_regs_fp);
35362306a36Sopenharmony_ci		write_regs_fp(client, vid_regs_fp_pal);
35462306a36Sopenharmony_ci		write_reg_fp(client, 0x20, vidsource);
35562306a36Sopenharmony_ci	} else {
35662306a36Sopenharmony_ci		write_regs_fp(client, vid_regs_fp);
35762306a36Sopenharmony_ci		write_reg_fp(client, 0x20, vidsource | 1);
35862306a36Sopenharmony_ci	}
35962306a36Sopenharmony_ci	state->std = norm;
36062306a36Sopenharmony_ci	return 0;
36162306a36Sopenharmony_ci}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_cistatic int s2250_s_ctrl(struct v4l2_ctrl *ctrl)
36462306a36Sopenharmony_ci{
36562306a36Sopenharmony_ci	struct s2250 *state = container_of(ctrl->handler, struct s2250, hdl);
36662306a36Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
36762306a36Sopenharmony_ci	u16 oldvalue;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	switch (ctrl->id) {
37062306a36Sopenharmony_ci	case V4L2_CID_BRIGHTNESS:
37162306a36Sopenharmony_ci		read_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, &oldvalue);
37262306a36Sopenharmony_ci		write_reg_fp(client, VPX322_ADDR_BRIGHTNESS0,
37362306a36Sopenharmony_ci			     ctrl->val | (oldvalue & ~0xff));
37462306a36Sopenharmony_ci		read_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, &oldvalue);
37562306a36Sopenharmony_ci		write_reg_fp(client, VPX322_ADDR_BRIGHTNESS1,
37662306a36Sopenharmony_ci			     ctrl->val | (oldvalue & ~0xff));
37762306a36Sopenharmony_ci		write_reg_fp(client, 0x140, 0x60);
37862306a36Sopenharmony_ci		break;
37962306a36Sopenharmony_ci	case V4L2_CID_CONTRAST:
38062306a36Sopenharmony_ci		read_reg_fp(client, VPX322_ADDR_CONTRAST0, &oldvalue);
38162306a36Sopenharmony_ci		write_reg_fp(client, VPX322_ADDR_CONTRAST0,
38262306a36Sopenharmony_ci			     ctrl->val | (oldvalue & ~0x3f));
38362306a36Sopenharmony_ci		read_reg_fp(client, VPX322_ADDR_CONTRAST1, &oldvalue);
38462306a36Sopenharmony_ci		write_reg_fp(client, VPX322_ADDR_CONTRAST1,
38562306a36Sopenharmony_ci			     ctrl->val | (oldvalue & ~0x3f));
38662306a36Sopenharmony_ci		write_reg_fp(client, 0x140, 0x60);
38762306a36Sopenharmony_ci		break;
38862306a36Sopenharmony_ci	case V4L2_CID_SATURATION:
38962306a36Sopenharmony_ci		write_reg_fp(client, VPX322_ADDR_SAT, ctrl->val);
39062306a36Sopenharmony_ci		break;
39162306a36Sopenharmony_ci	case V4L2_CID_HUE:
39262306a36Sopenharmony_ci		write_reg_fp(client, VPX322_ADDR_HUE, ctrl->val);
39362306a36Sopenharmony_ci		break;
39462306a36Sopenharmony_ci	default:
39562306a36Sopenharmony_ci		return -EINVAL;
39662306a36Sopenharmony_ci	}
39762306a36Sopenharmony_ci	return 0;
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic int s2250_set_fmt(struct v4l2_subdev *sd,
40162306a36Sopenharmony_ci		struct v4l2_subdev_state *sd_state,
40262306a36Sopenharmony_ci		struct v4l2_subdev_format *format)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	struct v4l2_mbus_framefmt *fmt = &format->format;
40562306a36Sopenharmony_ci	struct s2250 *state = to_state(sd);
40662306a36Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(sd);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	if (format->pad)
40962306a36Sopenharmony_ci		return -EINVAL;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
41262306a36Sopenharmony_ci		return 0;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	if (fmt->height < 640) {
41562306a36Sopenharmony_ci		write_reg_fp(client, 0x12b, state->reg12b_val | 0x400);
41662306a36Sopenharmony_ci		write_reg_fp(client, 0x140, 0x060);
41762306a36Sopenharmony_ci	} else {
41862306a36Sopenharmony_ci		write_reg_fp(client, 0x12b, state->reg12b_val & ~0x400);
41962306a36Sopenharmony_ci		write_reg_fp(client, 0x140, 0x060);
42062306a36Sopenharmony_ci	}
42162306a36Sopenharmony_ci	return 0;
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic int s2250_s_audio_routing(struct v4l2_subdev *sd, u32 input, u32 output,
42562306a36Sopenharmony_ci				 u32 config)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci	struct s2250 *state = to_state(sd);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	switch (input) {
43062306a36Sopenharmony_ci	case 0:
43162306a36Sopenharmony_ci		write_reg(state->audio, 0x08, 0x02); /* Line In */
43262306a36Sopenharmony_ci		break;
43362306a36Sopenharmony_ci	case 1:
43462306a36Sopenharmony_ci		write_reg(state->audio, 0x08, 0x04); /* Mic */
43562306a36Sopenharmony_ci		break;
43662306a36Sopenharmony_ci	case 2:
43762306a36Sopenharmony_ci		write_reg(state->audio, 0x08, 0x05); /* Mic Boost */
43862306a36Sopenharmony_ci		break;
43962306a36Sopenharmony_ci	default:
44062306a36Sopenharmony_ci		return -EINVAL;
44162306a36Sopenharmony_ci	}
44262306a36Sopenharmony_ci	state->audio_input = input;
44362306a36Sopenharmony_ci	return 0;
44462306a36Sopenharmony_ci}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic int s2250_log_status(struct v4l2_subdev *sd)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	struct s2250 *state = to_state(sd);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	v4l2_info(sd, "Standard: %s\n", state->std == V4L2_STD_NTSC ? "NTSC" :
45262306a36Sopenharmony_ci					state->std == V4L2_STD_PAL ? "PAL" :
45362306a36Sopenharmony_ci					state->std == V4L2_STD_SECAM ? "SECAM" :
45462306a36Sopenharmony_ci					"unknown");
45562306a36Sopenharmony_ci	v4l2_info(sd, "Input: %s\n", state->input == 0 ? "Composite" :
45662306a36Sopenharmony_ci					state->input == 1 ? "S-video" :
45762306a36Sopenharmony_ci					"error");
45862306a36Sopenharmony_ci	v4l2_info(sd, "Audio input: %s\n", state->audio_input == 0 ? "Line In" :
45962306a36Sopenharmony_ci					state->audio_input == 1 ? "Mic" :
46062306a36Sopenharmony_ci					state->audio_input == 2 ? "Mic Boost" :
46162306a36Sopenharmony_ci					"error");
46262306a36Sopenharmony_ci	return v4l2_ctrl_subdev_log_status(sd);
46362306a36Sopenharmony_ci}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci/* --------------------------------------------------------------------------*/
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops s2250_ctrl_ops = {
46862306a36Sopenharmony_ci	.s_ctrl = s2250_s_ctrl,
46962306a36Sopenharmony_ci};
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_cistatic const struct v4l2_subdev_core_ops s2250_core_ops = {
47262306a36Sopenharmony_ci	.log_status = s2250_log_status,
47362306a36Sopenharmony_ci};
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_cistatic const struct v4l2_subdev_audio_ops s2250_audio_ops = {
47662306a36Sopenharmony_ci	.s_routing = s2250_s_audio_routing,
47762306a36Sopenharmony_ci};
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_cistatic const struct v4l2_subdev_video_ops s2250_video_ops = {
48062306a36Sopenharmony_ci	.s_std = s2250_s_std,
48162306a36Sopenharmony_ci	.s_routing = s2250_s_video_routing,
48262306a36Sopenharmony_ci};
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic const struct v4l2_subdev_pad_ops s2250_pad_ops = {
48562306a36Sopenharmony_ci	.set_fmt = s2250_set_fmt,
48662306a36Sopenharmony_ci};
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_cistatic const struct v4l2_subdev_ops s2250_ops = {
48962306a36Sopenharmony_ci	.core = &s2250_core_ops,
49062306a36Sopenharmony_ci	.audio = &s2250_audio_ops,
49162306a36Sopenharmony_ci	.video = &s2250_video_ops,
49262306a36Sopenharmony_ci	.pad = &s2250_pad_ops,
49362306a36Sopenharmony_ci};
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci/* --------------------------------------------------------------------------*/
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_cistatic int s2250_probe(struct i2c_client *client)
49862306a36Sopenharmony_ci{
49962306a36Sopenharmony_ci	struct i2c_client *audio;
50062306a36Sopenharmony_ci	struct i2c_adapter *adapter = client->adapter;
50162306a36Sopenharmony_ci	struct s2250 *state;
50262306a36Sopenharmony_ci	struct v4l2_subdev *sd;
50362306a36Sopenharmony_ci	u8 *data;
50462306a36Sopenharmony_ci	struct go7007 *go = i2c_get_adapdata(adapter);
50562306a36Sopenharmony_ci	struct go7007_usb *usb = go->hpi_context;
50662306a36Sopenharmony_ci	int err = -EIO;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	audio = i2c_new_dummy_device(adapter, TLV320_ADDRESS >> 1);
50962306a36Sopenharmony_ci	if (IS_ERR(audio))
51062306a36Sopenharmony_ci		return PTR_ERR(audio);
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	state = kzalloc(sizeof(struct s2250), GFP_KERNEL);
51362306a36Sopenharmony_ci	if (state == NULL) {
51462306a36Sopenharmony_ci		i2c_unregister_device(audio);
51562306a36Sopenharmony_ci		return -ENOMEM;
51662306a36Sopenharmony_ci	}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	sd = &state->sd;
51962306a36Sopenharmony_ci	v4l2_i2c_subdev_init(sd, client, &s2250_ops);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	v4l2_info(sd, "initializing %s at address 0x%x on %s\n",
52262306a36Sopenharmony_ci	       "Sensoray 2250/2251", client->addr, client->adapter->name);
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	v4l2_ctrl_handler_init(&state->hdl, 4);
52562306a36Sopenharmony_ci	v4l2_ctrl_new_std(&state->hdl, &s2250_ctrl_ops,
52662306a36Sopenharmony_ci		V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
52762306a36Sopenharmony_ci	v4l2_ctrl_new_std(&state->hdl, &s2250_ctrl_ops,
52862306a36Sopenharmony_ci		V4L2_CID_CONTRAST, 0, 0x3f, 1, 0x32);
52962306a36Sopenharmony_ci	v4l2_ctrl_new_std(&state->hdl, &s2250_ctrl_ops,
53062306a36Sopenharmony_ci		V4L2_CID_SATURATION, 0, 4094, 1, 2070);
53162306a36Sopenharmony_ci	v4l2_ctrl_new_std(&state->hdl, &s2250_ctrl_ops,
53262306a36Sopenharmony_ci		V4L2_CID_HUE, -512, 511, 1, 0);
53362306a36Sopenharmony_ci	sd->ctrl_handler = &state->hdl;
53462306a36Sopenharmony_ci	if (state->hdl.error) {
53562306a36Sopenharmony_ci		err = state->hdl.error;
53662306a36Sopenharmony_ci		goto fail;
53762306a36Sopenharmony_ci	}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci	state->std = V4L2_STD_NTSC;
54062306a36Sopenharmony_ci	state->brightness = 50;
54162306a36Sopenharmony_ci	state->contrast = 50;
54262306a36Sopenharmony_ci	state->saturation = 50;
54362306a36Sopenharmony_ci	state->hue = 0;
54462306a36Sopenharmony_ci	state->audio = audio;
54562306a36Sopenharmony_ci
54662306a36Sopenharmony_ci	/* initialize the audio */
54762306a36Sopenharmony_ci	if (write_regs(audio, aud_regs) < 0) {
54862306a36Sopenharmony_ci		dev_err(&client->dev, "error initializing audio\n");
54962306a36Sopenharmony_ci		goto fail;
55062306a36Sopenharmony_ci	}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	if (write_regs(client, vid_regs) < 0) {
55362306a36Sopenharmony_ci		dev_err(&client->dev, "error initializing decoder\n");
55462306a36Sopenharmony_ci		goto fail;
55562306a36Sopenharmony_ci	}
55662306a36Sopenharmony_ci	if (write_regs_fp(client, vid_regs_fp) < 0) {
55762306a36Sopenharmony_ci		dev_err(&client->dev, "error initializing decoder\n");
55862306a36Sopenharmony_ci		goto fail;
55962306a36Sopenharmony_ci	}
56062306a36Sopenharmony_ci	/* set default channel */
56162306a36Sopenharmony_ci	/* composite */
56262306a36Sopenharmony_ci	write_reg_fp(client, 0x20, 0x020 | 1);
56362306a36Sopenharmony_ci	write_reg_fp(client, 0x21, 0x662);
56462306a36Sopenharmony_ci	write_reg_fp(client, 0x140, 0x060);
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	/* set default audio input */
56762306a36Sopenharmony_ci	state->audio_input = 0;
56862306a36Sopenharmony_ci	write_reg(client, 0x08, 0x02); /* Line In */
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	if (mutex_lock_interruptible(&usb->i2c_lock) == 0) {
57162306a36Sopenharmony_ci		data = kzalloc(16, GFP_KERNEL);
57262306a36Sopenharmony_ci		if (data != NULL) {
57362306a36Sopenharmony_ci			int rc = go7007_usb_vendor_request(go, 0x41, 0, 0,
57462306a36Sopenharmony_ci						       data, 16, 1);
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci			if (rc > 0) {
57762306a36Sopenharmony_ci				u8 mask;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci				data[0] = 0;
58062306a36Sopenharmony_ci				mask = 1<<5;
58162306a36Sopenharmony_ci				data[0] &= ~mask;
58262306a36Sopenharmony_ci				data[1] |= mask;
58362306a36Sopenharmony_ci				go7007_usb_vendor_request(go, 0x40, 0,
58462306a36Sopenharmony_ci							  (data[1]<<8)
58562306a36Sopenharmony_ci							  + data[1],
58662306a36Sopenharmony_ci							  data, 16, 0);
58762306a36Sopenharmony_ci			}
58862306a36Sopenharmony_ci			kfree(data);
58962306a36Sopenharmony_ci		}
59062306a36Sopenharmony_ci		mutex_unlock(&usb->i2c_lock);
59162306a36Sopenharmony_ci	}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	v4l2_info(sd, "initialized successfully\n");
59462306a36Sopenharmony_ci	return 0;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_cifail:
59762306a36Sopenharmony_ci	i2c_unregister_device(audio);
59862306a36Sopenharmony_ci	v4l2_ctrl_handler_free(&state->hdl);
59962306a36Sopenharmony_ci	kfree(state);
60062306a36Sopenharmony_ci	return err;
60162306a36Sopenharmony_ci}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_cistatic void s2250_remove(struct i2c_client *client)
60462306a36Sopenharmony_ci{
60562306a36Sopenharmony_ci	struct s2250 *state = to_state(i2c_get_clientdata(client));
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	i2c_unregister_device(state->audio);
60862306a36Sopenharmony_ci	v4l2_device_unregister_subdev(&state->sd);
60962306a36Sopenharmony_ci	v4l2_ctrl_handler_free(&state->hdl);
61062306a36Sopenharmony_ci	kfree(state);
61162306a36Sopenharmony_ci}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_cistatic const struct i2c_device_id s2250_id[] = {
61462306a36Sopenharmony_ci	{ "s2250", 0 },
61562306a36Sopenharmony_ci	{ }
61662306a36Sopenharmony_ci};
61762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, s2250_id);
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_cistatic struct i2c_driver s2250_driver = {
62062306a36Sopenharmony_ci	.driver = {
62162306a36Sopenharmony_ci		.name	= "s2250",
62262306a36Sopenharmony_ci	},
62362306a36Sopenharmony_ci	.probe		= s2250_probe,
62462306a36Sopenharmony_ci	.remove		= s2250_remove,
62562306a36Sopenharmony_ci	.id_table	= s2250_id,
62662306a36Sopenharmony_ci};
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_cimodule_i2c_driver(s2250_driver);
629