Skip to content

Commit 35fd0b8

Browse files
committed
video: gc2145: support for CROP
Implements the set_selection and get_selection APIs Which are forwarded to it from video_stm32_dcmi as part of the Pull request. It uses the new messages to allow you to set a crop window on top of the current format window. It also then allows you to move this crop window around in the frame window. With this driver I also updated it to allow any resolution from the displays min to max limits. static const struct video_format_cap fmts[] = { GC2145_VIDEO_FORMAT_CAP_HL(128, 1600, 128, 1200, VIDEO_PIX_FMT_RGB565), GC2145_VIDEO_FORMAT_CAP_HL(128, 1600, 128, 1200, VIDEO_PIX_FMT_YUYV), When the resolution is set, it computes the scale factor. Using the set_selection(VIDEO_SEL_TGT_CROP) allows you define a crop window within the format window. It clamps the ratio to a max of 3 as some other drivers limit it saying it helps with frame rates. Signed-off-by: Kurt Eckhardt <[email protected]>
1 parent d3b2da1 commit 35fd0b8

File tree

1 file changed

+174
-58
lines changed

1 file changed

+174
-58
lines changed

drivers/video/gc2145.c

Lines changed: 174 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -768,14 +768,23 @@ struct gc2145_ctrls {
768768
struct gc2145_data {
769769
struct gc2145_ctrls ctrls;
770770
struct video_format fmt;
771+
struct video_rect crop;
772+
uint16_t format_width;
773+
uint16_t format_height;
774+
uint8_t ratio;
771775
};
772776

773-
#define GC2145_VIDEO_FORMAT_CAP(width, height, format) \
774-
{ \
775-
.pixelformat = format, .width_min = width, .width_max = width, \
776-
.height_min = height, .height_max = height, .width_step = 0, .height_step = 0, \
777+
#define GC2145_VIDEO_FORMAT_CAP_HL(width_l, width_h, height_l, height_h, format, step_w, step_h) \
778+
{ \
779+
.pixelformat = (format), \
780+
.width_min = (width_l), .width_max = (width_h), \
781+
.height_min = (height_l), .height_max = (height_h), \
782+
.width_step = (step_w), .height_step = (step_h), \
777783
}
778784

785+
#define GC2145_VIDEO_FORMAT_CAP(width, height, format) \
786+
GC2145_VIDEO_FORMAT_CAP_HL((width), (width), (height), (height), (format), 0, 0)
787+
779788
#define RESOLUTION_QVGA_W 320
780789
#define RESOLUTION_QVGA_H 240
781790

@@ -785,13 +794,25 @@ struct gc2145_data {
785794
#define RESOLUTION_UXGA_W 1600
786795
#define RESOLUTION_UXGA_H 1200
787796

797+
#define RESOLUTION_MAX_W RESOLUTION_UXGA_W
798+
#define RESOLUTION_MAX_H RESOLUTION_UXGA_H
799+
800+
/* Min not defined - smallest seen implementation is for QQVGA */
801+
#define RESOLUTION_MIN_W 160
802+
#define RESOLUTION_MIN_H 120
803+
788804
static const struct video_format_cap fmts[] = {
789805
GC2145_VIDEO_FORMAT_CAP(RESOLUTION_QVGA_W, RESOLUTION_QVGA_H, VIDEO_PIX_FMT_RGB565),
790806
GC2145_VIDEO_FORMAT_CAP(RESOLUTION_VGA_W, RESOLUTION_VGA_H, VIDEO_PIX_FMT_RGB565),
791807
GC2145_VIDEO_FORMAT_CAP(RESOLUTION_UXGA_W, RESOLUTION_UXGA_H, VIDEO_PIX_FMT_RGB565),
792808
GC2145_VIDEO_FORMAT_CAP(RESOLUTION_QVGA_W, RESOLUTION_QVGA_H, VIDEO_PIX_FMT_YUYV),
793809
GC2145_VIDEO_FORMAT_CAP(RESOLUTION_VGA_W, RESOLUTION_VGA_H, VIDEO_PIX_FMT_YUYV),
794810
GC2145_VIDEO_FORMAT_CAP(RESOLUTION_UXGA_W, RESOLUTION_UXGA_H, VIDEO_PIX_FMT_YUYV),
811+
/* Add catchall resolution */
812+
GC2145_VIDEO_FORMAT_CAP_HL(RESOLUTION_MIN_W, RESOLUTION_MAX_W, RESOLUTION_MIN_H,
813+
RESOLUTION_MAX_H, VIDEO_PIX_FMT_RGB565, 1, 1),
814+
GC2145_VIDEO_FORMAT_CAP_HL(RESOLUTION_MIN_W, RESOLUTION_MAX_W, RESOLUTION_MIN_H,
815+
RESOLUTION_MAX_H, VIDEO_PIX_FMT_YUYV, 1, 1),
795816
{0},
796817
};
797818

@@ -843,44 +864,74 @@ static int gc2145_set_output_format(const struct device *dev, int output_format)
843864
return 0;
844865
}
845866

867+
static int gc2145_set_crop_registers(const struct gc2145_config *cfg, uint16_t x, uint16_t y,
868+
uint16_t w, uint16_t h)
869+
{
870+
int ret;
871+
872+
ret = video_write_cci_reg(&cfg->i2c, GC2145_REG_OUT_WIN_ROW_START, y);
873+
if (ret < 0) {
874+
return ret;
875+
}
876+
ret = video_write_cci_reg(&cfg->i2c, GC2145_REG_OUT_WIN_COL_START, x);
877+
if (ret < 0) {
878+
return ret;
879+
}
880+
ret = video_write_cci_reg(&cfg->i2c, GC2145_REG_OUT_WIN_HEIGHT, h);
881+
if (ret < 0) {
882+
return ret;
883+
}
884+
ret = video_write_cci_reg(&cfg->i2c, GC2145_REG_OUT_WIN_WIDTH, w);
885+
if (ret < 0) {
886+
return ret;
887+
}
888+
889+
/* Enable crop */
890+
return video_write_cci_reg(&cfg->i2c, GC2145_REG_CROP_ENABLE, GC2145_CROP_SET_ENABLE);
891+
}
892+
846893
static int gc2145_set_resolution(const struct device *dev, uint32_t w, uint32_t h)
847894
{
848895
const struct gc2145_config *cfg = dev->config;
896+
struct gc2145_data *drv_data = dev->data;
849897
int ret;
850898

851899
uint16_t win_w;
852900
uint16_t win_h;
853-
uint16_t c_ratio;
854-
uint16_t r_ratio;
855-
uint16_t x;
856-
uint16_t y;
857901
uint16_t win_x;
858902
uint16_t win_y;
859903

860-
/* Add the subsampling factor depending on resolution */
861-
switch (w) {
862-
case RESOLUTION_QVGA_W:
863-
c_ratio = 3;
864-
r_ratio = 3;
865-
break;
866-
case RESOLUTION_VGA_W:
867-
c_ratio = 2;
868-
r_ratio = 2;
869-
break;
870-
case RESOLUTION_UXGA_W:
871-
c_ratio = 1;
872-
r_ratio = 1;
873-
break;
874-
default:
875-
LOG_ERR("Unsupported resolution %d %d", w, h);
904+
if ((w == 0) || (h == 0)) {
876905
return -EIO;
877-
};
906+
}
907+
908+
/* If we are called from set_format, then we compute ratio and initialize crop */
909+
drv_data->ratio = MIN(RESOLUTION_UXGA_W / w, RESOLUTION_UXGA_H / h);
910+
911+
/* make sure we don't end up with ratio of 0 */
912+
if (drv_data->ratio == 0) {
913+
return -EIO;
914+
}
915+
916+
/* Restrict ratio to 3 for faster refresh ? */
917+
if (drv_data->ratio > 3) {
918+
drv_data->ratio = 3;
919+
}
920+
921+
/* remember the width and height passed in */
922+
drv_data->format_width = w;
923+
drv_data->format_height = h;
924+
925+
/* Default to crop rectangle being same size as passed in resolution */
926+
drv_data->crop.left = 0;
927+
drv_data->crop.top = 0;
928+
drv_data->crop.width = w;
929+
drv_data->crop.height = h;
878930

879931
/* Calculates the window boundaries to obtain the desired resolution */
880-
win_w = w * c_ratio;
881-
win_h = h * r_ratio;
882-
x = (((win_w / c_ratio) - w) / 2);
883-
y = (((win_h / r_ratio) - h) / 2);
932+
933+
win_w = w * drv_data->ratio;
934+
win_h = h * drv_data->ratio;
884935
win_x = ((UXGA_HSIZE - win_w) / 2);
885936
win_y = ((UXGA_VSIZE - win_h) / 2);
886937

@@ -909,31 +960,14 @@ static int gc2145_set_resolution(const struct device *dev, uint32_t w, uint32_t
909960
}
910961

911962
/* Set cropping window next. */
912-
ret = video_write_cci_reg(&cfg->i2c, GC2145_REG_OUT_WIN_ROW_START, y);
913-
if (ret < 0) {
914-
return ret;
915-
}
916-
ret = video_write_cci_reg(&cfg->i2c, GC2145_REG_OUT_WIN_COL_START, x);
917-
if (ret < 0) {
918-
return ret;
919-
}
920-
ret = video_write_cci_reg(&cfg->i2c, GC2145_REG_OUT_WIN_HEIGHT, h);
921-
if (ret < 0) {
922-
return ret;
923-
}
924-
ret = video_write_cci_reg(&cfg->i2c, GC2145_REG_OUT_WIN_WIDTH, w);
925-
if (ret < 0) {
926-
return ret;
927-
}
928-
929-
/* Enable crop */
930-
ret = video_write_cci_reg(&cfg->i2c, GC2145_REG_CROP_ENABLE, GC2145_CROP_SET_ENABLE);
963+
ret = gc2145_set_crop_registers(cfg, 0, 0, w, h);
931964
if (ret < 0) {
932965
return ret;
933966
}
934967

935968
/* Set Sub-sampling ratio and mode */
936-
ret = video_write_cci_reg(&cfg->i2c, GC2145_REG_SUBSAMPLE, ((r_ratio << 4) | c_ratio));
969+
ret = video_write_cci_reg(&cfg->i2c, GC2145_REG_SUBSAMPLE,
970+
(drv_data->ratio << 4) | drv_data->ratio);
937971
if (ret < 0) {
938972
return ret;
939973
}
@@ -954,6 +988,52 @@ static int gc2145_set_resolution(const struct device *dev, uint32_t w, uint32_t
954988
return 0;
955989
}
956990

991+
static int gc2145_set_crop(const struct device *dev, struct video_selection *sel)
992+
{
993+
/* set the crop, start off with most of a duplicate of set resolution */
994+
int ret;
995+
const struct gc2145_config *cfg = dev->config;
996+
struct gc2145_data *drv_data = dev->data;
997+
998+
/* Verify the passed in rectangle is valid */
999+
if (((sel->rect.left + sel->rect.width) > drv_data->format_width) ||
1000+
((sel->rect.top + sel->rect.height) > drv_data->format_height)) {
1001+
LOG_INF("(%u %u) %ux%u > %ux%u", sel->rect.left, sel->rect.top,
1002+
sel->rect.width, sel->rect.height,
1003+
drv_data->format_width, drv_data->format_height);
1004+
return -EINVAL;
1005+
}
1006+
1007+
/* if rectangle passed in is same as current, simply return */
1008+
if ((drv_data->crop.left == sel->rect.left) && (drv_data->crop.top == sel->rect.top) &&
1009+
(drv_data->crop.width == sel->rect.width) &&
1010+
(drv_data->crop.height == sel->rect.height)) {
1011+
return 0;
1012+
}
1013+
1014+
/* save out the updated crop window registers */
1015+
ret = video_write_cci_reg(&cfg->i2c, GC2145_REG8(GC2145_REG_RESET),
1016+
GC2145_REG_RESET_P0_REGS);
1017+
if (ret < 0) {
1018+
return ret;
1019+
}
1020+
1021+
ret = gc2145_set_crop_registers(cfg, sel->rect.left, sel->rect.top,
1022+
sel->rect.width, sel->rect.height);
1023+
if (ret < 0) {
1024+
return ret;
1025+
}
1026+
1027+
/* Only if valid do we update our crop rectangle */
1028+
drv_data->crop = sel->rect;
1029+
1030+
/* enqueue/dequeue depend on this being set as well as the crop */
1031+
drv_data->fmt.width = drv_data->crop.width;
1032+
drv_data->fmt.height = drv_data->crop.height;
1033+
1034+
return 0;
1035+
}
1036+
9571037
static int gc2145_check_connection(const struct device *dev)
9581038
{
9591039
const struct gc2145_config *cfg = dev->config;
@@ -1047,7 +1127,7 @@ static int gc2145_set_fmt(const struct device *dev, struct video_format *fmt)
10471127
{
10481128
struct gc2145_data *drv_data = dev->data;
10491129
const struct gc2145_config *cfg = dev->config;
1050-
size_t res = ARRAY_SIZE(fmts);
1130+
size_t idx;
10511131
int ret;
10521132

10531133
if (memcmp(&drv_data->fmt, fmt, sizeof(drv_data->fmt)) == 0) {
@@ -1056,14 +1136,7 @@ static int gc2145_set_fmt(const struct device *dev, struct video_format *fmt)
10561136
}
10571137

10581138
/* Check if camera is capable of handling given format */
1059-
for (int i = 0; i < ARRAY_SIZE(fmts) - 1; i++) {
1060-
if (fmts[i].width_min == fmt->width && fmts[i].height_min == fmt->height &&
1061-
fmts[i].pixelformat == fmt->pixelformat) {
1062-
res = i;
1063-
break;
1064-
}
1065-
}
1066-
if (res == ARRAY_SIZE(fmts)) {
1139+
if (video_format_caps_index(fmts, fmt, &idx) < 0) {
10671140
LOG_ERR("Image format not supported");
10681141
return -ENOTSUP;
10691142
}
@@ -1171,12 +1244,55 @@ static int gc2145_set_ctrl(const struct device *dev, uint32_t id)
11711244
}
11721245
}
11731246

1247+
static int gc2145_set_selection(const struct device *dev, struct video_selection *sel)
1248+
{
1249+
LOG_DBG("called: (%u %u)", sel->type, sel->target);
1250+
if (sel->type != VIDEO_BUF_TYPE_OUTPUT) {
1251+
return -EINVAL;
1252+
}
1253+
1254+
if (sel->target != VIDEO_SEL_TGT_CROP) {
1255+
return -EINVAL;
1256+
}
1257+
1258+
return gc2145_set_crop(dev, sel);
1259+
}
1260+
1261+
static int gc2145_get_selection(const struct device *dev, struct video_selection *sel)
1262+
{
1263+
LOG_DBG("called: (%u %u)", sel->type, sel->target);
1264+
if (sel->type != VIDEO_BUF_TYPE_OUTPUT) {
1265+
return -EINVAL;
1266+
}
1267+
1268+
struct gc2145_data *drv_data = dev->data;
1269+
1270+
switch (sel->target) {
1271+
case VIDEO_SEL_TGT_CROP:
1272+
sel->rect = drv_data->crop;
1273+
break;
1274+
1275+
case VIDEO_SEL_TGT_NATIVE_SIZE:
1276+
sel->rect.top = 0;
1277+
sel->rect.left = 0;
1278+
sel->rect.width = drv_data->format_width;
1279+
sel->rect.height = drv_data->format_height;
1280+
break;
1281+
default:
1282+
return -EINVAL;
1283+
}
1284+
1285+
return 0;
1286+
}
1287+
11741288
static DEVICE_API(video, gc2145_driver_api) = {
11751289
.set_format = gc2145_set_fmt,
11761290
.get_format = gc2145_get_fmt,
11771291
.get_caps = gc2145_get_caps,
11781292
.set_stream = gc2145_set_stream,
11791293
.set_ctrl = gc2145_set_ctrl,
1294+
.set_selection = gc2145_set_selection,
1295+
.get_selection = gc2145_get_selection,
11801296
};
11811297

11821298
static int gc2145_init_controls(const struct device *dev)

0 commit comments

Comments
 (0)