Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Driver for AK8813 / AK8814 TV-ecoders from Asahi Kasei Microsystems Co., Ltd. (AKM)
0004  *
0005  * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
0006  */
0007 
0008 #include <linux/i2c.h>
0009 #include <linux/init.h>
0010 #include <linux/platform_device.h>
0011 #include <linux/slab.h>
0012 #include <linux/videodev2.h>
0013 #include <linux/module.h>
0014 
0015 #include <media/i2c/ak881x.h>
0016 #include <media/v4l2-common.h>
0017 #include <media/v4l2-device.h>
0018 
0019 #define AK881X_INTERFACE_MODE   0
0020 #define AK881X_VIDEO_PROCESS1   1
0021 #define AK881X_VIDEO_PROCESS2   2
0022 #define AK881X_VIDEO_PROCESS3   3
0023 #define AK881X_DAC_MODE     5
0024 #define AK881X_STATUS       0x24
0025 #define AK881X_DEVICE_ID    0x25
0026 #define AK881X_DEVICE_REVISION  0x26
0027 
0028 struct ak881x {
0029     struct v4l2_subdev subdev;
0030     struct ak881x_pdata *pdata;
0031     unsigned int lines;
0032     char revision;  /* DEVICE_REVISION content */
0033 };
0034 
0035 static int reg_read(struct i2c_client *client, const u8 reg)
0036 {
0037     return i2c_smbus_read_byte_data(client, reg);
0038 }
0039 
0040 static int reg_write(struct i2c_client *client, const u8 reg,
0041              const u8 data)
0042 {
0043     return i2c_smbus_write_byte_data(client, reg, data);
0044 }
0045 
0046 static int reg_set(struct i2c_client *client, const u8 reg,
0047            const u8 data, u8 mask)
0048 {
0049     int ret = reg_read(client, reg);
0050     if (ret < 0)
0051         return ret;
0052     return reg_write(client, reg, (ret & ~mask) | (data & mask));
0053 }
0054 
0055 static struct ak881x *to_ak881x(const struct i2c_client *client)
0056 {
0057     return container_of(i2c_get_clientdata(client), struct ak881x, subdev);
0058 }
0059 
0060 #ifdef CONFIG_VIDEO_ADV_DEBUG
0061 static int ak881x_g_register(struct v4l2_subdev *sd,
0062                  struct v4l2_dbg_register *reg)
0063 {
0064     struct i2c_client *client = v4l2_get_subdevdata(sd);
0065 
0066     if (reg->reg > 0x26)
0067         return -EINVAL;
0068 
0069     reg->size = 1;
0070     reg->val = reg_read(client, reg->reg);
0071 
0072     if (reg->val > 0xffff)
0073         return -EIO;
0074 
0075     return 0;
0076 }
0077 
0078 static int ak881x_s_register(struct v4l2_subdev *sd,
0079                  const struct v4l2_dbg_register *reg)
0080 {
0081     struct i2c_client *client = v4l2_get_subdevdata(sd);
0082 
0083     if (reg->reg > 0x26)
0084         return -EINVAL;
0085 
0086     if (reg_write(client, reg->reg, reg->val) < 0)
0087         return -EIO;
0088 
0089     return 0;
0090 }
0091 #endif
0092 
0093 static int ak881x_fill_fmt(struct v4l2_subdev *sd,
0094         struct v4l2_subdev_state *sd_state,
0095         struct v4l2_subdev_format *format)
0096 {
0097     struct v4l2_mbus_framefmt *mf = &format->format;
0098     struct i2c_client *client = v4l2_get_subdevdata(sd);
0099     struct ak881x *ak881x = to_ak881x(client);
0100 
0101     if (format->pad)
0102         return -EINVAL;
0103 
0104     v4l_bound_align_image(&mf->width, 0, 720, 2,
0105                   &mf->height, 0, ak881x->lines, 1, 0);
0106     mf->field   = V4L2_FIELD_INTERLACED;
0107     mf->code    = MEDIA_BUS_FMT_YUYV8_2X8;
0108     mf->colorspace  = V4L2_COLORSPACE_SMPTE170M;
0109 
0110     return 0;
0111 }
0112 
0113 static int ak881x_enum_mbus_code(struct v4l2_subdev *sd,
0114         struct v4l2_subdev_state *sd_state,
0115         struct v4l2_subdev_mbus_code_enum *code)
0116 {
0117     if (code->pad || code->index)
0118         return -EINVAL;
0119 
0120     code->code = MEDIA_BUS_FMT_YUYV8_2X8;
0121     return 0;
0122 }
0123 
0124 static int ak881x_get_selection(struct v4l2_subdev *sd,
0125                 struct v4l2_subdev_state *sd_state,
0126                 struct v4l2_subdev_selection *sel)
0127 {
0128     struct i2c_client *client = v4l2_get_subdevdata(sd);
0129     struct ak881x *ak881x = to_ak881x(client);
0130 
0131     if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)
0132         return -EINVAL;
0133 
0134     switch (sel->target) {
0135     case V4L2_SEL_TGT_CROP_BOUNDS:
0136         sel->r.left = 0;
0137         sel->r.top = 0;
0138         sel->r.width = 720;
0139         sel->r.height = ak881x->lines;
0140         return 0;
0141     default:
0142         return -EINVAL;
0143     }
0144 }
0145 
0146 static int ak881x_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
0147 {
0148     struct i2c_client *client = v4l2_get_subdevdata(sd);
0149     struct ak881x *ak881x = to_ak881x(client);
0150     u8 vp1;
0151 
0152     if (std == V4L2_STD_NTSC_443) {
0153         vp1 = 3;
0154         ak881x->lines = 480;
0155     } else if (std == V4L2_STD_PAL_M) {
0156         vp1 = 5;
0157         ak881x->lines = 480;
0158     } else if (std == V4L2_STD_PAL_60) {
0159         vp1 = 7;
0160         ak881x->lines = 480;
0161     } else if (std & V4L2_STD_NTSC) {
0162         vp1 = 0;
0163         ak881x->lines = 480;
0164     } else if (std & V4L2_STD_PAL) {
0165         vp1 = 0xf;
0166         ak881x->lines = 576;
0167     } else {
0168         /* No SECAM or PAL_N/Nc supported */
0169         return -EINVAL;
0170     }
0171 
0172     reg_set(client, AK881X_VIDEO_PROCESS1, vp1, 0xf);
0173 
0174     return 0;
0175 }
0176 
0177 static int ak881x_s_stream(struct v4l2_subdev *sd, int enable)
0178 {
0179     struct i2c_client *client = v4l2_get_subdevdata(sd);
0180     struct ak881x *ak881x = to_ak881x(client);
0181 
0182     if (enable) {
0183         u8 dac;
0184         /* For colour-bar testing set bit 6 of AK881X_VIDEO_PROCESS1 */
0185         /* Default: composite output */
0186         if (ak881x->pdata->flags & AK881X_COMPONENT)
0187             dac = 3;
0188         else
0189             dac = 4;
0190         /* Turn on the DAC(s) */
0191         reg_write(client, AK881X_DAC_MODE, dac);
0192         dev_dbg(&client->dev, "chip status 0x%x\n",
0193             reg_read(client, AK881X_STATUS));
0194     } else {
0195         /* ...and clear bit 6 of AK881X_VIDEO_PROCESS1 here */
0196         reg_write(client, AK881X_DAC_MODE, 0);
0197         dev_dbg(&client->dev, "chip status 0x%x\n",
0198             reg_read(client, AK881X_STATUS));
0199     }
0200 
0201     return 0;
0202 }
0203 
0204 static const struct v4l2_subdev_core_ops ak881x_subdev_core_ops = {
0205 #ifdef CONFIG_VIDEO_ADV_DEBUG
0206     .g_register = ak881x_g_register,
0207     .s_register = ak881x_s_register,
0208 #endif
0209 };
0210 
0211 static const struct v4l2_subdev_video_ops ak881x_subdev_video_ops = {
0212     .s_std_output   = ak881x_s_std_output,
0213     .s_stream   = ak881x_s_stream,
0214 };
0215 
0216 static const struct v4l2_subdev_pad_ops ak881x_subdev_pad_ops = {
0217     .enum_mbus_code = ak881x_enum_mbus_code,
0218     .get_selection  = ak881x_get_selection,
0219     .set_fmt    = ak881x_fill_fmt,
0220     .get_fmt    = ak881x_fill_fmt,
0221 };
0222 
0223 static const struct v4l2_subdev_ops ak881x_subdev_ops = {
0224     .core   = &ak881x_subdev_core_ops,
0225     .video  = &ak881x_subdev_video_ops,
0226     .pad    = &ak881x_subdev_pad_ops,
0227 };
0228 
0229 static int ak881x_probe(struct i2c_client *client,
0230             const struct i2c_device_id *did)
0231 {
0232     struct i2c_adapter *adapter = client->adapter;
0233     struct ak881x *ak881x;
0234     u8 ifmode, data;
0235 
0236     if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
0237         dev_warn(&adapter->dev,
0238              "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
0239         return -EIO;
0240     }
0241 
0242     ak881x = devm_kzalloc(&client->dev, sizeof(*ak881x), GFP_KERNEL);
0243     if (!ak881x)
0244         return -ENOMEM;
0245 
0246     v4l2_i2c_subdev_init(&ak881x->subdev, client, &ak881x_subdev_ops);
0247 
0248     data = reg_read(client, AK881X_DEVICE_ID);
0249 
0250     switch (data) {
0251     case 0x13:
0252     case 0x14:
0253         break;
0254     default:
0255         dev_err(&client->dev,
0256             "No ak881x chip detected, register read %x\n", data);
0257         return -ENODEV;
0258     }
0259 
0260     ak881x->revision = reg_read(client, AK881X_DEVICE_REVISION);
0261     ak881x->pdata = client->dev.platform_data;
0262 
0263     if (ak881x->pdata) {
0264         if (ak881x->pdata->flags & AK881X_FIELD)
0265             ifmode = 4;
0266         else
0267             ifmode = 0;
0268 
0269         switch (ak881x->pdata->flags & AK881X_IF_MODE_MASK) {
0270         case AK881X_IF_MODE_BT656:
0271             ifmode |= 1;
0272             break;
0273         case AK881X_IF_MODE_MASTER:
0274             ifmode |= 2;
0275             break;
0276         case AK881X_IF_MODE_SLAVE:
0277         default:
0278             break;
0279         }
0280 
0281         dev_dbg(&client->dev, "IF mode %x\n", ifmode);
0282 
0283         /*
0284          * "Line Blanking No." seems to be the same as the number of
0285          * "black" lines on, e.g., SuperH VOU, whose default value of 20
0286          * "incidentally" matches ak881x' default
0287          */
0288         reg_write(client, AK881X_INTERFACE_MODE, ifmode | (20 << 3));
0289     }
0290 
0291     /* Hardware default: NTSC-M */
0292     ak881x->lines = 480;
0293 
0294     dev_info(&client->dev, "Detected an ak881x chip ID %x, revision %x\n",
0295          data, ak881x->revision);
0296 
0297     return 0;
0298 }
0299 
0300 static int ak881x_remove(struct i2c_client *client)
0301 {
0302     struct ak881x *ak881x = to_ak881x(client);
0303 
0304     v4l2_device_unregister_subdev(&ak881x->subdev);
0305 
0306     return 0;
0307 }
0308 
0309 static const struct i2c_device_id ak881x_id[] = {
0310     { "ak8813", 0 },
0311     { "ak8814", 0 },
0312     { }
0313 };
0314 MODULE_DEVICE_TABLE(i2c, ak881x_id);
0315 
0316 static struct i2c_driver ak881x_i2c_driver = {
0317     .driver = {
0318         .name = "ak881x",
0319     },
0320     .probe      = ak881x_probe,
0321     .remove     = ak881x_remove,
0322     .id_table   = ak881x_id,
0323 };
0324 
0325 module_i2c_driver(ak881x_i2c_driver);
0326 
0327 MODULE_DESCRIPTION("TV-output driver for ak8813/ak8814");
0328 MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
0329 MODULE_LICENSE("GPL v2");