Commit aec11e5d495a6c9b10ded81dde5b0e42b0875541

Authored by Guennadi Liakhovetski
Committed by Mauro Carvalho Chehab
1 parent a81fb9b223

V4L/DVB: V4L: v4l2-subdev driver for AK8813 and AK8814 TV-encoders from AKM

AK8814 only differs from AK8813 by included Macrovision Copy Protection
function. This patch adds a driver for AK8813 and AK8814 I2C PAL/NTSC TV
encoders.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>

Showing 5 changed files with 405 additions and 0 deletions Side-by-side Diff

drivers/media/video/Kconfig
... ... @@ -480,6 +480,12 @@
480 480 To compile this driver as a module, choose M here: the
481 481 module will be called adv7343.
482 482  
  483 +config VIDEO_AK881X
  484 + tristate "AK8813/AK8814 video encoders"
  485 + depends on I2C
  486 + help
  487 + Video output driver for AKM AK8813 and AK8814 TV encoders
  488 +
483 489 comment "Video improvement chips"
484 490  
485 491 config VIDEO_UPD64031A
drivers/media/video/Makefile
... ... @@ -151,6 +151,8 @@
151 151 obj-$(CONFIG_VIDEO_VIVI) += vivi.o
152 152 obj-$(CONFIG_VIDEO_CX23885) += cx23885/
153 153  
  154 +obj-$(CONFIG_VIDEO_AK881X) += ak881x.o
  155 +
