1cb7a01acSMauro Carvalho Chehab /* 2cb7a01acSMauro Carvalho Chehab * vpx3220a, vpx3216b & vpx3214c video decoder driver version 0.0.1 3cb7a01acSMauro Carvalho Chehab * 4cb7a01acSMauro Carvalho Chehab * Copyright (C) 2001 Laurent Pinchart <lpinchart@freegates.be> 5cb7a01acSMauro Carvalho Chehab * 6cb7a01acSMauro Carvalho Chehab * This program is free software; you can redistribute it and/or modify 7cb7a01acSMauro Carvalho Chehab * it under the terms of the GNU General Public License as published by 8cb7a01acSMauro Carvalho Chehab * the Free Software Foundation; either version 2 of the License, or 9cb7a01acSMauro Carvalho Chehab * (at your option) any later version. 10cb7a01acSMauro Carvalho Chehab * 11cb7a01acSMauro Carvalho Chehab * This program is distributed in the hope that it will be useful, 12cb7a01acSMauro Carvalho Chehab * but WITHOUT ANY WARRANTY; without even the implied warranty of 13cb7a01acSMauro Carvalho Chehab * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14cb7a01acSMauro Carvalho Chehab * GNU General Public License for more details. 15cb7a01acSMauro Carvalho Chehab * 16cb7a01acSMauro Carvalho Chehab * You should have received a copy of the GNU General Public License 17cb7a01acSMauro Carvalho Chehab * along with this program; if not, write to the Free Software 18cb7a01acSMauro Carvalho Chehab * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 19cb7a01acSMauro Carvalho Chehab */ 20cb7a01acSMauro Carvalho Chehab 21cb7a01acSMauro Carvalho Chehab #include <linux/module.h> 22cb7a01acSMauro Carvalho Chehab #include <linux/init.h> 23cb7a01acSMauro Carvalho Chehab #include <linux/delay.h> 24cb7a01acSMauro Carvalho Chehab #include <linux/types.h> 25cb7a01acSMauro Carvalho Chehab #include <linux/slab.h> 26cb7a01acSMauro Carvalho Chehab #include <asm/uaccess.h> 27cb7a01acSMauro Carvalho Chehab #include <linux/i2c.h> 28cb7a01acSMauro Carvalho Chehab #include <linux/videodev2.h> 29cb7a01acSMauro Carvalho Chehab #include <media/v4l2-device.h> 30cb7a01acSMauro Carvalho Chehab #include <media/v4l2-chip-ident.h> 31cb7a01acSMauro Carvalho Chehab #include <media/v4l2-ctrls.h> 32cb7a01acSMauro Carvalho Chehab 33cb7a01acSMauro Carvalho Chehab MODULE_DESCRIPTION("vpx3220a/vpx3216b/vpx3214c video decoder driver"); 34cb7a01acSMauro Carvalho Chehab MODULE_AUTHOR("Laurent Pinchart"); 35cb7a01acSMauro Carvalho Chehab MODULE_LICENSE("GPL"); 36cb7a01acSMauro Carvalho Chehab 37cb7a01acSMauro Carvalho Chehab static int debug; 38cb7a01acSMauro Carvalho Chehab module_param(debug, int, 0); 39cb7a01acSMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Debug level (0-1)"); 40cb7a01acSMauro Carvalho Chehab 41cb7a01acSMauro Carvalho Chehab 42cb7a01acSMauro Carvalho Chehab #define VPX_TIMEOUT_COUNT 10 43cb7a01acSMauro Carvalho Chehab 44cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */ 45cb7a01acSMauro Carvalho Chehab 46cb7a01acSMauro Carvalho Chehab struct vpx3220 { 47cb7a01acSMauro Carvalho Chehab struct v4l2_subdev sd; 48cb7a01acSMauro Carvalho Chehab struct v4l2_ctrl_handler hdl; 49cb7a01acSMauro Carvalho Chehab unsigned char reg[255]; 50cb7a01acSMauro Carvalho Chehab 51cb7a01acSMauro Carvalho Chehab v4l2_std_id norm; 52cb7a01acSMauro Carvalho Chehab int ident; 53cb7a01acSMauro Carvalho Chehab int input; 54cb7a01acSMauro Carvalho Chehab int enable; 55cb7a01acSMauro Carvalho Chehab }; 56cb7a01acSMauro Carvalho Chehab 57cb7a01acSMauro Carvalho Chehab static inline struct vpx3220 *to_vpx3220(struct v4l2_subdev *sd) 58cb7a01acSMauro Carvalho Chehab { 59cb7a01acSMauro Carvalho Chehab return container_of(sd, struct vpx3220, sd); 60cb7a01acSMauro Carvalho Chehab } 61cb7a01acSMauro Carvalho Chehab 62cb7a01acSMauro Carvalho Chehab static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) 63cb7a01acSMauro Carvalho Chehab { 64cb7a01acSMauro Carvalho Chehab return &container_of(ctrl->handler, struct vpx3220, hdl)->sd; 65cb7a01acSMauro Carvalho Chehab } 66cb7a01acSMauro Carvalho Chehab 67cb7a01acSMauro Carvalho Chehab static char *inputs[] = { "internal", "composite", "svideo" }; 68cb7a01acSMauro Carvalho Chehab 69cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */ 70cb7a01acSMauro Carvalho Chehab 71cb7a01acSMauro Carvalho Chehab static inline int vpx3220_write(struct v4l2_subdev *sd, u8 reg, u8 value) 72cb7a01acSMauro Carvalho Chehab { 73cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(sd); 74cb7a01acSMauro Carvalho Chehab struct vpx3220 *decoder = i2c_get_clientdata(client); 75cb7a01acSMauro Carvalho Chehab 76cb7a01acSMauro Carvalho Chehab decoder->reg[reg] = value; 77cb7a01acSMauro Carvalho Chehab return i2c_smbus_write_byte_data(client, reg, value); 78cb7a01acSMauro Carvalho Chehab } 79cb7a01acSMauro Carvalho Chehab 80cb7a01acSMauro Carvalho Chehab static inline int vpx3220_read(struct v4l2_subdev *sd, u8 reg) 81cb7a01acSMauro Carvalho Chehab { 82cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(sd); 83cb7a01acSMauro Carvalho Chehab 84cb7a01acSMauro Carvalho Chehab return i2c_smbus_read_byte_data(client, reg); 85cb7a01acSMauro Carvalho Chehab } 86cb7a01acSMauro Carvalho Chehab 87cb7a01acSMauro Carvalho Chehab static int vpx3220_fp_status(struct v4l2_subdev *sd) 88cb7a01acSMauro Carvalho Chehab { 89cb7a01acSMauro Carvalho Chehab unsigned char status; 90cb7a01acSMauro Carvalho Chehab unsigned int i; 91cb7a01acSMauro Carvalho Chehab 92cb7a01acSMauro Carvalho Chehab for (i = 0; i < VPX_TIMEOUT_COUNT; i++) { 93cb7a01acSMauro Carvalho Chehab status = vpx3220_read(sd, 0x29); 94cb7a01acSMauro Carvalho Chehab 95cb7a01acSMauro Carvalho Chehab if (!(status & 4)) 96cb7a01acSMauro Carvalho Chehab return 0; 97cb7a01acSMauro Carvalho Chehab 98cb7a01acSMauro Carvalho Chehab udelay(10); 99cb7a01acSMauro Carvalho Chehab 100cb7a01acSMauro Carvalho Chehab if (need_resched()) 101cb7a01acSMauro Carvalho Chehab cond_resched(); 102cb7a01acSMauro Carvalho Chehab } 103cb7a01acSMauro Carvalho Chehab 104cb7a01acSMauro Carvalho Chehab return -1; 105cb7a01acSMauro Carvalho Chehab } 106cb7a01acSMauro Carvalho Chehab 107cb7a01acSMauro Carvalho Chehab static int vpx3220_fp_write(struct v4l2_subdev *sd, u8 fpaddr, u16 data) 108cb7a01acSMauro Carvalho Chehab { 109cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(sd); 110cb7a01acSMauro Carvalho Chehab 111cb7a01acSMauro Carvalho Chehab /* Write the 16-bit address to the FPWR register */ 112cb7a01acSMauro Carvalho Chehab if (i2c_smbus_write_word_data(client, 0x27, swab16(fpaddr)) == -1) { 113cb7a01acSMauro Carvalho Chehab v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); 114cb7a01acSMauro Carvalho Chehab return -1; 115cb7a01acSMauro Carvalho Chehab } 116cb7a01acSMauro Carvalho Chehab 117cb7a01acSMauro Carvalho Chehab if (vpx3220_fp_status(sd) < 0) 118cb7a01acSMauro Carvalho Chehab return -1; 119cb7a01acSMauro Carvalho Chehab 120cb7a01acSMauro Carvalho Chehab /* Write the 16-bit data to the FPDAT register */ 121cb7a01acSMauro Carvalho Chehab if (i2c_smbus_write_word_data(client, 0x28, swab16(data)) == -1) { 122cb7a01acSMauro Carvalho Chehab v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); 123cb7a01acSMauro Carvalho Chehab return -1; 124cb7a01acSMauro Carvalho Chehab } 125cb7a01acSMauro Carvalho Chehab 126cb7a01acSMauro Carvalho Chehab return 0; 127cb7a01acSMauro Carvalho Chehab } 128cb7a01acSMauro Carvalho Chehab 129cb7a01acSMauro Carvalho Chehab static u16 vpx3220_fp_read(struct v4l2_subdev *sd, u16 fpaddr) 130cb7a01acSMauro Carvalho Chehab { 131cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(sd); 132cb7a01acSMauro Carvalho Chehab s16 data; 133cb7a01acSMauro Carvalho Chehab 134cb7a01acSMauro Carvalho Chehab /* Write the 16-bit address to the FPRD register */ 135cb7a01acSMauro Carvalho Chehab if (i2c_smbus_write_word_data(client, 0x26, swab16(fpaddr)) == -1) { 136cb7a01acSMauro Carvalho Chehab v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); 137cb7a01acSMauro Carvalho Chehab return -1; 138cb7a01acSMauro Carvalho Chehab } 139cb7a01acSMauro Carvalho Chehab 140cb7a01acSMauro Carvalho Chehab if (vpx3220_fp_status(sd) < 0) 141cb7a01acSMauro Carvalho Chehab return -1; 142cb7a01acSMauro Carvalho Chehab 143cb7a01acSMauro Carvalho Chehab /* Read the 16-bit data from the FPDAT register */ 144cb7a01acSMauro Carvalho Chehab data = i2c_smbus_read_word_data(client, 0x28); 145cb7a01acSMauro Carvalho Chehab if (data == -1) { 146cb7a01acSMauro Carvalho Chehab v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); 147cb7a01acSMauro Carvalho Chehab return -1; 148cb7a01acSMauro Carvalho Chehab } 149cb7a01acSMauro Carvalho Chehab 150cb7a01acSMauro Carvalho Chehab return swab16(data); 151cb7a01acSMauro Carvalho Chehab } 152cb7a01acSMauro Carvalho Chehab 153cb7a01acSMauro Carvalho Chehab static int vpx3220_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len) 154cb7a01acSMauro Carvalho Chehab { 155cb7a01acSMauro Carvalho Chehab u8 reg; 156cb7a01acSMauro Carvalho Chehab int ret = -1; 157cb7a01acSMauro Carvalho Chehab 158cb7a01acSMauro Carvalho Chehab while (len >= 2) { 159cb7a01acSMauro Carvalho Chehab reg = *data++; 160cb7a01acSMauro Carvalho Chehab ret = vpx3220_write(sd, reg, *data++); 161cb7a01acSMauro Carvalho Chehab if (ret < 0) 162cb7a01acSMauro Carvalho Chehab break; 163cb7a01acSMauro Carvalho Chehab len -= 2; 164cb7a01acSMauro Carvalho Chehab } 165cb7a01acSMauro Carvalho Chehab 166cb7a01acSMauro Carvalho Chehab return ret; 167cb7a01acSMauro Carvalho Chehab } 168cb7a01acSMauro Carvalho Chehab 169cb7a01acSMauro Carvalho Chehab static int vpx3220_write_fp_block(struct v4l2_subdev *sd, 170cb7a01acSMauro Carvalho Chehab const u16 *data, unsigned int len) 171cb7a01acSMauro Carvalho Chehab { 172cb7a01acSMauro Carvalho Chehab u8 reg; 173cb7a01acSMauro Carvalho Chehab int ret = 0; 174cb7a01acSMauro Carvalho Chehab 175cb7a01acSMauro Carvalho Chehab while (len > 1) { 176cb7a01acSMauro Carvalho Chehab reg = *data++; 177cb7a01acSMauro Carvalho Chehab ret |= vpx3220_fp_write(sd, reg, *data++); 178cb7a01acSMauro Carvalho Chehab len -= 2; 179cb7a01acSMauro Carvalho Chehab } 180cb7a01acSMauro Carvalho Chehab 181cb7a01acSMauro Carvalho Chehab return ret; 182cb7a01acSMauro Carvalho Chehab } 183cb7a01acSMauro Carvalho Chehab 184cb7a01acSMauro Carvalho Chehab /* ---------------------------------------------------------------------- */ 185cb7a01acSMauro Carvalho Chehab 186cb7a01acSMauro Carvalho Chehab static const unsigned short init_ntsc[] = { 187cb7a01acSMauro Carvalho Chehab 0x1c, 0x00, /* NTSC tint angle */ 188cb7a01acSMauro Carvalho Chehab 0x88, 17, /* Window 1 vertical */ 189cb7a01acSMauro Carvalho Chehab 0x89, 240, /* Vertical lines in */ 190cb7a01acSMauro Carvalho Chehab 0x8a, 240, /* Vertical lines out */ 191cb7a01acSMauro Carvalho Chehab 0x8b, 000, /* Horizontal begin */ 192cb7a01acSMauro Carvalho Chehab 0x8c, 640, /* Horizontal length */ 193cb7a01acSMauro Carvalho Chehab 0x8d, 640, /* Number of pixels */ 194cb7a01acSMauro Carvalho Chehab 0x8f, 0xc00, /* Disable window 2 */ 195cb7a01acSMauro Carvalho Chehab 0xf0, 0x73, /* 13.5 MHz transport, Forced 196cb7a01acSMauro Carvalho Chehab * mode, latch windows */ 197cb7a01acSMauro Carvalho Chehab 0xf2, 0x13, /* NTSC M, composite input */ 198cb7a01acSMauro Carvalho Chehab 0xe7, 0x1e1, /* Enable vertical standard 199cb7a01acSMauro Carvalho Chehab * locking @ 240 lines */ 200cb7a01acSMauro Carvalho Chehab }; 201cb7a01acSMauro Carvalho Chehab 202cb7a01acSMauro Carvalho Chehab static const unsigned short init_pal[] = { 203cb7a01acSMauro Carvalho Chehab 0x88, 23, /* Window 1 vertical begin */ 204cb7a01acSMauro Carvalho Chehab 0x89, 288, /* Vertical lines in (16 lines 205cb7a01acSMauro Carvalho Chehab * skipped by the VFE) */ 206cb7a01acSMauro Carvalho Chehab 0x8a, 288, /* Vertical lines out (16 lines 207cb7a01acSMauro Carvalho Chehab * skipped by the VFE) */ 208cb7a01acSMauro Carvalho Chehab 0x8b, 16, /* Horizontal begin */ 209cb7a01acSMauro Carvalho Chehab 0x8c, 768, /* Horizontal length */ 210cb7a01acSMauro Carvalho Chehab 0x8d, 784, /* Number of pixels 211cb7a01acSMauro Carvalho Chehab * Must be >= Horizontal begin + Horizontal length */ 212cb7a01acSMauro Carvalho Chehab 0x8f, 0xc00, /* Disable window 2 */ 213cb7a01acSMauro Carvalho Chehab 0xf0, 0x77, /* 13.5 MHz transport, Forced 214cb7a01acSMauro Carvalho Chehab * mode, latch windows */ 215cb7a01acSMauro Carvalho Chehab 0xf2, 0x3d1, /* PAL B,G,H,I, composite input */ 216cb7a01acSMauro Carvalho Chehab 0xe7, 0x241, /* PAL/SECAM set to 288 lines */ 217cb7a01acSMauro Carvalho Chehab }; 218cb7a01acSMauro Carvalho Chehab 219cb7a01acSMauro Carvalho Chehab static const unsigned short init_secam[] = { 220cb7a01acSMauro Carvalho Chehab 0x88, 23, /* Window 1 vertical begin */ 221cb7a01acSMauro Carvalho Chehab 0x89, 288, /* Vertical lines in (16 lines 222cb7a01acSMauro Carvalho Chehab * skipped by the VFE) */ 223cb7a01acSMauro Carvalho Chehab 0x8a, 288, /* Vertical lines out (16 lines 224cb7a01acSMauro Carvalho Chehab * skipped by the VFE) */ 225cb7a01acSMauro Carvalho Chehab 0x8b, 16, /* Horizontal begin */ 226cb7a01acSMauro Carvalho Chehab 0x8c, 768, /* Horizontal length */ 227cb7a01acSMauro Carvalho Chehab 0x8d, 784, /* Number of pixels 228cb7a01acSMauro Carvalho Chehab * Must be >= Horizontal begin + Horizontal length */ 229cb7a01acSMauro Carvalho Chehab 0x8f, 0xc00, /* Disable window 2 */ 230cb7a01acSMauro Carvalho Chehab 0xf0, 0x77, /* 13.5 MHz transport, Forced 231cb7a01acSMauro Carvalho Chehab * mode, latch windows */ 232cb7a01acSMauro Carvalho Chehab 0xf2, 0x3d5, /* SECAM, composite input */ 233cb7a01acSMauro Carvalho Chehab 0xe7, 0x241, /* PAL/SECAM set to 288 lines */ 234cb7a01acSMauro Carvalho Chehab }; 235cb7a01acSMauro Carvalho Chehab 236cb7a01acSMauro Carvalho Chehab static const unsigned char init_common[] = { 237cb7a01acSMauro Carvalho Chehab 0xf2, 0x00, /* Disable all outputs */ 238cb7a01acSMauro Carvalho Chehab 0x33, 0x0d, /* Luma : VIN2, Chroma : CIN 239cb7a01acSMauro Carvalho Chehab * (clamp off) */ 240cb7a01acSMauro Carvalho Chehab 0xd8, 0xa8, /* HREF/VREF active high, VREF 241cb7a01acSMauro Carvalho Chehab * pulse = 2, Odd/Even flag */ 242cb7a01acSMauro Carvalho Chehab 0x20, 0x03, /* IF compensation 0dB/oct */ 243cb7a01acSMauro Carvalho Chehab 0xe0, 0xff, /* Open up all comparators */ 244cb7a01acSMauro Carvalho Chehab 0xe1, 0x00, 245cb7a01acSMauro Carvalho Chehab 0xe2, 0x7f, 246cb7a01acSMauro Carvalho Chehab 0xe3, 0x80, 247cb7a01acSMauro Carvalho Chehab 0xe4, 0x7f, 248cb7a01acSMauro Carvalho Chehab 0xe5, 0x80, 249cb7a01acSMauro Carvalho Chehab 0xe6, 0x00, /* Brightness set to 0 */ 250cb7a01acSMauro Carvalho Chehab 0xe7, 0xe0, /* Contrast to 1.0, noise shaping 251cb7a01acSMauro Carvalho Chehab * 10 to 8 2-bit error diffusion */ 252cb7a01acSMauro Carvalho Chehab 0xe8, 0xf8, /* YUV422, CbCr binary offset, 253cb7a01acSMauro Carvalho Chehab * ... (p.32) */ 254cb7a01acSMauro Carvalho Chehab 0xea, 0x18, /* LLC2 connected, output FIFO 255cb7a01acSMauro Carvalho Chehab * reset with VACTintern */ 256cb7a01acSMauro Carvalho Chehab 0xf0, 0x8a, /* Half full level to 10, bus 257cb7a01acSMauro Carvalho Chehab * shuffler [7:0, 23:16, 15:8] */ 258cb7a01acSMauro Carvalho Chehab 0xf1, 0x18, /* Single clock, sync mode, no 259cb7a01acSMauro Carvalho Chehab * FE delay, no HLEN counter */ 260cb7a01acSMauro Carvalho Chehab 0xf8, 0x12, /* Port A, PIXCLK, HF# & FE# 261cb7a01acSMauro Carvalho Chehab * strength to 2 */ 262cb7a01acSMauro Carvalho Chehab 0xf9, 0x24, /* Port B, HREF, VREF, PREF & 263cb7a01acSMauro Carvalho Chehab * ALPHA strength to 4 */ 264cb7a01acSMauro Carvalho Chehab }; 265cb7a01acSMauro Carvalho Chehab 266cb7a01acSMauro Carvalho Chehab static const unsigned short init_fp[] = { 267cb7a01acSMauro Carvalho Chehab 0x59, 0, 268cb7a01acSMauro Carvalho Chehab 0xa0, 2070, /* ACC reference */ 269cb7a01acSMauro Carvalho Chehab 0xa3, 0, 270cb7a01acSMauro Carvalho Chehab 0xa4, 0, 271cb7a01acSMauro Carvalho Chehab 0xa8, 30, 272cb7a01acSMauro Carvalho Chehab 0xb2, 768, 273cb7a01acSMauro Carvalho Chehab 0xbe, 27, 274cb7a01acSMauro Carvalho Chehab 0x58, 0, 275cb7a01acSMauro Carvalho Chehab 0x26, 0, 276cb7a01acSMauro Carvalho Chehab 0x4b, 0x298, /* PLL gain */ 277cb7a01acSMauro Carvalho Chehab }; 278cb7a01acSMauro Carvalho Chehab 279cb7a01acSMauro Carvalho Chehab 280cb7a01acSMauro Carvalho Chehab static int vpx3220_init(struct v4l2_subdev *sd, u32 val) 281cb7a01acSMauro Carvalho Chehab { 282cb7a01acSMauro Carvalho Chehab struct vpx3220 *decoder = to_vpx3220(sd); 283cb7a01acSMauro Carvalho Chehab 284cb7a01acSMauro Carvalho Chehab vpx3220_write_block(sd, init_common, sizeof(init_common)); 285cb7a01acSMauro Carvalho Chehab vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1); 286cb7a01acSMauro Carvalho Chehab if (decoder->norm & V4L2_STD_NTSC) 287cb7a01acSMauro Carvalho Chehab vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1); 288cb7a01acSMauro Carvalho Chehab else if (decoder->norm & V4L2_STD_PAL) 289cb7a01acSMauro Carvalho Chehab vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); 290cb7a01acSMauro Carvalho Chehab else if (decoder->norm & V4L2_STD_SECAM) 291cb7a01acSMauro Carvalho Chehab vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1); 292cb7a01acSMauro Carvalho Chehab else 293cb7a01acSMauro Carvalho Chehab vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); 294cb7a01acSMauro Carvalho Chehab return 0; 295cb7a01acSMauro Carvalho Chehab } 296cb7a01acSMauro Carvalho Chehab 297cb7a01acSMauro Carvalho Chehab static int vpx3220_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd) 298cb7a01acSMauro Carvalho Chehab { 299cb7a01acSMauro Carvalho Chehab int res = V4L2_IN_ST_NO_SIGNAL, status; 300cb7a01acSMauro Carvalho Chehab v4l2_std_id std = 0; 301cb7a01acSMauro Carvalho Chehab 302cb7a01acSMauro Carvalho Chehab status = vpx3220_fp_read(sd, 0x0f3); 303cb7a01acSMauro Carvalho Chehab 304cb7a01acSMauro Carvalho Chehab v4l2_dbg(1, debug, sd, "status: 0x%04x\n", status); 305cb7a01acSMauro Carvalho Chehab 306cb7a01acSMauro Carvalho Chehab if (status < 0) 307cb7a01acSMauro Carvalho Chehab return status; 308cb7a01acSMauro Carvalho Chehab 309cb7a01acSMauro Carvalho Chehab if ((status & 0x20) == 0) { 310cb7a01acSMauro Carvalho Chehab res = 0; 311cb7a01acSMauro Carvalho Chehab 312cb7a01acSMauro Carvalho Chehab switch (status & 0x18) { 313cb7a01acSMauro Carvalho Chehab case 0x00: 314cb7a01acSMauro Carvalho Chehab case 0x10: 315cb7a01acSMauro Carvalho Chehab case 0x14: 316cb7a01acSMauro Carvalho Chehab case 0x18: 317cb7a01acSMauro Carvalho Chehab std = V4L2_STD_PAL; 318cb7a01acSMauro Carvalho Chehab break; 319cb7a01acSMauro Carvalho Chehab 320cb7a01acSMauro Carvalho Chehab case 0x08: 321cb7a01acSMauro Carvalho Chehab std = V4L2_STD_SECAM; 322cb7a01acSMauro Carvalho Chehab break; 323cb7a01acSMauro Carvalho Chehab 324cb7a01acSMauro Carvalho Chehab case 0x04: 325cb7a01acSMauro Carvalho Chehab case 0x0c: 326cb7a01acSMauro Carvalho Chehab case 0x1c: 327cb7a01acSMauro Carvalho Chehab std = V4L2_STD_NTSC; 328cb7a01acSMauro Carvalho Chehab break; 329cb7a01acSMauro Carvalho Chehab } 330cb7a01acSMauro Carvalho Chehab } 331cb7a01acSMauro Carvalho Chehab if (pstd) 332cb7a01acSMauro Carvalho Chehab *pstd = std; 333cb7a01acSMauro Carvalho Chehab if (pstatus) 334cb7a01acSMauro Carvalho Chehab *pstatus = res; 335cb7a01acSMauro Carvalho Chehab return 0; 336cb7a01acSMauro Carvalho Chehab } 337cb7a01acSMauro Carvalho Chehab 338cb7a01acSMauro Carvalho Chehab static int vpx3220_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) 339cb7a01acSMauro Carvalho Chehab { 340cb7a01acSMauro Carvalho Chehab v4l2_dbg(1, debug, sd, "querystd\n"); 341cb7a01acSMauro Carvalho Chehab return vpx3220_status(sd, NULL, std); 342cb7a01acSMauro Carvalho Chehab } 343cb7a01acSMauro Carvalho Chehab 344cb7a01acSMauro Carvalho Chehab static int vpx3220_g_input_status(struct v4l2_subdev *sd, u32 *status) 345cb7a01acSMauro Carvalho Chehab { 346cb7a01acSMauro Carvalho Chehab v4l2_dbg(1, debug, sd, "g_input_status\n"); 347cb7a01acSMauro Carvalho Chehab return vpx3220_status(sd, status, NULL); 348cb7a01acSMauro Carvalho Chehab } 349cb7a01acSMauro Carvalho Chehab 350cb7a01acSMauro Carvalho Chehab static int vpx3220_s_std(struct v4l2_subdev *sd, v4l2_std_id std) 351cb7a01acSMauro Carvalho Chehab { 352cb7a01acSMauro Carvalho Chehab struct vpx3220 *decoder = to_vpx3220(sd); 353cb7a01acSMauro Carvalho Chehab int temp_input; 354cb7a01acSMauro Carvalho Chehab 355cb7a01acSMauro Carvalho Chehab /* Here we back up the input selection because it gets 356cb7a01acSMauro Carvalho Chehab overwritten when we fill the registers with the 357cb7a01acSMauro Carvalho Chehab chosen video norm */ 358cb7a01acSMauro Carvalho Chehab temp_input = vpx3220_fp_read(sd, 0xf2); 359cb7a01acSMauro Carvalho Chehab 360cb7a01acSMauro Carvalho Chehab v4l2_dbg(1, debug, sd, "s_std %llx\n", (unsigned long long)std); 361cb7a01acSMauro Carvalho Chehab if (std & V4L2_STD_NTSC) { 362cb7a01acSMauro Carvalho Chehab vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1); 363cb7a01acSMauro Carvalho Chehab v4l2_dbg(1, debug, sd, "norm switched to NTSC\n"); 364cb7a01acSMauro Carvalho Chehab } else if (std & V4L2_STD_PAL) { 365cb7a01acSMauro Carvalho Chehab vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); 366cb7a01acSMauro Carvalho Chehab v4l2_dbg(1, debug, sd, "norm switched to PAL\n"); 367cb7a01acSMauro Carvalho Chehab } else if (std & V4L2_STD_SECAM) { 368cb7a01acSMauro Carvalho Chehab vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1); 369cb7a01acSMauro Carvalho Chehab v4l2_dbg(1, debug, sd, "norm switched to SECAM\n"); 370cb7a01acSMauro Carvalho Chehab } else { 371cb7a01acSMauro Carvalho Chehab return -EINVAL; 372cb7a01acSMauro Carvalho Chehab } 373cb7a01acSMauro Carvalho Chehab 374cb7a01acSMauro Carvalho Chehab decoder->norm = std; 375cb7a01acSMauro Carvalho Chehab 376cb7a01acSMauro Carvalho Chehab /* And here we set the backed up video input again */ 377cb7a01acSMauro Carvalho Chehab vpx3220_fp_write(sd, 0xf2, temp_input | 0x0010); 378cb7a01acSMauro Carvalho Chehab udelay(10); 379cb7a01acSMauro Carvalho Chehab return 0; 380cb7a01acSMauro Carvalho Chehab } 381cb7a01acSMauro Carvalho Chehab 382cb7a01acSMauro Carvalho Chehab static int vpx3220_s_routing(struct v4l2_subdev *sd, 383cb7a01acSMauro Carvalho Chehab u32 input, u32 output, u32 config) 384cb7a01acSMauro Carvalho Chehab { 385cb7a01acSMauro Carvalho Chehab int data; 386cb7a01acSMauro Carvalho Chehab 387cb7a01acSMauro Carvalho Chehab /* RJ: input = 0: ST8 (PCTV) input 388cb7a01acSMauro Carvalho Chehab input = 1: COMPOSITE input 389cb7a01acSMauro Carvalho Chehab input = 2: SVHS input */ 390cb7a01acSMauro Carvalho Chehab 391cb7a01acSMauro Carvalho Chehab const int input_vals[3][2] = { 392cb7a01acSMauro Carvalho Chehab {0x0c, 0}, 393cb7a01acSMauro Carvalho Chehab {0x0d, 0}, 394cb7a01acSMauro Carvalho Chehab {0x0e, 1} 395cb7a01acSMauro Carvalho Chehab }; 396cb7a01acSMauro Carvalho Chehab 397cb7a01acSMauro Carvalho Chehab if (input > 2) 398cb7a01acSMauro Carvalho Chehab return -EINVAL; 399cb7a01acSMauro Carvalho Chehab 400cb7a01acSMauro Carvalho Chehab v4l2_dbg(1, debug, sd, "input switched to %s\n", inputs[input]); 401cb7a01acSMauro Carvalho Chehab 402cb7a01acSMauro Carvalho Chehab vpx3220_write(sd, 0x33, input_vals[input][0]); 403cb7a01acSMauro Carvalho Chehab 404cb7a01acSMauro Carvalho Chehab data = vpx3220_fp_read(sd, 0xf2) & ~(0x0020); 405cb7a01acSMauro Carvalho Chehab if (data < 0) 406cb7a01acSMauro Carvalho Chehab return data; 407cb7a01acSMauro Carvalho Chehab /* 0x0010 is required to latch the setting */ 408cb7a01acSMauro Carvalho Chehab vpx3220_fp_write(sd, 0xf2, 409cb7a01acSMauro Carvalho Chehab data | (input_vals[input][1] << 5) | 0x0010); 410cb7a01acSMauro Carvalho Chehab 411cb7a01acSMauro Carvalho Chehab udelay(10); 412cb7a01acSMauro Carvalho Chehab return 0; 413cb7a01acSMauro Carvalho Chehab } 414cb7a01acSMauro Carvalho Chehab 415cb7a01acSMauro Carvalho Chehab static int vpx3220_s_stream(struct v4l2_subdev *sd, int enable) 416cb7a01acSMauro Carvalho Chehab { 417cb7a01acSMauro Carvalho Chehab v4l2_dbg(1, debug, sd, "s_stream %s\n", enable ? "on" : "off"); 418cb7a01acSMauro Carvalho Chehab 419cb7a01acSMauro Carvalho Chehab vpx3220_write(sd, 0xf2, (enable ? 0x1b : 0x00)); 420cb7a01acSMauro Carvalho Chehab return 0; 421cb7a01acSMauro Carvalho Chehab } 422cb7a01acSMauro Carvalho Chehab 423cb7a01acSMauro Carvalho Chehab static int vpx3220_s_ctrl(struct v4l2_ctrl *ctrl) 424cb7a01acSMauro Carvalho Chehab { 425cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *sd = to_sd(ctrl); 426cb7a01acSMauro Carvalho Chehab 427cb7a01acSMauro Carvalho Chehab switch (ctrl->id) { 428cb7a01acSMauro Carvalho Chehab case V4L2_CID_BRIGHTNESS: 429cb7a01acSMauro Carvalho Chehab vpx3220_write(sd, 0xe6, ctrl->val); 430cb7a01acSMauro Carvalho Chehab return 0; 431cb7a01acSMauro Carvalho Chehab case V4L2_CID_CONTRAST: 432cb7a01acSMauro Carvalho Chehab /* Bit 7 and 8 is for noise shaping */ 433cb7a01acSMauro Carvalho Chehab vpx3220_write(sd, 0xe7, ctrl->val + 192); 434cb7a01acSMauro Carvalho Chehab return 0; 435cb7a01acSMauro Carvalho Chehab case V4L2_CID_SATURATION: 436cb7a01acSMauro Carvalho Chehab vpx3220_fp_write(sd, 0xa0, ctrl->val); 437cb7a01acSMauro Carvalho Chehab return 0; 438cb7a01acSMauro Carvalho Chehab case V4L2_CID_HUE: 439cb7a01acSMauro Carvalho Chehab vpx3220_fp_write(sd, 0x1c, ctrl->val); 440cb7a01acSMauro Carvalho Chehab return 0; 441cb7a01acSMauro Carvalho Chehab } 442cb7a01acSMauro Carvalho Chehab return -EINVAL; 443cb7a01acSMauro Carvalho Chehab } 444cb7a01acSMauro Carvalho Chehab 445cb7a01acSMauro Carvalho Chehab static int vpx3220_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) 446cb7a01acSMauro Carvalho Chehab { 447cb7a01acSMauro Carvalho Chehab struct vpx3220 *decoder = to_vpx3220(sd); 448cb7a01acSMauro Carvalho Chehab struct i2c_client *client = v4l2_get_subdevdata(sd); 449cb7a01acSMauro Carvalho Chehab 450cb7a01acSMauro Carvalho Chehab return v4l2_chip_ident_i2c_client(client, chip, decoder->ident, 0); 451cb7a01acSMauro Carvalho Chehab } 452cb7a01acSMauro Carvalho Chehab 453cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- */ 454cb7a01acSMauro Carvalho Chehab 455cb7a01acSMauro Carvalho Chehab static const struct v4l2_ctrl_ops vpx3220_ctrl_ops = { 456cb7a01acSMauro Carvalho Chehab .s_ctrl = vpx3220_s_ctrl, 457cb7a01acSMauro Carvalho Chehab }; 458cb7a01acSMauro Carvalho Chehab 459cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_core_ops vpx3220_core_ops = { 460cb7a01acSMauro Carvalho Chehab .g_chip_ident = vpx3220_g_chip_ident, 461cb7a01acSMauro Carvalho Chehab .init = vpx3220_init, 462cb7a01acSMauro Carvalho Chehab .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, 463cb7a01acSMauro Carvalho Chehab .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, 464cb7a01acSMauro Carvalho Chehab .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, 465cb7a01acSMauro Carvalho Chehab .g_ctrl = v4l2_subdev_g_ctrl, 466cb7a01acSMauro Carvalho Chehab .s_ctrl = v4l2_subdev_s_ctrl, 467cb7a01acSMauro Carvalho Chehab .queryctrl = v4l2_subdev_queryctrl, 468cb7a01acSMauro Carvalho Chehab .querymenu = v4l2_subdev_querymenu, 469cb7a01acSMauro Carvalho Chehab .s_std = vpx3220_s_std, 470cb7a01acSMauro Carvalho Chehab }; 471cb7a01acSMauro Carvalho Chehab 472cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_video_ops vpx3220_video_ops = { 473cb7a01acSMauro Carvalho Chehab .s_routing = vpx3220_s_routing, 474cb7a01acSMauro Carvalho Chehab .s_stream = vpx3220_s_stream, 475cb7a01acSMauro Carvalho Chehab .querystd = vpx3220_querystd, 476cb7a01acSMauro Carvalho Chehab .g_input_status = vpx3220_g_input_status, 477cb7a01acSMauro Carvalho Chehab }; 478cb7a01acSMauro Carvalho Chehab 479cb7a01acSMauro Carvalho Chehab static const struct v4l2_subdev_ops vpx3220_ops = { 480cb7a01acSMauro Carvalho Chehab .core = &vpx3220_core_ops, 481cb7a01acSMauro Carvalho Chehab .video = &vpx3220_video_ops, 482cb7a01acSMauro Carvalho Chehab }; 483cb7a01acSMauro Carvalho Chehab 484cb7a01acSMauro Carvalho Chehab /* ----------------------------------------------------------------------- 485cb7a01acSMauro Carvalho Chehab * Client management code 486cb7a01acSMauro Carvalho Chehab */ 487cb7a01acSMauro Carvalho Chehab 488cb7a01acSMauro Carvalho Chehab static int vpx3220_probe(struct i2c_client *client, 489cb7a01acSMauro Carvalho Chehab const struct i2c_device_id *id) 490cb7a01acSMauro Carvalho Chehab { 491cb7a01acSMauro Carvalho Chehab struct vpx3220 *decoder; 492cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *sd; 493cb7a01acSMauro Carvalho Chehab const char *name = NULL; 494cb7a01acSMauro Carvalho Chehab u8 ver; 495cb7a01acSMauro Carvalho Chehab u16 pn; 496cb7a01acSMauro Carvalho Chehab 497cb7a01acSMauro Carvalho Chehab /* Check if the adapter supports the needed features */ 498cb7a01acSMauro Carvalho Chehab if (!i2c_check_functionality(client->adapter, 499cb7a01acSMauro Carvalho Chehab I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) 500cb7a01acSMauro Carvalho Chehab return -ENODEV; 501cb7a01acSMauro Carvalho Chehab 502c02b211dSLaurent Pinchart decoder = devm_kzalloc(&client->dev, sizeof(*decoder), GFP_KERNEL); 503cb7a01acSMauro Carvalho Chehab if (decoder == NULL) 504cb7a01acSMauro Carvalho Chehab return -ENOMEM; 505cb7a01acSMauro Carvalho Chehab sd = &decoder->sd; 506cb7a01acSMauro Carvalho Chehab v4l2_i2c_subdev_init(sd, client, &vpx3220_ops); 507cb7a01acSMauro Carvalho Chehab decoder->norm = V4L2_STD_PAL; 508cb7a01acSMauro Carvalho Chehab decoder->input = 0; 509cb7a01acSMauro Carvalho Chehab decoder->enable = 1; 510cb7a01acSMauro Carvalho Chehab v4l2_ctrl_handler_init(&decoder->hdl, 4); 511cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops, 512cb7a01acSMauro Carvalho Chehab V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); 513cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops, 514cb7a01acSMauro Carvalho Chehab V4L2_CID_CONTRAST, 0, 63, 1, 32); 515cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops, 516cb7a01acSMauro Carvalho Chehab V4L2_CID_SATURATION, 0, 4095, 1, 2048); 517cb7a01acSMauro Carvalho Chehab v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops, 518cb7a01acSMauro Carvalho Chehab V4L2_CID_HUE, -512, 511, 1, 0); 519cb7a01acSMauro Carvalho Chehab sd->ctrl_handler = &decoder->hdl; 520cb7a01acSMauro Carvalho Chehab if (decoder->hdl.error) { 521cb7a01acSMauro Carvalho Chehab int err = decoder->hdl.error; 522cb7a01acSMauro Carvalho Chehab 523cb7a01acSMauro Carvalho Chehab v4l2_ctrl_handler_free(&decoder->hdl); 524cb7a01acSMauro Carvalho Chehab return err; 525cb7a01acSMauro Carvalho Chehab } 526cb7a01acSMauro Carvalho Chehab v4l2_ctrl_handler_setup(&decoder->hdl); 527cb7a01acSMauro Carvalho Chehab 528cb7a01acSMauro Carvalho Chehab ver = i2c_smbus_read_byte_data(client, 0x00); 529cb7a01acSMauro Carvalho Chehab pn = (i2c_smbus_read_byte_data(client, 0x02) << 8) + 530cb7a01acSMauro Carvalho Chehab i2c_smbus_read_byte_data(client, 0x01); 531cb7a01acSMauro Carvalho Chehab decoder->ident = V4L2_IDENT_VPX3220A; 532cb7a01acSMauro Carvalho Chehab if (ver == 0xec) { 533cb7a01acSMauro Carvalho Chehab switch (pn) { 534cb7a01acSMauro Carvalho Chehab case 0x4680: 535cb7a01acSMauro Carvalho Chehab name = "vpx3220a"; 536cb7a01acSMauro Carvalho Chehab break; 537cb7a01acSMauro Carvalho Chehab case 0x4260: 538cb7a01acSMauro Carvalho Chehab name = "vpx3216b"; 539cb7a01acSMauro Carvalho Chehab decoder->ident = V4L2_IDENT_VPX3216B; 540cb7a01acSMauro Carvalho Chehab break; 541cb7a01acSMauro Carvalho Chehab case 0x4280: 542cb7a01acSMauro Carvalho Chehab name = "vpx3214c"; 543cb7a01acSMauro Carvalho Chehab decoder->ident = V4L2_IDENT_VPX3214C; 544cb7a01acSMauro Carvalho Chehab break; 545cb7a01acSMauro Carvalho Chehab } 546cb7a01acSMauro Carvalho Chehab } 547cb7a01acSMauro Carvalho Chehab if (name) 548cb7a01acSMauro Carvalho Chehab v4l2_info(sd, "%s found @ 0x%x (%s)\n", name, 549cb7a01acSMauro Carvalho Chehab client->addr << 1, client->adapter->name); 550cb7a01acSMauro Carvalho Chehab else 551cb7a01acSMauro Carvalho Chehab v4l2_info(sd, "chip (%02x:%04x) found @ 0x%x (%s)\n", 552cb7a01acSMauro Carvalho Chehab ver, pn, client->addr << 1, client->adapter->name); 553cb7a01acSMauro Carvalho Chehab 554cb7a01acSMauro Carvalho Chehab vpx3220_write_block(sd, init_common, sizeof(init_common)); 555cb7a01acSMauro Carvalho Chehab vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1); 556cb7a01acSMauro Carvalho Chehab /* Default to PAL */ 557cb7a01acSMauro Carvalho Chehab vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); 558cb7a01acSMauro Carvalho Chehab return 0; 559cb7a01acSMauro Carvalho Chehab } 560cb7a01acSMauro Carvalho Chehab 561cb7a01acSMauro Carvalho Chehab static int vpx3220_remove(struct i2c_client *client) 562cb7a01acSMauro Carvalho Chehab { 563cb7a01acSMauro Carvalho Chehab struct v4l2_subdev *sd = i2c_get_clientdata(client); 564cb7a01acSMauro Carvalho Chehab struct vpx3220 *decoder = to_vpx3220(sd); 565cb7a01acSMauro Carvalho Chehab 566cb7a01acSMauro Carvalho Chehab v4l2_device_unregister_subdev(sd); 567cb7a01acSMauro Carvalho Chehab v4l2_ctrl_handler_free(&decoder->hdl); 568c02b211dSLaurent Pinchart 569cb7a01acSMauro Carvalho Chehab return 0; 570cb7a01acSMauro Carvalho Chehab } 571cb7a01acSMauro Carvalho Chehab 572cb7a01acSMauro Carvalho Chehab static const struct i2c_device_id vpx3220_id[] = { 573cb7a01acSMauro Carvalho Chehab { "vpx3220a", 0 }, 574cb7a01acSMauro Carvalho Chehab { "vpx3216b", 0 }, 575cb7a01acSMauro Carvalho Chehab { "vpx3214c", 0 }, 576cb7a01acSMauro Carvalho Chehab { } 577cb7a01acSMauro Carvalho Chehab }; 578cb7a01acSMauro Carvalho Chehab MODULE_DEVICE_TABLE(i2c, vpx3220_id); 579cb7a01acSMauro Carvalho Chehab 580cb7a01acSMauro Carvalho Chehab static struct i2c_driver vpx3220_driver = { 581cb7a01acSMauro Carvalho Chehab .driver = { 582cb7a01acSMauro Carvalho Chehab .owner = THIS_MODULE, 583cb7a01acSMauro Carvalho Chehab .name = "vpx3220", 584cb7a01acSMauro Carvalho Chehab }, 585cb7a01acSMauro Carvalho Chehab .probe = vpx3220_probe, 586cb7a01acSMauro Carvalho Chehab .remove = vpx3220_remove, 587cb7a01acSMauro Carvalho Chehab .id_table = vpx3220_id, 588cb7a01acSMauro Carvalho Chehab }; 589cb7a01acSMauro Carvalho Chehab 590cb7a01acSMauro Carvalho Chehab module_i2c_driver(vpx3220_driver); 591