diff options
| -rw-r--r-- | drivers/media/i2c/ov7670.c | 310 |
1 files changed, 112 insertions, 198 deletions
diff --git a/drivers/media/i2c/ov7670.c b/drivers/media/i2c/ov7670.c index 52c024a334df..93a051e8873f 100644 --- a/drivers/media/i2c/ov7670.c +++ b/drivers/media/i2c/ov7670.c | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | #include <linux/videodev2.h> | 18 | #include <linux/videodev2.h> |
| 19 | #include <media/v4l2-device.h> | 19 | #include <media/v4l2-device.h> |
| 20 | #include <media/v4l2-chip-ident.h> | 20 | #include <media/v4l2-chip-ident.h> |
| 21 | #include <media/v4l2-ctrls.h> | ||
| 21 | #include <media/v4l2-mediabus.h> | 22 | #include <media/v4l2-mediabus.h> |
| 22 | #include <media/ov7670.h> | 23 | #include <media/ov7670.h> |
| 23 | 24 | ||
| @@ -222,9 +223,23 @@ struct ov7670_devtype { | |||
| 222 | struct ov7670_format_struct; /* coming later */ | 223 | struct ov7670_format_struct; /* coming later */ |
| 223 | struct ov7670_info { | 224 | struct ov7670_info { |
| 224 | struct v4l2_subdev sd; | 225 | struct v4l2_subdev sd; |
| 226 | struct v4l2_ctrl_handler hdl; | ||
| 227 | struct { | ||
| 228 | /* gain cluster */ | ||
| 229 | struct v4l2_ctrl *auto_gain; | ||
| 230 | struct v4l2_ctrl *gain; | ||
| 231 | }; | ||
| 232 | struct { | ||
| 233 | /* exposure cluster */ | ||
| 234 | struct v4l2_ctrl *auto_exposure; | ||
| 235 | struct v4l2_ctrl *exposure; | ||
| 236 | }; | ||
| 237 | struct { | ||
| 238 | /* saturation/hue cluster */ | ||
| 239 | struct v4l2_ctrl *saturation; | ||
| 240 | struct v4l2_ctrl *hue; | ||
| 241 | }; | ||
| 225 | struct ov7670_format_struct *fmt; /* Current format */ | 242 | struct ov7670_format_struct *fmt; /* Current format */ |
| 226 | unsigned char sat; /* Saturation value */ | ||
| 227 | int hue; /* Hue value */ | ||
| 228 | int min_width; /* Filter out smaller sizes */ | 243 | int min_width; /* Filter out smaller sizes */ |
| 229 | int min_height; /* Filter out smaller sizes */ | 244 | int min_height; /* Filter out smaller sizes */ |
| 230 | int clock_speed; /* External clock speed (MHz) */ | 245 | int clock_speed; /* External clock speed (MHz) */ |
| @@ -240,6 +255,11 @@ static inline struct ov7670_info *to_state(struct v4l2_subdev *sd) | |||
| 240 | return container_of(sd, struct ov7670_info, sd); | 255 | return container_of(sd, struct ov7670_info, sd); |
| 241 | } | 256 | } |
| 242 | 257 | ||
| 258 | static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) | ||
| 259 | { | ||
| 260 | return &container_of(ctrl->handler, struct ov7670_info, hdl)->sd; | ||
| 261 | } | ||
| 262 | |||
| 243 | 263 | ||
| 244 | 264 | ||
| 245 | /* | 265 | /* |
| @@ -1195,23 +1215,23 @@ static int ov7670_cosine(int theta) | |||
| 1195 | 1215 | ||
| 1196 | 1216 | ||
| 1197 | static void ov7670_calc_cmatrix(struct ov7670_info *info, | 1217 | static void ov7670_calc_cmatrix(struct ov7670_info *info, |
| 1198 | int matrix[CMATRIX_LEN]) | 1218 | int matrix[CMATRIX_LEN], int sat, int hue) |
| 1199 | { | 1219 | { |
| 1200 | int i; | 1220 | int i; |
| 1201 | /* | 1221 | /* |
| 1202 | * Apply the current saturation setting first. | 1222 | * Apply the current saturation setting first. |
| 1203 | */ | 1223 | */ |
| 1204 | for (i = 0; i < CMATRIX_LEN; i++) | 1224 | for (i = 0; i < CMATRIX_LEN; i++) |
| 1205 | matrix[i] = (info->fmt->cmatrix[i]*info->sat) >> 7; | 1225 | matrix[i] = (info->fmt->cmatrix[i] * sat) >> 7; |
| 1206 | /* | 1226 | /* |
| 1207 | * Then, if need be, rotate the hue value. | 1227 | * Then, if need be, rotate the hue value. |
| 1208 | */ | 1228 | */ |
| 1209 | if (info->hue != 0) { | 1229 | if (hue != 0) { |
| 1210 | int sinth, costh, tmpmatrix[CMATRIX_LEN]; | 1230 | int sinth, costh, tmpmatrix[CMATRIX_LEN]; |
| 1211 | 1231 | ||
| 1212 | memcpy(tmpmatrix, matrix, CMATRIX_LEN*sizeof(int)); | 1232 | memcpy(tmpmatrix, matrix, CMATRIX_LEN*sizeof(int)); |
| 1213 | sinth = ov7670_sine(info->hue); | 1233 | sinth = ov7670_sine(hue); |
| 1214 | costh = ov7670_cosine(info->hue); | 1234 | costh = ov7670_cosine(hue); |
| 1215 | 1235 | ||
| 1216 | matrix[0] = (matrix[3]*sinth + matrix[0]*costh)/1000; | 1236 | matrix[0] = (matrix[3]*sinth + matrix[0]*costh)/1000; |
| 1217 | matrix[1] = (matrix[4]*sinth + matrix[1]*costh)/1000; | 1237 | matrix[1] = (matrix[4]*sinth + matrix[1]*costh)/1000; |
| @@ -1224,60 +1244,21 @@ static void ov7670_calc_cmatrix(struct ov7670_info *info, | |||
| 1224 | 1244 | ||
| 1225 | 1245 | ||
| 1226 | 1246 | ||
| 1227 | static int ov7670_s_sat(struct v4l2_subdev *sd, int value) | 1247 | static int ov7670_s_sat_hue(struct v4l2_subdev *sd, int sat, int hue) |
| 1228 | { | 1248 | { |
| 1229 | struct ov7670_info *info = to_state(sd); | 1249 | struct ov7670_info *info = to_state(sd); |
| 1230 | int matrix[CMATRIX_LEN]; | 1250 | int matrix[CMATRIX_LEN]; |
| 1231 | int ret; | 1251 | int ret; |
| 1232 | 1252 | ||
| 1233 | info->sat = value; | 1253 | ov7670_calc_cmatrix(info, matrix, sat, hue); |
| 1234 | ov7670_calc_cmatrix(info, matrix); | ||
| 1235 | ret = ov7670_store_cmatrix(sd, matrix); | 1254 | ret = ov7670_store_cmatrix(sd, matrix); |
| 1236 | return ret; | 1255 | return ret; |
| 1237 | } | 1256 | } |
| 1238 | 1257 | ||
| 1239 | static int ov7670_g_sat(struct v4l2_subdev *sd, __s32 *value) | ||
| 1240 | { | ||
| 1241 | struct ov7670_info *info = to_state(sd); | ||
| 1242 | |||
| 1243 | *value = info->sat; | ||
| 1244 | return 0; | ||
| 1245 | } | ||
| 1246 | |||
| 1247 | static int ov7670_s_hue(struct v4l2_subdev *sd, int value) | ||
| 1248 | { | ||
| 1249 | struct ov7670_info *info = to_state(sd); | ||
| 1250 | int matrix[CMATRIX_LEN]; | ||
| 1251 | int ret; | ||
| 1252 | |||
| 1253 | if (value < -180 || value > 180) | ||
| 1254 | return -EINVAL; | ||
| 1255 | info->hue = value; | ||
| 1256 | ov7670_calc_cmatrix(info, matrix); | ||
| 1257 | ret = ov7670_store_cmatrix(sd, matrix); | ||
| 1258 | return ret; | ||
| 1259 | } | ||
| 1260 | |||
| 1261 | |||
| 1262 | static int ov7670_g_hue(struct v4l2_subdev *sd, __s32 *value) | ||
| 1263 | { | ||
| 1264 | struct ov7670_info *info = to_state(sd); | ||
| 1265 | |||
| 1266 | *value = info->hue; | ||
| 1267 | return 0; | ||
| 1268 | } | ||
| 1269 | |||
| 1270 | 1258 | ||
| 1271 | /* | 1259 | /* |
| 1272 | * Some weird registers seem to store values in a sign/magnitude format! | 1260 | * Some weird registers seem to store values in a sign/magnitude format! |
| 1273 | */ | 1261 | */ |
| 1274 | static unsigned char ov7670_sm_to_abs(unsigned char v) | ||
| 1275 | { | ||
| 1276 | if ((v & 0x80) == 0) | ||
| 1277 | return v + 128; | ||
| 1278 | return 128 - (v & 0x7f); | ||
| 1279 | } | ||
| 1280 | |||
| 1281 | 1262 | ||
| 1282 | static unsigned char ov7670_abs_to_sm(unsigned char v) | 1263 | static unsigned char ov7670_abs_to_sm(unsigned char v) |
| 1283 | { | 1264 | { |
| @@ -1299,40 +1280,11 @@ static int ov7670_s_brightness(struct v4l2_subdev *sd, int value) | |||
| 1299 | return ret; | 1280 | return ret; |
| 1300 | } | 1281 | } |
| 1301 | 1282 | ||
| 1302 | static int ov7670_g_brightness(struct v4l2_subdev *sd, __s32 *value) | ||
| 1303 | { | ||
| 1304 | unsigned char v = 0; | ||
| 1305 | int ret = ov7670_read(sd, REG_BRIGHT, &v); | ||
| 1306 | |||
| 1307 | *value = ov7670_sm_to_abs(v); | ||
| 1308 | return ret; | ||
| 1309 | } | ||
| 1310 | |||
| 1311 | static int ov7670_s_contrast(struct v4l2_subdev *sd, int value) | 1283 | static int ov7670_s_contrast(struct v4l2_subdev *sd, int value) |
| 1312 | { | 1284 | { |
| 1313 | return ov7670_write(sd, REG_CONTRAS, (unsigned char) value); | 1285 | return ov7670_write(sd, REG_CONTRAS, (unsigned char) value); |
| 1314 | } | 1286 | } |
| 1315 | 1287 | ||
| 1316 | static int ov7670_g_contrast(struct v4l2_subdev *sd, __s32 *value) | ||
| 1317 | { | ||
| 1318 | unsigned char v = 0; | ||
| 1319 | int ret = ov7670_read(sd, REG_CONTRAS, &v); | ||
| 1320 | |||
| 1321 | *value = v; | ||
| 1322 | return ret; | ||
| 1323 | } | ||
| 1324 | |||
| 1325 | static int ov7670_g_hflip(struct v4l2_subdev *sd, __s32 *value) | ||
| 1326 | { | ||
| 1327 | int ret; | ||
| 1328 | unsigned char v = 0; | ||
| 1329 | |||
| 1330 | ret = ov7670_read(sd, REG_MVFP, &v); | ||
| 1331 | *value = (v & MVFP_MIRROR) == MVFP_MIRROR; | ||
| 1332 | return ret; | ||
| 1333 | } | ||
| 1334 | |||
| 1335 | |||
| 1336 | static int ov7670_s_hflip(struct v4l2_subdev *sd, int value) | 1288 | static int ov7670_s_hflip(struct v4l2_subdev *sd, int value) |
| 1337 | { | 1289 | { |
| 1338 | unsigned char v = 0; | 1290 | unsigned char v = 0; |
| @@ -1348,19 +1300,6 @@ static int ov7670_s_hflip(struct v4l2_subdev *sd, int value) | |||
| 1348 | return ret; | 1300 | return ret; |
| 1349 | } | 1301 | } |
| 1350 | 1302 | ||
| 1351 | |||
| 1352 | |||
| 1353 | static int ov7670_g_vflip(struct v4l2_subdev *sd, __s32 *value) | ||
| 1354 | { | ||
| 1355 | int ret; | ||
| 1356 | unsigned char v = 0; | ||
| 1357 | |||
| 1358 | ret = ov7670_read(sd, REG_MVFP, &v); | ||
| 1359 | *value = (v & MVFP_FLIP) == MVFP_FLIP; | ||
| 1360 | return ret; | ||
| 1361 | } | ||
| 1362 | |||
| 1363 | |||
| 1364 | static int ov7670_s_vflip(struct v4l2_subdev *sd, int value) | 1303 | static int ov7670_s_vflip(struct v4l2_subdev *sd, int value) |
| 1365 | { | 1304 | { |
| 1366 | unsigned char v = 0; | 1305 | unsigned char v = 0; |
| @@ -1409,16 +1348,6 @@ static int ov7670_s_gain(struct v4l2_subdev *sd, int value) | |||
| 1409 | /* | 1348 | /* |
| 1410 | * Tweak autogain. | 1349 | * Tweak autogain. |
| 1411 | */ | 1350 | */ |
| 1412 | static int ov7670_g_autogain(struct v4l2_subdev *sd, __s32 *value) | ||
| 1413 | { | ||
| 1414 | int ret; | ||
| 1415 | unsigned char com8; | ||
| 1416 | |||
| 1417 | ret = ov7670_read(sd, REG_COM8, &com8); | ||
| 1418 | *value = (com8 & COM8_AGC) != 0; | ||
| 1419 | return ret; | ||
| 1420 | } | ||
| 1421 | |||
| 1422 | static int ov7670_s_autogain(struct v4l2_subdev *sd, int value) | 1351 | static int ov7670_s_autogain(struct v4l2_subdev *sd, int value) |
| 1423 | { | 1352 | { |
| 1424 | int ret; | 1353 | int ret; |
| @@ -1435,22 +1364,6 @@ static int ov7670_s_autogain(struct v4l2_subdev *sd, int value) | |||
| 1435 | return ret; | 1364 | return ret; |
| 1436 | } | 1365 | } |
| 1437 | 1366 | ||
| 1438 | /* | ||
| 1439 | * Exposure is spread all over the place: top 6 bits in AECHH, middle | ||
| 1440 | * 8 in AECH, and two stashed in COM1 just for the hell of it. | ||
| 1441 | */ | ||
| 1442 | static int ov7670_g_exp(struct v4l2_subdev *sd, __s32 *value) | ||
| 1443 | { | ||
| 1444 | int ret; | ||
| 1445 | unsigned char com1, aech, aechh; | ||
| 1446 | |||
| 1447 | ret = ov7670_read(sd, REG_COM1, &com1) + | ||
| 1448 | ov7670_read(sd, REG_AECH, &aech) + | ||
| 1449 | ov7670_read(sd, REG_AECHH, &aechh); | ||
| 1450 | *value = ((aechh & 0x3f) << 10) | (aech << 2) | (com1 & 0x03); | ||
| 1451 | return ret; | ||
| 1452 | } | ||
| 1453 | |||
| 1454 | static int ov7670_s_exp(struct v4l2_subdev *sd, int value) | 1367 | static int ov7670_s_exp(struct v4l2_subdev *sd, int value) |
| 1455 | { | 1368 | { |
| 1456 | int ret; | 1369 | int ret; |
| @@ -1477,20 +1390,6 @@ static int ov7670_s_exp(struct v4l2_subdev *sd, int value) | |||
| 1477 | /* | 1390 | /* |
| 1478 | * Tweak autoexposure. | 1391 | * Tweak autoexposure. |
| 1479 | */ | 1392 | */ |
| 1480 | static int ov7670_g_autoexp(struct v4l2_subdev *sd, __s32 *value) | ||
| 1481 | { | ||
| 1482 | int ret; | ||
| 1483 | unsigned char com8; | ||
| 1484 | enum v4l2_exposure_auto_type *atype = (enum v4l2_exposure_auto_type *) value; | ||
| 1485 | |||
| 1486 | ret = ov7670_read(sd, REG_COM8, &com8); | ||
| 1487 | if (com8 & COM8_AEC) | ||
| 1488 | *atype = V4L2_EXPOSURE_AUTO; | ||
| 1489 | else | ||
| 1490 | *atype = V4L2_EXPOSURE_MANUAL; | ||
| 1491 | return ret; | ||
| 1492 | } | ||
| 1493 | |||
| 1494 | static int ov7670_s_autoexp(struct v4l2_subdev *sd, | 1393 | static int ov7670_s_autoexp(struct v4l2_subdev *sd, |
| 1495 | enum v4l2_exposure_auto_type value) | 1394 | enum v4l2_exposure_auto_type value) |
| 1496 | { | 1395 | { |
| @@ -1509,90 +1408,60 @@ static int ov7670_s_autoexp(struct v4l2_subdev *sd, | |||
| 1509 | } | 1408 | } |
| 1510 | 1409 | ||
| 1511 | 1410 | ||
| 1512 | 1411 | static int ov7670_g_volatile_ctrl(struct v4l2_ctrl *ctrl) | |
| 1513 | static int ov7670_queryctrl(struct v4l2_subdev *sd, | ||
| 1514 | struct v4l2_queryctrl *qc) | ||
| 1515 | { | 1412 | { |
| 1516 | /* Fill in min, max, step and default value for these controls. */ | 1413 | struct v4l2_subdev *sd = to_sd(ctrl); |
| 1517 | switch (qc->id) { | 1414 | struct ov7670_info *info = to_state(sd); |
| 1518 | case V4L2_CID_BRIGHTNESS: | ||
| 1519 | return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); | ||
| 1520 | case V4L2_CID_CONTRAST: | ||
| 1521 | return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64); | ||
| 1522 | case V4L2_CID_VFLIP: | ||
| 1523 | case V4L2_CID_HFLIP: | ||
| 1524 | return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); | ||
| 1525 | case V4L2_CID_SATURATION: | ||
| 1526 | return v4l2_ctrl_query_fill(qc, 0, 256, 1, 128); | ||
| 1527 | case V4L2_CID_HUE: | ||
| 1528 | return v4l2_ctrl_query_fill(qc, -180, 180, 5, 0); | ||
| 1529 | case V4L2_CID_GAIN: | ||
| 1530 | return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); | ||
| 1531 | case V4L2_CID_AUTOGAIN: | ||
| 1532 | return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); | ||
| 1533 | case V4L2_CID_EXPOSURE: | ||
| 1534 | return v4l2_ctrl_query_fill(qc, 0, 65535, 1, 500); | ||
| 1535 | case V4L2_CID_EXPOSURE_AUTO: | ||
| 1536 | return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); | ||
| 1537 | } | ||
| 1538 | return -EINVAL; | ||
| 1539 | } | ||
| 1540 | 1415 | ||
| 1541 | static int ov7670_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) | ||
| 1542 | { | ||
| 1543 | switch (ctrl->id) { | 1416 | switch (ctrl->id) { |
| 1544 | case V4L2_CID_BRIGHTNESS: | ||
| 1545 | return ov7670_g_brightness(sd, &ctrl->value); | ||
| 1546 | case V4L2_CID_CONTRAST: | ||
| 1547 | return ov7670_g_contrast(sd, &ctrl->value); | ||
| 1548 | case V4L2_CID_SATURATION: | ||
| 1549 | return ov7670_g_sat(sd, &ctrl->value); | ||
| 1550 | case V4L2_CID_HUE: | ||
| 1551 | return ov7670_g_hue(sd, &ctrl->value); | ||
| 1552 | case V4L2_CID_VFLIP: | ||
| 1553 | return ov7670_g_vflip(sd, &ctrl->value); | ||
| 1554 | case V4L2_CID_HFLIP: | ||
| 1555 | return ov7670_g_hflip(sd, &ctrl->value); | ||
| 1556 | case V4L2_CID_GAIN: | ||
| 1557 | return ov7670_g_gain(sd, &ctrl->value); | ||
| 1558 | case V4L2_CID_AUTOGAIN: | 1417 | case V4L2_CID_AUTOGAIN: |
| 1559 | return ov7670_g_autogain(sd, &ctrl->value); | 1418 | return ov7670_g_gain(sd, &info->gain->val); |
| 1560 | case V4L2_CID_EXPOSURE: | ||
| 1561 | return ov7670_g_exp(sd, &ctrl->value); | ||
| 1562 | case V4L2_CID_EXPOSURE_AUTO: | ||
| 1563 | return ov7670_g_autoexp(sd, &ctrl->value); | ||
| 1564 | } | 1419 | } |
| 1565 | return -EINVAL; | 1420 | return -EINVAL; |
| 1566 | } | 1421 | } |
| 1567 | 1422 | ||
| 1568 | static int ov7670_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) | 1423 | static int ov7670_s_ctrl(struct v4l2_ctrl *ctrl) |
| 1569 | { | 1424 | { |
| 1425 | struct v4l2_subdev *sd = to_sd(ctrl); | ||
| 1426 | struct ov7670_info *info = to_state(sd); | ||
| 1427 | |||
| 1570 | switch (ctrl->id) { | 1428 | switch (ctrl->id) { |
| 1571 | case V4L2_CID_BRIGHTNESS: | 1429 | case V4L2_CID_BRIGHTNESS: |
| 1572 | return ov7670_s_brightness(sd, ctrl->value); | 1430 | return ov7670_s_brightness(sd, ctrl->val); |
| 1573 | case V4L2_CID_CONTRAST: | 1431 | case V4L2_CID_CONTRAST: |
| 1574 | return ov7670_s_contrast(sd, ctrl->value); | 1432 | return ov7670_s_contrast(sd, ctrl->val); |
| 1575 | case V4L2_CID_SATURATION: | 1433 | case V4L2_CID_SATURATION: |
| 1576 | return ov7670_s_sat(sd, ctrl->value); | 1434 | return ov7670_s_sat_hue(sd, |
| 1577 | case V4L2_CID_HUE: | 1435 | info->saturation->val, info->hue->val); |
| 1578 | return ov7670_s_hue(sd, ctrl->value); | ||
| 1579 | case V4L2_CID_VFLIP: | 1436 | case V4L2_CID_VFLIP: |
| 1580 | return ov7670_s_vflip(sd, ctrl->value); | 1437 | return ov7670_s_vflip(sd, ctrl->val); |
| 1581 | case V4L2_CID_HFLIP: | 1438 | case V4L2_CID_HFLIP: |
| 1582 | return ov7670_s_hflip(sd, ctrl->value); | 1439 | return ov7670_s_hflip(sd, ctrl->val); |
| 1583 | case V4L2_CID_GAIN: | ||
| 1584 | return ov7670_s_gain(sd, ctrl->value); | ||
| 1585 | case V4L2_CID_AUTOGAIN: | 1440 | case V4L2_CID_AUTOGAIN: |
| 1586 | return ov7670_s_autogain(sd, ctrl->value); | 1441 | /* Only set manual gain if auto gain is not explicitly |
| 1587 | case V4L2_CID_EXPOSURE: | 1442 | turned on. */ |
| 1588 | return ov7670_s_exp(sd, ctrl->value); | 1443 | if (!ctrl->val) { |
| 1444 | /* ov7670_s_gain turns off auto gain */ | ||
| 1445 | return ov7670_s_gain(sd, info->gain->val); | ||
| 1446 | } | ||
| 1447 | return ov7670_s_autogain(sd, ctrl->val); | ||
| 1589 | case V4L2_CID_EXPOSURE_AUTO: | 1448 | case V4L2_CID_EXPOSURE_AUTO: |
| 1590 | return ov7670_s_autoexp(sd, | 1449 | /* Only set manual exposure if auto exposure is not explicitly |
| 1591 | (enum v4l2_exposure_auto_type) ctrl->value); | 1450 | turned on. */ |
| 1451 | if (ctrl->val == V4L2_EXPOSURE_MANUAL) { | ||
| 1452 | /* ov7670_s_exp turns off auto exposure */ | ||
| 1453 | return ov7670_s_exp(sd, info->exposure->val); | ||
| 1454 | } | ||
| 1455 | return ov7670_s_autoexp(sd, ctrl->val); | ||
| 1592 | } | 1456 | } |
| 1593 | return -EINVAL; | 1457 | return -EINVAL; |
| 1594 | } | 1458 | } |
| 1595 | 1459 | ||
| 1460 | static const struct v4l2_ctrl_ops ov7670_ctrl_ops = { | ||
| 1461 | .s_ctrl = ov7670_s_ctrl, | ||
| 1462 | .g_volatile_ctrl = ov7670_g_volatile_ctrl, | ||
| 1463 | }; | ||
| 1464 | |||
| 1596 | static int ov7670_g_chip_ident(struct v4l2_subdev *sd, | 1465 | static int ov7670_g_chip_ident(struct v4l2_subdev *sd, |
| 1597 | struct v4l2_dbg_chip_ident *chip) | 1466 | struct v4l2_dbg_chip_ident *chip) |
| 1598 | { | 1467 | { |
| @@ -1635,9 +1504,13 @@ static int ov7670_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *r | |||
| 1635 | 1504 | ||
| 1636 | static const struct v4l2_subdev_core_ops ov7670_core_ops = { | 1505 | static const struct v4l2_subdev_core_ops ov7670_core_ops = { |
| 1637 | .g_chip_ident = ov7670_g_chip_ident, | 1506 | .g_chip_ident = ov7670_g_chip_ident, |
| 1638 | .g_ctrl = ov7670_g_ctrl, | 1507 | .g_ext_ctrls = v4l2_subdev_g_ext_ctrls, |
| 1639 | .s_ctrl = ov7670_s_ctrl, | 1508 | .try_ext_ctrls = v4l2_subdev_try_ext_ctrls, |
| 1640 | .queryctrl = ov7670_queryctrl, | 1509 | .s_ext_ctrls = v4l2_subdev_s_ext_ctrls, |
| 1510 | .g_ctrl = v4l2_subdev_g_ctrl, | ||
| 1511 | .s_ctrl = v4l2_subdev_s_ctrl, | ||
| 1512 | .queryctrl = v4l2_subdev_queryctrl, | ||
| 1513 | .querymenu = v4l2_subdev_querymenu, | ||
| 1641 | .reset = ov7670_reset, | 1514 | .reset = ov7670_reset, |
| 1642 | .init = ov7670_init, | 1515 | .init = ov7670_init, |
| 1643 | #ifdef CONFIG_VIDEO_ADV_DEBUG | 1516 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
| @@ -1732,7 +1605,6 @@ static int ov7670_probe(struct i2c_client *client, | |||
| 1732 | 1605 | ||
| 1733 | info->devtype = &ov7670_devdata[id->driver_data]; | 1606 | info->devtype = &ov7670_devdata[id->driver_data]; |
| 1734 | info->fmt = &ov7670_formats[0]; | 1607 | info->fmt = &ov7670_formats[0]; |
| 1735 | info->sat = 128; /* Review this */ | ||
| 1736 | info->clkrc = 0; | 1608 | info->clkrc = 0; |
| 1737 | 1609 | ||
| 1738 | /* Set default frame rate to 30 fps */ | 1610 | /* Set default frame rate to 30 fps */ |
| @@ -1743,6 +1615,46 @@ static int ov7670_probe(struct i2c_client *client, | |||
| 1743 | if (info->pclk_hb_disable) | 1615 | if (info->pclk_hb_disable) |
| 1744 | ov7670_write(sd, REG_COM10, COM10_PCLK_HB); | 1616 | ov7670_write(sd, REG_COM10, COM10_PCLK_HB); |
| 1745 | 1617 | ||
| 1618 | v4l2_ctrl_handler_init(&info->hdl, 10); | ||
| 1619 | v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, | ||
| 1620 | V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); | ||
| 1621 | v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, | ||
| 1622 | V4L2_CID_CONTRAST, 0, 127, 1, 64); | ||
| 1623 | v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, | ||
| 1624 | V4L2_CID_VFLIP, 0, 1, 1, 0); | ||
| 1625 | v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, | ||
| 1626 | V4L2_CID_HFLIP, 0, 1, 1, 0); | ||
| 1627 | info->saturation = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, | ||
| 1628 | V4L2_CID_SATURATION, 0, 256, 1, 128); | ||
| 1629 | info->hue = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, | ||
| 1630 | V4L2_CID_HUE, -180, 180, 5, 0); | ||
| 1631 | info->gain = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, | ||
| 1632 | V4L2_CID_GAIN, 0, 255, 1, 128); | ||
| 1633 | info->auto_gain = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, | ||
| 1634 | V4L2_CID_AUTOGAIN, 0, 1, 1, 1); | ||
| 1635 | info->exposure = v4l2_ctrl_new_std(&info->hdl, &ov7670_ctrl_ops, | ||
| 1636 | V4L2_CID_EXPOSURE, 0, 65535, 1, 500); | ||
| 1637 | info->auto_exposure = v4l2_ctrl_new_std_menu(&info->hdl, &ov7670_ctrl_ops, | ||
| 1638 | V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0, | ||
| 1639 | V4L2_EXPOSURE_AUTO); | ||
| 1640 | sd->ctrl_handler = &info->hdl; | ||
| 1641 | if (info->hdl.error) { | ||
| 1642 | int err = info->hdl.error; | ||
| 1643 | |||
| 1644 | v4l2_ctrl_handler_free(&info->hdl); | ||
| 1645 | kfree(info); | ||
| 1646 | return err; | ||
| 1647 | } | ||
| 1648 | /* | ||
| 1649 | * We have checked empirically that hw allows to read back the gain | ||
| 1650 | * value chosen by auto gain but that's not the case for auto exposure. | ||
| 1651 | */ | ||
| 1652 | v4l2_ctrl_auto_cluster(2, &info->auto_gain, 0, true); | ||
| 1653 | v4l2_ctrl_auto_cluster(2, &info->auto_exposure, | ||
| 1654 | V4L2_EXPOSURE_MANUAL, false); | ||
| 1655 | v4l2_ctrl_cluster(2, &info->saturation); | ||
| 1656 | v4l2_ctrl_handler_setup(&info->hdl); | ||
| 1657 | |||
| 1746 | return 0; | 1658 | return 0; |
| 1747 | } | 1659 | } |
| 1748 | 1660 | ||
| @@ -1750,9 +1662,11 @@ static int ov7670_probe(struct i2c_client *client, | |||
| 1750 | static int ov7670_remove(struct i2c_client *client) | 1662 | static int ov7670_remove(struct i2c_client *client) |
| 1751 | { | 1663 | { |
| 1752 | struct v4l2_subdev *sd = i2c_get_clientdata(client); | 1664 | struct v4l2_subdev *sd = i2c_get_clientdata(client); |
| 1665 | struct ov7670_info *info = to_state(sd); | ||
| 1753 | 1666 | ||
| 1754 | v4l2_device_unregister_subdev(sd); | 1667 | v4l2_device_unregister_subdev(sd); |
| 1755 | kfree(to_state(sd)); | 1668 | v4l2_ctrl_handler_free(&info->hdl); |
| 1669 | kfree(info); | ||
| 1756 | return 0; | 1670 | return 0; |
| 1757 | } | 1671 | } |
| 1758 | 1672 | ||
