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