154 156 obj-$(CONFIG_VIDEO_OMAP2) += omap2cam.o
155 157 obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o
156 158 obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o
drivers/media/video/ak881x.c
  1 +/*
  2 + * Driver for AK8813 / AK8814 TV-ecoders from Asahi Kasei Microsystems Co., Ltd. (AKM)
  3 + *
  4 + * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
  5 + *
  6 + * This program is free software; you can redistribute it and/or modify
  7 + * it under the terms of the GNU General Public License version 2 as
  8 + * published by the Free Software Foundation.
  9 + */
  10 +
  11 +#include <linux/i2c.h>
  12 +#include <linux/init.h>
  13 +#include <linux/platform_device.h>
  14 +#include <linux/videodev2.h>
  15 +
  16 +#include <media/ak881x.h>
  17 +#include <media/v4l2-chip-ident.h>
  18 +#include <media/v4l2-common.h>
  19 +#include <media/v4l2-device.h>
  20 +
  21 +#define AK881X_INTERFACE_MODE 0
  22 +#define AK881X_VIDEO_PROCESS1 1
  23 +#define AK881X_VIDEO_PROCESS2 2
  24 +#define AK881X_VIDEO_PROCESS3 3
  25 +#define AK881X_DAC_MODE 5
  26 +#define AK881X_STATUS 0x24
  27 +#define AK881X_DEVICE_ID 0x25
  28 +#define AK881X_DEVICE_REVISION 0x26
  29 +
  30 +struct ak881x {
  31 + struct v4l2_subdev subdev;
  32 + struct ak881x_pdata *pdata;
  33 + unsigned int lines;
  34 + int id; /* DEVICE_ID code V4L2_IDENT_AK881X code from v4l2-chip-ident.h */
  35 + char revision; /* DEVICE_REVISION content */
  36 +};
  37 +
  38 +static int reg_read(struct i2c_client *client, const u8 reg)
  39 +{
  40 + return i2c_smbus_read_byte_data(client, reg);
  41 +}
  42 +
  43 +static int reg_write(struct i2c_client *client, const u8 reg,
  44 + const u8 data)
  45 +{
  46 + return i2c_smbus_write_byte_data(client, reg, data);
  47 +}
  48 +
  49 +static int reg_set(struct i2c_client *client, const u8 reg,
  50 + const u8 data, u8 mask)
  51 +{
  52 + int ret = reg_read(client, reg);
  53 + if (ret < 0)
  54 + return ret;
  55 + return reg_write(client, reg, (ret & ~mask) | (data & mask));
  56 +}
  57 +
  58 +static struct ak881x *to_ak881x(const struct i2c_client *client)
  59 +{
  60 + return container_of(i2c_get_clientdata(client), struct ak881x, subdev);
  61 +}
  62 +
  63 +static int ak881x_g_chip_ident(struct v4l2_subdev *sd,
  64 + struct v4l2_dbg_chip_ident *id)
  65 +{
  66 + struct i2c_client *client = v4l2_get_subdevdata(sd);
  67 + struct ak881x *ak881x = to_ak881x(client);
  68 +
  69 + if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR)
  70 + return -EINVAL;
  71 +
  72 + if (id->match.addr != client->addr)
  73 + return -ENODEV;
  74 +
  75 + id->ident = ak881x->id;
  76 + id->revision = ak881x->revision;
  77 +
  78 + return 0;
  79 +}
  80 +
  81 +#ifdef CONFIG_VIDEO_ADV_DEBUG
  82 +static int ak881x_g_register(struct v4l2_subdev *sd,
  83 + struct v4l2_dbg_register *reg)
  84 +{
  85 + struct i2c_client *client = v4l2_get_subdevdata(sd);
  86 +
  87 + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x26)
  88 + return -EINVAL;
  89 +
  90 + if (reg->match.addr != client->addr)
  91 + return -ENODEV;
  92 +
  93 + reg->val = reg_read(client, reg->reg);
  94 +
  95 + if (reg->val > 0xffff)
  96 + return -EIO;
  97 +
  98 + return 0;
  99 +}
  100 +
  101 +static int ak881x_s_register(struct v4l2_subdev *sd,
  102 + struct v4l2_dbg_register *reg)
  103 +{
  104 + struct i2c_client *client = v4l2_get_subdevdata(sd);
  105 +
  106 + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x26)
  107 + return -EINVAL;
  108 +
  109 + if (reg->match.addr != client->addr)
  110 + return -ENODEV;
  111 +
  112 + if (reg_write(client, reg->reg, reg->val) < 0)
  113 + return -EIO;
  114 +
  115 + return 0;
  116 +}
  117 +#endif
  118 +
  119 +static int ak881x_try_g_mbus_fmt(struct v4l2_subdev *sd,
  120 + struct v4l2_mbus_framefmt *mf)
  121 +{
  122 + struct i2c_client *client = v4l2_get_subdevdata(sd);
  123 + struct ak881x *ak881x = to_ak881x(client);
  124 +
  125 + v4l_bound_align_image(&mf->width, 0, 720, 2,
  126 + &mf->height, 0, ak881x->lines, 1, 0);
  127 + mf->field = V4L2_FIELD_INTERLACED;
  128 + mf->code = V4L2_MBUS_FMT_YUYV8_2X8_LE;
  129 + mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
  130 +
  131 + return 0;
  132 +}
  133 +
  134 +static int ak881x_s_mbus_fmt(struct v4l2_subdev *sd,
  135 + struct v4l2_mbus_framefmt *mf)
  136 +{
  137 + if (mf->field != V4L2_FIELD_INTERLACED ||
  138 + mf->code != V4L2_MBUS_FMT_YUYV8_2X8_LE)
  139 + return -EINVAL;
  140 +
  141 + return ak881x_try_g_mbus_fmt(sd, mf);
  142 +}
  143 +
  144 +static int ak881x_enum_mbus_fmt(struct v4l2_subdev *sd, int index,
  145 + enum v4l2_mbus_pixelcode *code)
  146 +{
  147 + if (index)
  148 + return -EINVAL;
  149 +
  150 + *code = V4L2_MBUS_FMT_YUYV8_2X8_LE;
  151 + return 0;
  152 +}
  153 +
  154 +static int ak881x_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a)
  155 +{
  156 + struct i2c_client *client = v4l2_get_subdevdata(sd);
  157 + struct ak881x *ak881x = to_ak881x(client);
  158 +
  159 + a->bounds.left = 0;
  160 + a->bounds.top = 0;
  161 + a->bounds.width = 720;
  162 + a->bounds.height = ak881x->lines;
  163 + a->defrect = a->bounds;
  164 + a->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
  165 + a->pixelaspect.numerator = 1;
  166 + a->pixelaspect.denominator = 1;
  167 +
  168 + return 0;
  169 +}
  170 +
  171 +static int ak881x_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
  172 +{
  173 + struct i2c_client *client = v4l2_get_subdevdata(sd);
  174 + struct ak881x *ak881x = to_ak881x(client);
  175 + u8 vp1;
  176 +
  177 + if (std == V4L2_STD_NTSC_443) {
  178 + vp1 = 3;
  179 + ak881x->lines = 480;
  180 + } else if (std == V4L2_STD_PAL_M) {
  181 + vp1 = 5;
  182 + ak881x->lines = 480;
  183 + } else if (std == V4L2_STD_PAL_60) {
  184 + vp1 = 7;
  185 + ak881x->lines = 480;
  186 + } else if (std && !(std & ~V4L2_STD_PAL)) {
  187 + vp1 = 0xf;
  188 + ak881x->lines = 576;
  189 + } else if (std && !(std & ~V4L2_STD_NTSC)) {
  190 + vp1 = 0;
  191 + ak881x->lines = 480;
  192 + } else {
  193 + /* No SECAM or PAL_N/Nc supported */
  194 + return -EINVAL;
  195 + }
  196 +
  197 + reg_set(client, AK881X_VIDEO_PROCESS1, vp1, 0xf);
  198 +
  199 + return 0;
  200 +}
  201 +
  202 +static int ak881x_s_stream(struct v4l2_subdev *sd, int enable)
  203 +{
  204 + struct i2c_client *client = v4l2_get_subdevdata(sd);
  205 + struct ak881x *ak881x = to_ak881x(client);
  206 +
  207 + if (enable) {
  208 + u8 dac;
  209 + /* For colour-bar testing set bit 6 of AK881X_VIDEO_PROCESS1 */
  210 + /* Default: composite output */
  211 + if (ak881x->pdata->flags & AK881X_COMPONENT)
  212 + dac = 3;
  213 + else
  214 + dac = 4;
  215 + /* Turn on the DAC(s) */
  216 + reg_write(client, AK881X_DAC_MODE, dac);
  217 + dev_dbg(&client->dev, "chip status 0x%x\n",
  218 + reg_read(client, AK881X_STATUS));
  219 + } else {
  220 + /* ...and clear bit 6 of AK881X_VIDEO_PROCESS1 here */
  221 + reg_write(client, AK881X_DAC_MODE, 0);
  222 + dev_dbg(&client->dev, "chip status 0x%x\n",
  223 + reg_read(client, AK881X_STATUS));
  224 + }
  225 +
  226 + return 0;
  227 +}
  228 +
  229 +static struct v4l2_subdev_core_ops ak881x_subdev_core_ops = {
  230 + .g_chip_ident = ak881x_g_chip_ident,
  231 +#ifdef CONFIG_VIDEO_ADV_DEBUG
  232 + .g_register = ak881x_g_register,
  233 + .s_register = ak881x_s_register,
  234 +#endif
  235 +};
  236 +
  237 +static struct v4l2_subdev_video_ops ak881x_subdev_video_ops = {
  238 + .s_mbus_fmt = ak881x_s_mbus_fmt,
  239 + .g_mbus_fmt = ak881x_try_g_mbus_fmt,
  240 + .try_mbus_fmt = ak881x_try_g_mbus_fmt,
  241 + .cropcap = ak881x_cropcap,
  242 + .enum_mbus_fmt = ak881x_enum_mbus_fmt,
  243 + .s_std_output = ak881x_s_std_output,
  244 + .s_stream = ak881x_s_stream,
  245 +};
  246 +
  247 +static struct v4l2_subdev_ops ak881x_subdev_ops = {
  248 + .core = &ak881x_subdev_core_ops,
  249 + .video = &ak881x_subdev_video_ops,
  250 +};
  251 +
  252 +static int ak881x_probe(struct i2c_client *client,
  253 + const struct i2c_device_id *did)
  254 +{
  255 + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
  256 + struct ak881x *ak881x;
  257 + u8 ifmode, data;
  258 +
  259 + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
  260 + dev_warn(&adapter->dev,
  261 + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_WORD\n");
  262 + return -EIO;
  263 + }
  264 +
  265 + ak881x = kzalloc(sizeof(struct ak881x), GFP_KERNEL);
  266 + if (!ak881x)
  267 + return -ENOMEM;
  268 +
  269 + v4l2_i2c_subdev_init(&ak881x->subdev, client, &ak881x_subdev_ops);
  270 +
  271 + data = reg_read(client, AK881X_DEVICE_ID);
  272 +
  273 + switch (data) {
  274 + case 0x13:
  275 + ak881x->id = V4L2_IDENT_AK8813;
  276 + break;
  277 + case 0x14:
  278 + ak881x->id = V4L2_IDENT_AK8814;
  279 + break;
  280 + default:
  281 + dev_err(&client->dev,
  282 + "No ak881x chip detected, register read %x\n", data);
  283 + kfree(ak881x);
  284 + return -ENODEV;
  285 + }
  286 +
  287 + ak881x->revision = reg_read(client, AK881X_DEVICE_REVISION);
  288 + ak881x->pdata = client->dev.platform_data;
  289 +
  290 + if (ak881x->pdata) {
  291 + if (ak881x->pdata->flags & AK881X_FIELD)
  292 + ifmode = 4;
  293 + else
  294 + ifmode = 0;
  295 +
  296 + switch (ak881x->pdata->flags & AK881X_IF_MODE_MASK) {
  297 + case AK881X_IF_MODE_BT656:
  298 + ifmode |= 1;
  299 + break;
  300 + case AK881X_IF_MODE_MASTER:
  301 + ifmode |= 2;
  302 + break;
  303 + case AK881X_IF_MODE_SLAVE:
  304 + default:
  305 + break;
  306 + }
  307 +
  308 + dev_dbg(&client->dev, "IF mode %x\n", ifmode);
  309 +
  310 + /*
  311 + * "Line Blanking No." seems to be the same as the number of
  312 + * "black" lines on, e.g., SuperH VOU, whose default value of 20
  313 + * "incidentally" matches ak881x' default
  314 + */
  315 + reg_write(client, AK881X_INTERFACE_MODE, ifmode | (20 << 3));
  316 + }
  317 +
  318 + /* Hardware default: NTSC-M */
  319 + ak881x->lines = 480;
  320 +
  321 + dev_info(&client->dev, "Detected an ak881x chip ID %x, revision %x\n",
  322 + data, ak881x->revision);
  323 +
  324 + return 0;
  325 +}
  326 +
  327 +static int ak881x_remove(struct i2c_client *client)
  328 +{
  329 + struct ak881x *ak881x = to_ak881x(client);
  330 +
  331 + v4l2_device_unregister_subdev(&ak881x->subdev);
  332 + kfree(ak881x);
  333 +
  334 + return 0;
  335 +}
  336 +
  337 +static const struct i2c_device_id ak881x_id[] = {
  338 + { "ak8813", 0 },
  339 + { "ak8814", 0 },
  340 + { }
  341 +};
  342 +MODULE_DEVICE_TABLE(i2c, ak881x_id);
  343 +
  344 +static struct i2c_driver ak881x_i2c_driver = {
  345 + .driver = {
  346 + .name = "ak881x",
  347 + },
  348 + .probe = ak881x_probe,
  349 + .remove = ak881x_remove,
  350 + .id_table = ak881x_id,
  351 +};
  352 +
  353 +static int __init ak881x_module_init(void)
  354 +{
  355 + return i2c_add_driver(&ak881x_i2c_driver);
  356 +}
  357 +
  358 +static void __exit ak881x_module_exit(void)
  359 +{
  360 + i2c_del_driver(&ak881x_i2c_driver);
  361 +}
  362 +
  363 +module_init(ak881x_module_init);
  364 +module_exit(ak881x_module_exit);
  365 +
  366 +MODULE_DESCRIPTION("TV-output driver for ak8813/ak8814");
  367 +MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
  368 +MODULE_LICENSE("GPL v2");
