| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * ov2640 Camera Driver |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 7 | 8 | * |
|---|
| 8 | 9 | * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved. |
|---|
| 9 | 10 | * Copyright (C) 2006, OmniVision |
|---|
| 10 | | - * |
|---|
| 11 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 12 | | - * it under the terms of the GNU General Public License version 2 as |
|---|
| 13 | | - * published by the Free Software Foundation. |
|---|
| 14 | 11 | */ |
|---|
| 15 | 12 | |
|---|
| 16 | 13 | #include <linux/init.h> |
|---|
| .. | .. |
|---|
| 26 | 23 | #include <linux/videodev2.h> |
|---|
| 27 | 24 | |
|---|
| 28 | 25 | #include <media/v4l2-device.h> |
|---|
| 26 | +#include <media/v4l2-event.h> |
|---|
| 29 | 27 | #include <media/v4l2-subdev.h> |
|---|
| 30 | 28 | #include <media/v4l2-ctrls.h> |
|---|
| 31 | 29 | #include <media/v4l2-image-sizes.h> |
|---|
| .. | .. |
|---|
| 705 | 703 | return ret; |
|---|
| 706 | 704 | } |
|---|
| 707 | 705 | |
|---|
| 706 | +static const char * const ov2640_test_pattern_menu[] = { |
|---|
| 707 | + "Disabled", |
|---|
| 708 | + "Eight Vertical Colour Bars", |
|---|
| 709 | +}; |
|---|
| 710 | + |
|---|
| 708 | 711 | /* |
|---|
| 709 | 712 | * functions |
|---|
| 710 | 713 | */ |
|---|
| .. | .. |
|---|
| 740 | 743 | case V4L2_CID_HFLIP: |
|---|
| 741 | 744 | val = ctrl->val ? REG04_HFLIP_IMG : 0x00; |
|---|
| 742 | 745 | return ov2640_mask_set(client, REG04, REG04_HFLIP_IMG, val); |
|---|
| 746 | + case V4L2_CID_TEST_PATTERN: |
|---|
| 747 | + val = ctrl->val ? COM7_COLOR_BAR_TEST : 0x00; |
|---|
| 748 | + return ov2640_mask_set(client, COM7, COM7_COLOR_BAR_TEST, val); |
|---|
| 743 | 749 | } |
|---|
| 744 | 750 | |
|---|
| 745 | 751 | return -EINVAL; |
|---|
| .. | .. |
|---|
| 833 | 839 | u8 val; |
|---|
| 834 | 840 | int ret; |
|---|
| 835 | 841 | |
|---|
| 836 | | - if (!win) |
|---|
| 837 | | - return -EINVAL; |
|---|
| 838 | | - |
|---|
| 839 | 842 | switch (code) { |
|---|
| 840 | 843 | case MEDIA_BUS_FMT_RGB565_2X8_BE: |
|---|
| 841 | 844 | dev_dbg(&client->dev, "%s: Selected cfmt RGB565 BE", __func__); |
|---|
| .. | .. |
|---|
| 920 | 923 | if (format->pad) |
|---|
| 921 | 924 | return -EINVAL; |
|---|
| 922 | 925 | |
|---|
| 923 | | - if (!priv->win) { |
|---|
| 924 | | - priv->win = ov2640_select_win(SVGA_WIDTH, SVGA_HEIGHT); |
|---|
| 925 | | - priv->cfmt_code = MEDIA_BUS_FMT_UYVY8_2X8; |
|---|
| 926 | + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { |
|---|
| 927 | +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API |
|---|
| 928 | + mf = v4l2_subdev_get_try_format(sd, cfg, 0); |
|---|
| 929 | + format->format = *mf; |
|---|
| 930 | + return 0; |
|---|
| 931 | +#else |
|---|
| 932 | + return -EINVAL; |
|---|
| 933 | +#endif |
|---|
| 926 | 934 | } |
|---|
| 927 | 935 | |
|---|
| 928 | 936 | mf->width = priv->win->width; |
|---|
| .. | .. |
|---|
| 930 | 938 | mf->code = priv->cfmt_code; |
|---|
| 931 | 939 | mf->colorspace = V4L2_COLORSPACE_SRGB; |
|---|
| 932 | 940 | mf->field = V4L2_FIELD_NONE; |
|---|
| 941 | + mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; |
|---|
| 942 | + mf->quantization = V4L2_QUANTIZATION_DEFAULT; |
|---|
| 943 | + mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; |
|---|
| 933 | 944 | |
|---|
| 934 | 945 | return 0; |
|---|
| 935 | 946 | } |
|---|
| .. | .. |
|---|
| 956 | 967 | |
|---|
| 957 | 968 | mf->field = V4L2_FIELD_NONE; |
|---|
| 958 | 969 | mf->colorspace = V4L2_COLORSPACE_SRGB; |
|---|
| 970 | + mf->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; |
|---|
| 971 | + mf->quantization = V4L2_QUANTIZATION_DEFAULT; |
|---|
| 972 | + mf->xfer_func = V4L2_XFER_FUNC_DEFAULT; |
|---|
| 959 | 973 | |
|---|
| 960 | 974 | switch (mf->code) { |
|---|
| 961 | 975 | case MEDIA_BUS_FMT_RGB565_2X8_BE: |
|---|
| .. | .. |
|---|
| 990 | 1004 | return ret; |
|---|
| 991 | 1005 | } |
|---|
| 992 | 1006 | |
|---|
| 1007 | +static int ov2640_init_cfg(struct v4l2_subdev *sd, |
|---|
| 1008 | + struct v4l2_subdev_pad_config *cfg) |
|---|
| 1009 | +{ |
|---|
| 1010 | +#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API |
|---|
| 1011 | + struct v4l2_mbus_framefmt *try_fmt = |
|---|
| 1012 | + v4l2_subdev_get_try_format(sd, cfg, 0); |
|---|
| 1013 | + const struct ov2640_win_size *win = |
|---|
| 1014 | + ov2640_select_win(SVGA_WIDTH, SVGA_HEIGHT); |
|---|
| 1015 | + |
|---|
| 1016 | + try_fmt->width = win->width; |
|---|
| 1017 | + try_fmt->height = win->height; |
|---|
| 1018 | + try_fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; |
|---|
| 1019 | + try_fmt->colorspace = V4L2_COLORSPACE_SRGB; |
|---|
| 1020 | + try_fmt->field = V4L2_FIELD_NONE; |
|---|
| 1021 | + try_fmt->ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; |
|---|
| 1022 | + try_fmt->quantization = V4L2_QUANTIZATION_DEFAULT; |
|---|
| 1023 | + try_fmt->xfer_func = V4L2_XFER_FUNC_DEFAULT; |
|---|
| 1024 | +#endif |
|---|
| 1025 | + return 0; |
|---|
| 1026 | +} |
|---|
| 1027 | + |
|---|
| 993 | 1028 | static int ov2640_enum_mbus_code(struct v4l2_subdev *sd, |
|---|
| 994 | 1029 | struct v4l2_subdev_pad_config *cfg, |
|---|
| 995 | 1030 | struct v4l2_subdev_mbus_code_enum *code) |
|---|
| .. | .. |
|---|
| 1010 | 1045 | |
|---|
| 1011 | 1046 | switch (sel->target) { |
|---|
| 1012 | 1047 | case V4L2_SEL_TGT_CROP_BOUNDS: |
|---|
| 1013 | | - case V4L2_SEL_TGT_CROP_DEFAULT: |
|---|
| 1014 | 1048 | case V4L2_SEL_TGT_CROP: |
|---|
| 1015 | 1049 | sel->r.left = 0; |
|---|
| 1016 | 1050 | sel->r.top = 0; |
|---|
| .. | .. |
|---|
| 1089 | 1123 | }; |
|---|
| 1090 | 1124 | |
|---|
| 1091 | 1125 | static const struct v4l2_subdev_core_ops ov2640_subdev_core_ops = { |
|---|
| 1126 | + .log_status = v4l2_ctrl_subdev_log_status, |
|---|
| 1127 | + .subscribe_event = v4l2_ctrl_subdev_subscribe_event, |
|---|
| 1128 | + .unsubscribe_event = v4l2_event_subdev_unsubscribe, |
|---|
| 1092 | 1129 | #ifdef CONFIG_VIDEO_ADV_DEBUG |
|---|
| 1093 | 1130 | .g_register = ov2640_g_register, |
|---|
| 1094 | 1131 | .s_register = ov2640_s_register, |
|---|
| .. | .. |
|---|
| 1097 | 1134 | }; |
|---|
| 1098 | 1135 | |
|---|
| 1099 | 1136 | static const struct v4l2_subdev_pad_ops ov2640_subdev_pad_ops = { |
|---|
| 1137 | + .init_cfg = ov2640_init_cfg, |
|---|
| 1100 | 1138 | .enum_mbus_code = ov2640_enum_mbus_code, |
|---|
| 1101 | 1139 | .get_selection = ov2640_get_selection, |
|---|
| 1102 | 1140 | .get_fmt = ov2640_get_fmt, |
|---|
| .. | .. |
|---|
| 1152 | 1190 | /* |
|---|
| 1153 | 1191 | * i2c_driver functions |
|---|
| 1154 | 1192 | */ |
|---|
| 1155 | | -static int ov2640_probe(struct i2c_client *client, |
|---|
| 1156 | | - const struct i2c_device_id *did) |
|---|
| 1193 | +static int ov2640_probe(struct i2c_client *client) |
|---|
| 1157 | 1194 | { |
|---|
| 1158 | 1195 | struct ov2640_priv *priv; |
|---|
| 1159 | | - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); |
|---|
| 1196 | + struct i2c_adapter *adapter = client->adapter; |
|---|
| 1160 | 1197 | int ret; |
|---|
| 1161 | 1198 | |
|---|
| 1162 | 1199 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { |
|---|
| .. | .. |
|---|
| 1182 | 1219 | if (ret) |
|---|
| 1183 | 1220 | goto err_clk; |
|---|
| 1184 | 1221 | |
|---|
| 1222 | + priv->win = ov2640_select_win(SVGA_WIDTH, SVGA_HEIGHT); |
|---|
| 1223 | + priv->cfmt_code = MEDIA_BUS_FMT_UYVY8_2X8; |
|---|
| 1224 | + |
|---|
| 1185 | 1225 | v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops); |
|---|
| 1186 | | - priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; |
|---|
| 1226 | + priv->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | |
|---|
| 1227 | + V4L2_SUBDEV_FL_HAS_EVENTS; |
|---|
| 1187 | 1228 | mutex_init(&priv->lock); |
|---|
| 1188 | | - v4l2_ctrl_handler_init(&priv->hdl, 2); |
|---|
| 1229 | + v4l2_ctrl_handler_init(&priv->hdl, 3); |
|---|
| 1189 | 1230 | priv->hdl.lock = &priv->lock; |
|---|
| 1190 | 1231 | v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops, |
|---|
| 1191 | 1232 | V4L2_CID_VFLIP, 0, 1, 1, 0); |
|---|
| 1192 | 1233 | v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops, |
|---|
| 1193 | 1234 | V4L2_CID_HFLIP, 0, 1, 1, 0); |
|---|
| 1235 | + v4l2_ctrl_new_std_menu_items(&priv->hdl, &ov2640_ctrl_ops, |
|---|
| 1236 | + V4L2_CID_TEST_PATTERN, |
|---|
| 1237 | + ARRAY_SIZE(ov2640_test_pattern_menu) - 1, 0, 0, |
|---|
| 1238 | + ov2640_test_pattern_menu); |
|---|
| 1194 | 1239 | priv->subdev.ctrl_handler = &priv->hdl; |
|---|
| 1195 | 1240 | if (priv->hdl.error) { |
|---|
| 1196 | 1241 | ret = priv->hdl.error; |
|---|
| .. | .. |
|---|
| 1256 | 1301 | .name = "ov2640", |
|---|
| 1257 | 1302 | .of_match_table = of_match_ptr(ov2640_of_match), |
|---|
| 1258 | 1303 | }, |
|---|
| 1259 | | - .probe = ov2640_probe, |
|---|
| 1304 | + .probe_new = ov2640_probe, |
|---|
| 1260 | 1305 | .remove = ov2640_remove, |
|---|
| 1261 | 1306 | .id_table = ov2640_id, |
|---|
| 1262 | 1307 | }; |
|---|