Skip to content

Commit 6e93264

Browse files
committed
media: imx219: implement selection api
this patch exposes the hw's crop and compose capabilities by implementing the selection API. Horizontal and vertical binning being separate registers, `imx219_binning_goodness` computes the best possible height and width of the compose specification using the selection flags. Compose operation updates the subdev's format object to keep them in sync. Signed-off-by: Vinay Varma <[email protected]>
1 parent ee0772b commit 6e93264

File tree

1 file changed

+190
-32
lines changed

1 file changed

+190
-32
lines changed

drivers/media/i2c/imx219.c

Lines changed: 190 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <media/v4l2-event.h>
3030
#include <media/v4l2-fwnode.h>
3131
#include <media/v4l2-mediabus.h>
32+
#include <media/v4l2-rect.h>
3233

3334
/* Chip ID */
3435
#define IMX219_REG_CHIP_ID CCI_REG16(0x0000)
@@ -73,6 +74,7 @@
7374
/* V_TIMING internal */
7475
#define IMX219_REG_VTS CCI_REG16(0x0160)
7576
#define IMX219_VTS_MAX 0xffff
77+
#define IMX219_VTS_DEF 1763
7678

7779
#define IMX219_VBLANK_MIN 32
7880

@@ -146,6 +148,7 @@
146148
#define IMX219_PIXEL_ARRAY_TOP 8U
147149
#define IMX219_PIXEL_ARRAY_WIDTH 3280U
148150
#define IMX219_PIXEL_ARRAY_HEIGHT 2464U
151+
#define IMX219_MIN_COMPOSE_SIZE 8U
149152

