Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[media] V4L2: soc-camera: move generic functions into a separate file

The sh_mobile_ceu_camera driver implements a generic algorithm for setting
up an optimal client and host scaling and cropping configuration. This
patch makes those functions available for all drivers.

Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>

authored by

Guennadi Liakhovetski and committed by
Mauro Carvalho Chehab
22e0099a eca430c8

+459 -388
+4
drivers/media/platform/soc_camera/Kconfig
··· 8 8 over a bus like PCI or USB. For example some i2c camera connected 9 9 directly to the data bus of an SoC. 10 10 11 + config SOC_CAMERA_SCALE_CROP 12 + tristate 13 + 11 14 config SOC_CAMERA_PLATFORM 12 15 tristate "platform camera support" 13 16 depends on SOC_CAMERA ··· 54 51 tristate "SuperH Mobile CEU Interface driver" 55 52 depends on VIDEO_DEV && SOC_CAMERA && HAS_DMA && HAVE_CLK 56 53 select VIDEOBUF2_DMA_CONTIG 54 + select SOC_CAMERA_SCALE_CROP 57 55 ---help--- 58 56 This is a v4l2 driver for the SuperH Mobile CEU Interface 59 57
+4
drivers/media/platform/soc_camera/Makefile
··· 1 1 obj-$(CONFIG_SOC_CAMERA) += soc_camera.o soc_mediabus.o 2 + obj-$(CONFIG_SOC_CAMERA_SCALE_CROP) += soc_scale_crop.o 3 + 4 + # a platform subdevice driver stub, allowing to support cameras by adding a 5 + # couple of callback functions to the board code 2 6 obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o 3 7 4 8 # soc-camera host drivers have to be linked after camera drivers
+3 -388
drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c
··· 46 46 #include <media/v4l2-mediabus.h> 47 47 #include <media/soc_mediabus.h> 48 48 49 + #include "soc_scale_crop.h" 50 + 49 51 /* register offsets for sh7722 / sh7723 */ 50 52 51 53 #define CAPSR 0x00 /* Capture start register */ ··· 1007 1005 fmt->packing == SOC_MBUS_PACKING_EXTEND16); 1008 1006 } 1009 1007 1010 - static int soc_camera_client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect); 1011 - 1012 1008 static struct soc_camera_device *ctrl_to_icd(struct v4l2_ctrl *ctrl) 1013 1009 { 1014 1010 return container_of(ctrl->handler, struct soc_camera_device, ··· 1194 1194 icd->host_priv = NULL; 1195 1195 } 1196 1196 1197 - /* Check if any dimension of r1 is smaller than respective one of r2 */ 1198 - static bool is_smaller(const struct v4l2_rect *r1, const struct v4l2_rect *r2) 1199 - { 1200 - return r1->width < r2->width || r1->height < r2->height; 1201 - } 1202 - 1203 - /* Check if r1 fails to cover r2 */ 1204 - static bool is_inside(const struct v4l2_rect *r1, const struct v4l2_rect *r2) 1205 - { 1206 - return r1->left > r2->left || r1->top > r2->top || 1207 - r1->left + r1->width < r2->left + r2->width || 1208 - r1->top + r1->height < r2->top + r2->height; 1209 - } 1210 - 1211 - static unsigned int soc_camera_shift_scale(unsigned int size, unsigned int shift, 1212 - unsigned int scale) 1213 - { 1214 - return ((size << shift) + scale / 2) / scale; 1215 - } 1216 - 1217 - static unsigned int soc_camera_calc_scale(unsigned int input, unsigned int shift, 1218 - unsigned int output) 1219 - { 1220 - return soc_camera_shift_scale(input, shift, output); 1221 - } 1222 - 1223 1197 #define scale_down(size, scale) soc_camera_shift_scale(size, 12, scale) 1224 - #define calc_generic_scale(in, out) soc_camera_shift_scale(in, 12, out) 1225 - 1226 - /* Get and store current client crop */ 1227 - static int soc_camera_client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect) 1228 - { 1229 - struct v4l2_crop crop; 1230 - struct v4l2_cropcap cap; 1231 - int ret; 1232 - 1233 - crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 1234 - 1235 - ret = v4l2_subdev_call(sd, video, g_crop, &crop); 1236 - if (!ret) { 1237 - *rect = crop.c; 1238 - return ret; 1239 - } 1240 - 1241 - /* Camera driver doesn't support .g_crop(), assume default rectangle */ 1242 - cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 1243 - 1244 - ret = v4l2_subdev_call(sd, video, cropcap, &cap); 1245 - if (!ret) 1246 - *rect = cap.defrect; 1247 - 1248 - return ret; 1249 - } 1250 - 1251 - /* Client crop has changed, update our sub-rectangle to remain within the area */ 1252 - static void update_subrect(struct v4l2_rect *rect, struct v4l2_rect *subrect) 1253 - { 1254 - if (rect->width < subrect->width) 1255 - subrect->width = rect->width; 1256 - 1257 - if (rect->height < subrect->height) 1258 - subrect->height = rect->height; 1259 - 1260 - if (rect->left > subrect->left) 1261 - subrect->left = rect->left; 1262 - else if (rect->left + rect->width > 1263 - subrect->left + subrect->width) 1264 - subrect->left = rect->left + rect->width - 1265 - subrect->width; 1266 - 1267 - if (rect->top > subrect->top) 1268 - subrect->top = rect->top; 1269 - else if (rect->top + rect->height > 1270 - subrect->top + subrect->height) 1271 - subrect->top = rect->top + rect->height - 1272 - subrect->height; 1273 - } 1274 - 1275 - /* 1276 - * The common for both scaling and cropping iterative approach is: 1277 - * 1. try if the client can produce exactly what requested by the user 1278 - * 2. if (1) failed, try to double the client image until we get one big enough 1279 - * 3. if (2) failed, try to request the maximum image 1280 - */ 1281 - static int soc_camera_client_s_crop(struct v4l2_subdev *sd, 1282 - struct v4l2_crop *crop, struct v4l2_crop *cam_crop, 1283 - struct v4l2_rect *target_rect, struct v4l2_rect *subrect) 1284 - { 1285 - struct v4l2_rect *rect = &crop->c, *cam_rect = &cam_crop->c; 1286 - struct device *dev = sd->v4l2_dev->dev; 1287 - struct v4l2_cropcap cap; 1288 - int ret; 1289 - unsigned int width, height; 1290 - 1291 - v4l2_subdev_call(sd, video, s_crop, crop); 1292 - ret = soc_camera_client_g_rect(sd, cam_rect); 1293 - if (ret < 0) 1294 - return ret; 1295 - 1296 - /* 1297 - * Now cam_crop contains the current camera input rectangle, and it must 1298 - * be within camera cropcap bounds 1299 - */ 1300 - if (!memcmp(rect, cam_rect, sizeof(*rect))) { 1301 - /* Even if camera S_CROP failed, but camera rectangle matches */ 1302 - dev_dbg(dev, "Camera S_CROP successful for %dx%d@%d:%d\n", 1303 - rect->width, rect->height, rect->left, rect->top); 1304 - *target_rect = *cam_rect; 1305 - return 0; 1306 - } 1307 - 1308 - /* Try to fix cropping, that camera hasn't managed to set */ 1309 - dev_geo(dev, "Fix camera S_CROP for %dx%d@%d:%d to %dx%d@%d:%d\n", 1310 - cam_rect->width, cam_rect->height, 1311 - cam_rect->left, cam_rect->top, 1312 - rect->width, rect->height, rect->left, rect->top); 1313 - 1314 - /* We need sensor maximum rectangle */ 1315 - ret = v4l2_subdev_call(sd, video, cropcap, &cap); 1316 - if (ret < 0) 1317 - return ret; 1318 - 1319 - /* Put user requested rectangle within sensor bounds */ 1320 - soc_camera_limit_side(&rect->left, &rect->width, cap.bounds.left, 2, 1321 - cap.bounds.width); 1322 - soc_camera_limit_side(&rect->top, &rect->height, cap.bounds.top, 4, 1323 - cap.bounds.height); 1324 - 1325 - /* 1326 - * Popular special case - some cameras can only handle fixed sizes like 1327 - * QVGA, VGA,... Take care to avoid infinite loop. 1328 - */ 1329 - width = max(cam_rect->width, 2); 1330 - height = max(cam_rect->height, 2); 1331 - 1332 - /* 1333 - * Loop as long as sensor is not covering the requested rectangle and 1334 - * is still within its bounds 1335 - */ 1336 - while (!ret && (is_smaller(cam_rect, rect) || 1337 - is_inside(cam_rect, rect)) && 1338 - (cap.bounds.width > width || cap.bounds.height > height)) { 1339 - 1340 - width *= 2; 1341 - height *= 2; 1342 - 1343 - cam_rect->width = width; 1344 - cam_rect->height = height; 1345 - 1346 - /* 1347 - * We do not know what capabilities the camera has to set up 1348 - * left and top borders. We could try to be smarter in iterating 1349 - * them, e.g., if camera current left is to the right of the 1350 - * target left, set it to the middle point between the current 1351 - * left and minimum left. But that would add too much 1352 - * complexity: we would have to iterate each border separately. 1353 - * Instead we just drop to the left and top bounds. 1354 - */ 1355 - if (cam_rect->left > rect->left) 1356 - cam_rect->left = cap.bounds.left; 1357 - 1358 - if (cam_rect->left + cam_rect->width < rect->left + rect->width) 1359 - cam_rect->width = rect->left + rect->width - 1360 - cam_rect->left; 1361 - 1362 - if (cam_rect->top > rect->top) 1363 - cam_rect->top = cap.bounds.top; 1364 - 1365 - if (cam_rect->top + cam_rect->height < rect->top + rect->height) 1366 - cam_rect->height = rect->top + rect->height - 1367 - cam_rect->top; 1368 - 1369 - v4l2_subdev_call(sd, video, s_crop, cam_crop); 1370 - ret = soc_camera_client_g_rect(sd, cam_rect); 1371 - dev_geo(dev, "Camera S_CROP %d for %dx%d@%d:%d\n", ret, 1372 - cam_rect->width, cam_rect->height, 1373 - cam_rect->left, cam_rect->top); 1374 - } 1375 - 1376 - /* S_CROP must not modify the rectangle */ 1377 - if (is_smaller(cam_rect, rect) || is_inside(cam_rect, rect)) { 1378 - /* 1379 - * The camera failed to configure a suitable cropping, 1380 - * we cannot use the current rectangle, set to max 1381 - */ 1382 - *cam_rect = cap.bounds; 1383 - v4l2_subdev_call(sd, video, s_crop, cam_crop); 1384 - ret = soc_camera_client_g_rect(sd, cam_rect); 1385 - dev_geo(dev, "Camera S_CROP %d for max %dx%d@%d:%d\n", ret, 1386 - cam_rect->width, cam_rect->height, 1387 - cam_rect->left, cam_rect->top); 1388 - } 1389 - 1390 - if (!ret) { 1391 - *target_rect = *cam_rect; 1392 - update_subrect(target_rect, subrect); 1393 - } 1394 - 1395 - return ret; 1396 - } 1397 - 1398 - /* Iterative s_mbus_fmt, also updates cached client crop on success */ 1399 - static int client_s_fmt(struct soc_camera_device *icd, 1400 - struct v4l2_rect *rect, struct v4l2_rect *subrect, 1401 - unsigned int max_width, unsigned int max_height, 1402 - struct v4l2_mbus_framefmt *mf, bool host_can_scale) 1403 - { 1404 - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); 1405 - struct device *dev = icd->parent; 1406 - unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h; 1407 - struct v4l2_cropcap cap; 1408 - bool ceu_1to1; 1409 - int ret; 1410 - 1411 - ret = v4l2_device_call_until_err(sd->v4l2_dev, 1412 - soc_camera_grp_id(icd), video, 1413 - s_mbus_fmt, mf); 1414 - if (ret < 0) 1415 - return ret; 1416 - 1417 - dev_geo(dev, "camera scaled to %ux%u\n", mf->width, mf->height); 1418 - 1419 - if (width == mf->width && height == mf->height) { 1420 - /* Perfect! The client has done it all. */ 1421 - ceu_1to1 = true; 1422 - goto update_cache; 1423 - } 1424 - 1425 - ceu_1to1 = false; 1426 - if (!host_can_scale) 1427 - goto update_cache; 1428 - 1429 - cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 1430 - 1431 - ret = v4l2_subdev_call(sd, video, cropcap, &cap); 1432 - if (ret < 0) 1433 - return ret; 1434 - 1435 - if (max_width > cap.bounds.width) 1436 - max_width = cap.bounds.width; 1437 - if (max_height > cap.bounds.height) 1438 - max_height = cap.bounds.height; 1439 - 1440 - /* Camera set a format, but geometry is not precise, try to improve */ 1441 - tmp_w = mf->width; 1442 - tmp_h = mf->height; 1443 - 1444 - /* width <= max_width && height <= max_height - guaranteed by try_fmt */ 1445 - while ((width > tmp_w || height > tmp_h) && 1446 - tmp_w < max_width && tmp_h < max_height) { 1447 - tmp_w = min(2 * tmp_w, max_width); 1448 - tmp_h = min(2 * tmp_h, max_height); 1449 - mf->width = tmp_w; 1450 - mf->height = tmp_h; 1451 - ret = v4l2_device_call_until_err(sd->v4l2_dev, 1452 - soc_camera_grp_id(icd), video, 1453 - s_mbus_fmt, mf); 1454 - dev_geo(dev, "Camera scaled to %ux%u\n", 1455 - mf->width, mf->height); 1456 - if (ret < 0) { 1457 - /* This shouldn't happen */ 1458 - dev_err(dev, "Client failed to set format: %d\n", ret); 1459 - return ret; 1460 - } 1461 - } 1462 - 1463 - update_cache: 1464 - /* Update cache */ 1465 - ret = soc_camera_client_g_rect(sd, rect); 1466 - if (ret < 0) 1467 - return ret; 1468 - 1469 - if (ceu_1to1) 1470 - *subrect = *rect; 1471 - else 1472 - update_subrect(rect, subrect); 1473 - 1474 - return 0; 1475 - } 1476 - 1477 - /** 1478 - * @icd - soc-camera device 1479 - * @rect - camera cropping window 1480 - * @subrect - part of rect, sent to the user 1481 - * @mf - in- / output camera output window 1482 - * @width - on input: max host input width 1483 - * on output: user width, mapped back to input 1484 - * @height - on input: max host input height 1485 - * on output: user height, mapped back to input 1486 - * @host_can_scale - host can scale this pixel format 1487 - * @shift - shift, used for scaling 1488 - */ 1489 - static int soc_camera_client_scale(struct soc_camera_device *icd, 1490 - struct v4l2_rect *rect, struct v4l2_rect *subrect, 1491 - struct v4l2_mbus_framefmt *mf, 1492 - unsigned int *width, unsigned int *height, 1493 - bool host_can_scale, unsigned int shift) 1494 - { 1495 - struct device *dev = icd->parent; 1496 - struct v4l2_mbus_framefmt mf_tmp = *mf; 1497 - unsigned int scale_h, scale_v; 1498 - int ret; 1499 - 1500 - /* 1501 - * 5. Apply iterative camera S_FMT for camera user window (also updates 1502 - * client crop cache and the imaginary sub-rectangle). 1503 - */ 1504 - ret = client_s_fmt(icd, rect, subrect, *width, *height, 1505 - &mf_tmp, host_can_scale); 1506 - if (ret < 0) 1507 - return ret; 1508 - 1509 - dev_geo(dev, "5: camera scaled to %ux%u\n", 1510 - mf_tmp.width, mf_tmp.height); 1511 - 1512 - /* 6. Retrieve camera output window (g_fmt) */ 1513 - 1514 - /* unneeded - it is already in "mf_tmp" */ 1515 - 1516 - /* 7. Calculate new client scales. */ 1517 - scale_h = soc_camera_calc_scale(rect->width, shift, mf_tmp.width); 1518 - scale_v = soc_camera_calc_scale(rect->height, shift, mf_tmp.height); 1519 - 1520 - mf->width = mf_tmp.width; 1521 - mf->height = mf_tmp.height; 1522 - mf->colorspace = mf_tmp.colorspace; 1523 - 1524 - /* 1525 - * 8. Calculate new CEU crop - apply camera scales to previously 1526 - * updated "effective" crop. 1527 - */ 1528 - *width = soc_camera_shift_scale(subrect->width, shift, scale_h); 1529 - *height = soc_camera_shift_scale(subrect->height, shift, scale_v); 1530 - 1531 - dev_geo(dev, "8: new client sub-window %ux%u\n", *width, *height); 1532 - 1533 - return 0; 1534 - } 1198 + #define calc_generic_scale(in, out) soc_camera_calc_scale(in, 12, out) 1535 1199 1536 1200 /* 1537 1201 * CEU can scale and crop, but we don't want to waste bandwidth and kill the ··· 1351 1687 a->c = cam->subrect; 1352 1688 1353 1689 return 0; 1354 - } 1355 - 1356 - /* 1357 - * Calculate real client output window by applying new scales to the current 1358 - * client crop. New scales are calculated from the requested output format and 1359 - * CEU crop, mapped backed onto the client input (subrect). 1360 - */ 1361 - static void soc_camera_calc_client_output(struct soc_camera_device *icd, 1362 - struct v4l2_rect *rect, struct v4l2_rect *subrect, 1363 - const struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf, 1364 - unsigned int shift) 1365 - { 1366 - struct device *dev = icd->parent; 1367 - unsigned int scale_v, scale_h; 1368 - 1369 - if (subrect->width == rect->width && 1370 - subrect->height == rect->height) { 1371 - /* No sub-cropping */ 1372 - mf->width = pix->width; 1373 - mf->height = pix->height; 1374 - return; 1375 - } 1376 - 1377 - /* 1.-2. Current camera scales and subwin - cached. */ 1378 - 1379 - dev_geo(dev, "2: subwin %ux%u@%u:%u\n", 1380 - subrect->width, subrect->height, 1381 - subrect->left, subrect->top); 1382 - 1383 - /* 1384 - * 3. Calculate new combined scales from input sub-window to requested 1385 - * user window. 1386 - */ 1387 - 1388 - /* 1389 - * TODO: CEU cannot scale images larger than VGA to smaller than SubQCIF 1390 - * (128x96) or larger than VGA 1391 - */ 1392 - scale_h = soc_camera_calc_scale(subrect->width, shift, pix->width); 1393 - scale_v = soc_camera_calc_scale(subrect->height, shift, pix->height); 1394 - 1395 - dev_geo(dev, "3: scales %u:%u\n", scale_h, scale_v); 1396 - 1397 - /* 1398 - * 4. Calculate desired client output window by applying combined scales 1399 - * to client (real) input window. 1400 - */ 1401 - mf->width = soc_camera_shift_scale(rect->width, shift, scale_h); 1402 - mf->height = soc_camera_shift_scale(rect->height, shift, scale_v); 1403 1690 } 1404 1691 1405 1692 /* Similar to set_crop multistage iterative algorithm */
+401
drivers/media/platform/soc_camera/soc_scale_crop.c
··· 1 + /* 2 + * soc-camera generic scaling-cropping manipulation functions 3 + * 4 + * Copyright (C) 2013 Guennadi Liakhovetski <g.liakhovetski@gmx.de> 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation; either version 2 of the License, or 9 + * (at your option) any later version. 10 + */ 11 + 12 + #include <linux/device.h> 13 + #include <linux/module.h> 14 + 15 + #include <media/soc_camera.h> 16 + #include <media/v4l2-common.h> 17 + 18 + #include "soc_scale_crop.h" 19 + 20 + #ifdef DEBUG_GEOMETRY 21 + #define dev_geo dev_info 22 + #else 23 + #define dev_geo dev_dbg 24 + #endif 25 + 26 + /* Check if any dimension of r1 is smaller than respective one of r2 */ 27 + static bool is_smaller(const struct v4l2_rect *r1, const struct v4l2_rect *r2) 28 + { 29 + return r1->width < r2->width || r1->height < r2->height; 30 + } 31 + 32 + /* Check if r1 fails to cover r2 */ 33 + static bool is_inside(const struct v4l2_rect *r1, const struct v4l2_rect *r2) 34 + { 35 + return r1->left > r2->left || r1->top > r2->top || 36 + r1->left + r1->width < r2->left + r2->width || 37 + r1->top + r1->height < r2->top + r2->height; 38 + } 39 + 40 + /* Get and store current client crop */ 41 + int soc_camera_client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect) 42 + { 43 + struct v4l2_crop crop; 44 + struct v4l2_cropcap cap; 45 + int ret; 46 + 47 + crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 48 + 49 + ret = v4l2_subdev_call(sd, video, g_crop, &crop); 50 + if (!ret) { 51 + *rect = crop.c; 52 + return ret; 53 + } 54 + 55 + /* Camera driver doesn't support .g_crop(), assume default rectangle */ 56 + cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 57 + 58 + ret = v4l2_subdev_call(sd, video, cropcap, &cap); 59 + if (!ret) 60 + *rect = cap.defrect; 61 + 62 + return ret; 63 + } 64 + EXPORT_SYMBOL(soc_camera_client_g_rect); 65 + 66 + /* Client crop has changed, update our sub-rectangle to remain within the area */ 67 + static void update_subrect(struct v4l2_rect *rect, struct v4l2_rect *subrect) 68 + { 69 + if (rect->width < subrect->width) 70 + subrect->width = rect->width; 71 + 72 + if (rect->height < subrect->height) 73 + subrect->height = rect->height; 74 + 75 + if (rect->left > subrect->left) 76 + subrect->left = rect->left; 77 + else if (rect->left + rect->width > 78 + subrect->left + subrect->width) 79 + subrect->left = rect->left + rect->width - 80 + subrect->width; 81 + 82 + if (rect->top > subrect->top) 83 + subrect->top = rect->top; 84 + else if (rect->top + rect->height > 85 + subrect->top + subrect->height) 86 + subrect->top = rect->top + rect->height - 87 + subrect->height; 88 + } 89 + 90 + /* 91 + * The common for both scaling and cropping iterative approach is: 92 + * 1. try if the client can produce exactly what requested by the user 93 + * 2. if (1) failed, try to double the client image until we get one big enough 94 + * 3. if (2) failed, try to request the maximum image 95 + */ 96 + int soc_camera_client_s_crop(struct v4l2_subdev *sd, 97 + struct v4l2_crop *crop, struct v4l2_crop *cam_crop, 98 + struct v4l2_rect *target_rect, struct v4l2_rect *subrect) 99 + { 100 + struct v4l2_rect *rect = &crop->c, *cam_rect = &cam_crop->c; 101 + struct device *dev = sd->v4l2_dev->dev; 102 + struct v4l2_cropcap cap; 103 + int ret; 104 + unsigned int width, height; 105 + 106 + v4l2_subdev_call(sd, video, s_crop, crop); 107 + ret = soc_camera_client_g_rect(sd, cam_rect); 108 + if (ret < 0) 109 + return ret; 110 + 111 + /* 112 + * Now cam_crop contains the current camera input rectangle, and it must 113 + * be within camera cropcap bounds 114 + */ 115 + if (!memcmp(rect, cam_rect, sizeof(*rect))) { 116 + /* Even if camera S_CROP failed, but camera rectangle matches */ 117 + dev_dbg(dev, "Camera S_CROP successful for %dx%d@%d:%d\n", 118 + rect->width, rect->height, rect->left, rect->top); 119 + *target_rect = *cam_rect; 120 + return 0; 121 + } 122 + 123 + /* Try to fix cropping, that camera hasn't managed to set */ 124 + dev_geo(dev, "Fix camera S_CROP for %dx%d@%d:%d to %dx%d@%d:%d\n", 125 + cam_rect->width, cam_rect->height, 126 + cam_rect->left, cam_rect->top, 127 + rect->width, rect->height, rect->left, rect->top); 128 + 129 + /* We need sensor maximum rectangle */ 130 + ret = v4l2_subdev_call(sd, video, cropcap, &cap); 131 + if (ret < 0) 132 + return ret; 133 + 134 + /* Put user requested rectangle within sensor bounds */ 135 + soc_camera_limit_side(&rect->left, &rect->width, cap.bounds.left, 2, 136 + cap.bounds.width); 137 + soc_camera_limit_side(&rect->top, &rect->height, cap.bounds.top, 4, 138 + cap.bounds.height); 139 + 140 + /* 141 + * Popular special case - some cameras can only handle fixed sizes like 142 + * QVGA, VGA,... Take care to avoid infinite loop. 143 + */ 144 + width = max(cam_rect->width, 2); 145 + height = max(cam_rect->height, 2); 146 + 147 + /* 148 + * Loop as long as sensor is not covering the requested rectangle and 149 + * is still within its bounds 150 + */ 151 + while (!ret && (is_smaller(cam_rect, rect) || 152 + is_inside(cam_rect, rect)) && 153 + (cap.bounds.width > width || cap.bounds.height > height)) { 154 + 155 + width *= 2; 156 + height *= 2; 157 + 158 + cam_rect->width = width; 159 + cam_rect->height = height; 160 + 161 + /* 162 + * We do not know what capabilities the camera has to set up 163 + * left and top borders. We could try to be smarter in iterating 164 + * them, e.g., if camera current left is to the right of the 165 + * target left, set it to the middle point between the current 166 + * left and minimum left. But that would add too much 167 + * complexity: we would have to iterate each border separately. 168 + * Instead we just drop to the left and top bounds. 169 + */ 170 + if (cam_rect->left > rect->left) 171 + cam_rect->left = cap.bounds.left; 172 + 173 + if (cam_rect->left + cam_rect->width < rect->left + rect->width) 174 + cam_rect->width = rect->left + rect->width - 175 + cam_rect->left; 176 + 177 + if (cam_rect->top > rect->top) 178 + cam_rect->top = cap.bounds.top; 179 + 180 + if (cam_rect->top + cam_rect->height < rect->top + rect->height) 181 + cam_rect->height = rect->top + rect->height - 182 + cam_rect->top; 183 + 184 + v4l2_subdev_call(sd, video, s_crop, cam_crop); 185 + ret = soc_camera_client_g_rect(sd, cam_rect); 186 + dev_geo(dev, "Camera S_CROP %d for %dx%d@%d:%d\n", ret, 187 + cam_rect->width, cam_rect->height, 188 + cam_rect->left, cam_rect->top); 189 + } 190 + 191 + /* S_CROP must not modify the rectangle */ 192 + if (is_smaller(cam_rect, rect) || is_inside(cam_rect, rect)) { 193 + /* 194 + * The camera failed to configure a suitable cropping, 195 + * we cannot use the current rectangle, set to max 196 + */ 197 + *cam_rect = cap.bounds; 198 + v4l2_subdev_call(sd, video, s_crop, cam_crop); 199 + ret = soc_camera_client_g_rect(sd, cam_rect); 200 + dev_geo(dev, "Camera S_CROP %d for max %dx%d@%d:%d\n", ret, 201 + cam_rect->width, cam_rect->height, 202 + cam_rect->left, cam_rect->top); 203 + } 204 + 205 + if (!ret) { 206 + *target_rect = *cam_rect; 207 + update_subrect(target_rect, subrect); 208 + } 209 + 210 + return ret; 211 + } 212 + EXPORT_SYMBOL(soc_camera_client_s_crop); 213 + 214 + /* Iterative s_mbus_fmt, also updates cached client crop on success */ 215 + static int client_s_fmt(struct soc_camera_device *icd, 216 + struct v4l2_rect *rect, struct v4l2_rect *subrect, 217 + unsigned int max_width, unsigned int max_height, 218 + struct v4l2_mbus_framefmt *mf, bool host_can_scale) 219 + { 220 + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); 221 + struct device *dev = icd->parent; 222 + unsigned int width = mf->width, height = mf->height, tmp_w, tmp_h; 223 + struct v4l2_cropcap cap; 224 + bool ceu_1to1; 225 + int ret; 226 + 227 + ret = v4l2_device_call_until_err(sd->v4l2_dev, 228 + soc_camera_grp_id(icd), video, 229 + s_mbus_fmt, mf); 230 + if (ret < 0) 231 + return ret; 232 + 233 + dev_geo(dev, "camera scaled to %ux%u\n", mf->width, mf->height); 234 + 235 + if (width == mf->width && height == mf->height) { 236 + /* Perfect! The client has done it all. */ 237 + ceu_1to1 = true; 238 + goto update_cache; 239 + } 240 + 241 + ceu_1to1 = false; 242 + if (!host_can_scale) 243 + goto update_cache; 244 + 245 + cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 246 + 247 + ret = v4l2_subdev_call(sd, video, cropcap, &cap); 248 + if (ret < 0) 249 + return ret; 250 + 251 + if (max_width > cap.bounds.width) 252 + max_width = cap.bounds.width; 253 + if (max_height > cap.bounds.height) 254 + max_height = cap.bounds.height; 255 + 256 + /* Camera set a format, but geometry is not precise, try to improve */ 257 + tmp_w = mf->width; 258 + tmp_h = mf->height; 259 + 260 + /* width <= max_width && height <= max_height - guaranteed by try_fmt */ 261 + while ((width > tmp_w || height > tmp_h) && 262 + tmp_w < max_width && tmp_h < max_height) { 263 + tmp_w = min(2 * tmp_w, max_width); 264 + tmp_h = min(2 * tmp_h, max_height); 265 + mf->width = tmp_w; 266 + mf->height = tmp_h; 267 + ret = v4l2_device_call_until_err(sd->v4l2_dev, 268 + soc_camera_grp_id(icd), video, 269 + s_mbus_fmt, mf); 270 + dev_geo(dev, "Camera scaled to %ux%u\n", 271 + mf->width, mf->height); 272 + if (ret < 0) { 273 + /* This shouldn't happen */ 274 + dev_err(dev, "Client failed to set format: %d\n", ret); 275 + return ret; 276 + } 277 + } 278 + 279 + update_cache: 280 + /* Update cache */ 281 + ret = soc_camera_client_g_rect(sd, rect); 282 + if (ret < 0) 283 + return ret; 284 + 285 + if (ceu_1to1) 286 + *subrect = *rect; 287 + else 288 + update_subrect(rect, subrect); 289 + 290 + return 0; 291 + } 292 + 293 + /** 294 + * @icd - soc-camera device 295 + * @rect - camera cropping window 296 + * @subrect - part of rect, sent to the user 297 + * @mf - in- / output camera output window 298 + * @width - on input: max host input width 299 + * on output: user width, mapped back to input 300 + * @height - on input: max host input height 301 + * on output: user height, mapped back to input 302 + * @host_can_scale - host can scale this pixel format 303 + * @shift - shift, used for scaling 304 + */ 305 + int soc_camera_client_scale(struct soc_camera_device *icd, 306 + struct v4l2_rect *rect, struct v4l2_rect *subrect, 307 + struct v4l2_mbus_framefmt *mf, 308 + unsigned int *width, unsigned int *height, 309 + bool host_can_scale, unsigned int shift) 310 + { 311 + struct device *dev = icd->parent; 312 + struct v4l2_mbus_framefmt mf_tmp = *mf; 313 + unsigned int scale_h, scale_v; 314 + int ret; 315 + 316 + /* 317 + * 5. Apply iterative camera S_FMT for camera user window (also updates 318 + * client crop cache and the imaginary sub-rectangle). 319 + */ 320 + ret = client_s_fmt(icd, rect, subrect, *width, *height, 321 + &mf_tmp, host_can_scale); 322 + if (ret < 0) 323 + return ret; 324 + 325 + dev_geo(dev, "5: camera scaled to %ux%u\n", 326 + mf_tmp.width, mf_tmp.height); 327 + 328 + /* 6. Retrieve camera output window (g_fmt) */ 329 + 330 + /* unneeded - it is already in "mf_tmp" */ 331 + 332 + /* 7. Calculate new client scales. */ 333 + scale_h = soc_camera_calc_scale(rect->width, shift, mf_tmp.width); 334 + scale_v = soc_camera_calc_scale(rect->height, shift, mf_tmp.height); 335 + 336 + mf->width = mf_tmp.width; 337 + mf->height = mf_tmp.height; 338 + mf->colorspace = mf_tmp.colorspace; 339 + 340 + /* 341 + * 8. Calculate new CEU crop - apply camera scales to previously 342 + * updated "effective" crop. 343 + */ 344 + *width = soc_camera_shift_scale(subrect->width, shift, scale_h); 345 + *height = soc_camera_shift_scale(subrect->height, shift, scale_v); 346 + 347 + dev_geo(dev, "8: new client sub-window %ux%u\n", *width, *height); 348 + 349 + return 0; 350 + } 351 + EXPORT_SYMBOL(soc_camera_client_scale); 352 + 353 + /* 354 + * Calculate real client output window by applying new scales to the current 355 + * client crop. New scales are calculated from the requested output format and 356 + * CEU crop, mapped backed onto the client input (subrect). 357 + */ 358 + void soc_camera_calc_client_output(struct soc_camera_device *icd, 359 + struct v4l2_rect *rect, struct v4l2_rect *subrect, 360 + const struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf, 361 + unsigned int shift) 362 + { 363 + struct device *dev = icd->parent; 364 + unsigned int scale_v, scale_h; 365 + 366 + if (subrect->width == rect->width && 367 + subrect->height == rect->height) { 368 + /* No sub-cropping */ 369 + mf->width = pix->width; 370 + mf->height = pix->height; 371 + return; 372 + } 373 + 374 + /* 1.-2. Current camera scales and subwin - cached. */ 375 + 376 + dev_geo(dev, "2: subwin %ux%u@%u:%u\n", 377 + subrect->width, subrect->height, 378 + subrect->left, subrect->top); 379 + 380 + /* 381 + * 3. Calculate new combined scales from input sub-window to requested 382 + * user window. 383 + */ 384 + 385 + /* 386 + * TODO: CEU cannot scale images larger than VGA to smaller than SubQCIF 387 + * (128x96) or larger than VGA 388 + */ 389 + scale_h = soc_camera_calc_scale(subrect->width, shift, pix->width); 390 + scale_v = soc_camera_calc_scale(subrect->height, shift, pix->height); 391 + 392 + dev_geo(dev, "3: scales %u:%u\n", scale_h, scale_v); 393 + 394 + /* 395 + * 4. Calculate desired client output window by applying combined scales 396 + * to client (real) input window. 397 + */ 398 + mf->width = soc_camera_shift_scale(rect->width, shift, scale_h); 399 + mf->height = soc_camera_shift_scale(rect->height, shift, scale_v); 400 + } 401 + EXPORT_SYMBOL(soc_camera_calc_client_output);
+47
drivers/media/platform/soc_camera/soc_scale_crop.h
··· 1 + /* 2 + * soc-camera generic scaling-cropping manipulation functions 3 + * 4 + * Copyright (C) 2013 Guennadi Liakhovetski <g.liakhovetski@gmx.de> 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation; either version 2 of the License, or 9 + * (at your option) any later version. 10 + */ 11 + 12 + #ifndef SOC_SCALE_CROP_H 13 + #define SOC_SCALE_CROP_H 14 + 15 + #include <linux/kernel.h> 16 + 17 + struct soc_camera_device; 18 + 19 + struct v4l2_crop; 20 + struct v4l2_mbus_framefmt; 21 + struct v4l2_pix_format; 22 + struct v4l2_rect; 23 + struct v4l2_subdev; 24 + 25 + static inline unsigned int soc_camera_shift_scale(unsigned int size, 26 + unsigned int shift, unsigned int scale) 27 + { 28 + return DIV_ROUND_CLOSEST(size << shift, scale); 29 + } 30 + 31 + #define soc_camera_calc_scale(in, shift, out) soc_camera_shift_scale(in, shift, out) 32 + 33 + int soc_camera_client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect); 34 + int soc_camera_client_s_crop(struct v4l2_subdev *sd, 35 + struct v4l2_crop *crop, struct v4l2_crop *cam_crop, 36 + struct v4l2_rect *target_rect, struct v4l2_rect *subrect); 37 + int soc_camera_client_scale(struct soc_camera_device *icd, 38 + struct v4l2_rect *rect, struct v4l2_rect *subrect, 39 + struct v4l2_mbus_framefmt *mf, 40 + unsigned int *width, unsigned int *height, 41 + bool host_can_scale, unsigned int shift); 42 + void soc_camera_calc_client_output(struct soc_camera_device *icd, 43 + struct v4l2_rect *rect, struct v4l2_rect *subrect, 44 + const struct v4l2_pix_format *pix, struct v4l2_mbus_framefmt *mf, 45 + unsigned int shift); 46 + 47 + #endif