include/media/ak881x.h
  1 +/*
  2 + * Header for AK8813 / AK8814 TV-ecoders from Asahi Kasei Microsystems Co., Ltd. (AKM)
  3 + *
  4 + * Copyright (C) 2010, Guennadi Liakhovetski <g.liakhovetski@gmx.de>
  5 + *
  6 + * This program is free software; you can redistribute it and/or modify
  7 + * it under the terms of the GNU General Public License version 2 as
  8 + * published by the Free Software Foundation.
  9 + */
  10 +
  11 +#ifndef AK881X_H
  12 +#define AK881X_H
  13 +
  14 +#define AK881X_IF_MODE_MASK (3 << 0)
  15 +#define AK881X_IF_MODE_BT656 (0 << 0)
  16 +#define AK881X_IF_MODE_MASTER (1 << 0)
  17 +#define AK881X_IF_MODE_SLAVE (2 << 0)
  18 +#define AK881X_FIELD (1 << 2)
  19 +#define AK881X_COMPONENT (1 << 3)
  20 +
  21 +struct ak881x_pdata {
  22 + unsigned long flags;
  23 +};
  24 +
  25 +#endif
include/media/v4l2-chip-ident.h
... ... @@ -178,6 +178,10 @@
178 178 /* module cafe_ccic, just ident 8801 */
179 179 V4L2_IDENT_CAFE = 8801,
180 180  
  181 + /* AKM AK8813/AK8814 */
  182 + V4L2_IDENT_AK8813 = 8813,
  183 + V4L2_IDENT_AK8814 = 8814,
  184 +
181 185 /* module cx23885 and cx25840 */
182 186 V4L2_IDENT_CX23885 = 8850,
183 187 V4L2_IDENT_CX23885_AV = 8851, /* Integrated A/V decoder */