aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media
diff options
context:
space:
mode:
authorGuennadi Liakhovetski <lyakh@axis700.grange>2010-03-29 03:45:22 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2010-05-19 11:58:02 -0400
commitaec11e5d495a6c9b10ded81dde5b0e42b0875541 (patch)
treec57f57d1dc588dc2e0ac6c5c60f4e0435a1e1dec /drivers/media
parenta81fb9b223508f5773d8d7e226b1574d1b068642 (diff)
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>
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/video/Kconfig6
-rw-r--r--drivers/media/video/Makefile2
-rw-r--r--drivers/media/video/ak881x.c368
3 files changed, 376 insertions, 0 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index e2fad460a759..747f106e7b53 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -480,6 +480,12 @@ config VIDEO_ADV7343
480 To compile this driver as a module, choose M here: the 480 To compile this driver as a module, choose M here: the
481 module will be called adv7343. 481 module will be called adv7343.
482 482
483config 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
483comment "Video improvement chips" 489comment "Video improvement chips"
484 490
485config VIDEO_UPD64031A 491config VIDEO_UPD64031A
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index ed52b6be4eca..4555f5f31ff0 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -151,6 +151,8 @@ obj-$(CONFIG_VIDEO_CX18) += cx18/
151obj-$(CONFIG_VIDEO_VIVI) += vivi.o 151obj-$(CONFIG_VIDEO_VIVI) += vivi.o
152obj-$(CONFIG_VIDEO_CX23885) += cx23885/ 152obj-$(CONFIG_VIDEO_CX23885) += cx23885/
153 153
154obj-$(CONFIG_VIDEO_AK881X) += ak881x.o
155
154obj-$(CONFIG_VIDEO_OMAP2) += omap2cam.o 156obj-$(CONFIG_VIDEO_OMAP2) += omap2cam.o
155obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o 157obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o
156obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o 158obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o
diff --git a/drivers/media/video/ak881x.c b/drivers/media/video/ak881x.c
new file mode 100644
index 000000000000..35390d4717b9
--- /dev/null
+++ b/drivers/media/video/ak881x.c
@@ -0,0 +1,368 @@
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
30struct 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
38static int reg_read(struct i2c_client *client, const u8 reg)
39{
40 return i2c_smbus_read_byte_data(client, reg);
41}
42
43static 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
49static 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
58static struct ak881x *to_ak881x(const struct i2c_client *client)
59{
60 return container_of(i2c_get_clientdata(client), struct ak881x, subdev);
61}
62
63static 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
82static 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
101static 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
119static 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
134static 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
144static 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
154static 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
171static 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
202static 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
229static 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
237static 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
247static struct v4l2_subdev_ops ak881x_subdev_ops = {
248 .core = &ak881x_subdev_core_ops,
249 .video = &ak881x_subdev_video_ops,
250};
251
252static 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
327static 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
337static const struct i2c_device_id ak881x_id[] = {
338 { "ak8813", 0 },
339 { "ak8814", 0 },
340 { }
341};
342MODULE_DEVICE_TABLE(i2c, ak881x_id);
343
344static 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
353static int __init ak881x_module_init(void)
354{
355 return i2c_add_driver(&ak881x_i2c_driver);
356}
357
358static void __exit ak881x_module_exit(void)
359{
360 i2c_del_driver(&ak881x_i2c_driver);
361}
362
363module_init(ak881x_module_init);
364module_exit(ak881x_module_exit);
365
366MODULE_DESCRIPTION("TV-output driver for ak8813/ak8814");
367MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
368MODULE_LICENSE("GPL v2");