diff options
Diffstat (limited to 'drivers/media/i2c/mt9v032.c')
-rw-r--r-- | drivers/media/i2c/mt9v032.c | 115 |
1 files changed, 92 insertions, 23 deletions
diff --git a/drivers/media/i2c/mt9v032.c b/drivers/media/i2c/mt9v032.c index bd3f979a4d49..977f4006edbd 100644 --- a/drivers/media/i2c/mt9v032.c +++ b/drivers/media/i2c/mt9v032.c @@ -17,6 +17,8 @@ #include <linux/i2c.h> #include <linux/log2.h> #include <linux/mutex.h> +#include <linux/of.h> +#include <linux/of_gpio.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/videodev2.h> @@ -26,6 +28,7 @@ #include <media/mt9v032.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> +#include <media/v4l2-of.h> #include <media/v4l2-subdev.h> /* The first four rows are black rows. The active area spans 753x481 pixels. */ @@ -371,12 +374,12 @@ static int __mt9v032_set_power(struct mt9v032 *mt9v032, bool on) */ static struct v4l2_mbus_framefmt * -__mt9v032_get_pad_format(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh, +__mt9v032_get_pad_format(struct mt9v032 *mt9v032, struct v4l2_subdev_pad_config *cfg, unsigned int pad, enum v4l2_subdev_format_whence which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_format(fh, pad); + return v4l2_subdev_get_try_format(&mt9v032->subdev, cfg, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9v032->format; default: @@ -385,12 +388,12 @@ __mt9v032_get_pad_format(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh, } static struct v4l2_rect * -__mt9v032_get_pad_crop(struct mt9v032 *mt9v032, struct v4l2_subdev_fh *fh, +__mt9v032_get_pad_crop(struct mt9v032 *mt9v032, struct v4l2_subdev_pad_config *cfg, unsigned int pad, enum v4l2_subdev_format_whence which) { switch (which) { case V4L2_SUBDEV_FORMAT_TRY: - return v4l2_subdev_get_try_crop(fh, pad); + return v4l2_subdev_get_try_crop(&mt9v032->subdev, cfg, pad); case V4L2_SUBDEV_FORMAT_ACTIVE: return &mt9v032->crop; default: @@ -448,7 +451,7 @@ static int mt9v032_s_stream(struct v4l2_subdev *subdev, int enable) } static int mt9v032_enum_mbus_code(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { if (code->index > 0) @@ -459,7 +462,7 @@ static int mt9v032_enum_mbus_code(struct v4l2_subdev *subdev, } static int mt9v032_enum_frame_size(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { if (fse->index >= 3 || fse->code != MEDIA_BUS_FMT_SGRBG10_1X10) @@ -474,12 +477,12 @@ static int mt9v032_enum_frame_size(struct v4l2_subdev *subdev, } static int mt9v032_get_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { struct mt9v032 *mt9v032 = to_mt9v032(subdev); - format->format = *__mt9v032_get_pad_format(mt9v032, fh, format->pad, + format->format = *__mt9v032_get_pad_format(mt9v032, cfg, format->pad, format->which); return 0; } @@ -509,7 +512,7 @@ static unsigned int mt9v032_calc_ratio(unsigned int input, unsigned int output) } static int mt9v032_set_format(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) { struct mt9v032 *mt9v032 = to_mt9v032(subdev); @@ -520,7 +523,7 @@ static int mt9v032_set_format(struct v4l2_subdev *subdev, unsigned int hratio; unsigned int vratio; - __crop = __mt9v032_get_pad_crop(mt9v032, fh, format->pad, + __crop = __mt9v032_get_pad_crop(mt9v032, cfg, format->pad, format->which); /* Clamp the width and height to avoid dividing by zero. */ @@ -536,7 +539,7 @@ static int mt9v032_set_format(struct v4l2_subdev *subdev, hratio = mt9v032_calc_ratio(__crop->width, width); vratio = mt9v032_calc_ratio(__crop->height, height); - __format = __mt9v032_get_pad_format(mt9v032, fh, format->pad, + __format = __mt9v032_get_pad_format(mt9v032, cfg, format->pad, format->which); __format->width = __crop->width / hratio; __format->height = __crop->height / vratio; @@ -553,7 +556,7 @@ static int mt9v032_set_format(struct v4l2_subdev *subdev, } static int mt9v032_get_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct mt9v032 *mt9v032 = to_mt9v032(subdev); @@ -561,12 +564,12 @@ static int mt9v032_get_selection(struct v4l2_subdev *subdev, if (sel->target != V4L2_SEL_TGT_CROP) return -EINVAL; - sel->r = *__mt9v032_get_pad_crop(mt9v032, fh, sel->pad, sel->which); + sel->r = *__mt9v032_get_pad_crop(mt9v032, cfg, sel->pad, sel->which); return 0; } static int mt9v032_set_selection(struct v4l2_subdev *subdev, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct mt9v032 *mt9v032 = to_mt9v032(subdev); @@ -598,13 +601,13 @@ static int mt9v032_set_selection(struct v4l2_subdev *subdev, rect.height = min_t(unsigned int, rect.height, MT9V032_PIXEL_ARRAY_HEIGHT - rect.top); - __crop = __mt9v032_get_pad_crop(mt9v032, fh, sel->pad, sel->which); + __crop = __mt9v032_get_pad_crop(mt9v032, cfg, sel->pad, sel->which); if (rect.width != __crop->width || rect.height != __crop->height) { /* Reset the output image size if the crop rectangle size has * been modified. */ - __format = __mt9v032_get_pad_format(mt9v032, fh, sel->pad, + __format = __mt9v032_get_pad_format(mt9v032, cfg, sel->pad, sel->which); __format->width = rect.width; __format->height = rect.height; @@ -810,13 +813,13 @@ static int mt9v032_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - crop = v4l2_subdev_get_try_crop(fh, 0); + crop = v4l2_subdev_get_try_crop(subdev, fh->pad, 0); crop->left = MT9V032_COLUMN_START_DEF; crop->top = MT9V032_ROW_START_DEF; crop->width = MT9V032_WINDOW_WIDTH_DEF; crop->height = MT9V032_WINDOW_HEIGHT_DEF; - format = v4l2_subdev_get_try_format(fh, 0); + format = v4l2_subdev_get_try_format(subdev, fh->pad, 0); if (mt9v032->model->color) format->code = MEDIA_BUS_FMT_SGRBG10_1X10; @@ -876,10 +879,58 @@ static const struct regmap_config mt9v032_regmap_config = { * Driver initialization and probing */ +static struct mt9v032_platform_data * +mt9v032_get_pdata(struct i2c_client *client) +{ + struct mt9v032_platform_data *pdata; + struct v4l2_of_endpoint endpoint; + struct device_node *np; + struct property *prop; + + if (!IS_ENABLED(CONFIG_OF) || !client->dev.of_node) + return client->dev.platform_data; + + np = of_graph_get_next_endpoint(client->dev.of_node, NULL); + if (!np) + return NULL; + + if (v4l2_of_parse_endpoint(np, &endpoint) < 0) + goto done; + + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + goto done; + + prop = of_find_property(np, "link-frequencies", NULL); + if (prop) { + u64 *link_freqs; + size_t size = prop->length / sizeof(*link_freqs); + + link_freqs = devm_kcalloc(&client->dev, size, + sizeof(*link_freqs), GFP_KERNEL); + if (!link_freqs) + goto done; + + if (of_property_read_u64_array(np, "link-frequencies", + link_freqs, size) < 0) + goto done; + + pdata->link_freqs = link_freqs; + pdata->link_def_freq = link_freqs[0]; + } + + pdata->clk_pol = !!(endpoint.bus.parallel.flags & + V4L2_MBUS_PCLK_SAMPLE_RISING); + +done: + of_node_put(np); + return pdata; +} + static int mt9v032_probe(struct i2c_client *client, const struct i2c_device_id *did) { - struct mt9v032_platform_data *pdata = client->dev.platform_data; + struct mt9v032_platform_data *pdata = mt9v032_get_pdata(client); struct mt9v032 *mt9v032; unsigned int i; int ret; @@ -961,9 +1012,12 @@ static int mt9v032_probe(struct i2c_client *client, mt9v032->subdev.ctrl_handler = &mt9v032->ctrls; - if (mt9v032->ctrls.error) - printk(KERN_INFO "%s: control initialization error %d\n", - __func__, mt9v032->ctrls.error); + if (mt9v032->ctrls.error) { + dev_err(&client->dev, "control initialization error %d\n", + mt9v032->ctrls.error); + ret = mt9v032->ctrls.error; + goto err; + } mt9v032->crop.left = MT9V032_COLUMN_START_DEF; mt9v032->crop.top = MT9V032_ROW_START_DEF; @@ -1016,7 +1070,6 @@ static int mt9v032_remove(struct i2c_client *client) v4l2_async_unregister_subdev(subdev); v4l2_ctrl_handler_free(&mt9v032->ctrls); - v4l2_device_unregister_subdev(subdev); media_entity_cleanup(&subdev->entity); return 0; @@ -1035,9 +1088,25 @@ static const struct i2c_device_id mt9v032_id[] = { }; MODULE_DEVICE_TABLE(i2c, mt9v032_id); +#if IS_ENABLED(CONFIG_OF) +static const struct of_device_id mt9v032_of_match[] = { + { .compatible = "aptina,mt9v022" }, + { .compatible = "aptina,mt9v022m" }, + { .compatible = "aptina,mt9v024" }, + { .compatible = "aptina,mt9v024m" }, + { .compatible = "aptina,mt9v032" }, + { .compatible = "aptina,mt9v032m" }, + { .compatible = "aptina,mt9v034" }, + { .compatible = "aptina,mt9v034m" }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mt9v032_of_match); +#endif + static struct i2c_driver mt9v032_driver = { .driver = { .name = "mt9v032", + .of_match_table = of_match_ptr(mt9v032_of_match), }, .probe = mt9v032_probe, .remove = mt9v032_remove, |