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