327 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Driver for AK8813 / AK8814 TV-ecoders from Asahi Kasei Microsystems Co., Ltd. (AKM)4 *5 * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>6 */7 8#include <linux/i2c.h>9#include <linux/init.h>10#include <linux/platform_device.h>11#include <linux/slab.h>12#include <linux/videodev2.h>13#include <linux/module.h>14 15#include <media/i2c/ak881x.h>16#include <media/v4l2-common.h>17#include <media/v4l2-device.h>18 19#define AK881X_INTERFACE_MODE 020#define AK881X_VIDEO_PROCESS1 121#define AK881X_VIDEO_PROCESS2 222#define AK881X_VIDEO_PROCESS3 323#define AK881X_DAC_MODE 524#define AK881X_STATUS 0x2425#define AK881X_DEVICE_ID 0x2526#define AK881X_DEVICE_REVISION 0x2627 28struct ak881x {29 struct v4l2_subdev subdev;30 struct ak881x_pdata *pdata;31 unsigned int lines;32 char revision; /* DEVICE_REVISION content */33};34 35static int reg_read(struct i2c_client *client, const u8 reg)36{37 return i2c_smbus_read_byte_data(client, reg);38}39 40static int reg_write(struct i2c_client *client, const u8 reg,41 const u8 data)42{43 return i2c_smbus_write_byte_data(client, reg, data);44}45 46static int reg_set(struct i2c_client *client, const u8 reg,47 const u8 data, u8 mask)48{49 int ret = reg_read(client, reg);50 if (ret < 0)51 return ret;52 return reg_write(client, reg, (ret & ~mask) | (data & mask));53}54 55static struct ak881x *to_ak881x(const struct i2c_client *client)56{57 return container_of(i2c_get_clientdata(client), struct ak881x, subdev);58}59 60#ifdef CONFIG_VIDEO_ADV_DEBUG61static int ak881x_g_register(struct v4l2_subdev *sd,62 struct v4l2_dbg_register *reg)63{64 struct i2c_client *client = v4l2_get_subdevdata(sd);65 66 if (reg->reg > 0x26)67 return -EINVAL;68 69 reg->size = 1;70 reg->val = reg_read(client, reg->reg);71 72 if (reg->val > 0xffff)73 return -EIO;74 75 return 0;76}77 78static int ak881x_s_register(struct v4l2_subdev *sd,79 const struct v4l2_dbg_register *reg)80{81 struct i2c_client *client = v4l2_get_subdevdata(sd);82 83 if (reg->reg > 0x26)84 return -EINVAL;85 86 if (reg_write(client, reg->reg, reg->val) < 0)87 return -EIO;88 89 return 0;90}91#endif92 93static int ak881x_fill_fmt(struct v4l2_subdev *sd,94 struct v4l2_subdev_state *sd_state,95 struct v4l2_subdev_format *format)96{97 struct v4l2_mbus_framefmt *mf = &format->format;98 struct i2c_client *client = v4l2_get_subdevdata(sd);99 struct ak881x *ak881x = to_ak881x(client);100 101 if (format->pad)102 return -EINVAL;103 104 v4l_bound_align_image(&mf->width, 0, 720, 2,105 &mf->height, 0, ak881x->lines, 1, 0);106 mf->field = V4L2_FIELD_INTERLACED;107 mf->code = MEDIA_BUS_FMT_YUYV8_2X8;108 mf->colorspace = V4L2_COLORSPACE_SMPTE170M;109 110 return 0;111}112 113static int ak881x_enum_mbus_code(struct v4l2_subdev *sd,114 struct v4l2_subdev_state *sd_state,115 struct v4l2_subdev_mbus_code_enum *code)116{117 if (code->pad || code->index)118 return -EINVAL;119 120 code->code = MEDIA_BUS_FMT_YUYV8_2X8;121 return 0;122}123 124static int ak881x_get_selection(struct v4l2_subdev *sd,125 struct v4l2_subdev_state *sd_state,126 struct v4l2_subdev_selection *sel)127{128 struct i2c_client *client = v4l2_get_subdevdata(sd);129 struct ak881x *ak881x = to_ak881x(client);130 131 if (sel->which != V4L2_SUBDEV_FORMAT_ACTIVE)132 return -EINVAL;133 134 switch (sel->target) {135 case V4L2_SEL_TGT_CROP_BOUNDS:136 sel->r.left = 0;137 sel->r.top = 0;138 sel->r.width = 720;139 sel->r.height = ak881x->lines;140 return 0;141 default:142 return -EINVAL;143 }144}145 146static int ak881x_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)147{148 struct i2c_client *client = v4l2_get_subdevdata(sd);149 struct ak881x *ak881x = to_ak881x(client);150 u8 vp1;151 152 if (std == V4L2_STD_NTSC_443) {153 vp1 = 3;154 ak881x->lines = 480;155 } else if (std == V4L2_STD_PAL_M) {156 vp1 = 5;157 ak881x->lines = 480;158 } else if (std == V4L2_STD_PAL_60) {159 vp1 = 7;160 ak881x->lines = 480;161 } else if (std & V4L2_STD_NTSC) {162 vp1 = 0;163 ak881x->lines = 480;164 } else if (std & V4L2_STD_PAL) {165 vp1 = 0xf;166 ak881x->lines = 576;167 } else {168 /* No SECAM or PAL_N/Nc supported */169 return -EINVAL;170 }171 172 reg_set(client, AK881X_VIDEO_PROCESS1, vp1, 0xf);173 174 return 0;175}176 177static int ak881x_s_stream(struct v4l2_subdev *sd, int enable)178{179 struct i2c_client *client = v4l2_get_subdevdata(sd);180 struct ak881x *ak881x = to_ak881x(client);181 182 if (enable) {183 u8 dac;184 /* For colour-bar testing set bit 6 of AK881X_VIDEO_PROCESS1 */185 /* Default: composite output */186 if (ak881x->pdata->flags & AK881X_COMPONENT)187 dac = 3;188 else189 dac = 4;190 /* Turn on the DAC(s) */191 reg_write(client, AK881X_DAC_MODE, dac);192 dev_dbg(&client->dev, "chip status 0x%x\n",193 reg_read(client, AK881X_STATUS));194 } else {195 /* ...and clear bit 6 of AK881X_VIDEO_PROCESS1 here */196 reg_write(client, AK881X_DAC_MODE, 0);197 dev_dbg(&client->dev, "chip status 0x%x\n",198 reg_read(client, AK881X_STATUS));199 }200 201 return 0;202}203 204static const struct v4l2_subdev_core_ops ak881x_subdev_core_ops = {205#ifdef CONFIG_VIDEO_ADV_DEBUG206 .g_register = ak881x_g_register,207 .s_register = ak881x_s_register,208#endif209};210 211static const struct v4l2_subdev_video_ops ak881x_subdev_video_ops = {212 .s_std_output = ak881x_s_std_output,213 .s_stream = ak881x_s_stream,214};215 216static const struct v4l2_subdev_pad_ops ak881x_subdev_pad_ops = {217 .enum_mbus_code = ak881x_enum_mbus_code,218 .get_selection = ak881x_get_selection,219 .set_fmt = ak881x_fill_fmt,220 .get_fmt = ak881x_fill_fmt,221};222 223static const struct v4l2_subdev_ops ak881x_subdev_ops = {224 .core = &ak881x_subdev_core_ops,225 .video = &ak881x_subdev_video_ops,226 .pad = &ak881x_subdev_pad_ops,227};228 229static int ak881x_probe(struct i2c_client *client)230{231 struct i2c_adapter *adapter = client->adapter;232 struct ak881x *ak881x;233 u8 ifmode, data;234 235 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {236 dev_warn(&adapter->dev,237 "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");238 return -EIO;239 }240 241 ak881x = devm_kzalloc(&client->dev, sizeof(*ak881x), GFP_KERNEL);242 if (!ak881x)243 return -ENOMEM;244 245 v4l2_i2c_subdev_init(&ak881x->subdev, client, &ak881x_subdev_ops);246 247 data = reg_read(client, AK881X_DEVICE_ID);248 249 switch (data) {250 case 0x13:251 case 0x14:252 break;253 default:254 dev_err(&client->dev,255 "No ak881x chip detected, register read %x\n", data);256 return -ENODEV;257 }258 259 ak881x->revision = reg_read(client, AK881X_DEVICE_REVISION);260 ak881x->pdata = client->dev.platform_data;261 262 if (ak881x->pdata) {263 if (ak881x->pdata->flags & AK881X_FIELD)264 ifmode = 4;265 else266 ifmode = 0;267 268 switch (ak881x->pdata->flags & AK881X_IF_MODE_MASK) {269 case AK881X_IF_MODE_BT656:270 ifmode |= 1;271 break;272 case AK881X_IF_MODE_MASTER:273 ifmode |= 2;274 break;275 case AK881X_IF_MODE_SLAVE:276 default:277 break;278 }279 280 dev_dbg(&client->dev, "IF mode %x\n", ifmode);281 282 /*283 * "Line Blanking No." seems to be the same as the number of284 * "black" lines on, e.g., SuperH VOU, whose default value of 20285 * "incidentally" matches ak881x' default286 */287 reg_write(client, AK881X_INTERFACE_MODE, ifmode | (20 << 3));288 }289 290 /* Hardware default: NTSC-M */291 ak881x->lines = 480;292 293 dev_info(&client->dev, "Detected an ak881x chip ID %x, revision %x\n",294 data, ak881x->revision);295 296 return 0;297}298 299static void ak881x_remove(struct i2c_client *client)300{301 struct ak881x *ak881x = to_ak881x(client);302 303 v4l2_device_unregister_subdev(&ak881x->subdev);304}305 306static const struct i2c_device_id ak881x_id[] = {307 { "ak8813" },308 { "ak8814" },309 { }310};311MODULE_DEVICE_TABLE(i2c, ak881x_id);312 313static struct i2c_driver ak881x_i2c_driver = {314 .driver = {315 .name = "ak881x",316 },317 .probe = ak881x_probe,318 .remove = ak881x_remove,319 .id_table = ak881x_id,320};321 322module_i2c_driver(ak881x_i2c_driver);323 324MODULE_DESCRIPTION("TV-output driver for ak8813/ak8814");325MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");326MODULE_LICENSE("GPL v2");327