summaryrefslogtreecommitdiff
path: root/drivers/media/i2c/ov772x.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/i2c/ov772x.c')
-rw-r--r--drivers/media/i2c/ov772x.c128
1 files changed, 103 insertions, 25 deletions
diff --git a/drivers/media/i2c/ov772x.c b/drivers/media/i2c/ov772x.c
index 2cc6a678069a..062e1023a411 100644
--- a/drivers/media/i2c/ov772x.c
+++ b/drivers/media/i2c/ov772x.c
@@ -31,6 +31,7 @@
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-event.h>
+#include <media/v4l2-fwnode.h>
#include <media/v4l2-image-sizes.h>
#include <media/v4l2-subdev.h>
@@ -226,7 +227,7 @@
/* COM3 */
#define SWAP_MASK (SWAP_RGB | SWAP_YUV | SWAP_ML)
-#define IMG_MASK (VFLIP_IMG | HFLIP_IMG)
+#define IMG_MASK (VFLIP_IMG | HFLIP_IMG | SCOLOR_TEST)
#define VFLIP_IMG 0x80 /* Vertical flip image ON/OFF selection */
#define HFLIP_IMG 0x40 /* Horizontal mirror image ON/OFF selection */
@@ -268,7 +269,7 @@
#define AF_8x 0x08 /* Add frame when AGC reaches 8x gain */
#define AF_16x 0x0c /* Add frame when AGC reaches 16x gain */
/* AEC max step control */
-#define AEC_NO_LIMIT 0x01 /* 0 : AEC incease step has limit */
+#define AEC_NO_LIMIT 0x01 /* 0 : AEC increase step has limit */
/* 1 : No limit to AEC increase step */
/* CLKRC */
/* Input clock divider register */
@@ -424,6 +425,7 @@ struct ov772x_priv {
const struct ov772x_win_size *win;
struct v4l2_ctrl *vflip_ctrl;
struct v4l2_ctrl *hflip_ctrl;
+ unsigned int test_pattern;
/* band_filter = COM8[5] ? 256 - BDBASE : 0 */
struct v4l2_ctrl *band_filter_ctrl;
unsigned int fps;
@@ -431,9 +433,8 @@ struct ov772x_priv {
struct mutex lock;
int power_count;
int streaming;
-#ifdef CONFIG_MEDIA_CONTROLLER
struct media_pad pad;
-#endif
+ enum v4l2_mbus_type bus_type;
};
/*
@@ -538,6 +539,11 @@ static const struct ov772x_win_size ov772x_win_sizes[] = {
},
};
+static const char * const ov772x_test_pattern_menu[] = {
+ "Disabled",
+ "Vertical Color Bar Type 1",
+};
+
/*
* frame rate settings lists
*/
@@ -581,6 +587,14 @@ static int ov772x_s_stream(struct v4l2_subdev *sd, int enable)
if (priv->streaming == enable)
goto done;
+ if (priv->bus_type == V4L2_MBUS_BT656) {
+ ret = regmap_update_bits(priv->regmap, COM7, ITU656_ON_OFF,
+ enable ?
+ ITU656_ON_OFF : ~ITU656_ON_OFF);
+ if (ret)
+ goto done;
+ }
+
ret = regmap_update_bits(priv->regmap, COM2, SOFT_SLEEP_MODE,
enable ? 0 : SOFT_SLEEP_MODE);
if (ret)
@@ -703,26 +717,42 @@ static int ov772x_set_frame_rate(struct ov772x_priv *priv,
return 0;
}
-static int ov772x_g_frame_interval(struct v4l2_subdev *sd,
- struct v4l2_subdev_frame_interval *ival)
+static int ov772x_get_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_interval *ival)
{
struct ov772x_priv *priv = to_ov772x(sd);
struct v4l2_fract *tpf = &ival->interval;
+ /*
+ * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2
+ * subdev active state API.
+ */
+ if (ival->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
tpf->numerator = 1;
tpf->denominator = priv->fps;
return 0;
}
-static int ov772x_s_frame_interval(struct v4l2_subdev *sd,
- struct v4l2_subdev_frame_interval *ival)
+static int ov772x_set_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *sd_state,
+ struct v4l2_subdev_frame_interval *ival)
{
struct ov772x_priv *priv = to_ov772x(sd);
struct v4l2_fract *tpf = &ival->interval;
unsigned int fps;
int ret = 0;
+ /*
+ * FIXME: Implement support for V4L2_SUBDEV_FORMAT_TRY, using the V4L2
+ * subdev active state API.
+ */
+ if (ival->which != V4L2_SUBDEV_FORMAT_ACTIVE)
+ return -EINVAL;
+
mutex_lock(&priv->lock);
if (priv->streaming) {
@@ -800,6 +830,9 @@ static int ov772x_s_ctrl(struct v4l2_ctrl *ctrl)
}
return ret;
+ case V4L2_CID_TEST_PATTERN:
+ priv->test_pattern = ctrl->val;
+ return 0;
}
return -EINVAL;
@@ -1098,6 +1131,8 @@ static int ov772x_set_params(struct ov772x_priv *priv,
val ^= VFLIP_IMG;
if (priv->hflip_ctrl->val)
val ^= HFLIP_IMG;
+ if (priv->test_pattern)
+ val |= SCOLOR_TEST;
ret = regmap_update_bits(priv->regmap, COM3, SWAP_MASK | IMG_MASK, val);
if (ret < 0)
@@ -1136,7 +1171,7 @@ ov772x_set_fmt_error:
}
static int ov772x_get_selection(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_selection *sel)
{
struct ov772x_priv *priv = to_ov772x(sd);
@@ -1158,7 +1193,7 @@ static int ov772x_get_selection(struct v4l2_subdev *sd,
}
static int ov772x_get_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
struct v4l2_mbus_framefmt *mf = &format->format;
@@ -1177,7 +1212,7 @@ static int ov772x_get_fmt(struct v4l2_subdev *sd,
}
static int ov772x_set_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *format)
{
struct ov772x_priv *priv = to_ov772x(sd);
@@ -1201,7 +1236,7 @@ static int ov772x_set_fmt(struct v4l2_subdev *sd,
mf->xfer_func = V4L2_XFER_FUNC_DEFAULT;
if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
- cfg->try_fmt = *mf;
+ *v4l2_subdev_state_get_format(sd_state, 0) = *mf;
return 0;
}
@@ -1299,7 +1334,7 @@ static const struct v4l2_subdev_core_ops ov772x_subdev_core_ops = {
};
static int ov772x_enum_frame_interval(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_frame_interval_enum *fie)
{
if (fie->pad || fie->index >= ARRAY_SIZE(ov772x_frame_intervals))
@@ -1317,7 +1352,7 @@ static int ov772x_enum_frame_interval(struct v4l2_subdev *sd,
}
static int ov772x_enum_mbus_code(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_mbus_code_enum *code)
{
if (code->pad || code->index >= ARRAY_SIZE(ov772x_cfmts))
@@ -1330,8 +1365,6 @@ static int ov772x_enum_mbus_code(struct v4l2_subdev *sd,
static const struct v4l2_subdev_video_ops ov772x_subdev_video_ops = {
.s_stream = ov772x_s_stream,
- .s_frame_interval = ov772x_s_frame_interval,
- .g_frame_interval = ov772x_g_frame_interval,
};
static const struct v4l2_subdev_pad_ops ov772x_subdev_pad_ops = {
@@ -1340,6 +1373,8 @@ static const struct v4l2_subdev_pad_ops ov772x_subdev_pad_ops = {
.get_selection = ov772x_get_selection,
.get_fmt = ov772x_get_fmt,
.set_fmt = ov772x_set_fmt,
+ .get_frame_interval = ov772x_get_frame_interval,
+ .set_frame_interval = ov772x_set_frame_interval,
};
static const struct v4l2_subdev_ops ov772x_subdev_ops = {
@@ -1348,6 +1383,46 @@ static const struct v4l2_subdev_ops ov772x_subdev_ops = {
.pad = &ov772x_subdev_pad_ops,
};
+static int ov772x_parse_dt(struct i2c_client *client,
+ struct ov772x_priv *priv)
+{
+ struct v4l2_fwnode_endpoint bus_cfg = {
+ .bus_type = V4L2_MBUS_PARALLEL
+ };
+ struct fwnode_handle *ep;
+ int ret;
+
+ ep = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL);
+ if (!ep) {
+ dev_err(&client->dev, "Endpoint node not found\n");
+ return -EINVAL;
+ }
+
+ /*
+ * For backward compatibility with older DTS where the
+ * bus-type property was not mandatory, assume
+ * V4L2_MBUS_PARALLEL as it was the only supported bus at the
+ * time. v4l2_fwnode_endpoint_alloc_parse() will not fail if
+ * 'bus-type' is not specified.
+ */
+ ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
+ if (ret) {
+ bus_cfg = (struct v4l2_fwnode_endpoint)
+ { .bus_type = V4L2_MBUS_BT656 };
+ ret = v4l2_fwnode_endpoint_alloc_parse(ep, &bus_cfg);
+ if (ret)
+ goto error_fwnode_put;
+ }
+
+ priv->bus_type = bus_cfg.bus_type;
+ v4l2_fwnode_endpoint_free(&bus_cfg);
+
+error_fwnode_put:
+ fwnode_handle_put(ep);
+
+ return ret;
+}
+
/*
* i2c_driver function
*/
@@ -1394,10 +1469,14 @@ static int ov772x_probe(struct i2c_client *client)
priv->band_filter_ctrl = v4l2_ctrl_new_std(&priv->hdl, &ov772x_ctrl_ops,
V4L2_CID_BAND_STOP_FILTER,
0, 256, 1, 0);
+ v4l2_ctrl_new_std_menu_items(&priv->hdl, &ov772x_ctrl_ops,
+ V4L2_CID_TEST_PATTERN,
+ ARRAY_SIZE(ov772x_test_pattern_menu) - 1,
+ 0, 0, ov772x_test_pattern_menu);
priv->subdev.ctrl_handler = &priv->hdl;
if (priv->hdl.error) {
ret = priv->hdl.error;
- goto error_mutex_destroy;
+ goto error_ctrl_free;
}
priv->clk = clk_get(&client->dev, NULL);
@@ -1415,17 +1494,19 @@ static int ov772x_probe(struct i2c_client *client)
goto error_clk_put;
}
+ ret = ov772x_parse_dt(client, priv);
+ if (ret)
+ goto error_clk_put;
+
ret = ov772x_video_probe(priv);
if (ret < 0)
goto error_gpio_put;
-#ifdef CONFIG_MEDIA_CONTROLLER
priv->pad.flags = MEDIA_PAD_FL_SOURCE;
priv->subdev.entity.function = MEDIA_ENT_F_CAM_SENSOR;
ret = media_entity_pads_init(&priv->subdev.entity, 1, &priv->pad);
if (ret < 0)
goto error_gpio_put;
-#endif
priv->cfmt = &ov772x_cfmts[0];
priv->win = &ov772x_win_sizes[0];
@@ -1446,13 +1527,12 @@ error_clk_put:
clk_put(priv->clk);
error_ctrl_free:
v4l2_ctrl_handler_free(&priv->hdl);
-error_mutex_destroy:
mutex_destroy(&priv->lock);
return ret;
}
-static int ov772x_remove(struct i2c_client *client)
+static void ov772x_remove(struct i2c_client *client)
{
struct ov772x_priv *priv = to_ov772x(i2c_get_clientdata(client));
@@ -1463,12 +1543,10 @@ static int ov772x_remove(struct i2c_client *client)
v4l2_async_unregister_subdev(&priv->subdev);
v4l2_ctrl_handler_free(&priv->hdl);
mutex_destroy(&priv->lock);
-
- return 0;
}
static const struct i2c_device_id ov772x_id[] = {
- { "ov772x", 0 },
+ { "ov772x" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ov772x_id);
@@ -1485,7 +1563,7 @@ static struct i2c_driver ov772x_i2c_driver = {
.name = "ov772x",
.of_match_table = ov772x_of_match,
},
- .probe_new = ov772x_probe,
+ .probe = ov772x_probe,
.remove = ov772x_remove,
.id_table = ov772x_id,
};