aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/ov6650.c
diff options
context:
space:
mode:
authorHans Verkuil <hans.verkuil@cisco.com>2011-09-12 08:52:01 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2011-11-03 16:28:50 -0400
commitafd9690c72c3acf77b7f8731b2fcafafd3b7e29e (patch)
treec8ea648010d2c4d2be4aaa2f5543afca71f0385c /drivers/media/video/ov6650.c
parentf026671d2bbbe8b25906bd266a1164a73fdeaa7f (diff)
[media] ov6650: convert to the control framework
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com> [g.liakhovetski@gmx.de: simplified pointer arithmetic] [jkrzyszt@tis.icnet.pl: fix a typo in the register name] Acked-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl> Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/ov6650.c')
-rw-r--r--drivers/media/video/ov6650.c381
1 files changed, 115 insertions, 266 deletions
diff --git a/drivers/media/video/ov6650.c b/drivers/media/video/ov6650.c
index 654b2f591ae2..efa45132c992 100644
--- a/drivers/media/video/ov6650.c
+++ b/drivers/media/video/ov6650.c
@@ -32,6 +32,7 @@
32#include <media/soc_camera.h> 32#include <media/soc_camera.h>
33#include <media/soc_mediabus.h> 33#include <media/soc_mediabus.h>
34#include <media/v4l2-chip-ident.h> 34#include <media/v4l2-chip-ident.h>
35#include <media/v4l2-ctrls.h>
35 36
36/* Register definitions */ 37/* Register definitions */
37#define REG_GAIN 0x00 /* range 00 - 3F */ 38#define REG_GAIN 0x00 /* range 00 - 3F */
@@ -177,20 +178,23 @@ struct ov6650_reg {
177 178
178struct ov6650 { 179struct ov6650 {
179 struct v4l2_subdev subdev; 180 struct v4l2_subdev subdev;
180 181 struct v4l2_ctrl_handler hdl;
181 int gain; 182 struct {
182 int blue; 183 /* exposure/autoexposure cluster */
183 int red; 184 struct v4l2_ctrl *autoexposure;
184 int saturation; 185 struct v4l2_ctrl *exposure;
185 int hue; 186 };
186 int brightness; 187 struct {
187 int exposure; 188 /* gain/autogain cluster */
188 int gamma; 189 struct v4l2_ctrl *autogain;
189 int aec; 190 struct v4l2_ctrl *gain;
190 bool vflip; 191 };
191 bool hflip; 192 struct {
192 bool awb; 193 /* blue/red/autowhitebalance cluster */
193 bool agc; 194 struct v4l2_ctrl *autowb;
195 struct v4l2_ctrl *blue;
196 struct v4l2_ctrl *red;
197 };
194 bool half_scale; /* scale down output by 2 */ 198 bool half_scale; /* scale down output by 2 */
195 struct v4l2_rect rect; /* sensor cropping window */ 199 struct v4l2_rect rect; /* sensor cropping window */
196 unsigned long pclk_limit; /* from host */ 200 unsigned long pclk_limit; /* from host */
@@ -210,126 +214,6 @@ static enum v4l2_mbus_pixelcode ov6650_codes[] = {
210 V4L2_MBUS_FMT_Y8_1X8, 214 V4L2_MBUS_FMT_Y8_1X8,
211}; 215};
212 216
213static const struct v4l2_queryctrl ov6650_controls[] = {
214 {
215 .id = V4L2_CID_AUTOGAIN,
216 .type = V4L2_CTRL_TYPE_BOOLEAN,
217 .name = "AGC",
218 .minimum = 0,
219 .maximum = 1,
220 .step = 1,
221 .default_value = 1,
222 },
223 {
224 .id = V4L2_CID_GAIN,
225 .type = V4L2_CTRL_TYPE_INTEGER,
226 .name = "Gain",
227 .minimum = 0,
228 .maximum = 0x3f,
229 .step = 1,
230 .default_value = DEF_GAIN,
231 },
232 {
233 .id = V4L2_CID_AUTO_WHITE_BALANCE,
234 .type = V4L2_CTRL_TYPE_BOOLEAN,
235 .name = "AWB",
236 .minimum = 0,
237 .maximum = 1,
238 .step = 1,
239 .default_value = 1,
240 },
241 {
242 .id = V4L2_CID_BLUE_BALANCE,
243 .type = V4L2_CTRL_TYPE_INTEGER,
244 .name = "Blue",
245 .minimum = 0,
246 .maximum = 0xff,
247 .step = 1,
248 .default_value = DEF_BLUE,
249 },
250 {
251 .id = V4L2_CID_RED_BALANCE,
252 .type = V4L2_CTRL_TYPE_INTEGER,
253 .name = "Red",
254 .minimum = 0,
255 .maximum = 0xff,
256 .step = 1,
257 .default_value = DEF_RED,
258 },
259 {
260 .id = V4L2_CID_SATURATION,
261 .type = V4L2_CTRL_TYPE_INTEGER,
262 .name = "Saturation",
263 .minimum = 0,
264 .maximum = 0xf,
265 .step = 1,
266 .default_value = 0x8,
267 },
268 {
269 .id = V4L2_CID_HUE,
270 .type = V4L2_CTRL_TYPE_INTEGER,
271 .name = "Hue",
272 .minimum = 0,
273 .maximum = HUE_MASK,
274 .step = 1,
275 .default_value = DEF_HUE,
276 },
277 {
278 .id = V4L2_CID_BRIGHTNESS,
279 .type = V4L2_CTRL_TYPE_INTEGER,
280 .name = "Brightness",
281 .minimum = 0,
282 .maximum = 0xff,
283 .step = 1,
284 .default_value = 0x80,
285 },
286 {
287 .id = V4L2_CID_EXPOSURE_AUTO,
288 .type = V4L2_CTRL_TYPE_INTEGER,
289 .name = "AEC",
290 .minimum = 0,
291 .maximum = 3,
292 .step = 1,
293 .default_value = 0,
294 },
295 {
296 .id = V4L2_CID_EXPOSURE,
297 .type = V4L2_CTRL_TYPE_INTEGER,
298 .name = "Exposure",
299 .minimum = 0,
300 .maximum = 0xff,
301 .step = 1,
302 .default_value = DEF_AECH,
303 },
304 {
305 .id = V4L2_CID_GAMMA,
306 .type = V4L2_CTRL_TYPE_INTEGER,
307 .name = "Gamma",
308 .minimum = 0,
309 .maximum = 0xff,
310 .step = 1,
311 .default_value = 0x12,
312 },
313 {
314 .id = V4L2_CID_VFLIP,
315 .type = V4L2_CTRL_TYPE_BOOLEAN,
316 .name = "Flip Vertically",
317 .minimum = 0,
318 .maximum = 1,
319 .step = 1,
320 .default_value = 0,
321 },
322 {
323 .id = V4L2_CID_HFLIP,
324 .type = V4L2_CTRL_TYPE_BOOLEAN,
325 .name = "Flip Horizontally",
326 .minimum = 0,
327 .maximum = 1,
328 .step = 1,
329 .default_value = 0,
330 },
331};
332
333/* read a register */ 217/* read a register */
334static int ov6650_reg_read(struct i2c_client *client, u8 reg, u8 *val) 218static int ov6650_reg_read(struct i2c_client *client, u8 reg, u8 *val)
335{ 219{
@@ -420,166 +304,91 @@ static int ov6650_s_stream(struct v4l2_subdev *sd, int enable)
420} 304}
421 305
422/* Get status of additional camera capabilities */ 306/* Get status of additional camera capabilities */
423static int ov6650_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) 307static int ov6550_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
424{ 308{
309 struct ov6650 *priv = container_of(ctrl->handler, struct ov6650, hdl);
310 struct v4l2_subdev *sd = &priv->subdev;
425 struct i2c_client *client = v4l2_get_subdevdata(sd); 311 struct i2c_client *client = v4l2_get_subdevdata(sd);
426 struct ov6650 *priv = to_ov6650(client); 312 uint8_t reg, reg2;
427 uint8_t reg;
428 int ret = 0; 313 int ret = 0;
429 314
430 switch (ctrl->id) { 315 switch (ctrl->id) {
431 case V4L2_CID_AUTOGAIN: 316 case V4L2_CID_AUTOGAIN:
432 ctrl->value = priv->agc; 317 ret = ov6650_reg_read(client, REG_GAIN, &reg);
433 break; 318 if (!ret)
434 case V4L2_CID_GAIN: 319 priv->gain->val = reg;
435 if (priv->agc) { 320 return ret;
436 ret = ov6650_reg_read(client, REG_GAIN, &reg);
437 ctrl->value = reg;
438 } else {
439 ctrl->value = priv->gain;
440 }
441 break;
442 case V4L2_CID_AUTO_WHITE_BALANCE: 321 case V4L2_CID_AUTO_WHITE_BALANCE:
443 ctrl->value = priv->awb; 322 ret = ov6650_reg_read(client, REG_BLUE, &reg);
444 break; 323 if (!ret)
445 case V4L2_CID_BLUE_BALANCE: 324 ret = ov6650_reg_read(client, REG_RED, &reg2);
446 if (priv->awb) { 325 if (!ret) {
447 ret = ov6650_reg_read(client, REG_BLUE, &reg); 326 priv->blue->val = reg;
448 ctrl->value = reg; 327 priv->red->val = reg2;
449 } else {
450 ctrl->value = priv->blue;
451 }
452 break;
453 case V4L2_CID_RED_BALANCE:
454 if (priv->awb) {
455 ret = ov6650_reg_read(client, REG_RED, &reg);
456 ctrl->value = reg;
457 } else {
458 ctrl->value = priv->red;
459 } 328 }
460 break; 329 return ret;
461 case V4L2_CID_SATURATION:
462 ctrl->value = priv->saturation;
463 break;
464 case V4L2_CID_HUE:
465 ctrl->value = priv->hue;
466 break;
467 case V4L2_CID_BRIGHTNESS:
468 ctrl->value = priv->brightness;
469 break;
470 case V4L2_CID_EXPOSURE_AUTO: 330 case V4L2_CID_EXPOSURE_AUTO:
471 ctrl->value = priv->aec; 331 ret = ov6650_reg_read(client, REG_AECH, &reg);
472 break; 332 if (!ret)
473 case V4L2_CID_EXPOSURE: 333 priv->exposure->val = reg;
474 if (priv->aec) { 334 return ret;
475 ret = ov6650_reg_read(client, REG_AECH, &reg);
476 ctrl->value = reg;
477 } else {
478 ctrl->value = priv->exposure;
479 }
480 break;
481 case V4L2_CID_GAMMA:
482 ctrl->value = priv->gamma;
483 break;
484 case V4L2_CID_VFLIP:
485 ctrl->value = priv->vflip;
486 break;
487 case V4L2_CID_HFLIP:
488 ctrl->value = priv->hflip;
489 break;
490 } 335 }
491 return ret; 336 return -EINVAL;
492} 337}
493 338
494/* Set status of additional camera capabilities */ 339/* Set status of additional camera capabilities */
495static int ov6650_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) 340static int ov6550_s_ctrl(struct v4l2_ctrl *ctrl)
496{ 341{
342 struct ov6650 *priv = container_of(ctrl->handler, struct ov6650, hdl);
343 struct v4l2_subdev *sd = &priv->subdev;
497 struct i2c_client *client = v4l2_get_subdevdata(sd); 344 struct i2c_client *client = v4l2_get_subdevdata(sd);
498 struct ov6650 *priv = to_ov6650(client);
499 int ret = 0; 345 int ret = 0;
500 346
501 switch (ctrl->id) { 347 switch (ctrl->id) {
502 case V4L2_CID_AUTOGAIN: 348 case V4L2_CID_AUTOGAIN:
503 ret = ov6650_reg_rmw(client, REG_COMB, 349 ret = ov6650_reg_rmw(client, REG_COMB,
504 ctrl->value ? COMB_AGC : 0, COMB_AGC); 350 ctrl->val ? COMB_AGC : 0, COMB_AGC);
505 if (!ret) 351 if (!ret && !ctrl->val)
506 priv->agc = ctrl->value; 352 ret = ov6650_reg_write(client, REG_GAIN, priv->gain->val);
507 break; 353 return ret;
508 case V4L2_CID_GAIN:
509 ret = ov6650_reg_write(client, REG_GAIN, ctrl->value);
510 if (!ret)
511 priv->gain = ctrl->value;
512 break;
513 case V4L2_CID_AUTO_WHITE_BALANCE: 354 case V4L2_CID_AUTO_WHITE_BALANCE:
514 ret = ov6650_reg_rmw(client, REG_COMB, 355 ret = ov6650_reg_rmw(client, REG_COMB,
515 ctrl->value ? COMB_AWB : 0, COMB_AWB); 356 ctrl->val ? COMB_AWB : 0, COMB_AWB);
516 if (!ret) 357 if (!ret && !ctrl->val) {
517 priv->awb = ctrl->value; 358 ret = ov6650_reg_write(client, REG_BLUE, priv->blue->val);
518 break; 359 if (!ret)
519 case V4L2_CID_BLUE_BALANCE: 360 ret = ov6650_reg_write(client, REG_RED,
520 ret = ov6650_reg_write(client, REG_BLUE, ctrl->value); 361 priv->red->val);
521 if (!ret) 362 }
522 priv->blue = ctrl->value; 363 return ret;
523 break;
524 case V4L2_CID_RED_BALANCE:
525 ret = ov6650_reg_write(client, REG_RED, ctrl->value);
526 if (!ret)
527 priv->red = ctrl->value;
528 break;
529 case V4L2_CID_SATURATION: 364 case V4L2_CID_SATURATION:
530 ret = ov6650_reg_rmw(client, REG_SAT, SET_SAT(ctrl->value), 365 return ov6650_reg_rmw(client, REG_SAT, SET_SAT(ctrl->val),
531 SAT_MASK); 366 SAT_MASK);
532 if (!ret)
533 priv->saturation = ctrl->value;
534 break;
535 case V4L2_CID_HUE: 367 case V4L2_CID_HUE:
536 ret = ov6650_reg_rmw(client, REG_HUE, SET_HUE(ctrl->value), 368 return ov6650_reg_rmw(client, REG_HUE, SET_HUE(ctrl->val),
537 HUE_MASK); 369 HUE_MASK);
538 if (!ret)
539 priv->hue = ctrl->value;
540 break;
541 case V4L2_CID_BRIGHTNESS: 370 case V4L2_CID_BRIGHTNESS:
542 ret = ov6650_reg_write(client, REG_BRT, ctrl->value); 371 return ov6650_reg_write(client, REG_BRT, ctrl->val);
543 if (!ret)
544 priv->brightness = ctrl->value;
545 break;
546 case V4L2_CID_EXPOSURE_AUTO: 372 case V4L2_CID_EXPOSURE_AUTO:
547 switch (ctrl->value) { 373 if (ctrl->val == V4L2_EXPOSURE_AUTO)
548 case V4L2_EXPOSURE_AUTO:
549 ret = ov6650_reg_rmw(client, REG_COMB, COMB_AEC, 0); 374 ret = ov6650_reg_rmw(client, REG_COMB, COMB_AEC, 0);
550 break; 375 else
551 default:
552 ret = ov6650_reg_rmw(client, REG_COMB, 0, COMB_AEC); 376 ret = ov6650_reg_rmw(client, REG_COMB, 0, COMB_AEC);
553 break; 377 if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL)
554 } 378 ret = ov6650_reg_write(client, REG_AECH,
555 if (!ret) 379 priv->exposure->val);
556 priv->aec = ctrl->value; 380 return ret;
557 break;
558 case V4L2_CID_EXPOSURE:
559 ret = ov6650_reg_write(client, REG_AECH, ctrl->value);
560 if (!ret)
561 priv->exposure = ctrl->value;
562 break;
563 case V4L2_CID_GAMMA: 381 case V4L2_CID_GAMMA:
564 ret = ov6650_reg_write(client, REG_GAM1, ctrl->value); 382 return ov6650_reg_write(client, REG_GAM1, ctrl->val);
565 if (!ret)
566 priv->gamma = ctrl->value;
567 break;
568 case V4L2_CID_VFLIP: 383 case V4L2_CID_VFLIP:
569 ret = ov6650_reg_rmw(client, REG_COMB, 384 return ov6650_reg_rmw(client, REG_COMB,
570 ctrl->value ? COMB_FLIP_V : 0, COMB_FLIP_V); 385 ctrl->val ? COMB_FLIP_V : 0, COMB_FLIP_V);
571 if (!ret)
572 priv->vflip = ctrl->value;
573 break;
574 case V4L2_CID_HFLIP: 386 case V4L2_CID_HFLIP:
575 ret = ov6650_reg_rmw(client, REG_COMB, 387 return ov6650_reg_rmw(client, REG_COMB,
576 ctrl->value ? COMB_FLIP_H : 0, COMB_FLIP_H); 388 ctrl->val ? COMB_FLIP_H : 0, COMB_FLIP_H);
577 if (!ret)
578 priv->hflip = ctrl->value;
579 break;
580 } 389 }
581 390
582 return ret; 391 return -EINVAL;
583} 392}
584 393
585/* Get chip identification */ 394/* Get chip identification */
@@ -1048,14 +857,12 @@ static int ov6650_video_probe(struct soc_camera_device *icd,
1048 return ret; 857 return ret;
1049} 858}
1050 859
1051static struct soc_camera_ops ov6650_ops = { 860static const struct v4l2_ctrl_ops ov6550_ctrl_ops = {
1052 .controls = ov6650_controls, 861 .g_volatile_ctrl = ov6550_g_volatile_ctrl,
1053 .num_controls = ARRAY_SIZE(ov6650_controls), 862 .s_ctrl = ov6550_s_ctrl,
1054}; 863};
1055 864
1056static struct v4l2_subdev_core_ops ov6650_core_ops = { 865static struct v4l2_subdev_core_ops ov6650_core_ops = {
1057 .g_ctrl = ov6650_g_ctrl,
1058 .s_ctrl = ov6650_s_ctrl,
1059 .g_chip_ident = ov6650_g_chip_ident, 866 .g_chip_ident = ov6650_g_chip_ident,
1060#ifdef CONFIG_VIDEO_ADV_DEBUG 867#ifdef CONFIG_VIDEO_ADV_DEBUG
1061 .g_register = ov6650_get_register, 868 .g_register = ov6650_get_register,
@@ -1164,8 +971,46 @@ static int ov6650_probe(struct i2c_client *client,
1164 } 971 }
1165 972
1166 v4l2_i2c_subdev_init(&priv->subdev, client, &ov6650_subdev_ops); 973 v4l2_i2c_subdev_init(&priv->subdev, client, &ov6650_subdev_ops);
974 v4l2_ctrl_handler_init(&priv->hdl, 13);
975 v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
976 V4L2_CID_VFLIP, 0, 1, 1, 0);
977 v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
978 V4L2_CID_HFLIP, 0, 1, 1, 0);
979 priv->autogain = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
980 V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
981 priv->gain = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
982 V4L2_CID_GAIN, 0, 0x3f, 1, DEF_GAIN);
983 priv->autowb = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
984 V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 1);
985 priv->blue = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
986 V4L2_CID_BLUE_BALANCE, 0, 0xff, 1, DEF_BLUE);
987 priv->red = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
988 V4L2_CID_RED_BALANCE, 0, 0xff, 1, DEF_RED);
989 v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
990 V4L2_CID_SATURATION, 0, 0xf, 1, 0x8);
991 v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
992 V4L2_CID_HUE, 0, HUE_MASK, 1, DEF_HUE);
993 v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
994 V4L2_CID_BRIGHTNESS, 0, 0xff, 1, 0x80);
995 priv->autoexposure = v4l2_ctrl_new_std_menu(&priv->hdl,
996 &ov6550_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, 1, 0,
997 V4L2_EXPOSURE_AUTO);
998 priv->exposure = v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
999 V4L2_CID_EXPOSURE, 0, 0xff, 1, DEF_AECH);
1000 v4l2_ctrl_new_std(&priv->hdl, &ov6550_ctrl_ops,
1001 V4L2_CID_GAMMA, 0, 0xff, 1, 0x12);
1002
1003 priv->subdev.ctrl_handler = &priv->hdl;
1004 if (priv->hdl.error) {
1005 int err = priv->hdl.error;
1167 1006
1168 icd->ops = &ov6650_ops; 1007 kfree(priv);
1008 return err;
1009 }
1010 v4l2_ctrl_auto_cluster(2, &priv->autogain, 0, true);
1011 v4l2_ctrl_auto_cluster(3, &priv->autowb, 0, true);
1012 v4l2_ctrl_auto_cluster(2, &priv->autoexposure,
1013 V4L2_EXPOSURE_MANUAL, true);
1169 1014
1170 priv->rect.left = DEF_HSTRT << 1; 1015 priv->rect.left = DEF_HSTRT << 1;
1171 priv->rect.top = DEF_VSTRT << 1; 1016 priv->rect.top = DEF_VSTRT << 1;
@@ -1176,9 +1021,11 @@ static int ov6650_probe(struct i2c_client *client,
1176 priv->colorspace = V4L2_COLORSPACE_JPEG; 1021 priv->colorspace = V4L2_COLORSPACE_JPEG;
1177 1022
1178 ret = ov6650_video_probe(icd, client); 1023 ret = ov6650_video_probe(icd, client);
1024 if (!ret)
1025 ret = v4l2_ctrl_handler_setup(&priv->hdl);
1179 1026
1180 if (ret) { 1027 if (ret) {
1181 icd->ops = NULL; 1028 v4l2_ctrl_handler_free(&priv->hdl);
1182 kfree(priv); 1029 kfree(priv);
1183 } 1030 }
1184 1031
@@ -1189,6 +1036,8 @@ static int ov6650_remove(struct i2c_client *client)
1189{ 1036{
1190 struct ov6650 *priv = to_ov6650(client); 1037 struct ov6650 *priv = to_ov6650(client);
1191 1038
1039 v4l2_device_unregister_subdev(&priv->subdev);
1040 v4l2_ctrl_handler_free(&priv->hdl);
1192 kfree(priv); 1041 kfree(priv);
1193 return 0; 1042 return 0;
1194} 1043}