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