150153
/* Mode : resolution and related config&values */
151154
struct imx219_mode {
@@ -284,6 +287,8 @@ static const u32 imx219_mbus_formats[] = {
284287
#define IMX219_XCLR_MIN_DELAY_US 6200
285288
#define IMX219_XCLR_DELAY_RANGE_US 1000
286289

290+
static const u32 binning_ratios[] = { 1, 2 };
291+
287292
/* Mode configs */
288293
static const struct imx219_mode supported_modes[] = {
289294
{
@@ -296,19 +301,19 @@ static const struct imx219_mode supported_modes[] = {
296301
/* 1080P 30fps cropped */
297302
.width = 1920,
298303
.height = 1080,
299-
.vts_def = 1763,
304+
.vts_def = IMX219_VTS_DEF,
300305
},
301306
{
302307
/* 2x2 binned 30fps mode */
303308
.width = 1640,
304309
.height = 1232,
305-
.vts_def = 1763,
310+
.vts_def = IMX219_VTS_DEF,
306311
},
307312
{
308313
/* 640x480 30fps mode */
309314
.width = 640,
310315
.height = 480,
311-
.vts_def = 1763,
316+
.vts_def = IMX219_VTS_DEF,
312317
},
313318
};
314319

@@ -809,14 +814,47 @@ static int imx219_enum_frame_size(struct v4l2_subdev *sd,
809814
return 0;
810815
}
811816

817+
static void imx219_refresh_ctrls(struct v4l2_subdev *sd,
818+
struct v4l2_subdev_state *state,
819+
unsigned int vts_def)
820+
{
821+
int exposure_max;
822+
int exposure_def;
823+
int hblank;
824+
struct imx219 *imx219 = to_imx219(sd);
825+
struct v4l2_mbus_framefmt *fmt =
826+
v4l2_subdev_get_pad_format(sd, state, 0);
827+
828+
/* Update limits and set FPS to default */
829+
__v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN,
830+
IMX219_VTS_MAX - fmt->height, 1,
831+
vts_def - fmt->height);
832+
__v4l2_ctrl_s_ctrl(imx219->vblank, vts_def - fmt->height);
833+
/* Update max exposure while meeting expected vblanking */
834+
exposure_max = vts_def - 4;
835+
exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
836+
exposure_max :
837+
IMX219_EXPOSURE_DEFAULT;
838+
__v4l2_ctrl_modify_range(imx219->exposure, imx219->exposure->minimum,
839+
exposure_max, imx219->exposure->step,
840+
exposure_def);
841+
/*
842+
* Currently PPL is fixed to IMX219_PPL_DEFAULT, so hblank
843+
* depends on mode->width only, and is not changeble in any
844+
* way other than changing the mode.
845+
*/
846+
hblank = IMX219_PPL_DEFAULT - fmt->width;
847+
__v4l2_ctrl_modify_range(imx219->hblank, hblank, hblank, 1, hblank);
848+
}
849+
812850
static int imx219_set_pad_format(struct v4l2_subdev *sd,
813851
struct v4l2_subdev_state *state,
814852
struct v4l2_subdev_format *fmt)
815853
{
816854
struct imx219 *imx219 = to_imx219(sd);
817855
const struct imx219_mode *mode;
818856
struct v4l2_mbus_framefmt *format;
819-
struct v4l2_rect *crop;
857+
struct v4l2_rect *crop, *compose;
820858
unsigned int bin_h, bin_v;
821859

822860
mode = v4l2_find_nearest_size(supported_modes,
@@ -842,34 +880,14 @@ static int imx219_set_pad_format(struct v4l2_subdev *sd,
842880
crop->left = (IMX219_NATIVE_WIDTH - crop->width) / 2;
843881
crop->top = (IMX219_NATIVE_HEIGHT - crop->height) / 2;
844882

845-
if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
846-
int exposure_max;
847-
int exposure_def;
848-
int hblank;
849-
850-
/* Update limits and set FPS to default */
851-
__v4l2_ctrl_modify_range(imx219->vblank, IMX219_VBLANK_MIN,
852-
IMX219_VTS_MAX - mode->height, 1,
853-
mode->vts_def - mode->height);
854-
__v4l2_ctrl_s_ctrl(imx219->vblank,
855-
mode->vts_def - mode->height);
856-
/* Update max exposure while meeting expected vblanking */
857-
exposure_max = mode->vts_def - 4;
858-
exposure_def = (exposure_max < IMX219_EXPOSURE_DEFAULT) ?
859-
exposure_max : IMX219_EXPOSURE_DEFAULT;
860-
__v4l2_ctrl_modify_range(imx219->exposure,
861-
imx219->exposure->minimum,
862-
exposure_max, imx219->exposure->step,
863-
exposure_def);
864-
/*
865-
* Currently PPL is fixed to IMX219_PPL_DEFAULT, so hblank
866-
* depends on mode->width only, and is not changeble in any
867-
* way other than changing the mode.
868-
*/
869-
hblank = IMX219_PPL_DEFAULT - mode->width;
870-
__v4l2_ctrl_modify_range(imx219->hblank, hblank, hblank, 1,
871-
hblank);
872-
}
883+
compose = v4l2_subdev_get_pad_compose(sd, state, 0);
884+
compose->width = format->width;
885+
compose->height = format->height;
886+
compose->left = 0;
887+
compose->top = 0;
888+
889+
if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE)
890+
imx219_refresh_ctrls(sd, state, mode->vts_def);
873891

874892
return 0;
875893
}
@@ -884,6 +902,11 @@ static int imx219_get_selection(struct v4l2_subdev *sd,
884902
return 0;
885903
}
886904

905+
case V4L2_SEL_TGT_COMPOSE: {
906+
sel->r = *v4l2_subdev_get_pad_compose(sd, state, 0);
907+
return 0;
908+
}
909+
887910
case V4L2_SEL_TGT_NATIVE_SIZE:
888911
sel->r.top = 0;
889912
sel->r.left = 0;
@@ -900,11 +923,145 @@ static int imx219_get_selection(struct v4l2_subdev *sd,
900923
sel->r.height = IMX219_PIXEL_ARRAY_HEIGHT;
901924

902925
return 0;
926+
927+
case V4L2_SEL_TGT_COMPOSE_DEFAULT:
928+
case V4L2_SEL_TGT_COMPOSE_BOUNDS:
929+
case V4L2_SEL_TGT_COMPOSE_PADDED:
930+
sel->r.top = 0;
931+
sel->r.left = 0;
932+
sel->r.width = IMX219_PIXEL_ARRAY_WIDTH;
933+
sel->r.height = IMX219_PIXEL_ARRAY_HEIGHT;
934+
return 0;
903935
}
904936

905937
return -EINVAL;
906938
}
907939

940+
#define IMX219_ROUND(dim, step, flags) \
941+
((flags) & V4L2_SEL_FLAG_GE ? \
942+
round_up((dim), (step)) : \
943+
((flags) & V4L2_SEL_FLAG_LE ? \
944+
round_down((dim), (step)) : \
945+
round_down((dim) + (step) / 2, (step))))
946+
947+
static int imx219_set_selection_crop(struct v4l2_subdev *sd,
948+
struct v4l2_subdev_state *sd_state,
949+
struct v4l2_subdev_selection *sel)
950+
{
951+
u32 max_binning;
952+
struct v4l2_rect *compose, *crop;
953+
struct v4l2_mbus_framefmt *fmt;
954+
955+
crop = v4l2_subdev_get_pad_crop(sd, sd_state, 0);
956+
if (v4l2_rect_equal(&sel->r, crop))
957+
return false;
958+
max_binning = binning_ratios[ARRAY_SIZE(binning_ratios) - 1];
959+
sel->r.width =
960+
clamp(IMX219_ROUND(sel->r.width, max_binning, sel->flags),
961+
max_binning * IMX219_MIN_COMPOSE_SIZE,
962+
IMX219_PIXEL_ARRAY_WIDTH);
963+
sel->r.height =
964+
clamp(IMX219_ROUND(sel->r.width, max_binning, sel->flags),
965+
max_binning * IMX219_MIN_COMPOSE_SIZE,
966+
IMX219_PIXEL_ARRAY_WIDTH);
967+
sel->r.left =
968+
min_t(u32, sel->r.left, IMX219_PIXEL_ARRAY_LEFT - sel->r.width);
969+
sel->r.top =
970+
min_t(u32, sel->r.top, IMX219_PIXEL_ARRAY_TOP - sel->r.top);
971+
972+
compose = v4l2_subdev_get_pad_compose(sd, sd_state, 0);
973+
fmt = v4l2_subdev_get_pad_format(sd, sd_state, 0);
974+
*crop = sel->r;
975+
compose->height = crop->height;
976+
compose->width = crop->width;
977+
return true;
978+
}
979+
980+
static int imx219_binning_goodness(u32 act, u32 ask, u32 flags)
981+
{
982+
const int goodness = 100000;
983+
int val = 0;
984+
985+
if (flags & V4L2_SEL_FLAG_GE)
986+
if (act < ask)
987+
val -= goodness;
988+
989+
if (flags & V4L2_SEL_FLAG_LE)
990+
if (act > ask)
991+
val -= goodness;
992+
993+
val -= abs(act - ask);
994+
995+
return val;
996+
}
997+
998+
static bool imx219_set_selection_compose(struct v4l2_subdev *sd,
999+
struct v4l2_subdev_state *state,
1000+
struct v4l2_subdev_selection *sel)
1001+
{
1002+
int best_goodness;
1003+
struct v4l2_rect *compose, *crop;
1004+
1005+
compose = v4l2_subdev_get_pad_compose(sd, state, 0);
1006+
if (v4l2_rect_equal(compose, &sel->r))
1007+
return false;
1008+
1009+
crop = v4l2_subdev_get_pad_crop(sd, state, 0);
1010+
1011+
best_goodness = INT_MIN;
1012+
for (int i = 0; i < ARRAY_SIZE(binning_ratios); ++i) {
1013+
u32 width = crop->width / binning_ratios[i];
1014+
int goodness = imx219_binning_goodness(width, sel->r.width,
1015+
sel->flags);
1016+
if (goodness > best_goodness) {
1017+
best_goodness = goodness;
1018+
compose->width = width;
1019+
}
1020+
}
1021+
best_goodness = INT_MIN;
1022+
for (int i = 0; i < ARRAY_SIZE(binning_ratios); ++i) {
1023+
u32 height = crop->height / binning_ratios[i];
1024+
int goodness = imx219_binning_goodness(height, sel->r.height,
1025+
sel->flags);
1026+
if (goodness > best_goodness) {
1027+
best_goodness = goodness;
1028+
compose->height = height;
1029+
}
1030+
}
1031+
return true;
1032+
}
1033+
1034+
static int imx219_set_selection(struct v4l2_subdev *sd,
1035+
struct v4l2_subdev_state *sd_state,
1036+
struct v4l2_subdev_selection *sel)
1037+
{
1038+
bool compose_updated = false;
1039+
1040+
switch (sel->target) {
1041+
case V4L2_SEL_TGT_CROP:
1042+
compose_updated = imx219_set_selection_crop(sd, sd_state, sel);
1043+
break;
1044+
case V4L2_SEL_TGT_COMPOSE:
1045+
compose_updated =
1046+
imx219_set_selection_compose(sd, sd_state, sel);
1047+
break;
1048+
default:
1049+
return -EINVAL;
1050+
}
1051+
if (compose_updated) {
1052+
struct v4l2_rect *compose =
1053+
v4l2_subdev_get_pad_compose(sd, sd_state, 0);
1054+
struct v4l2_mbus_framefmt *fmt =
1055+
v4l2_subdev_get_pad_format(sd, sd_state, 0);
1056+
fmt->height = compose->height;
1057+
fmt->width = compose->width;
1058+
}
1059+
if (compose_updated && sel->which == V4L2_SUBDEV_FORMAT_ACTIVE)
1060+
imx219_refresh_ctrls(sd, sd_state, IMX219_VTS_DEF);
1061+
1062+
return 0;
1063+
}
1064+
9081065
static int imx219_init_cfg(struct v4l2_subdev *sd,
9091066
struct v4l2_subdev_state *state)
9101067
{
@@ -938,6 +1095,7 @@ static const struct v4l2_subdev_pad_ops imx219_pad_ops = {
9381095
.get_fmt = v4l2_subdev_get_fmt,
9391096
.set_fmt = imx219_set_pad_format,
9401097
.get_selection = imx219_get_selection,
1098+
.set_selection = imx219_set_selection,
9411099
.enum_frame_size = imx219_enum_frame_size,
9421100
};
9431101

0 commit comments

Comments
 (0)