Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * vpx3220a, vpx3216b & vpx3214c video decoder driver version 0.0.1
0004  *
0005  * Copyright (C) 2001 Laurent Pinchart <lpinchart@freegates.be>
0006  */
0007 
0008 #include <linux/module.h>
0009 #include <linux/init.h>
0010 #include <linux/delay.h>
0011 #include <linux/types.h>
0012 #include <linux/slab.h>
0013 #include <linux/uaccess.h>
0014 #include <linux/i2c.h>
0015 #include <linux/videodev2.h>
0016 #include <media/v4l2-device.h>
0017 #include <media/v4l2-ctrls.h>
0018 
0019 MODULE_DESCRIPTION("vpx3220a/vpx3216b/vpx3214c video decoder driver");
0020 MODULE_AUTHOR("Laurent Pinchart");
0021 MODULE_LICENSE("GPL");
0022 
0023 static int debug;
0024 module_param(debug, int, 0);
0025 MODULE_PARM_DESC(debug, "Debug level (0-1)");
0026 
0027 
0028 #define VPX_TIMEOUT_COUNT  10
0029 
0030 /* ----------------------------------------------------------------------- */
0031 
0032 struct vpx3220 {
0033     struct v4l2_subdev sd;
0034     struct v4l2_ctrl_handler hdl;
0035     unsigned char reg[255];
0036 
0037     v4l2_std_id norm;
0038     int input;
0039     int enable;
0040 };
0041 
0042 static inline struct vpx3220 *to_vpx3220(struct v4l2_subdev *sd)
0043 {
0044     return container_of(sd, struct vpx3220, sd);
0045 }
0046 
0047 static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
0048 {
0049     return &container_of(ctrl->handler, struct vpx3220, hdl)->sd;
0050 }
0051 
0052 static char *inputs[] = { "internal", "composite", "svideo" };
0053 
0054 /* ----------------------------------------------------------------------- */
0055 
0056 static inline int vpx3220_write(struct v4l2_subdev *sd, u8 reg, u8 value)
0057 {
0058     struct i2c_client *client = v4l2_get_subdevdata(sd);
0059     struct vpx3220 *decoder = i2c_get_clientdata(client);
0060 
0061     decoder->reg[reg] = value;
0062     return i2c_smbus_write_byte_data(client, reg, value);
0063 }
0064 
0065 static inline int vpx3220_read(struct v4l2_subdev *sd, u8 reg)
0066 {
0067     struct i2c_client *client = v4l2_get_subdevdata(sd);
0068 
0069     return i2c_smbus_read_byte_data(client, reg);
0070 }
0071 
0072 static int vpx3220_fp_status(struct v4l2_subdev *sd)
0073 {
0074     unsigned char status;
0075     unsigned int i;
0076 
0077     for (i = 0; i < VPX_TIMEOUT_COUNT; i++) {
0078         status = vpx3220_read(sd, 0x29);
0079 
0080         if (!(status & 4))
0081             return 0;
0082 
0083         udelay(10);
0084 
0085         if (need_resched())
0086             cond_resched();
0087     }
0088 
0089     return -1;
0090 }
0091 
0092 static int vpx3220_fp_write(struct v4l2_subdev *sd, u8 fpaddr, u16 data)
0093 {
0094     struct i2c_client *client = v4l2_get_subdevdata(sd);
0095 
0096     /* Write the 16-bit address to the FPWR register */
0097     if (i2c_smbus_write_word_data(client, 0x27, swab16(fpaddr)) == -1) {
0098         v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
0099         return -1;
0100     }
0101 
0102     if (vpx3220_fp_status(sd) < 0)
0103         return -1;
0104 
0105     /* Write the 16-bit data to the FPDAT register */
0106     if (i2c_smbus_write_word_data(client, 0x28, swab16(data)) == -1) {
0107         v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
0108         return -1;
0109     }
0110 
0111     return 0;
0112 }
0113 
0114 static int vpx3220_fp_read(struct v4l2_subdev *sd, u16 fpaddr)
0115 {
0116     struct i2c_client *client = v4l2_get_subdevdata(sd);
0117     s16 data;
0118 
0119     /* Write the 16-bit address to the FPRD register */
0120     if (i2c_smbus_write_word_data(client, 0x26, swab16(fpaddr)) == -1) {
0121         v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
0122         return -1;
0123     }
0124 
0125     if (vpx3220_fp_status(sd) < 0)
0126         return -1;
0127 
0128     /* Read the 16-bit data from the FPDAT register */
0129     data = i2c_smbus_read_word_data(client, 0x28);
0130     if (data == -1) {
0131         v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
0132         return -1;
0133     }
0134 
0135     return swab16(data);
0136 }
0137 
0138 static int vpx3220_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len)
0139 {
0140     u8 reg;
0141     int ret = -1;
0142 
0143     while (len >= 2) {
0144         reg = *data++;
0145         ret = vpx3220_write(sd, reg, *data++);
0146         if (ret < 0)
0147             break;
0148         len -= 2;
0149     }
0150 
0151     return ret;
0152 }
0153 
0154 static int vpx3220_write_fp_block(struct v4l2_subdev *sd,
0155         const u16 *data, unsigned int len)
0156 {
0157     u8 reg;
0158     int ret = 0;
0159 
0160     while (len > 1) {
0161         reg = *data++;
0162         ret |= vpx3220_fp_write(sd, reg, *data++);
0163         len -= 2;
0164     }
0165 
0166     return ret;
0167 }
0168 
0169 /* ---------------------------------------------------------------------- */
0170 
0171 static const unsigned short init_ntsc[] = {
0172     0x1c, 0x00,     /* NTSC tint angle */
0173     0x88, 17,       /* Window 1 vertical */
0174     0x89, 240,      /* Vertical lines in */
0175     0x8a, 240,      /* Vertical lines out */
0176     0x8b, 000,      /* Horizontal begin */
0177     0x8c, 640,      /* Horizontal length */
0178     0x8d, 640,      /* Number of pixels */
0179     0x8f, 0xc00,        /* Disable window 2 */
0180     0xf0, 0x73,     /* 13.5 MHz transport, Forced
0181                  * mode, latch windows */
0182     0xf2, 0x13,     /* NTSC M, composite input */
0183     0xe7, 0x1e1,        /* Enable vertical standard
0184                  * locking @ 240 lines */
0185 };
0186 
0187 static const unsigned short init_pal[] = {
0188     0x88, 23,       /* Window 1 vertical begin */
0189     0x89, 288,      /* Vertical lines in (16 lines
0190                  * skipped by the VFE) */
0191     0x8a, 288,      /* Vertical lines out (16 lines
0192                  * skipped by the VFE) */
0193     0x8b, 16,       /* Horizontal begin */
0194     0x8c, 768,      /* Horizontal length */
0195     0x8d, 784,      /* Number of pixels
0196                  * Must be >= Horizontal begin + Horizontal length */
0197     0x8f, 0xc00,        /* Disable window 2 */
0198     0xf0, 0x77,     /* 13.5 MHz transport, Forced
0199                  * mode, latch windows */
0200     0xf2, 0x3d1,        /* PAL B,G,H,I, composite input */
0201     0xe7, 0x241,        /* PAL/SECAM set to 288 lines */
0202 };
0203 
0204 static const unsigned short init_secam[] = {
0205     0x88, 23,       /* Window 1 vertical begin */
0206     0x89, 288,      /* Vertical lines in (16 lines
0207                  * skipped by the VFE) */
0208     0x8a, 288,      /* Vertical lines out (16 lines
0209                  * skipped by the VFE) */
0210     0x8b, 16,       /* Horizontal begin */
0211     0x8c, 768,      /* Horizontal length */
0212     0x8d, 784,      /* Number of pixels
0213                  * Must be >= Horizontal begin + Horizontal length */
0214     0x8f, 0xc00,        /* Disable window 2 */
0215     0xf0, 0x77,     /* 13.5 MHz transport, Forced
0216                  * mode, latch windows */
0217     0xf2, 0x3d5,        /* SECAM, composite input */
0218     0xe7, 0x241,        /* PAL/SECAM set to 288 lines */
0219 };
0220 
0221 static const unsigned char init_common[] = {
0222     0xf2, 0x00,     /* Disable all outputs */
0223     0x33, 0x0d,     /* Luma : VIN2, Chroma : CIN
0224                  * (clamp off) */
0225     0xd8, 0xa8,     /* HREF/VREF active high, VREF
0226                  * pulse = 2, Odd/Even flag */
0227     0x20, 0x03,     /* IF compensation 0dB/oct */
0228     0xe0, 0xff,     /* Open up all comparators */
0229     0xe1, 0x00,
0230     0xe2, 0x7f,
0231     0xe3, 0x80,
0232     0xe4, 0x7f,
0233     0xe5, 0x80,
0234     0xe6, 0x00,     /* Brightness set to 0 */
0235     0xe7, 0xe0,     /* Contrast to 1.0, noise shaping
0236                  * 10 to 8 2-bit error diffusion */
0237     0xe8, 0xf8,     /* YUV422, CbCr binary offset,
0238                  * ... (p.32) */
0239     0xea, 0x18,     /* LLC2 connected, output FIFO
0240                  * reset with VACTintern */
0241     0xf0, 0x8a,     /* Half full level to 10, bus
0242                  * shuffler [7:0, 23:16, 15:8] */
0243     0xf1, 0x18,     /* Single clock, sync mode, no
0244                  * FE delay, no HLEN counter */
0245     0xf8, 0x12,     /* Port A, PIXCLK, HF# & FE#
0246                  * strength to 2 */
0247     0xf9, 0x24,     /* Port B, HREF, VREF, PREF &
0248                  * ALPHA strength to 4 */
0249 };
0250 
0251 static const unsigned short init_fp[] = {
0252     0x59, 0,
0253     0xa0, 2070,     /* ACC reference */
0254     0xa3, 0,
0255     0xa4, 0,
0256     0xa8, 30,
0257     0xb2, 768,
0258     0xbe, 27,
0259     0x58, 0,
0260     0x26, 0,
0261     0x4b, 0x298,        /* PLL gain */
0262 };
0263 
0264 
0265 static int vpx3220_init(struct v4l2_subdev *sd, u32 val)
0266 {
0267     struct vpx3220 *decoder = to_vpx3220(sd);
0268 
0269     vpx3220_write_block(sd, init_common, sizeof(init_common));
0270     vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1);
0271     if (decoder->norm & V4L2_STD_NTSC)
0272         vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1);
0273     else if (decoder->norm & V4L2_STD_PAL)
0274         vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
0275     else if (decoder->norm & V4L2_STD_SECAM)
0276         vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1);
0277     else
0278         vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
0279     return 0;
0280 }
0281 
0282 static int vpx3220_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd)
0283 {
0284     int res = V4L2_IN_ST_NO_SIGNAL, status;
0285     v4l2_std_id std = pstd ? *pstd : V4L2_STD_ALL;
0286 
0287     status = vpx3220_fp_read(sd, 0x0f3);
0288 
0289     v4l2_dbg(1, debug, sd, "status: 0x%04x\n", status);
0290 
0291     if (status < 0)
0292         return status;
0293 
0294     if ((status & 0x20) == 0) {
0295         res = 0;
0296 
0297         switch (status & 0x18) {
0298         case 0x00:
0299         case 0x10:
0300         case 0x14:
0301         case 0x18:
0302             std &= V4L2_STD_PAL;
0303             break;
0304 
0305         case 0x08:
0306             std &= V4L2_STD_SECAM;
0307             break;
0308 
0309         case 0x04:
0310         case 0x0c:
0311         case 0x1c:
0312             std &= V4L2_STD_NTSC;
0313             break;
0314         }
0315     } else {
0316         std = V4L2_STD_UNKNOWN;
0317     }
0318     if (pstd)
0319         *pstd = std;
0320     if (pstatus)
0321         *pstatus = res;
0322     return 0;
0323 }
0324 
0325 static int vpx3220_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
0326 {
0327     v4l2_dbg(1, debug, sd, "querystd\n");
0328     return vpx3220_status(sd, NULL, std);
0329 }
0330 
0331 static int vpx3220_g_input_status(struct v4l2_subdev *sd, u32 *status)
0332 {
0333     v4l2_dbg(1, debug, sd, "g_input_status\n");
0334     return vpx3220_status(sd, status, NULL);
0335 }
0336 
0337 static int vpx3220_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
0338 {
0339     struct vpx3220 *decoder = to_vpx3220(sd);
0340     int temp_input;
0341 
0342     /* Here we back up the input selection because it gets
0343        overwritten when we fill the registers with the
0344        chosen video norm */
0345     temp_input = vpx3220_fp_read(sd, 0xf2);
0346 
0347     v4l2_dbg(1, debug, sd, "s_std %llx\n", (unsigned long long)std);
0348     if (std & V4L2_STD_NTSC) {
0349         vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1);
0350         v4l2_dbg(1, debug, sd, "norm switched to NTSC\n");
0351     } else if (std & V4L2_STD_PAL) {
0352         vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
0353         v4l2_dbg(1, debug, sd, "norm switched to PAL\n");
0354     } else if (std & V4L2_STD_SECAM) {
0355         vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1);
0356         v4l2_dbg(1, debug, sd, "norm switched to SECAM\n");
0357     } else {
0358         return -EINVAL;
0359     }
0360 
0361     decoder->norm = std;
0362 
0363     /* And here we set the backed up video input again */
0364     vpx3220_fp_write(sd, 0xf2, temp_input | 0x0010);
0365     udelay(10);
0366     return 0;
0367 }
0368 
0369 static int vpx3220_s_routing(struct v4l2_subdev *sd,
0370                  u32 input, u32 output, u32 config)
0371 {
0372     int data;
0373 
0374     /* RJ:   input = 0: ST8 (PCTV) input
0375          input = 1: COMPOSITE  input
0376          input = 2: SVHS       input  */
0377 
0378     static const int input_vals[3][2] = {
0379         {0x0c, 0},
0380         {0x0d, 0},
0381         {0x0e, 1}
0382     };
0383 
0384     if (input > 2)
0385         return -EINVAL;
0386 
0387     v4l2_dbg(1, debug, sd, "input switched to %s\n", inputs[input]);
0388 
0389     vpx3220_write(sd, 0x33, input_vals[input][0]);
0390 
0391     data = vpx3220_fp_read(sd, 0xf2) & ~(0x0020);
0392     if (data < 0)
0393         return data;
0394     /* 0x0010 is required to latch the setting */
0395     vpx3220_fp_write(sd, 0xf2,
0396             data | (input_vals[input][1] << 5) | 0x0010);
0397 
0398     udelay(10);
0399     return 0;
0400 }
0401 
0402 static int vpx3220_s_stream(struct v4l2_subdev *sd, int enable)
0403 {
0404     v4l2_dbg(1, debug, sd, "s_stream %s\n", enable ? "on" : "off");
0405 
0406     vpx3220_write(sd, 0xf2, (enable ? 0x1b : 0x00));
0407     return 0;
0408 }
0409 
0410 static int vpx3220_s_ctrl(struct v4l2_ctrl *ctrl)
0411 {
0412     struct v4l2_subdev *sd = to_sd(ctrl);
0413 
0414     switch (ctrl->id) {
0415     case V4L2_CID_BRIGHTNESS:
0416         vpx3220_write(sd, 0xe6, ctrl->val);
0417         return 0;
0418     case V4L2_CID_CONTRAST:
0419         /* Bit 7 and 8 is for noise shaping */
0420         vpx3220_write(sd, 0xe7, ctrl->val + 192);
0421         return 0;
0422     case V4L2_CID_SATURATION:
0423         vpx3220_fp_write(sd, 0xa0, ctrl->val);
0424         return 0;
0425     case V4L2_CID_HUE:
0426         vpx3220_fp_write(sd, 0x1c, ctrl->val);
0427         return 0;
0428     }
0429     return -EINVAL;
0430 }
0431 
0432 /* ----------------------------------------------------------------------- */
0433 
0434 static const struct v4l2_ctrl_ops vpx3220_ctrl_ops = {
0435     .s_ctrl = vpx3220_s_ctrl,
0436 };
0437 
0438 static const struct v4l2_subdev_core_ops vpx3220_core_ops = {
0439     .init = vpx3220_init,
0440 };
0441 
0442 static const struct v4l2_subdev_video_ops vpx3220_video_ops = {
0443     .s_std = vpx3220_s_std,
0444     .s_routing = vpx3220_s_routing,
0445     .s_stream = vpx3220_s_stream,
0446     .querystd = vpx3220_querystd,
0447     .g_input_status = vpx3220_g_input_status,
0448 };
0449 
0450 static const struct v4l2_subdev_ops vpx3220_ops = {
0451     .core = &vpx3220_core_ops,
0452     .video = &vpx3220_video_ops,
0453 };
0454 
0455 /* -----------------------------------------------------------------------
0456  * Client management code
0457  */
0458 
0459 static int vpx3220_probe(struct i2c_client *client,
0460             const struct i2c_device_id *id)
0461 {
0462     struct vpx3220 *decoder;
0463     struct v4l2_subdev *sd;
0464     const char *name = NULL;
0465     u8 ver;
0466     u16 pn;
0467 
0468     /* Check if the adapter supports the needed features */
0469     if (!i2c_check_functionality(client->adapter,
0470         I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
0471         return -ENODEV;
0472 
0473     decoder = devm_kzalloc(&client->dev, sizeof(*decoder), GFP_KERNEL);
0474     if (decoder == NULL)
0475         return -ENOMEM;
0476     sd = &decoder->sd;
0477     v4l2_i2c_subdev_init(sd, client, &vpx3220_ops);
0478     decoder->norm = V4L2_STD_PAL;
0479     decoder->input = 0;
0480     decoder->enable = 1;
0481     v4l2_ctrl_handler_init(&decoder->hdl, 4);
0482     v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops,
0483         V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
0484     v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops,
0485         V4L2_CID_CONTRAST, 0, 63, 1, 32);
0486     v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops,
0487         V4L2_CID_SATURATION, 0, 4095, 1, 2048);
0488     v4l2_ctrl_new_std(&decoder->hdl, &vpx3220_ctrl_ops,
0489         V4L2_CID_HUE, -512, 511, 1, 0);
0490     sd->ctrl_handler = &decoder->hdl;
0491     if (decoder->hdl.error) {
0492         int err = decoder->hdl.error;
0493 
0494         v4l2_ctrl_handler_free(&decoder->hdl);
0495         return err;
0496     }
0497     v4l2_ctrl_handler_setup(&decoder->hdl);
0498 
0499     ver = i2c_smbus_read_byte_data(client, 0x00);
0500     pn = (i2c_smbus_read_byte_data(client, 0x02) << 8) +
0501         i2c_smbus_read_byte_data(client, 0x01);
0502     if (ver == 0xec) {
0503         switch (pn) {
0504         case 0x4680:
0505             name = "vpx3220a";
0506             break;
0507         case 0x4260:
0508             name = "vpx3216b";
0509             break;
0510         case 0x4280:
0511             name = "vpx3214c";
0512             break;
0513         }
0514     }
0515     if (name)
0516         v4l2_info(sd, "%s found @ 0x%x (%s)\n", name,
0517             client->addr << 1, client->adapter->name);
0518     else
0519         v4l2_info(sd, "chip (%02x:%04x) found @ 0x%x (%s)\n",
0520             ver, pn, client->addr << 1, client->adapter->name);
0521 
0522     vpx3220_write_block(sd, init_common, sizeof(init_common));
0523     vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1);
0524     /* Default to PAL */
0525     vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
0526     return 0;
0527 }
0528 
0529 static int vpx3220_remove(struct i2c_client *client)
0530 {
0531     struct v4l2_subdev *sd = i2c_get_clientdata(client);
0532     struct vpx3220 *decoder = to_vpx3220(sd);
0533 
0534     v4l2_device_unregister_subdev(sd);
0535     v4l2_ctrl_handler_free(&decoder->hdl);
0536 
0537     return 0;
0538 }
0539 
0540 static const struct i2c_device_id vpx3220_id[] = {
0541     { "vpx3220a", 0 },
0542     { "vpx3216b", 0 },
0543     { "vpx3214c", 0 },
0544     { }
0545 };
0546 MODULE_DEVICE_TABLE(i2c, vpx3220_id);
0547 
0548 static struct i2c_driver vpx3220_driver = {
0549     .driver = {
0550         .name   = "vpx3220",
0551     },
0552     .probe      = vpx3220_probe,
0553     .remove     = vpx3220_remove,
0554     .id_table   = vpx3220_id,
0555 };
0556 
0557 module_i2c_driver(vpx3220_driver);