18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2008 Sensoray Company Inc.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/module.h>
78c2ecf20Sopenharmony_ci#include <linux/usb.h>
88c2ecf20Sopenharmony_ci#include <linux/i2c.h>
98c2ecf20Sopenharmony_ci#include <linux/videodev2.h>
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci#include <media/v4l2-device.h>
128c2ecf20Sopenharmony_ci#include <media/v4l2-common.h>
138c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h>
148c2ecf20Sopenharmony_ci#include "go7007-priv.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Sensoray 2250/2251 i2c v4l2 subdev driver");
178c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/*
208c2ecf20Sopenharmony_ci * Note: this board has two i2c devices: a vpx3226f and a tlv320aic23b.
218c2ecf20Sopenharmony_ci * Due to the unusual way these are accessed on this device we do not
228c2ecf20Sopenharmony_ci * reuse the i2c drivers, but instead they are implemented in this
238c2ecf20Sopenharmony_ci * driver. It would be nice to improve on this, though.
248c2ecf20Sopenharmony_ci */
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define TLV320_ADDRESS      0x34
278c2ecf20Sopenharmony_ci#define VPX322_ADDR_ANALOGCONTROL1	0x02
288c2ecf20Sopenharmony_ci#define VPX322_ADDR_BRIGHTNESS0		0x0127
298c2ecf20Sopenharmony_ci#define VPX322_ADDR_BRIGHTNESS1		0x0131
308c2ecf20Sopenharmony_ci#define VPX322_ADDR_CONTRAST0		0x0128
318c2ecf20Sopenharmony_ci#define VPX322_ADDR_CONTRAST1		0x0132
328c2ecf20Sopenharmony_ci#define VPX322_ADDR_HUE			0x00dc
338c2ecf20Sopenharmony_ci#define VPX322_ADDR_SAT			0x0030
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistruct go7007_usb_board {
368c2ecf20Sopenharmony_ci	unsigned int flags;
378c2ecf20Sopenharmony_ci	struct go7007_board_info main_info;
388c2ecf20Sopenharmony_ci};
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistruct go7007_usb {
418c2ecf20Sopenharmony_ci	struct go7007_usb_board *board;
428c2ecf20Sopenharmony_ci	struct mutex i2c_lock;
438c2ecf20Sopenharmony_ci	struct usb_device *usbdev;
448c2ecf20Sopenharmony_ci	struct urb *video_urbs[8];
458c2ecf20Sopenharmony_ci	struct urb *audio_urbs[8];
468c2ecf20Sopenharmony_ci	struct urb *intr_urb;
478c2ecf20Sopenharmony_ci};
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic unsigned char aud_regs[] = {
508c2ecf20Sopenharmony_ci	0x1e, 0x00,
518c2ecf20Sopenharmony_ci	0x00, 0x17,
528c2ecf20Sopenharmony_ci	0x02, 0x17,
538c2ecf20Sopenharmony_ci	0x04, 0xf9,
548c2ecf20Sopenharmony_ci	0x06, 0xf9,
558c2ecf20Sopenharmony_ci	0x08, 0x02,
568c2ecf20Sopenharmony_ci	0x0a, 0x00,
578c2ecf20Sopenharmony_ci	0x0c, 0x00,
588c2ecf20Sopenharmony_ci	0x0a, 0x00,
598c2ecf20Sopenharmony_ci	0x0c, 0x00,
608c2ecf20Sopenharmony_ci	0x0e, 0x02,
618c2ecf20Sopenharmony_ci	0x10, 0x00,
628c2ecf20Sopenharmony_ci	0x12, 0x01,
638c2ecf20Sopenharmony_ci	0x00, 0x00,
648c2ecf20Sopenharmony_ci};
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic unsigned char vid_regs[] = {
688c2ecf20Sopenharmony_ci	0xF2, 0x0f,
698c2ecf20Sopenharmony_ci	0xAA, 0x00,
708c2ecf20Sopenharmony_ci	0xF8, 0xff,
718c2ecf20Sopenharmony_ci	0x00, 0x00,
728c2ecf20Sopenharmony_ci};
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic u16 vid_regs_fp[] = {
758c2ecf20Sopenharmony_ci	0x028, 0x067,
768c2ecf20Sopenharmony_ci	0x120, 0x016,
778c2ecf20Sopenharmony_ci	0x121, 0xcF2,
788c2ecf20Sopenharmony_ci	0x122, 0x0F2,
798c2ecf20Sopenharmony_ci	0x123, 0x00c,
808c2ecf20Sopenharmony_ci	0x124, 0x2d0,
818c2ecf20Sopenharmony_ci	0x125, 0x2e0,
828c2ecf20Sopenharmony_ci	0x126, 0x004,
838c2ecf20Sopenharmony_ci	0x128, 0x1E0,
848c2ecf20Sopenharmony_ci	0x12A, 0x016,
858c2ecf20Sopenharmony_ci	0x12B, 0x0F2,
868c2ecf20Sopenharmony_ci	0x12C, 0x0F2,
878c2ecf20Sopenharmony_ci	0x12D, 0x00c,
888c2ecf20Sopenharmony_ci	0x12E, 0x2d0,
898c2ecf20Sopenharmony_ci	0x12F, 0x2e0,
908c2ecf20Sopenharmony_ci	0x130, 0x004,
918c2ecf20Sopenharmony_ci	0x132, 0x1E0,
928c2ecf20Sopenharmony_ci	0x140, 0x060,
938c2ecf20Sopenharmony_ci	0x153, 0x00C,
948c2ecf20Sopenharmony_ci	0x154, 0x200,
958c2ecf20Sopenharmony_ci	0x150, 0x801,
968c2ecf20Sopenharmony_ci	0x000, 0x000
978c2ecf20Sopenharmony_ci};
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci/* PAL specific values */
1008c2ecf20Sopenharmony_cistatic u16 vid_regs_fp_pal[] = {
1018c2ecf20Sopenharmony_ci	0x120, 0x017,
1028c2ecf20Sopenharmony_ci	0x121, 0xd22,
1038c2ecf20Sopenharmony_ci	0x122, 0x122,
1048c2ecf20Sopenharmony_ci	0x12A, 0x017,
1058c2ecf20Sopenharmony_ci	0x12B, 0x122,
1068c2ecf20Sopenharmony_ci	0x12C, 0x122,
1078c2ecf20Sopenharmony_ci	0x140, 0x060,
1088c2ecf20Sopenharmony_ci	0x000, 0x000,
1098c2ecf20Sopenharmony_ci};
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistruct s2250 {
1128c2ecf20Sopenharmony_ci	struct v4l2_subdev sd;
1138c2ecf20Sopenharmony_ci	struct v4l2_ctrl_handler hdl;
1148c2ecf20Sopenharmony_ci	v4l2_std_id std;
1158c2ecf20Sopenharmony_ci	int input;
1168c2ecf20Sopenharmony_ci	int brightness;
1178c2ecf20Sopenharmony_ci	int contrast;
1188c2ecf20Sopenharmony_ci	int saturation;
1198c2ecf20Sopenharmony_ci	int hue;
1208c2ecf20Sopenharmony_ci	int reg12b_val;
1218c2ecf20Sopenharmony_ci	int audio_input;
1228c2ecf20Sopenharmony_ci	struct i2c_client *audio;
1238c2ecf20Sopenharmony_ci};
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistatic inline struct s2250 *to_state(struct v4l2_subdev *sd)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	return container_of(sd, struct s2250, sd);
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci/* from go7007-usb.c which is Copyright (C) 2005-2006 Micronas USA Inc.*/
1318c2ecf20Sopenharmony_cistatic int go7007_usb_vendor_request(struct go7007 *go, u16 request,
1328c2ecf20Sopenharmony_ci	u16 value, u16 index, void *transfer_buffer, int length, int in)
1338c2ecf20Sopenharmony_ci{
1348c2ecf20Sopenharmony_ci	struct go7007_usb *usb = go->hpi_context;
1358c2ecf20Sopenharmony_ci	int timeout = 5000;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	if (in) {
1388c2ecf20Sopenharmony_ci		return usb_control_msg(usb->usbdev,
1398c2ecf20Sopenharmony_ci				usb_rcvctrlpipe(usb->usbdev, 0), request,
1408c2ecf20Sopenharmony_ci				USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
1418c2ecf20Sopenharmony_ci				value, index, transfer_buffer, length, timeout);
1428c2ecf20Sopenharmony_ci	} else {
1438c2ecf20Sopenharmony_ci		return usb_control_msg(usb->usbdev,
1448c2ecf20Sopenharmony_ci				usb_sndctrlpipe(usb->usbdev, 0), request,
1458c2ecf20Sopenharmony_ci				USB_TYPE_VENDOR | USB_RECIP_DEVICE,
1468c2ecf20Sopenharmony_ci				value, index, transfer_buffer, length, timeout);
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci/* end from go7007-usb.c which is Copyright (C) 2005-2006 Micronas USA Inc.*/
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic int write_reg(struct i2c_client *client, u8 reg, u8 value)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	struct go7007 *go = i2c_get_adapdata(client->adapter);
1548c2ecf20Sopenharmony_ci	struct go7007_usb *usb;
1558c2ecf20Sopenharmony_ci	int rc;
1568c2ecf20Sopenharmony_ci	int dev_addr = client->addr << 1;  /* firmware wants 8-bit address */
1578c2ecf20Sopenharmony_ci	u8 *buf;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if (go == NULL)
1608c2ecf20Sopenharmony_ci		return -ENODEV;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (go->status == STATUS_SHUTDOWN)
1638c2ecf20Sopenharmony_ci		return -EBUSY;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	buf = kzalloc(16, GFP_KERNEL);
1668c2ecf20Sopenharmony_ci	if (buf == NULL)
1678c2ecf20Sopenharmony_ci		return -ENOMEM;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	usb = go->hpi_context;
1708c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&usb->i2c_lock) != 0) {
1718c2ecf20Sopenharmony_ci		dev_info(&client->dev, "i2c lock failed\n");
1728c2ecf20Sopenharmony_ci		kfree(buf);
1738c2ecf20Sopenharmony_ci		return -EINTR;
1748c2ecf20Sopenharmony_ci	}
1758c2ecf20Sopenharmony_ci	rc = go7007_usb_vendor_request(go, 0x55, dev_addr,
1768c2ecf20Sopenharmony_ci				       (reg<<8 | value),
1778c2ecf20Sopenharmony_ci				       buf,
1788c2ecf20Sopenharmony_ci				       16, 1);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	mutex_unlock(&usb->i2c_lock);
1818c2ecf20Sopenharmony_ci	kfree(buf);
1828c2ecf20Sopenharmony_ci	return rc;
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic int write_reg_fp(struct i2c_client *client, u16 addr, u16 val)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	struct go7007 *go = i2c_get_adapdata(client->adapter);
1888c2ecf20Sopenharmony_ci	struct go7007_usb *usb;
1898c2ecf20Sopenharmony_ci	int rc;
1908c2ecf20Sopenharmony_ci	u8 *buf;
1918c2ecf20Sopenharmony_ci	struct s2250 *dec = i2c_get_clientdata(client);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	if (go == NULL)
1948c2ecf20Sopenharmony_ci		return -ENODEV;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	if (go->status == STATUS_SHUTDOWN)
1978c2ecf20Sopenharmony_ci		return -EBUSY;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	buf = kzalloc(16, GFP_KERNEL);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	if (buf == NULL)
2028c2ecf20Sopenharmony_ci		return -ENOMEM;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	memset(buf, 0xcd, 6);
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	usb = go->hpi_context;
2098c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&usb->i2c_lock) != 0) {
2108c2ecf20Sopenharmony_ci		dev_info(&client->dev, "i2c lock failed\n");
2118c2ecf20Sopenharmony_ci		kfree(buf);
2128c2ecf20Sopenharmony_ci		return -EINTR;
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci	rc = go7007_usb_vendor_request(go, 0x57, addr, val, buf, 16, 1);
2158c2ecf20Sopenharmony_ci	mutex_unlock(&usb->i2c_lock);
2168c2ecf20Sopenharmony_ci	if (rc < 0) {
2178c2ecf20Sopenharmony_ci		kfree(buf);
2188c2ecf20Sopenharmony_ci		return rc;
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	if (buf[0] == 0) {
2228c2ecf20Sopenharmony_ci		unsigned int subaddr, val_read;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci		subaddr = (buf[4] << 8) + buf[5];
2258c2ecf20Sopenharmony_ci		val_read = (buf[2] << 8) + buf[3];
2268c2ecf20Sopenharmony_ci		kfree(buf);
2278c2ecf20Sopenharmony_ci		if (val_read != val) {
2288c2ecf20Sopenharmony_ci			dev_info(&client->dev, "invalid fp write %x %x\n",
2298c2ecf20Sopenharmony_ci				 val_read, val);
2308c2ecf20Sopenharmony_ci			return -EFAULT;
2318c2ecf20Sopenharmony_ci		}
2328c2ecf20Sopenharmony_ci		if (subaddr != addr) {
2338c2ecf20Sopenharmony_ci			dev_info(&client->dev, "invalid fp write addr %x %x\n",
2348c2ecf20Sopenharmony_ci				 subaddr, addr);
2358c2ecf20Sopenharmony_ci			return -EFAULT;
2368c2ecf20Sopenharmony_ci		}
2378c2ecf20Sopenharmony_ci	} else {
2388c2ecf20Sopenharmony_ci		kfree(buf);
2398c2ecf20Sopenharmony_ci		return -EFAULT;
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	/* save last 12b value */
2438c2ecf20Sopenharmony_ci	if (addr == 0x12b)
2448c2ecf20Sopenharmony_ci		dec->reg12b_val = val;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	return 0;
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic int read_reg_fp(struct i2c_client *client, u16 addr, u16 *val)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	struct go7007 *go = i2c_get_adapdata(client->adapter);
2528c2ecf20Sopenharmony_ci	struct go7007_usb *usb;
2538c2ecf20Sopenharmony_ci	int rc;
2548c2ecf20Sopenharmony_ci	u8 *buf;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	if (go == NULL)
2578c2ecf20Sopenharmony_ci		return -ENODEV;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	if (go->status == STATUS_SHUTDOWN)
2608c2ecf20Sopenharmony_ci		return -EBUSY;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	buf = kzalloc(16, GFP_KERNEL);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	if (buf == NULL)
2658c2ecf20Sopenharmony_ci		return -ENOMEM;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	memset(buf, 0xcd, 6);
2708c2ecf20Sopenharmony_ci	usb = go->hpi_context;
2718c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&usb->i2c_lock) != 0) {
2728c2ecf20Sopenharmony_ci		dev_info(&client->dev, "i2c lock failed\n");
2738c2ecf20Sopenharmony_ci		kfree(buf);
2748c2ecf20Sopenharmony_ci		return -EINTR;
2758c2ecf20Sopenharmony_ci	}
2768c2ecf20Sopenharmony_ci	rc = go7007_usb_vendor_request(go, 0x58, addr, 0, buf, 16, 1);
2778c2ecf20Sopenharmony_ci	mutex_unlock(&usb->i2c_lock);
2788c2ecf20Sopenharmony_ci	if (rc < 0) {
2798c2ecf20Sopenharmony_ci		kfree(buf);
2808c2ecf20Sopenharmony_ci		return rc;
2818c2ecf20Sopenharmony_ci	}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	*val = (buf[0] << 8) | buf[1];
2848c2ecf20Sopenharmony_ci	kfree(buf);
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	return 0;
2878c2ecf20Sopenharmony_ci}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistatic int write_regs(struct i2c_client *client, u8 *regs)
2918c2ecf20Sopenharmony_ci{
2928c2ecf20Sopenharmony_ci	int i;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) {
2958c2ecf20Sopenharmony_ci		if (write_reg(client, regs[i], regs[i+1]) < 0) {
2968c2ecf20Sopenharmony_ci			dev_info(&client->dev, "failed\n");
2978c2ecf20Sopenharmony_ci			return -1;
2988c2ecf20Sopenharmony_ci		}
2998c2ecf20Sopenharmony_ci	}
3008c2ecf20Sopenharmony_ci	return 0;
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_cistatic int write_regs_fp(struct i2c_client *client, u16 *regs)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	int i;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) {
3088c2ecf20Sopenharmony_ci		if (write_reg_fp(client, regs[i], regs[i+1]) < 0) {
3098c2ecf20Sopenharmony_ci			dev_info(&client->dev, "failed fp\n");
3108c2ecf20Sopenharmony_ci			return -1;
3118c2ecf20Sopenharmony_ci		}
3128c2ecf20Sopenharmony_ci	}
3138c2ecf20Sopenharmony_ci	return 0;
3148c2ecf20Sopenharmony_ci}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------- */
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_cistatic int s2250_s_video_routing(struct v4l2_subdev *sd, u32 input, u32 output,
3208c2ecf20Sopenharmony_ci				 u32 config)
3218c2ecf20Sopenharmony_ci{
3228c2ecf20Sopenharmony_ci	struct s2250 *state = to_state(sd);
3238c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(sd);
3248c2ecf20Sopenharmony_ci	int vidsys;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	vidsys = (state->std == V4L2_STD_NTSC) ? 0x01 : 0x00;
3278c2ecf20Sopenharmony_ci	if (input == 0) {
3288c2ecf20Sopenharmony_ci		/* composite */
3298c2ecf20Sopenharmony_ci		write_reg_fp(client, 0x20, 0x020 | vidsys);
3308c2ecf20Sopenharmony_ci		write_reg_fp(client, 0x21, 0x662);
3318c2ecf20Sopenharmony_ci		write_reg_fp(client, 0x140, 0x060);
3328c2ecf20Sopenharmony_ci	} else if (input == 1) {
3338c2ecf20Sopenharmony_ci		/* S-Video */
3348c2ecf20Sopenharmony_ci		write_reg_fp(client, 0x20, 0x040 | vidsys);
3358c2ecf20Sopenharmony_ci		write_reg_fp(client, 0x21, 0x666);
3368c2ecf20Sopenharmony_ci		write_reg_fp(client, 0x140, 0x060);
3378c2ecf20Sopenharmony_ci	} else {
3388c2ecf20Sopenharmony_ci		return -EINVAL;
3398c2ecf20Sopenharmony_ci	}
3408c2ecf20Sopenharmony_ci	state->input = input;
3418c2ecf20Sopenharmony_ci	return 0;
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_cistatic int s2250_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
3458c2ecf20Sopenharmony_ci{
3468c2ecf20Sopenharmony_ci	struct s2250 *state = to_state(sd);
3478c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(sd);
3488c2ecf20Sopenharmony_ci	u16 vidsource;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	vidsource = (state->input == 1) ? 0x040 : 0x020;
3518c2ecf20Sopenharmony_ci	if (norm & V4L2_STD_625_50) {
3528c2ecf20Sopenharmony_ci		write_regs_fp(client, vid_regs_fp);
3538c2ecf20Sopenharmony_ci		write_regs_fp(client, vid_regs_fp_pal);
3548c2ecf20Sopenharmony_ci		write_reg_fp(client, 0x20, vidsource);
3558c2ecf20Sopenharmony_ci	} else {
3568c2ecf20Sopenharmony_ci		write_regs_fp(client, vid_regs_fp);
3578c2ecf20Sopenharmony_ci		write_reg_fp(client, 0x20, vidsource | 1);
3588c2ecf20Sopenharmony_ci	}
3598c2ecf20Sopenharmony_ci	state->std = norm;
3608c2ecf20Sopenharmony_ci	return 0;
3618c2ecf20Sopenharmony_ci}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_cistatic int s2250_s_ctrl(struct v4l2_ctrl *ctrl)
3648c2ecf20Sopenharmony_ci{
3658c2ecf20Sopenharmony_ci	struct s2250 *state = container_of(ctrl->handler, struct s2250, hdl);
3668c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
3678c2ecf20Sopenharmony_ci	u16 oldvalue;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	switch (ctrl->id) {
3708c2ecf20Sopenharmony_ci	case V4L2_CID_BRIGHTNESS:
3718c2ecf20Sopenharmony_ci		read_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, &oldvalue);
3728c2ecf20Sopenharmony_ci		write_reg_fp(client, VPX322_ADDR_BRIGHTNESS0,
3738c2ecf20Sopenharmony_ci			     ctrl->val | (oldvalue & ~0xff));
3748c2ecf20Sopenharmony_ci		read_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, &oldvalue);
3758c2ecf20Sopenharmony_ci		write_reg_fp(client, VPX322_ADDR_BRIGHTNESS1,
3768c2ecf20Sopenharmony_ci			     ctrl->val | (oldvalue & ~0xff));
3778c2ecf20Sopenharmony_ci		write_reg_fp(client, 0x140, 0x60);
3788c2ecf20Sopenharmony_ci		break;
3798c2ecf20Sopenharmony_ci	case V4L2_CID_CONTRAST:
3808c2ecf20Sopenharmony_ci		read_reg_fp(client, VPX322_ADDR_CONTRAST0, &oldvalue);
3818c2ecf20Sopenharmony_ci		write_reg_fp(client, VPX322_ADDR_CONTRAST0,
3828c2ecf20Sopenharmony_ci			     ctrl->val | (oldvalue & ~0x3f));
3838c2ecf20Sopenharmony_ci		read_reg_fp(client, VPX322_ADDR_CONTRAST1, &oldvalue);
3848c2ecf20Sopenharmony_ci		write_reg_fp(client, VPX322_ADDR_CONTRAST1,
3858c2ecf20Sopenharmony_ci			     ctrl->val | (oldvalue & ~0x3f));
3868c2ecf20Sopenharmony_ci		write_reg_fp(client, 0x140, 0x60);
3878c2ecf20Sopenharmony_ci		break;
3888c2ecf20Sopenharmony_ci	case V4L2_CID_SATURATION:
3898c2ecf20Sopenharmony_ci		write_reg_fp(client, VPX322_ADDR_SAT, ctrl->val);
3908c2ecf20Sopenharmony_ci		break;
3918c2ecf20Sopenharmony_ci	case V4L2_CID_HUE:
3928c2ecf20Sopenharmony_ci		write_reg_fp(client, VPX322_ADDR_HUE, ctrl->val);
3938c2ecf20Sopenharmony_ci		break;
3948c2ecf20Sopenharmony_ci	default:
3958c2ecf20Sopenharmony_ci		return -EINVAL;
3968c2ecf20Sopenharmony_ci	}
3978c2ecf20Sopenharmony_ci	return 0;
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_cistatic int s2250_set_fmt(struct v4l2_subdev *sd,
4018c2ecf20Sopenharmony_ci		struct v4l2_subdev_pad_config *cfg,
4028c2ecf20Sopenharmony_ci		struct v4l2_subdev_format *format)
4038c2ecf20Sopenharmony_ci{
4048c2ecf20Sopenharmony_ci	struct v4l2_mbus_framefmt *fmt = &format->format;
4058c2ecf20Sopenharmony_ci	struct s2250 *state = to_state(sd);
4068c2ecf20Sopenharmony_ci	struct i2c_client *client = v4l2_get_subdevdata(sd);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	if (format->pad)
4098c2ecf20Sopenharmony_ci		return -EINVAL;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	if (format->which == V4L2_SUBDEV_FORMAT_TRY)
4128c2ecf20Sopenharmony_ci		return 0;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	if (fmt->height < 640) {
4158c2ecf20Sopenharmony_ci		write_reg_fp(client, 0x12b, state->reg12b_val | 0x400);
4168c2ecf20Sopenharmony_ci		write_reg_fp(client, 0x140, 0x060);
4178c2ecf20Sopenharmony_ci	} else {
4188c2ecf20Sopenharmony_ci		write_reg_fp(client, 0x12b, state->reg12b_val & ~0x400);
4198c2ecf20Sopenharmony_ci		write_reg_fp(client, 0x140, 0x060);
4208c2ecf20Sopenharmony_ci	}
4218c2ecf20Sopenharmony_ci	return 0;
4228c2ecf20Sopenharmony_ci}
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_cistatic int s2250_s_audio_routing(struct v4l2_subdev *sd, u32 input, u32 output,
4258c2ecf20Sopenharmony_ci				 u32 config)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci	struct s2250 *state = to_state(sd);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	switch (input) {
4308c2ecf20Sopenharmony_ci	case 0:
4318c2ecf20Sopenharmony_ci		write_reg(state->audio, 0x08, 0x02); /* Line In */
4328c2ecf20Sopenharmony_ci		break;
4338c2ecf20Sopenharmony_ci	case 1:
4348c2ecf20Sopenharmony_ci		write_reg(state->audio, 0x08, 0x04); /* Mic */
4358c2ecf20Sopenharmony_ci		break;
4368c2ecf20Sopenharmony_ci	case 2:
4378c2ecf20Sopenharmony_ci		write_reg(state->audio, 0x08, 0x05); /* Mic Boost */
4388c2ecf20Sopenharmony_ci		break;
4398c2ecf20Sopenharmony_ci	default:
4408c2ecf20Sopenharmony_ci		return -EINVAL;
4418c2ecf20Sopenharmony_ci	}
4428c2ecf20Sopenharmony_ci	state->audio_input = input;
4438c2ecf20Sopenharmony_ci	return 0;
4448c2ecf20Sopenharmony_ci}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_cistatic int s2250_log_status(struct v4l2_subdev *sd)
4488c2ecf20Sopenharmony_ci{
4498c2ecf20Sopenharmony_ci	struct s2250 *state = to_state(sd);
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	v4l2_info(sd, "Standard: %s\n", state->std == V4L2_STD_NTSC ? "NTSC" :
4528c2ecf20Sopenharmony_ci					state->std == V4L2_STD_PAL ? "PAL" :
4538c2ecf20Sopenharmony_ci					state->std == V4L2_STD_SECAM ? "SECAM" :
4548c2ecf20Sopenharmony_ci					"unknown");
4558c2ecf20Sopenharmony_ci	v4l2_info(sd, "Input: %s\n", state->input == 0 ? "Composite" :
4568c2ecf20Sopenharmony_ci					state->input == 1 ? "S-video" :
4578c2ecf20Sopenharmony_ci					"error");
4588c2ecf20Sopenharmony_ci	v4l2_info(sd, "Audio input: %s\n", state->audio_input == 0 ? "Line In" :
4598c2ecf20Sopenharmony_ci					state->audio_input == 1 ? "Mic" :
4608c2ecf20Sopenharmony_ci					state->audio_input == 2 ? "Mic Boost" :
4618c2ecf20Sopenharmony_ci					"error");
4628c2ecf20Sopenharmony_ci	return v4l2_ctrl_subdev_log_status(sd);
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------------*/
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops s2250_ctrl_ops = {
4688c2ecf20Sopenharmony_ci	.s_ctrl = s2250_s_ctrl,
4698c2ecf20Sopenharmony_ci};
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops s2250_core_ops = {
4728c2ecf20Sopenharmony_ci	.log_status = s2250_log_status,
4738c2ecf20Sopenharmony_ci};
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_audio_ops s2250_audio_ops = {
4768c2ecf20Sopenharmony_ci	.s_routing = s2250_s_audio_routing,
4778c2ecf20Sopenharmony_ci};
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops s2250_video_ops = {
4808c2ecf20Sopenharmony_ci	.s_std = s2250_s_std,
4818c2ecf20Sopenharmony_ci	.s_routing = s2250_s_video_routing,
4828c2ecf20Sopenharmony_ci};
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops s2250_pad_ops = {
4858c2ecf20Sopenharmony_ci	.set_fmt = s2250_set_fmt,
4868c2ecf20Sopenharmony_ci};
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops s2250_ops = {
4898c2ecf20Sopenharmony_ci	.core = &s2250_core_ops,
4908c2ecf20Sopenharmony_ci	.audio = &s2250_audio_ops,
4918c2ecf20Sopenharmony_ci	.video = &s2250_video_ops,
4928c2ecf20Sopenharmony_ci	.pad = &s2250_pad_ops,
4938c2ecf20Sopenharmony_ci};
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci/* --------------------------------------------------------------------------*/
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_cistatic int s2250_probe(struct i2c_client *client,
4988c2ecf20Sopenharmony_ci		       const struct i2c_device_id *id)
4998c2ecf20Sopenharmony_ci{
5008c2ecf20Sopenharmony_ci	struct i2c_client *audio;
5018c2ecf20Sopenharmony_ci	struct i2c_adapter *adapter = client->adapter;
5028c2ecf20Sopenharmony_ci	struct s2250 *state;
5038c2ecf20Sopenharmony_ci	struct v4l2_subdev *sd;
5048c2ecf20Sopenharmony_ci	u8 *data;
5058c2ecf20Sopenharmony_ci	struct go7007 *go = i2c_get_adapdata(adapter);
5068c2ecf20Sopenharmony_ci	struct go7007_usb *usb = go->hpi_context;
5078c2ecf20Sopenharmony_ci	int err = -EIO;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	audio = i2c_new_dummy_device(adapter, TLV320_ADDRESS >> 1);
5108c2ecf20Sopenharmony_ci	if (IS_ERR(audio))
5118c2ecf20Sopenharmony_ci		return PTR_ERR(audio);
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	state = kzalloc(sizeof(struct s2250), GFP_KERNEL);
5148c2ecf20Sopenharmony_ci	if (state == NULL) {
5158c2ecf20Sopenharmony_ci		i2c_unregister_device(audio);
5168c2ecf20Sopenharmony_ci		return -ENOMEM;
5178c2ecf20Sopenharmony_ci	}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	sd = &state->sd;
5208c2ecf20Sopenharmony_ci	v4l2_i2c_subdev_init(sd, client, &s2250_ops);
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci	v4l2_info(sd, "initializing %s at address 0x%x on %s\n",
5238c2ecf20Sopenharmony_ci	       "Sensoray 2250/2251", client->addr, client->adapter->name);
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_init(&state->hdl, 4);
5268c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(&state->hdl, &s2250_ctrl_ops,
5278c2ecf20Sopenharmony_ci		V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
5288c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(&state->hdl, &s2250_ctrl_ops,
5298c2ecf20Sopenharmony_ci		V4L2_CID_CONTRAST, 0, 0x3f, 1, 0x32);
5308c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(&state->hdl, &s2250_ctrl_ops,
5318c2ecf20Sopenharmony_ci		V4L2_CID_SATURATION, 0, 4094, 1, 2070);
5328c2ecf20Sopenharmony_ci	v4l2_ctrl_new_std(&state->hdl, &s2250_ctrl_ops,
5338c2ecf20Sopenharmony_ci		V4L2_CID_HUE, -512, 511, 1, 0);
5348c2ecf20Sopenharmony_ci	sd->ctrl_handler = &state->hdl;
5358c2ecf20Sopenharmony_ci	if (state->hdl.error) {
5368c2ecf20Sopenharmony_ci		err = state->hdl.error;
5378c2ecf20Sopenharmony_ci		goto fail;
5388c2ecf20Sopenharmony_ci	}
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	state->std = V4L2_STD_NTSC;
5418c2ecf20Sopenharmony_ci	state->brightness = 50;
5428c2ecf20Sopenharmony_ci	state->contrast = 50;
5438c2ecf20Sopenharmony_ci	state->saturation = 50;
5448c2ecf20Sopenharmony_ci	state->hue = 0;
5458c2ecf20Sopenharmony_ci	state->audio = audio;
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	/* initialize the audio */
5488c2ecf20Sopenharmony_ci	if (write_regs(audio, aud_regs) < 0) {
5498c2ecf20Sopenharmony_ci		dev_err(&client->dev, "error initializing audio\n");
5508c2ecf20Sopenharmony_ci		goto fail;
5518c2ecf20Sopenharmony_ci	}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	if (write_regs(client, vid_regs) < 0) {
5548c2ecf20Sopenharmony_ci		dev_err(&client->dev, "error initializing decoder\n");
5558c2ecf20Sopenharmony_ci		goto fail;
5568c2ecf20Sopenharmony_ci	}
5578c2ecf20Sopenharmony_ci	if (write_regs_fp(client, vid_regs_fp) < 0) {
5588c2ecf20Sopenharmony_ci		dev_err(&client->dev, "error initializing decoder\n");
5598c2ecf20Sopenharmony_ci		goto fail;
5608c2ecf20Sopenharmony_ci	}
5618c2ecf20Sopenharmony_ci	/* set default channel */
5628c2ecf20Sopenharmony_ci	/* composite */
5638c2ecf20Sopenharmony_ci	write_reg_fp(client, 0x20, 0x020 | 1);
5648c2ecf20Sopenharmony_ci	write_reg_fp(client, 0x21, 0x662);
5658c2ecf20Sopenharmony_ci	write_reg_fp(client, 0x140, 0x060);
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	/* set default audio input */
5688c2ecf20Sopenharmony_ci	state->audio_input = 0;
5698c2ecf20Sopenharmony_ci	write_reg(client, 0x08, 0x02); /* Line In */
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	if (mutex_lock_interruptible(&usb->i2c_lock) == 0) {
5728c2ecf20Sopenharmony_ci		data = kzalloc(16, GFP_KERNEL);
5738c2ecf20Sopenharmony_ci		if (data != NULL) {
5748c2ecf20Sopenharmony_ci			int rc = go7007_usb_vendor_request(go, 0x41, 0, 0,
5758c2ecf20Sopenharmony_ci						       data, 16, 1);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci			if (rc > 0) {
5788c2ecf20Sopenharmony_ci				u8 mask;
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci				data[0] = 0;
5818c2ecf20Sopenharmony_ci				mask = 1<<5;
5828c2ecf20Sopenharmony_ci				data[0] &= ~mask;
5838c2ecf20Sopenharmony_ci				data[1] |= mask;
5848c2ecf20Sopenharmony_ci				go7007_usb_vendor_request(go, 0x40, 0,
5858c2ecf20Sopenharmony_ci							  (data[1]<<8)
5868c2ecf20Sopenharmony_ci							  + data[1],
5878c2ecf20Sopenharmony_ci							  data, 16, 0);
5888c2ecf20Sopenharmony_ci			}
5898c2ecf20Sopenharmony_ci			kfree(data);
5908c2ecf20Sopenharmony_ci		}
5918c2ecf20Sopenharmony_ci		mutex_unlock(&usb->i2c_lock);
5928c2ecf20Sopenharmony_ci	}
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	v4l2_info(sd, "initialized successfully\n");
5958c2ecf20Sopenharmony_ci	return 0;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_cifail:
5988c2ecf20Sopenharmony_ci	i2c_unregister_device(audio);
5998c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_free(&state->hdl);
6008c2ecf20Sopenharmony_ci	kfree(state);
6018c2ecf20Sopenharmony_ci	return err;
6028c2ecf20Sopenharmony_ci}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_cistatic int s2250_remove(struct i2c_client *client)
6058c2ecf20Sopenharmony_ci{
6068c2ecf20Sopenharmony_ci	struct s2250 *state = to_state(i2c_get_clientdata(client));
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	i2c_unregister_device(state->audio);
6098c2ecf20Sopenharmony_ci	v4l2_device_unregister_subdev(&state->sd);
6108c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_free(&state->hdl);
6118c2ecf20Sopenharmony_ci	kfree(state);
6128c2ecf20Sopenharmony_ci	return 0;
6138c2ecf20Sopenharmony_ci}
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_cistatic const struct i2c_device_id s2250_id[] = {
6168c2ecf20Sopenharmony_ci	{ "s2250", 0 },
6178c2ecf20Sopenharmony_ci	{ }
6188c2ecf20Sopenharmony_ci};
6198c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, s2250_id);
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_cistatic struct i2c_driver s2250_driver = {
6228c2ecf20Sopenharmony_ci	.driver = {
6238c2ecf20Sopenharmony_ci		.name	= "s2250",
6248c2ecf20Sopenharmony_ci	},
6258c2ecf20Sopenharmony_ci	.probe		= s2250_probe,
6268c2ecf20Sopenharmony_ci	.remove		= s2250_remove,
6278c2ecf20Sopenharmony_ci	.id_table	= s2250_id,
6288c2ecf20Sopenharmony_ci};
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_cimodule_i2c_driver(s2250_driver);
631