1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27#include <linux/module.h>
28#include <linux/types.h>
29#include <linux/slab.h>
30#include <linux/ioctl.h>
31#include <linux/uaccess.h>
32#include <linux/i2c.h>
33#include <linux/videodev2.h>
34#include <media/v4l2-device.h>
35
36MODULE_DESCRIPTION("Analog Devices ADV7170 video encoder driver");
37MODULE_AUTHOR("Maxim Yevtyushkin");
38MODULE_LICENSE("GPL");
39
40
41static int debug;
42module_param(debug, int, 0);
43MODULE_PARM_DESC(debug, "Debug level (0-1)");
44
45
46
47struct adv7170 {
48 struct v4l2_subdev sd;
49 unsigned char reg[128];
50
51 v4l2_std_id norm;
52 int input;
53};
54
55static inline struct adv7170 *to_adv7170(struct v4l2_subdev *sd)
56{
57 return container_of(sd, struct adv7170, sd);
58}
59
60static char *inputs[] = { "pass_through", "play_back" };
61
62static u32 adv7170_codes[] = {
63 MEDIA_BUS_FMT_UYVY8_2X8,
64 MEDIA_BUS_FMT_UYVY8_1X16,
65};
66
67
68
69static inline int adv7170_write(struct v4l2_subdev *sd, u8 reg, u8 value)
70{
71 struct i2c_client *client = v4l2_get_subdevdata(sd);
72 struct adv7170 *encoder = to_adv7170(sd);
73
74 encoder->reg[reg] = value;
75 return i2c_smbus_write_byte_data(client, reg, value);
76}
77
78static inline int adv7170_read(struct v4l2_subdev *sd, u8 reg)
79{
80 struct i2c_client *client = v4l2_get_subdevdata(sd);
81
82 return i2c_smbus_read_byte_data(client, reg);
83}
84
85static int adv7170_write_block(struct v4l2_subdev *sd,
86 const u8 *data, unsigned int len)
87{
88 struct i2c_client *client = v4l2_get_subdevdata(sd);
89 struct adv7170 *encoder = to_adv7170(sd);
90 int ret = -1;
91 u8 reg;
92
93
94
95 if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
96
97 u8 block_data[32];
98 int block_len;
99
100 while (len >= 2) {
101 block_len = 0;
102 block_data[block_len++] = reg = data[0];
103 do {
104 block_data[block_len++] =
105 encoder->reg[reg++] = data[1];
106 len -= 2;
107 data += 2;
108 } while (len >= 2 && data[0] == reg && block_len < 32);
109 ret = i2c_master_send(client, block_data, block_len);
110 if (ret < 0)
111 break;
112 }
113 } else {
114
115 while (len >= 2) {
116 reg = *data++;
117 ret = adv7170_write(sd, reg, *data++);
118 if (ret < 0)
119 break;
120 len -= 2;
121 }
122 }
123 return ret;
124}
125
126
127
128#define TR0MODE 0x4c
129#define TR0RST 0x80
130
131#define TR1CAPT 0x00
132#define TR1PLAY 0x00
133
134static const unsigned char init_NTSC[] = {
135 0x00, 0x10,
136 0x01, 0x20,
137 0x02, 0x0e,
138 0x03, 0x80,
139 0x04, 0x30,
140 0x05, 0x00,
141 0x06, 0x00,
142 0x07, TR0MODE,
143 0x08, TR1CAPT,
144 0x09, 0x16,
145 0x0a, 0x7c,
146 0x0b, 0xf0,
147 0x0c, 0x21,
148 0x0d, 0x00,
149 0x0e, 0x00,
150 0x0f, 0x00,
151 0x10, 0x00,
152 0x11, 0x00,
153 0x12, 0x00,
154 0x13, 0x00,
155 0x14, 0x00,
156 0x15, 0x00,
157 0x16, 0x00,
158 0x17, 0x00,
159 0x18, 0x00,
160 0x19, 0x00,
161};
162
163static const unsigned char init_PAL[] = {
164 0x00, 0x71,
165 0x01, 0x20,
166 0x02, 0x0e,
167 0x03, 0x80,
168 0x04, 0x30,
169 0x05, 0x00,
170 0x06, 0x00,
171 0x07, TR0MODE,
172 0x08, TR1CAPT,
173 0x09, 0xcb,
174 0x0a, 0x8a,
175 0x0b, 0x09,
176 0x0c, 0x2a,
177 0x0d, 0x00,
178 0x0e, 0x00,
179 0x0f, 0x00,
180 0x10, 0x00,
181 0x11, 0x00,
182 0x12, 0x00,
183 0x13, 0x00,
184 0x14, 0x00,
185 0x15, 0x00,
186 0x16, 0x00,
187 0x17, 0x00,
188 0x18, 0x00,
189 0x19, 0x00,
190};
191
192
193static int adv7170_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
194{
195 struct adv7170 *encoder = to_adv7170(sd);
196
197 v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std);
198
199 if (std & V4L2_STD_NTSC) {
200 adv7170_write_block(sd, init_NTSC, sizeof(init_NTSC));
201 if (encoder->input == 0)
202 adv7170_write(sd, 0x02, 0x0e);
203 adv7170_write(sd, 0x07, TR0MODE | TR0RST);
204 adv7170_write(sd, 0x07, TR0MODE);
205 } else if (std & V4L2_STD_PAL) {
206 adv7170_write_block(sd, init_PAL, sizeof(init_PAL));
207 if (encoder->input == 0)
208 adv7170_write(sd, 0x02, 0x0e);
209 adv7170_write(sd, 0x07, TR0MODE | TR0RST);
210 adv7170_write(sd, 0x07, TR0MODE);
211 } else {
212 v4l2_dbg(1, debug, sd, "illegal norm: %llx\n",
213 (unsigned long long)std);
214 return -EINVAL;
215 }
216 v4l2_dbg(1, debug, sd, "switched to %llx\n", (unsigned long long)std);
217 encoder->norm = std;
218 return 0;
219}
220
221static int adv7170_s_routing(struct v4l2_subdev *sd,
222 u32 input, u32 output, u32 config)
223{
224 struct adv7170 *encoder = to_adv7170(sd);
225
226
227
228
229
230 v4l2_dbg(1, debug, sd, "set input from %s\n",
231 input == 0 ? "decoder" : "ZR36060");
232
233 switch (input) {
234 case 0:
235 adv7170_write(sd, 0x01, 0x20);
236 adv7170_write(sd, 0x08, TR1CAPT);
237 adv7170_write(sd, 0x02, 0x0e);
238 adv7170_write(sd, 0x07, TR0MODE | TR0RST);
239 adv7170_write(sd, 0x07, TR0MODE);
240
241 break;
242
243 case 1:
244 adv7170_write(sd, 0x01, 0x00);
245 adv7170_write(sd, 0x08, TR1PLAY);
246 adv7170_write(sd, 0x02, 0x08);
247 adv7170_write(sd, 0x07, TR0MODE | TR0RST);
248 adv7170_write(sd, 0x07, TR0MODE);
249
250 break;
251
252 default:
253 v4l2_dbg(1, debug, sd, "illegal input: %d\n", input);
254 return -EINVAL;
255 }
256 v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[input]);
257 encoder->input = input;
258 return 0;
259}
260
261static int adv7170_enum_mbus_code(struct v4l2_subdev *sd,
262 struct v4l2_subdev_pad_config *cfg,
263 struct v4l2_subdev_mbus_code_enum *code)
264{
265 if (code->pad || code->index >= ARRAY_SIZE(adv7170_codes))
266 return -EINVAL;
267
268 code->code = adv7170_codes[code->index];
269 return 0;
270}
271
272static int adv7170_get_fmt(struct v4l2_subdev *sd,
273 struct v4l2_subdev_pad_config *cfg,
274 struct v4l2_subdev_format *format)
275{
276 struct v4l2_mbus_framefmt *mf = &format->format;
277 u8 val = adv7170_read(sd, 0x7);
278
279 if (format->pad)
280 return -EINVAL;
281
282 if ((val & 0x40) == (1 << 6))
283 mf->code = MEDIA_BUS_FMT_UYVY8_1X16;
284 else
285 mf->code = MEDIA_BUS_FMT_UYVY8_2X8;
286
287 mf->colorspace = V4L2_COLORSPACE_SMPTE170M;
288 mf->width = 0;
289 mf->height = 0;
290 mf->field = V4L2_FIELD_ANY;
291
292 return 0;
293}
294
295static int adv7170_set_fmt(struct v4l2_subdev *sd,
296 struct v4l2_subdev_pad_config *cfg,
297 struct v4l2_subdev_format *format)
298{
299 struct v4l2_mbus_framefmt *mf = &format->format;
300 u8 val = adv7170_read(sd, 0x7);
301
302 if (format->pad)
303 return -EINVAL;
304
305 switch (mf->code) {
306 case MEDIA_BUS_FMT_UYVY8_2X8:
307 val &= ~0x40;
308 break;
309
310 case MEDIA_BUS_FMT_UYVY8_1X16:
311 val |= 0x40;
312 break;
313
314 default:
315 v4l2_dbg(1, debug, sd,
316 "illegal v4l2_mbus_framefmt code: %d\n", mf->code);
317 return -EINVAL;
318 }
319
320 if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE)
321 return adv7170_write(sd, 0x7, val);
322
323 return 0;
324}
325
326
327
328static const struct v4l2_subdev_video_ops adv7170_video_ops = {
329 .s_std_output = adv7170_s_std_output,
330 .s_routing = adv7170_s_routing,
331};
332
333static const struct v4l2_subdev_pad_ops adv7170_pad_ops = {
334 .enum_mbus_code = adv7170_enum_mbus_code,
335 .get_fmt = adv7170_get_fmt,
336 .set_fmt = adv7170_set_fmt,
337};
338
339static const struct v4l2_subdev_ops adv7170_ops = {
340 .video = &adv7170_video_ops,
341 .pad = &adv7170_pad_ops,
342};
343
344
345
346static int adv7170_probe(struct i2c_client *client,
347 const struct i2c_device_id *id)
348{
349 struct adv7170 *encoder;
350 struct v4l2_subdev *sd;
351 int i;
352
353
354 if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
355 return -ENODEV;
356
357 v4l_info(client, "chip found @ 0x%x (%s)\n",
358 client->addr << 1, client->adapter->name);
359
360 encoder = devm_kzalloc(&client->dev, sizeof(*encoder), GFP_KERNEL);
361 if (encoder == NULL)
362 return -ENOMEM;
363 sd = &encoder->sd;
364 v4l2_i2c_subdev_init(sd, client, &adv7170_ops);
365 encoder->norm = V4L2_STD_NTSC;
366 encoder->input = 0;
367
368 i = adv7170_write_block(sd, init_NTSC, sizeof(init_NTSC));
369 if (i >= 0) {
370 i = adv7170_write(sd, 0x07, TR0MODE | TR0RST);
371 i = adv7170_write(sd, 0x07, TR0MODE);
372 i = adv7170_read(sd, 0x12);
373 v4l2_dbg(1, debug, sd, "revision %d\n", i & 1);
374 }
375 if (i < 0)
376 v4l2_dbg(1, debug, sd, "init error 0x%x\n", i);
377 return 0;
378}
379
380static int adv7170_remove(struct i2c_client *client)
381{
382 struct v4l2_subdev *sd = i2c_get_clientdata(client);
383
384 v4l2_device_unregister_subdev(sd);
385 return 0;
386}
387
388
389
390static const struct i2c_device_id adv7170_id[] = {
391 { "adv7170", 0 },
392 { "adv7171", 0 },
393 { }
394};
395MODULE_DEVICE_TABLE(i2c, adv7170_id);
396
397static struct i2c_driver adv7170_driver = {
398 .driver = {
399 .name = "adv7170",
400 },
401 .probe = adv7170_probe,
402 .remove = adv7170_remove,
403 .id_table = adv7170_id,
404};
405
406module_i2c_driver(adv7170_driver);
407