Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2005-2006 Micronas USA Inc.
0004  */
0005 
0006 #include <linux/module.h>
0007 #include <linux/init.h>
0008 #include <linux/i2c.h>
0009 #include <linux/videodev2.h>
0010 #include <linux/ioctl.h>
0011 #include <linux/slab.h>
0012 #include <media/v4l2-device.h>
0013 #include <media/v4l2-ctrls.h>
0014 
0015 MODULE_DESCRIPTION("TW9906 I2C subdev driver");
0016 MODULE_LICENSE("GPL v2");
0017 
0018 struct tw9906 {
0019     struct v4l2_subdev sd;
0020     struct v4l2_ctrl_handler hdl;
0021     v4l2_std_id norm;
0022 };
0023 
0024 static inline struct tw9906 *to_state(struct v4l2_subdev *sd)
0025 {
0026     return container_of(sd, struct tw9906, sd);
0027 }
0028 
0029 static const u8 initial_registers[] = {
0030     0x02, 0x40, /* input 0, composite */
0031     0x03, 0xa2, /* correct digital format */
0032     0x05, 0x81, /* or 0x01 for PAL */
0033     0x07, 0x02, /* window */
0034     0x08, 0x14, /* window */
0035     0x09, 0xf0, /* window */
0036     0x0a, 0x10, /* window */
0037     0x0b, 0xd0, /* window */
0038     0x0d, 0x00, /* scaling */
0039     0x0e, 0x11, /* scaling */
0040     0x0f, 0x00, /* scaling */
0041     0x10, 0x00, /* brightness */
0042     0x11, 0x60, /* contrast */
0043     0x12, 0x11, /* sharpness */
0044     0x13, 0x7e, /* U gain */
0045     0x14, 0x7e, /* V gain */
0046     0x15, 0x00, /* hue */
0047     0x19, 0x57, /* vbi */
0048     0x1a, 0x0f,
0049     0x1b, 0x40,
0050     0x29, 0x03,
0051     0x55, 0x00,
0052     0x6b, 0x26,
0053     0x6c, 0x36,
0054     0x6d, 0xf0,
0055     0x6e, 0x41,
0056     0x6f, 0x13,
0057     0xad, 0x70,
0058     0x00, 0x00, /* Terminator (reg 0x00 is read-only) */
0059 };
0060 
0061 static int write_reg(struct v4l2_subdev *sd, u8 reg, u8 value)
0062 {
0063     struct i2c_client *client = v4l2_get_subdevdata(sd);
0064 
0065     return i2c_smbus_write_byte_data(client, reg, value);
0066 }
0067 
0068 static int write_regs(struct v4l2_subdev *sd, const u8 *regs)
0069 {
0070     int i;
0071 
0072     for (i = 0; regs[i] != 0x00; i += 2)
0073         if (write_reg(sd, regs[i], regs[i + 1]) < 0)
0074             return -1;
0075     return 0;
0076 }
0077 
0078 static int tw9906_s_video_routing(struct v4l2_subdev *sd, u32 input,
0079                       u32 output, u32 config)
0080 {
0081     write_reg(sd, 0x02, 0x40 | (input << 1));
0082     return 0;
0083 }
0084 
0085 static int tw9906_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
0086 {
0087     struct tw9906 *dec = to_state(sd);
0088     bool is_60hz = norm & V4L2_STD_525_60;
0089     static const u8 config_60hz[] = {
0090         0x05, 0x81,
0091         0x07, 0x02,
0092         0x08, 0x14,
0093         0x09, 0xf0,
0094         0,    0,
0095     };
0096     static const u8 config_50hz[] = {
0097         0x05, 0x01,
0098         0x07, 0x12,
0099         0x08, 0x18,
0100         0x09, 0x20,
0101         0,    0,
0102     };
0103 
0104     write_regs(sd, is_60hz ? config_60hz : config_50hz);
0105     dec->norm = norm;
0106     return 0;
0107 }
0108 
0109 static int tw9906_s_ctrl(struct v4l2_ctrl *ctrl)
0110 {
0111     struct tw9906 *dec = container_of(ctrl->handler, struct tw9906, hdl);
0112     struct v4l2_subdev *sd = &dec->sd;
0113 
0114     switch (ctrl->id) {
0115     case V4L2_CID_BRIGHTNESS:
0116         write_reg(sd, 0x10, ctrl->val);
0117         break;
0118     case V4L2_CID_CONTRAST:
0119         write_reg(sd, 0x11, ctrl->val);
0120         break;
0121     case V4L2_CID_HUE:
0122         write_reg(sd, 0x15, ctrl->val);
0123         break;
0124     default:
0125         return -EINVAL;
0126     }
0127     return 0;
0128 }
0129 
0130 static int tw9906_log_status(struct v4l2_subdev *sd)
0131 {
0132     struct tw9906 *dec = to_state(sd);
0133     bool is_60hz = dec->norm & V4L2_STD_525_60;
0134 
0135     v4l2_info(sd, "Standard: %d Hz\n", is_60hz ? 60 : 50);
0136     v4l2_ctrl_subdev_log_status(sd);
0137     return 0;
0138 }
0139 
0140 /* --------------------------------------------------------------------------*/
0141 
0142 static const struct v4l2_ctrl_ops tw9906_ctrl_ops = {
0143     .s_ctrl = tw9906_s_ctrl,
0144 };
0145 
0146 static const struct v4l2_subdev_core_ops tw9906_core_ops = {
0147     .log_status = tw9906_log_status,
0148 };
0149 
0150 static const struct v4l2_subdev_video_ops tw9906_video_ops = {
0151     .s_std = tw9906_s_std,
0152     .s_routing = tw9906_s_video_routing,
0153 };
0154 
0155 static const struct v4l2_subdev_ops tw9906_ops = {
0156     .core = &tw9906_core_ops,
0157     .video = &tw9906_video_ops,
0158 };
0159 
0160 static int tw9906_probe(struct i2c_client *client,
0161                  const struct i2c_device_id *id)
0162 {
0163     struct tw9906 *dec;
0164     struct v4l2_subdev *sd;
0165     struct v4l2_ctrl_handler *hdl;
0166 
0167     /* Check if the adapter supports the needed features */
0168     if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
0169         return -EIO;
0170 
0171     v4l_info(client, "chip found @ 0x%02x (%s)\n",
0172             client->addr << 1, client->adapter->name);
0173 
0174     dec = devm_kzalloc(&client->dev, sizeof(*dec), GFP_KERNEL);
0175     if (dec == NULL)
0176         return -ENOMEM;
0177     sd = &dec->sd;
0178     v4l2_i2c_subdev_init(sd, client, &tw9906_ops);
0179     hdl = &dec->hdl;
0180     v4l2_ctrl_handler_init(hdl, 4);
0181     v4l2_ctrl_new_std(hdl, &tw9906_ctrl_ops,
0182         V4L2_CID_BRIGHTNESS, -128, 127, 1, 0);
0183     v4l2_ctrl_new_std(hdl, &tw9906_ctrl_ops,
0184         V4L2_CID_CONTRAST, 0, 255, 1, 0x60);
0185     v4l2_ctrl_new_std(hdl, &tw9906_ctrl_ops,
0186         V4L2_CID_HUE, -128, 127, 1, 0);
0187     sd->ctrl_handler = hdl;
0188     if (hdl->error) {
0189         int err = hdl->error;
0190 
0191         v4l2_ctrl_handler_free(hdl);
0192         return err;
0193     }
0194 
0195     /* Initialize tw9906 */
0196     dec->norm = V4L2_STD_NTSC;
0197 
0198     if (write_regs(sd, initial_registers) < 0) {
0199         v4l2_err(client, "error initializing TW9906\n");
0200         return -EINVAL;
0201     }
0202 
0203     return 0;
0204 }
0205 
0206 static int tw9906_remove(struct i2c_client *client)
0207 {
0208     struct v4l2_subdev *sd = i2c_get_clientdata(client);
0209 
0210     v4l2_device_unregister_subdev(sd);
0211     v4l2_ctrl_handler_free(&to_state(sd)->hdl);
0212     return 0;
0213 }
0214 
0215 /* ----------------------------------------------------------------------- */
0216 
0217 static const struct i2c_device_id tw9906_id[] = {
0218     { "tw9906", 0 },
0219     { }
0220 };
0221 MODULE_DEVICE_TABLE(i2c, tw9906_id);
0222 
0223 static struct i2c_driver tw9906_driver = {
0224     .driver = {
0225         .name   = "tw9906",
0226     },
0227     .probe = tw9906_probe,
0228     .remove = tw9906_remove,
0229     .id_table = tw9906_id,
0230 };
0231 module_i2c_driver(tw9906_driver);