diff options
author | Hans Verkuil <hverkuil@xs4all.nl> | 2009-02-19 12:56:37 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-03-30 11:42:58 -0400 |
commit | 35631dcc7f09522ff3119ba72d1252f80419779a (patch) | |
tree | 8bf5ad447503aaae167c71080945bccbb4c29d75 /drivers/media/video/adv7175.c | |
parent | 7d9ef21c2fd4d4d302cd2026c477c058f17d2ba8 (diff) |
V4L/DVB (10728): adv7175: convert to v4l2-subdev.
Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/adv7175.c')
-rw-r--r-- | drivers/media/video/adv7175.c | 307 |
1 files changed, 165 insertions, 142 deletions
diff --git a/drivers/media/video/adv7175.c b/drivers/media/video/adv7175.c index 154dff03a7d8..318c3053633a 100644 --- a/drivers/media/video/adv7175.c +++ b/drivers/media/video/adv7175.c | |||
@@ -30,15 +30,26 @@ | |||
30 | #include <asm/uaccess.h> | 30 | #include <asm/uaccess.h> |
31 | #include <linux/i2c.h> | 31 | #include <linux/i2c.h> |
32 | #include <linux/i2c-id.h> | 32 | #include <linux/i2c-id.h> |
33 | #include <linux/videodev.h> | 33 | #include <linux/videodev2.h> |
34 | #include <linux/video_encoder.h> | 34 | #include <media/v4l2-device.h> |
35 | #include <media/v4l2-common.h> | 35 | #include <media/v4l2-chip-ident.h> |
36 | #include <media/v4l2-i2c-drv-legacy.h> | 36 | #include <media/v4l2-i2c-drv-legacy.h> |
37 | 37 | ||
38 | MODULE_DESCRIPTION("Analog Devices ADV7175 video encoder driver"); | 38 | MODULE_DESCRIPTION("Analog Devices ADV7175 video encoder driver"); |
39 | MODULE_AUTHOR("Dave Perks"); | 39 | MODULE_AUTHOR("Dave Perks"); |
40 | MODULE_LICENSE("GPL"); | 40 | MODULE_LICENSE("GPL"); |
41 | 41 | ||
42 | #define I2C_ADV7175 0xd4 | ||
43 | #define I2C_ADV7176 0x54 | ||
44 | |||
45 | static unsigned short normal_i2c[] = { | ||
46 | I2C_ADV7175 >> 1, (I2C_ADV7175 >> 1) + 1, | ||
47 | I2C_ADV7176 >> 1, (I2C_ADV7176 >> 1) + 1, | ||
48 | I2C_CLIENT_END | ||
49 | }; | ||
50 | |||
51 | I2C_CLIENT_INSMOD; | ||
52 | |||
42 | static int debug; | 53 | static int debug; |
43 | module_param(debug, int, 0); | 54 | module_param(debug, int, 0); |
44 | MODULE_PARM_DESC(debug, "Debug level (0-1)"); | 55 | MODULE_PARM_DESC(debug, "Debug level (0-1)"); |
@@ -46,34 +57,38 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); | |||
46 | /* ----------------------------------------------------------------------- */ | 57 | /* ----------------------------------------------------------------------- */ |
47 | 58 | ||
48 | struct adv7175 { | 59 | struct adv7175 { |
60 | struct v4l2_subdev sd; | ||
49 | v4l2_std_id norm; | 61 | v4l2_std_id norm; |
50 | int input; | 62 | int input; |
51 | int bright; | ||
52 | int contrast; | ||
53 | int hue; | ||
54 | int sat; | ||
55 | }; | 63 | }; |
56 | 64 | ||
57 | #define I2C_ADV7175 0xd4 | 65 | static inline struct adv7175 *to_adv7175(struct v4l2_subdev *sd) |
58 | #define I2C_ADV7176 0x54 | 66 | { |
67 | return container_of(sd, struct adv7175, sd); | ||
68 | } | ||
59 | 69 | ||
60 | static char *inputs[] = { "pass_through", "play_back", "color_bar" }; | 70 | static char *inputs[] = { "pass_through", "play_back", "color_bar" }; |
61 | 71 | ||
62 | /* ----------------------------------------------------------------------- */ | 72 | /* ----------------------------------------------------------------------- */ |
63 | 73 | ||
64 | static inline int adv7175_write(struct i2c_client *client, u8 reg, u8 value) | 74 | static inline int adv7175_write(struct v4l2_subdev *sd, u8 reg, u8 value) |
65 | { | 75 | { |
76 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
77 | |||
66 | return i2c_smbus_write_byte_data(client, reg, value); | 78 | return i2c_smbus_write_byte_data(client, reg, value); |
67 | } | 79 | } |
68 | 80 | ||
69 | static inline int adv7175_read(struct i2c_client *client, u8 reg) | 81 | static inline int adv7175_read(struct v4l2_subdev *sd, u8 reg) |
70 | { | 82 | { |
83 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
84 | |||
71 | return i2c_smbus_read_byte_data(client, reg); | 85 | return i2c_smbus_read_byte_data(client, reg); |
72 | } | 86 | } |
73 | 87 | ||
74 | static int adv7175_write_block(struct i2c_client *client, | 88 | static int adv7175_write_block(struct v4l2_subdev *sd, |
75 | const u8 *data, unsigned int len) | 89 | const u8 *data, unsigned int len) |
76 | { | 90 | { |
91 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
77 | int ret = -1; | 92 | int ret = -1; |
78 | u8 reg; | 93 | u8 reg; |
79 | 94 | ||
@@ -101,7 +116,7 @@ static int adv7175_write_block(struct i2c_client *client, | |||
101 | /* do some slow I2C emulation kind of thing */ | 116 | /* do some slow I2C emulation kind of thing */ |
102 | while (len >= 2) { | 117 | while (len >= 2) { |
103 | reg = *data++; | 118 | reg = *data++; |
104 | ret = adv7175_write(client, reg, *data++); | 119 | ret = adv7175_write(sd, reg, *data++); |
105 | if (ret < 0) | 120 | if (ret < 0) |
106 | break; | 121 | break; |
107 | len -= 2; | 122 | len -= 2; |
@@ -111,18 +126,18 @@ static int adv7175_write_block(struct i2c_client *client, | |||
111 | return ret; | 126 | return ret; |
112 | } | 127 | } |
113 | 128 | ||
114 | static void set_subcarrier_freq(struct i2c_client *client, int pass_through) | 129 | static void set_subcarrier_freq(struct v4l2_subdev *sd, int pass_through) |
115 | { | 130 | { |
116 | /* for some reason pass_through NTSC needs | 131 | /* for some reason pass_through NTSC needs |
117 | * a different sub-carrier freq to remain stable. */ | 132 | * a different sub-carrier freq to remain stable. */ |
118 | if (pass_through) | 133 | if (pass_through) |
119 | adv7175_write(client, 0x02, 0x00); | 134 | adv7175_write(sd, 0x02, 0x00); |
120 | else | 135 | else |
121 | adv7175_write(client, 0x02, 0x55); | 136 | adv7175_write(sd, 0x02, 0x55); |
122 | 137 | ||
123 | adv7175_write(client, 0x03, 0x55); | 138 | adv7175_write(sd, 0x03, 0x55); |
124 | adv7175_write(client, 0x04, 0x55); | 139 | adv7175_write(sd, 0x04, 0x55); |
125 | adv7175_write(client, 0x05, 0x25); | 140 | adv7175_write(sd, 0x05, 0x25); |
126 | } | 141 | } |
127 | 142 | ||
128 | /* ----------------------------------------------------------------------- */ | 143 | /* ----------------------------------------------------------------------- */ |
@@ -182,144 +197,148 @@ static const unsigned char init_ntsc[] = { | |||
182 | 0x06, 0x1a, /* subc. phase */ | 197 | 0x06, 0x1a, /* subc. phase */ |
183 | }; | 198 | }; |
184 | 199 | ||
185 | static int adv7175_command(struct i2c_client *client, unsigned cmd, void *arg) | 200 | static int adv7175_init(struct v4l2_subdev *sd, u32 val) |
201 | { | ||
202 | /* This is just for testing!!! */ | ||
203 | adv7175_write_block(sd, init_common, sizeof(init_common)); | ||
204 | adv7175_write(sd, 0x07, TR0MODE | TR0RST); | ||
205 | adv7175_write(sd, 0x07, TR0MODE); | ||
206 | return 0; | ||
207 | } | ||
208 | |||
209 | static int adv7175_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) | ||
186 | { | 210 | { |
187 | struct adv7175 *encoder = i2c_get_clientdata(client); | 211 | struct adv7175 *encoder = to_adv7175(sd); |
188 | 212 | ||
189 | switch (cmd) { | 213 | if (std & V4L2_STD_NTSC) { |
190 | case VIDIOC_INT_INIT: | 214 | adv7175_write_block(sd, init_ntsc, sizeof(init_ntsc)); |
191 | /* This is just for testing!!! */ | 215 | if (encoder->input == 0) |
192 | adv7175_write_block(client, init_common, | 216 | adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */ |
193 | sizeof(init_common)); | 217 | adv7175_write(sd, 0x07, TR0MODE | TR0RST); |
194 | adv7175_write(client, 0x07, TR0MODE | TR0RST); | 218 | adv7175_write(sd, 0x07, TR0MODE); |
195 | adv7175_write(client, 0x07, TR0MODE); | 219 | } else if (std & V4L2_STD_PAL) { |
220 | adv7175_write_block(sd, init_pal, sizeof(init_pal)); | ||
221 | if (encoder->input == 0) | ||
222 | adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */ | ||
223 | adv7175_write(sd, 0x07, TR0MODE | TR0RST); | ||
224 | adv7175_write(sd, 0x07, TR0MODE); | ||
225 | } else if (std & V4L2_STD_SECAM) { | ||
226 | /* This is an attempt to convert | ||
227 | * SECAM->PAL (typically it does not work | ||
228 | * due to genlock: when decoder is in SECAM | ||
229 | * and encoder in in PAL the subcarrier can | ||
230 | * not be syncronized with horizontal | ||
231 | * quency) */ | ||
232 | adv7175_write_block(sd, init_pal, sizeof(init_pal)); | ||
233 | if (encoder->input == 0) | ||
234 | adv7175_write(sd, 0x0d, 0x49); /* Disable genlock */ | ||
235 | adv7175_write(sd, 0x07, TR0MODE | TR0RST); | ||
236 | adv7175_write(sd, 0x07, TR0MODE); | ||
237 | } else { | ||
238 | v4l2_dbg(1, debug, sd, "illegal norm: %llx\n", std); | ||
239 | return -EINVAL; | ||
240 | } | ||
241 | v4l2_dbg(1, debug, sd, "switched to %llx\n", std); | ||
242 | encoder->norm = std; | ||
243 | return 0; | ||
244 | } | ||
245 | |||
246 | static int adv7175_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) | ||
247 | { | ||
248 | struct adv7175 *encoder = to_adv7175(sd); | ||
249 | |||
250 | /* RJ: route->input = 0: input is from decoder | ||
251 | route->input = 1: input is from ZR36060 | ||
252 | route->input = 2: color bar */ | ||
253 | |||
254 | switch (route->input) { | ||
255 | case 0: | ||
256 | adv7175_write(sd, 0x01, 0x00); | ||
257 | |||
258 | if (encoder->norm & V4L2_STD_NTSC) | ||
259 | set_subcarrier_freq(sd, 1); | ||
260 | |||
261 | adv7175_write(sd, 0x0c, TR1CAPT); /* TR1 */ | ||
262 | if (encoder->norm & V4L2_STD_SECAM) | ||
263 | adv7175_write(sd, 0x0d, 0x49); /* Disable genlock */ | ||
264 | else | ||
265 | adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */ | ||
266 | adv7175_write(sd, 0x07, TR0MODE | TR0RST); | ||
267 | adv7175_write(sd, 0x07, TR0MODE); | ||
268 | /*udelay(10);*/ | ||
196 | break; | 269 | break; |
197 | 270 | ||
198 | case VIDIOC_INT_S_STD_OUTPUT: | 271 | case 1: |
199 | { | 272 | adv7175_write(sd, 0x01, 0x00); |
200 | v4l2_std_id iarg = *(v4l2_std_id *) arg; | 273 | |
201 | 274 | if (encoder->norm & V4L2_STD_NTSC) | |
202 | if (iarg & V4L2_STD_NTSC) { | 275 | set_subcarrier_freq(sd, 0); |
203 | adv7175_write_block(client, init_ntsc, | 276 | |
204 | sizeof(init_ntsc)); | 277 | adv7175_write(sd, 0x0c, TR1PLAY); /* TR1 */ |
205 | if (encoder->input == 0) | 278 | adv7175_write(sd, 0x0d, 0x49); |
206 | adv7175_write(client, 0x0d, 0x4f); // Enable genlock | 279 | adv7175_write(sd, 0x07, TR0MODE | TR0RST); |
207 | adv7175_write(client, 0x07, TR0MODE | TR0RST); | 280 | adv7175_write(sd, 0x07, TR0MODE); |
208 | adv7175_write(client, 0x07, TR0MODE); | 281 | /* udelay(10); */ |
209 | } else if (iarg & V4L2_STD_PAL) { | ||
210 | adv7175_write_block(client, init_pal, | ||
211 | sizeof(init_pal)); | ||
212 | if (encoder->input == 0) | ||
213 | adv7175_write(client, 0x0d, 0x4f); // Enable genlock | ||
214 | adv7175_write(client, 0x07, TR0MODE | TR0RST); | ||
215 | adv7175_write(client, 0x07, TR0MODE); | ||
216 | } else if (iarg & V4L2_STD_SECAM) { | ||
217 | /* This is an attempt to convert | ||
218 | * SECAM->PAL (typically it does not work | ||
219 | * due to genlock: when decoder is in SECAM | ||
220 | * and encoder in in PAL the subcarrier can | ||
221 | * not be syncronized with horizontal | ||
222 | * quency) */ | ||
223 | adv7175_write_block(client, init_pal, | ||
224 | sizeof(init_pal)); | ||
225 | if (encoder->input == 0) | ||
226 | adv7175_write(client, 0x0d, 0x49); // Disable genlock | ||
227 | adv7175_write(client, 0x07, TR0MODE | TR0RST); | ||
228 | adv7175_write(client, 0x07, TR0MODE); | ||
229 | } else { | ||
230 | v4l_dbg(1, debug, client, "illegal norm: %llx\n", iarg); | ||
231 | return -EINVAL; | ||
232 | } | ||
233 | v4l_dbg(1, debug, client, "switched to %llx\n", iarg); | ||
234 | encoder->norm = iarg; | ||
235 | break; | 282 | break; |
236 | } | ||
237 | 283 | ||
238 | case VIDIOC_INT_S_VIDEO_ROUTING: | 284 | case 2: |
239 | { | 285 | adv7175_write(sd, 0x01, 0x80); |
240 | struct v4l2_routing *route = arg; | 286 | |
241 | 287 | if (encoder->norm & V4L2_STD_NTSC) | |
242 | /* RJ: *iarg = 0: input is from SAA7110 | 288 | set_subcarrier_freq(sd, 0); |
243 | *iarg = 1: input is from ZR36060 | 289 | |
244 | *iarg = 2: color bar */ | 290 | adv7175_write(sd, 0x0d, 0x49); |
245 | 291 | adv7175_write(sd, 0x07, TR0MODE | TR0RST); | |
246 | switch (route->input) { | 292 | adv7175_write(sd, 0x07, TR0MODE); |
247 | case 0: | 293 | /* udelay(10); */ |
248 | adv7175_write(client, 0x01, 0x00); | ||
249 | |||
250 | if (encoder->norm & V4L2_STD_NTSC) | ||
251 | set_subcarrier_freq(client, 1); | ||
252 | |||
253 | adv7175_write(client, 0x0c, TR1CAPT); /* TR1 */ | ||
254 | if (encoder->norm & V4L2_STD_SECAM) | ||
255 | adv7175_write(client, 0x0d, 0x49); // Disable genlock | ||
256 | else | ||
257 | adv7175_write(client, 0x0d, 0x4f); // Enable genlock | ||
258 | adv7175_write(client, 0x07, TR0MODE | TR0RST); | ||
259 | adv7175_write(client, 0x07, TR0MODE); | ||
260 | //udelay(10); | ||
261 | break; | ||
262 | |||
263 | case 1: | ||
264 | adv7175_write(client, 0x01, 0x00); | ||
265 | |||
266 | if (encoder->norm & V4L2_STD_NTSC) | ||
267 | set_subcarrier_freq(client, 0); | ||
268 | |||
269 | adv7175_write(client, 0x0c, TR1PLAY); /* TR1 */ | ||
270 | adv7175_write(client, 0x0d, 0x49); | ||
271 | adv7175_write(client, 0x07, TR0MODE | TR0RST); | ||
272 | adv7175_write(client, 0x07, TR0MODE); | ||
273 | /* udelay(10); */ | ||
274 | break; | ||
275 | |||
276 | case 2: | ||
277 | adv7175_write(client, 0x01, 0x80); | ||
278 | |||
279 | if (encoder->norm & V4L2_STD_NTSC) | ||
280 | set_subcarrier_freq(client, 0); | ||
281 | |||
282 | adv7175_write(client, 0x0d, 0x49); | ||
283 | adv7175_write(client, 0x07, TR0MODE | TR0RST); | ||
284 | adv7175_write(client, 0x07, TR0MODE); | ||
285 | /* udelay(10); */ | ||
286 | break; | ||
287 | |||
288 | default: | ||
289 | v4l_dbg(1, debug, client, "illegal input: %d\n", route->input); | ||
290 | return -EINVAL; | ||
291 | } | ||
292 | v4l_dbg(1, debug, client, "switched to %s\n", inputs[route->input]); | ||
293 | encoder->input = route->input; | ||
294 | break; | 294 | break; |
295 | } | ||
296 | 295 | ||
297 | default: | 296 | default: |
297 | v4l2_dbg(1, debug, sd, "illegal input: %d\n", route->input); | ||
298 | return -EINVAL; | 298 | return -EINVAL; |
299 | } | 299 | } |
300 | 300 | v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[route->input]); | |
301 | encoder->input = route->input; | ||
301 | return 0; | 302 | return 0; |
302 | } | 303 | } |
303 | 304 | ||
305 | static int adv7175_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) | ||
306 | { | ||
307 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
308 | |||
309 | return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7175, 0); | ||
310 | } | ||
311 | |||
312 | static int adv7175_command(struct i2c_client *client, unsigned cmd, void *arg) | ||
313 | { | ||
314 | return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); | ||
315 | } | ||
316 | |||
304 | /* ----------------------------------------------------------------------- */ | 317 | /* ----------------------------------------------------------------------- */ |
305 | 318 | ||
306 | /* | 319 | static const struct v4l2_subdev_core_ops adv7175_core_ops = { |
307 | * Generic i2c probe | 320 | .g_chip_ident = adv7175_g_chip_ident, |
308 | * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' | 321 | .init = adv7175_init, |
309 | */ | ||
310 | static unsigned short normal_i2c[] = { | ||
311 | I2C_ADV7175 >> 1, (I2C_ADV7175 >> 1) + 1, | ||
312 | I2C_ADV7176 >> 1, (I2C_ADV7176 >> 1) + 1, | ||
313 | I2C_CLIENT_END | ||
314 | }; | 322 | }; |
315 | 323 | ||
316 | I2C_CLIENT_INSMOD; | 324 | static const struct v4l2_subdev_video_ops adv7175_video_ops = { |
325 | .s_std_output = adv7175_s_std_output, | ||
326 | .s_routing = adv7175_s_routing, | ||
327 | }; | ||
328 | |||
329 | static const struct v4l2_subdev_ops adv7175_ops = { | ||
330 | .core = &adv7175_core_ops, | ||
331 | .video = &adv7175_video_ops, | ||
332 | }; | ||
333 | |||
334 | /* ----------------------------------------------------------------------- */ | ||
317 | 335 | ||
318 | static int adv7175_probe(struct i2c_client *client, | 336 | static int adv7175_probe(struct i2c_client *client, |
319 | const struct i2c_device_id *id) | 337 | const struct i2c_device_id *id) |
320 | { | 338 | { |
321 | int i; | 339 | int i; |
322 | struct adv7175 *encoder; | 340 | struct adv7175 *encoder; |
341 | struct v4l2_subdev *sd; | ||
323 | 342 | ||
324 | /* Check if the adapter supports the needed features */ | 343 | /* Check if the adapter supports the needed features */ |
325 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | 344 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) |
@@ -331,25 +350,29 @@ static int adv7175_probe(struct i2c_client *client, | |||
331 | encoder = kzalloc(sizeof(struct adv7175), GFP_KERNEL); | 350 | encoder = kzalloc(sizeof(struct adv7175), GFP_KERNEL); |
332 | if (encoder == NULL) | 351 | if (encoder == NULL) |
333 | return -ENOMEM; | 352 | return -ENOMEM; |
353 | sd = &encoder->sd; | ||
354 | v4l2_i2c_subdev_init(sd, client, &adv7175_ops); | ||
334 | encoder->norm = V4L2_STD_NTSC; | 355 | encoder->norm = V4L2_STD_NTSC; |
335 | encoder->input = 0; | 356 | encoder->input = 0; |
336 | i2c_set_clientdata(client, encoder); | ||
337 | 357 | ||
338 | i = adv7175_write_block(client, init_common, sizeof(init_common)); | 358 | i = adv7175_write_block(sd, init_common, sizeof(init_common)); |
339 | if (i >= 0) { | 359 | if (i >= 0) { |
340 | i = adv7175_write(client, 0x07, TR0MODE | TR0RST); | 360 | i = adv7175_write(sd, 0x07, TR0MODE | TR0RST); |
341 | i = adv7175_write(client, 0x07, TR0MODE); | 361 | i = adv7175_write(sd, 0x07, TR0MODE); |
342 | i = adv7175_read(client, 0x12); | 362 | i = adv7175_read(sd, 0x12); |
343 | v4l_dbg(1, debug, client, "revision %d\n", i & 1); | 363 | v4l2_dbg(1, debug, sd, "revision %d\n", i & 1); |
344 | } | 364 | } |
345 | if (i < 0) | 365 | if (i < 0) |
346 | v4l_dbg(1, debug, client, "init error 0x%x\n", i); | 366 | v4l2_dbg(1, debug, sd, "init error 0x%x\n", i); |
347 | return 0; | 367 | return 0; |
348 | } | 368 | } |
349 | 369 | ||
350 | static int adv7175_remove(struct i2c_client *client) | 370 | static int adv7175_remove(struct i2c_client *client) |
351 | { | 371 | { |
352 | kfree(i2c_get_clientdata(client)); | 372 | struct v4l2_subdev *sd = i2c_get_clientdata(client); |
373 | |||
374 | v4l2_device_unregister_subdev(sd); | ||
375 | kfree(to_adv7175(sd)); | ||
353 | return 0; | 376 | return 0; |
354 | } | 377 | } |
355 | 378 | ||