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
28
29
30
31#include <linux/module.h>
32#include <linux/types.h>
33#include <linux/ioctl.h>
34#include <asm/uaccess.h>
35#include <linux/i2c.h>
36#include <linux/i2c-id.h>
37#include <linux/videodev2.h>
38#include <media/v4l2-device.h>
39#include <media/v4l2-chip-ident.h>
40#include <media/v4l2-i2c-drv.h>
41
42MODULE_DESCRIPTION("Analog Devices ADV7170 video encoder driver");
43MODULE_AUTHOR("Maxim Yevtyushkin");
44MODULE_LICENSE("GPL");
45
46
47static int debug;
48module_param(debug, int, 0);
49MODULE_PARM_DESC(debug, "Debug level (0-1)");
50
51
52
53struct adv7170 {
54 struct v4l2_subdev sd;
55 unsigned char reg[128];
56
57 v4l2_std_id norm;
58 int input;
59};
60
61static inline struct adv7170 *to_adv7170(struct v4l2_subdev *sd)
62{
63 return container_of(sd, struct adv7170, sd);
64}
65
66static char *inputs[] = { "pass_through", "play_back" };
67
68
69
70static inline int adv7170_write(struct v4l2_subdev *sd, u8 reg, u8 value)
71{
72 struct i2c_client *client = v4l2_get_subdevdata(sd);
73 struct adv7170 *encoder = to_adv7170(sd);
74
75 encoder->reg[reg] = value;
76 return i2c_smbus_write_byte_data(client, reg, value);
77}
78
79static inline int adv7170_read(struct v4l2_subdev *sd, u8 reg)
80{
81 struct i2c_client *client = v4l2_get_subdevdata(sd);
82
83 return i2c_smbus_read_byte_data(client, reg);
84}
85
86static int adv7170_write_block(struct v4l2_subdev *sd,
87 const u8 *data, unsigned int len)
88{
89 struct i2c_client *client = v4l2_get_subdevdata(sd);
90 struct adv7170 *encoder = to_adv7170(sd);
91 int ret = -1;
92 u8 reg;
93
94
95
96 if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
97
98 u8 block_data[32];
99 int block_len;
100
101 while (len >= 2) {
102 block_len = 0;
103 block_data[block_len++] = reg = data[0];
104 do {
105 block_data[block_len++] =
106 encoder->reg[reg++] = data[1];
107 len -= 2;
108 data += 2;
109 } while (len >= 2 && data[0] == reg && block_len < 32);
110 ret = i2c_master_send(client, block_data, block_len);
111 if (ret < 0)
112 break;
113 }
114 } else {
115
116 while (len >= 2) {
117 reg = *data++;
118 ret = adv7170_write(sd, reg, *data++);
119 if (ret < 0)
120 break;
121 len -= 2;
122 }
123 }
124 return ret;
125}
126
127
128
129#define TR0MODE 0x4c
130#define TR0RST 0x80
131
132#define TR1CAPT 0x00
133#define TR1PLAY 0x00
134
135static const unsigned char init_NTSC[] = {
136 0x00, 0x10,
137 0x01, 0x20,
138 0x02, 0x0e,
139 0x03, 0x80,
140 0x04, 0x30,
141 0x05, 0x00,
142 0x06, 0x00,
143 0x07, TR0MODE,
144 0x08, TR1CAPT,
145 0x09, 0x16,
146 0x0a, 0x7c,
147 0x0b, 0xf0,
148 0x0c, 0x21,
149 0x0d, 0x00,
150 0x0e, 0x00,
151 0x0f, 0x00,
152 0x10, 0x00,
153 0x11, 0x00,
154 0x12, 0x00,
155 0x13, 0x00,
156 0x14, 0x00,
157 0x15, 0x00,
158 0x16, 0x00,
159 0x17, 0x00,
160 0x18, 0x00,
161 0x19, 0x00,
162};
163
164static const unsigned char init_PAL[] = {
165 0x00, 0x71,
166 0x01, 0x20,
167 0x02, 0x0e,
168 0x03, 0x80,
169 0x04, 0x30,
170 0x05, 0x00,
171 0x06, 0x00,
172 0x07, TR0MODE,
173 0x08, TR1CAPT,
174 0x09, 0xcb,
175 0x0a, 0x8a,
176 0x0b, 0x09,
177 0x0c, 0x2a,
178 0x0d, 0x00,
179 0x0e, 0x00,
180 0x0f, 0x00,
181 0x10, 0x00,
182 0x11, 0x00,
183 0x12, 0x00,
184 0x13, 0x00,
185 0x14, 0x00,
186 0x15, 0x00,
187 0x16, 0x00,
188 0x17, 0x00,
189 0x18, 0x00,
190 0x19, 0x00,
191};
192
193
194static int adv7170_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
195{
196 struct adv7170 *encoder = to_adv7170(sd);
197
198 v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std);
199
200 if (std & V4L2_STD_NTSC) {
201 adv7170_write_block(sd, init_NTSC, sizeof(init_NTSC));
202 if (encoder->input == 0)
203 adv7170_write(sd, 0x02, 0x0e);
204 adv7170_write(sd, 0x07, TR0MODE | TR0RST);
205 adv7170_write(sd, 0x07, TR0MODE);
206 } else if (std & V4L2_STD_PAL) {
207 adv7170_write_block(sd, init_PAL, sizeof(init_PAL));
208 if (encoder->input == 0)
209 adv7170_write(sd, 0x02, 0x0e);
210 adv7170_write(sd, 0x07, TR0MODE | TR0RST);
211 adv7170_write(sd, 0x07, TR0MODE);
212 } else {
213 v4l2_dbg(1, debug, sd, "illegal norm: %llx\n",
214 (unsigned long long)std);
215 return -EINVAL;
216 }
217 v4l2_dbg(1, debug, sd, "switched to %llx\n", (unsigned long long)std);
218 encoder->norm = std;
219 return 0;
220}
221
222static int adv7170_s_routing(struct v4l2_subdev *sd,
223 u32 input, u32 output, u32 config)
224{
225 struct adv7170 *encoder = to_adv7170(sd);
226
227
228
229
230
231 v4l2_dbg(1, debug, sd, "set input from %s\n",
232 input == 0 ? "decoder" : "ZR36060");
233
234 switch (input) {
235 case 0:
236 adv7170_write(sd, 0x01, 0x20);
237 adv7170_write(sd, 0x08, TR1CAPT);
238 adv7170_write(sd, 0x02, 0x0e);
239 adv7170_write(sd, 0x07, TR0MODE | TR0RST);
240 adv7170_write(sd, 0x07, TR0MODE);
241
242 break;
243
244 case 1:
245 adv7170_write(sd, 0x01, 0x00);
246 adv7170_write(sd, 0x08, TR1PLAY);
247 adv7170_write(sd, 0x02, 0x08);
248 adv7170_write(sd, 0x07, TR0MODE | TR0RST);
249 adv7170_write(sd, 0x07, TR0MODE);
250
251 break;
252
253 default:
254 v4l2_dbg(1, debug, sd, "illegal input: %d\n", input);
255 return -EINVAL;
256 }
257 v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[input]);
258 encoder->input = input;
259 return 0;
260}
261
262static int adv7170_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
263{
264 struct i2c_client *client = v4l2_get_subdevdata(sd);
265
266 return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7170, 0);
267}
268
269
270
271static const struct v4l2_subdev_core_ops adv7170_core_ops = {
272 .g_chip_ident = adv7170_g_chip_ident,
273};
274
275static const struct v4l2_subdev_video_ops adv7170_video_ops = {
276 .s_std_output = adv7170_s_std_output,
277 .s_routing = adv7170_s_routing,
278};
279
280static const struct v4l2_subdev_ops adv7170_ops = {
281 .core = &adv7170_core_ops,
282 .video = &adv7170_video_ops,
283};
284
285
286
287static int adv7170_probe(struct i2c_client *client,
288 const struct i2c_device_id *id)
289{
290 struct adv7170 *encoder;
291 struct v4l2_subdev *sd;
292 int i;
293
294
295 if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
296 return -ENODEV;
297
298 v4l_info(client, "chip found @ 0x%x (%s)\n",
299 client->addr << 1, client->adapter->name);
300
301 encoder = kzalloc(sizeof(struct adv7170), GFP_KERNEL);
302 if (encoder == NULL)
303 return -ENOMEM;
304 sd = &encoder->sd;
305 v4l2_i2c_subdev_init(sd, client, &adv7170_ops);
306 encoder->norm = V4L2_STD_NTSC;
307 encoder->input = 0;
308
309 i = adv7170_write_block(sd, init_NTSC, sizeof(init_NTSC));
310 if (i >= 0) {
311 i = adv7170_write(sd, 0x07, TR0MODE | TR0RST);
312 i = adv7170_write(sd, 0x07, TR0MODE);
313 i = adv7170_read(sd, 0x12);
314 v4l2_dbg(1, debug, sd, "revision %d\n", i & 1);
315 }
316 if (i < 0)
317 v4l2_dbg(1, debug, sd, "init error 0x%x\n", i);
318 return 0;
319}
320
321static int adv7170_remove(struct i2c_client *client)
322{
323 struct v4l2_subdev *sd = i2c_get_clientdata(client);
324
325 v4l2_device_unregister_subdev(sd);
326 kfree(to_adv7170(sd));
327 return 0;
328}
329
330
331
332static const struct i2c_device_id adv7170_id[] = {
333 { "adv7170", 0 },
334 { "adv7171", 0 },
335 { }
336};
337MODULE_DEVICE_TABLE(i2c, adv7170_id);
338
339static struct v4l2_i2c_driver_data v4l2_i2c_data = {
340 .name = "adv7170",
341 .probe = adv7170_probe,
342 .remove = adv7170_remove,
343 .id_table = adv7170_id,
344};
345