From 32c69fcc785a2f8122c73d44ad160d9cfc4c9615 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 26 Jul 2011 11:38:01 -0300 Subject: [media] V4L: soc-camera: add helper functions for new bus configuration type Add helper functions to process the new media bus configuration type similar to soc_camera_apply_sensor_flags() and soc_camera_bus_param_compatible(). Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- include/media/soc_camera.h | 6 ++++-- include/media/soc_mediabus.h | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 7582952dceae..936a504f0bac 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -300,8 +300,10 @@ static inline void soc_camera_limit_side(int *start, int *length, *start = start_min + length_max - *length; } -extern unsigned long soc_camera_apply_sensor_flags(struct soc_camera_link *icl, - unsigned long flags); +unsigned long soc_camera_apply_sensor_flags(struct soc_camera_link *icl, + unsigned long flags); +unsigned long soc_camera_apply_board_flags(struct soc_camera_link *icl, + const struct v4l2_mbus_config *cfg); /* This is only temporary here - until v4l2-subdev begins to link to video_device */ #include diff --git a/include/media/soc_mediabus.h b/include/media/soc_mediabus.h index fae432544b41..73f1e7eb60f3 100644 --- a/include/media/soc_mediabus.h +++ b/include/media/soc_mediabus.h @@ -82,5 +82,7 @@ const struct soc_mbus_pixelfmt *soc_mbus_get_fmtdesc( s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf); int soc_mbus_samples_per_pixel(const struct soc_mbus_pixelfmt *mf, unsigned int *numerator, unsigned int *denominator); +unsigned int soc_mbus_config_compatible(const struct v4l2_mbus_config *cfg, + unsigned int flags); #endif -- cgit v1.2.3 From 2891f37c261306ae3c3d84775500da3cb9428e33 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 26 Jul 2011 12:20:42 -0300 Subject: [media] V4L: ov772x: rename macros to not pollute the global namespace Macros, defined in a header under include/ should be kept in a local namespace and not pollute the global one. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/ov772x.c | 8 ++++---- include/media/ov772x.h | 25 ++++++++++++------------- 2 files changed, 16 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c index 397870f076c1..458265b51e3c 100644 --- a/drivers/media/video/ov772x.c +++ b/drivers/media/video/ov772x.c @@ -822,13 +822,13 @@ static int ov772x_set_params(struct i2c_client *client, u32 *width, u32 *height, goto ov772x_set_fmt_error; ret = ov772x_mask_set(client, - EDGE_TRSHLD, EDGE_THRESHOLD_MASK, + EDGE_TRSHLD, OV772X_EDGE_THRESHOLD_MASK, priv->info->edgectrl.threshold); if (ret < 0) goto ov772x_set_fmt_error; ret = ov772x_mask_set(client, - EDGE_STRNGT, EDGE_STRENGTH_MASK, + EDGE_STRNGT, OV772X_EDGE_STRENGTH_MASK, priv->info->edgectrl.strength); if (ret < 0) goto ov772x_set_fmt_error; @@ -840,13 +840,13 @@ static int ov772x_set_params(struct i2c_client *client, u32 *width, u32 *height, * set upper and lower limit */ ret = ov772x_mask_set(client, - EDGE_UPPER, EDGE_UPPER_MASK, + EDGE_UPPER, OV772X_EDGE_UPPER_MASK, priv->info->edgectrl.upper); if (ret < 0) goto ov772x_set_fmt_error; ret = ov772x_mask_set(client, - EDGE_LOWER, EDGE_LOWER_MASK, + EDGE_LOWER, OV772X_EDGE_LOWER_MASK, priv->info->edgectrl.lower); if (ret < 0) goto ov772x_set_fmt_error; diff --git a/include/media/ov772x.h b/include/media/ov772x.h index 548bf1155c83..f9e27c0ce901 100644 --- a/include/media/ov772x.h +++ b/include/media/ov772x.h @@ -12,8 +12,6 @@ #ifndef __OV772X_H__ #define __OV772X_H__ -#include - /* for flags */ #define OV772X_FLAG_VFLIP (1 << 0) /* Vertical flip image */ #define OV772X_FLAG_HFLIP (1 << 1) /* Horizontal flip image */ @@ -32,22 +30,23 @@ struct ov772x_edge_ctrl { unsigned char lower; }; -#define OV772X_MANUAL_EDGE_CTRL 0x80 /* un-used bit of strength */ -#define EDGE_STRENGTH_MASK 0x1F -#define EDGE_THRESHOLD_MASK 0x0F -#define EDGE_UPPER_MASK 0xFF -#define EDGE_LOWER_MASK 0xFF +#define OV772X_MANUAL_EDGE_CTRL 0x80 /* un-used bit of strength */ +#define OV772X_EDGE_STRENGTH_MASK 0x1F +#define OV772X_EDGE_THRESHOLD_MASK 0x0F +#define OV772X_EDGE_UPPER_MASK 0xFF +#define OV772X_EDGE_LOWER_MASK 0xFF #define OV772X_AUTO_EDGECTRL(u, l) \ { \ - .upper = (u & EDGE_UPPER_MASK), \ - .lower = (l & EDGE_LOWER_MASK), \ + .upper = (u & OV772X_EDGE_UPPER_MASK), \ + .lower = (l & OV772X_EDGE_LOWER_MASK), \ } -#define OV772X_MANUAL_EDGECTRL(s, t) \ -{ \ - .strength = (s & EDGE_STRENGTH_MASK) | OV772X_MANUAL_EDGE_CTRL,\ - .threshold = (t & EDGE_THRESHOLD_MASK), \ +#define OV772X_MANUAL_EDGECTRL(s, t) \ +{ \ + .strength = (s & OV772X_EDGE_STRENGTH_MASK) | \ + OV772X_MANUAL_EDGE_CTRL, \ + .threshold = (t & OV772X_EDGE_THRESHOLD_MASK), \ } /* -- cgit v1.2.3 From 84c760a5dec0edab857cc02c29ef334722520310 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 27 Jul 2011 10:06:09 -0300 Subject: [media] V4L: soc_camera_platform: support the new mbus-config subdev ops Extend the driver to also support [gs]_mbus_config() subdevice video operations. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/soc_camera_platform.c | 12 ++++++++++++ include/media/soc_camera_platform.h | 3 +++ 2 files changed, 15 insertions(+) (limited to 'include') diff --git a/drivers/media/video/soc_camera_platform.c b/drivers/media/video/soc_camera_platform.c index 8069cd6bc5e8..7045e458a66e 100644 --- a/drivers/media/video/soc_camera_platform.c +++ b/drivers/media/video/soc_camera_platform.c @@ -115,6 +115,17 @@ static int soc_camera_platform_cropcap(struct v4l2_subdev *sd, return 0; } +static int soc_camera_platform_g_mbus_config(struct v4l2_subdev *sd, + struct v4l2_mbus_config *cfg) +{ + struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); + + cfg->flags = p->mbus_param; + cfg->type = p->mbus_type; + + return 0; +} + static struct v4l2_subdev_video_ops platform_subdev_video_ops = { .s_stream = soc_camera_platform_s_stream, .enum_mbus_fmt = soc_camera_platform_enum_fmt, @@ -123,6 +134,7 @@ static struct v4l2_subdev_video_ops platform_subdev_video_ops = { .try_mbus_fmt = soc_camera_platform_fill_fmt, .g_mbus_fmt = soc_camera_platform_fill_fmt, .s_mbus_fmt = soc_camera_platform_fill_fmt, + .g_mbus_config = soc_camera_platform_g_mbus_config, }; static struct v4l2_subdev_ops platform_subdev_ops = { diff --git a/include/media/soc_camera_platform.h b/include/media/soc_camera_platform.h index 74f0fa15ca47..a15f92be8aba 100644 --- a/include/media/soc_camera_platform.h +++ b/include/media/soc_camera_platform.h @@ -13,6 +13,7 @@ #include #include +#include struct device; @@ -21,6 +22,8 @@ struct soc_camera_platform_info { unsigned long format_depth; struct v4l2_mbus_framefmt format; unsigned long bus_param; + unsigned long mbus_param; + enum v4l2_mbus_type mbus_type; struct soc_camera_device *icd; int (*set_capture)(struct soc_camera_platform_info *info, int enable); }; -- cgit v1.2.3 From 9d3baeb4628fbd608282559758a75215ac865f4d Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 27 Jul 2011 10:10:43 -0300 Subject: [media] V4L: soc-camera: compatible bus-width flags With the new subdevice media-bus configuration methods bus-width is not configured along with other bus parameters, instead, it is derived from the data format. With those methods it is convenient to specify supported bus-widths in the platform data as (1 << (width - 1)). We redefine SOCAM_DATAWIDTH_* flags to use the same convention to make platform data seemlessly reusable. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- include/media/soc_camera.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 936a504f0bac..73337cff85a3 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -241,19 +241,19 @@ static inline struct v4l2_queryctrl const *soc_camera_find_qctrl( #define SOCAM_MASTER (1 << 0) #define SOCAM_SLAVE (1 << 1) #define SOCAM_HSYNC_ACTIVE_HIGH (1 << 2) -#define SOCAM_HSYNC_ACTIVE_LOW (1 << 3) +#define SOCAM_HSYNC_ACTIVE_LOW (1 << 6) #define SOCAM_VSYNC_ACTIVE_HIGH (1 << 4) #define SOCAM_VSYNC_ACTIVE_LOW (1 << 5) -#define SOCAM_DATAWIDTH_4 (1 << 6) +#define SOCAM_DATAWIDTH_4 (1 << 3) #define SOCAM_DATAWIDTH_8 (1 << 7) #define SOCAM_DATAWIDTH_9 (1 << 8) #define SOCAM_DATAWIDTH_10 (1 << 9) -#define SOCAM_DATAWIDTH_15 (1 << 10) -#define SOCAM_DATAWIDTH_16 (1 << 11) +#define SOCAM_DATAWIDTH_15 (1 << 14) +#define SOCAM_DATAWIDTH_16 (1 << 15) #define SOCAM_PCLK_SAMPLE_RISING (1 << 12) #define SOCAM_PCLK_SAMPLE_FALLING (1 << 13) -#define SOCAM_DATA_ACTIVE_HIGH (1 << 14) -#define SOCAM_DATA_ACTIVE_LOW (1 << 15) +#define SOCAM_DATA_ACTIVE_HIGH (1 << 10) +#define SOCAM_DATA_ACTIVE_LOW (1 << 11) #define SOCAM_MIPI_1LANE (1 << 16) #define SOCAM_MIPI_2LANE (1 << 17) #define SOCAM_MIPI_3LANE (1 << 18) -- cgit v1.2.3 From 3e1b6b72b9a33a12efbe29f046335098c53dbb58 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 28 Jul 2011 19:02:34 -0300 Subject: [media] V4L: ov772x: remove superfluous soc-camera client operations Now that all soc-camera hosts have been ported to use V4L2 subdevice mediabus-config operations and soc-camera client bus-parameter operations have been made optional, they can be removed. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/ov772x.c | 25 ------------------------- include/media/ov772x.h | 1 - 2 files changed, 26 deletions(-) (limited to 'include') diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c index b70ffba58494..f77e8995fe40 100644 --- a/drivers/media/video/ov772x.c +++ b/drivers/media/video/ov772x.c @@ -621,29 +621,6 @@ static int ov772x_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -static int ov772x_set_bus_param(struct soc_camera_device *icd, - unsigned long flags) -{ - return 0; -} - -static unsigned long ov772x_query_bus_param(struct soc_camera_device *icd) -{ - struct i2c_client *client = to_i2c_client(to_soc_camera_control(icd)); - struct ov772x_priv *priv = i2c_get_clientdata(client); - struct soc_camera_link *icl = to_soc_camera_link(icd); - unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER | - SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH | - SOCAM_DATA_ACTIVE_HIGH; - - if (priv->info->flags & OV772X_FLAG_8BIT) - flags |= SOCAM_DATAWIDTH_8; - else - flags |= SOCAM_DATAWIDTH_10; - - return soc_camera_apply_sensor_flags(icl, flags); -} - static int ov772x_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { struct ov772x_priv *priv = container_of(sd, struct ov772x_priv, subdev); @@ -1070,8 +1047,6 @@ static int ov772x_video_probe(struct soc_camera_device *icd, } static struct soc_camera_ops ov772x_ops = { - .set_bus_param = ov772x_set_bus_param, - .query_bus_param = ov772x_query_bus_param, .controls = ov772x_controls, .num_controls = ARRAY_SIZE(ov772x_controls), }; diff --git a/include/media/ov772x.h b/include/media/ov772x.h index f9e27c0ce901..00dbb7c4feae 100644 --- a/include/media/ov772x.h +++ b/include/media/ov772x.h @@ -15,7 +15,6 @@ /* for flags */ #define OV772X_FLAG_VFLIP (1 << 0) /* Vertical flip image */ #define OV772X_FLAG_HFLIP (1 << 1) /* Horizontal flip image */ -#define OV772X_FLAG_8BIT (1 << 2) /* default 10 bit */ /* * for Edge ctrl -- cgit v1.2.3 From 1067247f56c5a9325332148c0dea42a2aa7e718f Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 28 Jul 2011 18:36:01 -0300 Subject: [media] V4L: soc_camera_platform: remove superfluous soc-camera client operations Now that all soc-camera hosts have been ported to use V4L2 subdevice mediabus-config operations and soc-camera client bus-parameter operations have been made optional, they can be removed. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/soc_camera_platform.c | 31 +------------------------------ include/media/soc_camera_platform.h | 1 - 2 files changed, 1 insertion(+), 31 deletions(-) (limited to 'include') diff --git a/drivers/media/video/soc_camera_platform.c b/drivers/media/video/soc_camera_platform.c index 7045e458a66e..f5ebe59a5555 100644 --- a/drivers/media/video/soc_camera_platform.c +++ b/drivers/media/video/soc_camera_platform.c @@ -30,32 +30,12 @@ static struct soc_camera_platform_priv *get_priv(struct platform_device *pdev) return container_of(subdev, struct soc_camera_platform_priv, subdev); } -static struct soc_camera_platform_info *get_info(struct soc_camera_device *icd) -{ - struct platform_device *pdev = - to_platform_device(to_soc_camera_control(icd)); - return pdev->dev.platform_data; -} - static int soc_camera_platform_s_stream(struct v4l2_subdev *sd, int enable) { struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); return p->set_capture(p, enable); } -static int soc_camera_platform_set_bus_param(struct soc_camera_device *icd, - unsigned long flags) -{ - return 0; -} - -static unsigned long -soc_camera_platform_query_bus_param(struct soc_camera_device *icd) -{ - struct soc_camera_platform_info *p = get_info(icd); - return p->bus_param; -} - static int soc_camera_platform_fill_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) { @@ -142,11 +122,6 @@ static struct v4l2_subdev_ops platform_subdev_ops = { .video = &platform_subdev_video_ops, }; -static struct soc_camera_ops soc_camera_platform_ops = { - .set_bus_param = soc_camera_platform_set_bus_param, - .query_bus_param = soc_camera_platform_query_bus_param, -}; - static int soc_camera_platform_probe(struct platform_device *pdev) { struct soc_camera_host *ici; @@ -175,7 +150,7 @@ static int soc_camera_platform_probe(struct platform_device *pdev) /* Set the control device reference */ icd->control = &pdev->dev; - icd->ops = &soc_camera_platform_ops; + icd->ops = NULL; ici = to_soc_camera_host(icd->parent); @@ -190,7 +165,6 @@ static int soc_camera_platform_probe(struct platform_device *pdev) return ret; evdrs: - icd->ops = NULL; platform_set_drvdata(pdev, NULL); kfree(priv); return ret; @@ -199,11 +173,8 @@ evdrs: static int soc_camera_platform_remove(struct platform_device *pdev) { struct soc_camera_platform_priv *priv = get_priv(pdev); - struct soc_camera_platform_info *p = pdev->dev.platform_data; - struct soc_camera_device *icd = p->icd; v4l2_device_unregister_subdev(&priv->subdev); - icd->ops = NULL; platform_set_drvdata(pdev, NULL); kfree(priv); return 0; diff --git a/include/media/soc_camera_platform.h b/include/media/soc_camera_platform.h index a15f92be8aba..8aa4200a0b1d 100644 --- a/include/media/soc_camera_platform.h +++ b/include/media/soc_camera_platform.h @@ -21,7 +21,6 @@ struct soc_camera_platform_info { const char *format_name; unsigned long format_depth; struct v4l2_mbus_framefmt format; - unsigned long bus_param; unsigned long mbus_param; enum v4l2_mbus_type mbus_type; struct soc_camera_device *icd; -- cgit v1.2.3 From d839fe17a13562897e52e29c13d09ac0435dee85 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 28 Jul 2011 18:42:57 -0300 Subject: [media] V4L: soc-camera: remove soc-camera client bus-param operations and supporting code soc-camera has been completely ported over to V4L2 subdevice mbus-config operations, soc-camera client bus-param operations and supporting code can now be removed. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/soc_camera.c | 34 -------------------------- include/media/soc_camera.h | 52 +++++++--------------------------------- 2 files changed, 8 insertions(+), 78 deletions(-) (limited to 'include') diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index e05d1c7fee8d..ac23916552db 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -141,40 +141,6 @@ unsigned long soc_camera_apply_board_flags(struct soc_camera_link *icl, } EXPORT_SYMBOL(soc_camera_apply_board_flags); -/** - * soc_camera_apply_sensor_flags() - apply platform SOCAM_SENSOR_INVERT_* flags - * @icl: camera platform parameters - * @flags: flags to be inverted according to platform configuration - * @return: resulting flags - */ -unsigned long soc_camera_apply_sensor_flags(struct soc_camera_link *icl, - unsigned long flags) -{ - unsigned long f; - - /* If only one of the two polarities is supported, switch to the opposite */ - if (icl->flags & SOCAM_SENSOR_INVERT_HSYNC) { - f = flags & (SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW); - if (f == SOCAM_HSYNC_ACTIVE_HIGH || f == SOCAM_HSYNC_ACTIVE_LOW) - flags ^= SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW; - } - - if (icl->flags & SOCAM_SENSOR_INVERT_VSYNC) { - f = flags & (SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW); - if (f == SOCAM_VSYNC_ACTIVE_HIGH || f == SOCAM_VSYNC_ACTIVE_LOW) - flags ^= SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW; - } - - if (icl->flags & SOCAM_SENSOR_INVERT_PCLK) { - f = flags & (SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING); - if (f == SOCAM_PCLK_SAMPLE_RISING || f == SOCAM_PCLK_SAMPLE_FALLING) - flags ^= SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING; - } - - return flags; -} -EXPORT_SYMBOL(soc_camera_apply_sensor_flags); - #define pixfmtstr(x) (x) & 0xff, ((x) >> 8) & 0xff, ((x) >> 16) & 0xff, \ ((x) >> 24) & 0xff diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 73337cff85a3..1864e2242d45 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -12,6 +12,7 @@ #ifndef SOC_CAMERA_H #define SOC_CAMERA_H +#include #include #include #include @@ -194,8 +195,6 @@ struct soc_camera_format_xlate { }; struct soc_camera_ops { - unsigned long (*query_bus_param)(struct soc_camera_device *); - int (*set_bus_param)(struct soc_camera_device *, unsigned long); const struct v4l2_queryctrl *controls; int num_controls; }; @@ -238,53 +237,18 @@ static inline struct v4l2_queryctrl const *soc_camera_find_qctrl( return NULL; } -#define SOCAM_MASTER (1 << 0) -#define SOCAM_SLAVE (1 << 1) -#define SOCAM_HSYNC_ACTIVE_HIGH (1 << 2) -#define SOCAM_HSYNC_ACTIVE_LOW (1 << 6) -#define SOCAM_VSYNC_ACTIVE_HIGH (1 << 4) -#define SOCAM_VSYNC_ACTIVE_LOW (1 << 5) -#define SOCAM_DATAWIDTH_4 (1 << 3) -#define SOCAM_DATAWIDTH_8 (1 << 7) -#define SOCAM_DATAWIDTH_9 (1 << 8) -#define SOCAM_DATAWIDTH_10 (1 << 9) -#define SOCAM_DATAWIDTH_15 (1 << 14) -#define SOCAM_DATAWIDTH_16 (1 << 15) -#define SOCAM_PCLK_SAMPLE_RISING (1 << 12) -#define SOCAM_PCLK_SAMPLE_FALLING (1 << 13) -#define SOCAM_DATA_ACTIVE_HIGH (1 << 10) -#define SOCAM_DATA_ACTIVE_LOW (1 << 11) -#define SOCAM_MIPI_1LANE (1 << 16) -#define SOCAM_MIPI_2LANE (1 << 17) -#define SOCAM_MIPI_3LANE (1 << 18) -#define SOCAM_MIPI_4LANE (1 << 19) -#define SOCAM_MIPI (SOCAM_MIPI_1LANE | SOCAM_MIPI_2LANE | \ - SOCAM_MIPI_3LANE | SOCAM_MIPI_4LANE) +#define SOCAM_DATAWIDTH(x) BIT((x) - 1) +#define SOCAM_DATAWIDTH_4 SOCAM_DATAWIDTH(4) +#define SOCAM_DATAWIDTH_8 SOCAM_DATAWIDTH(8) +#define SOCAM_DATAWIDTH_9 SOCAM_DATAWIDTH(9) +#define SOCAM_DATAWIDTH_10 SOCAM_DATAWIDTH(10) +#define SOCAM_DATAWIDTH_15 SOCAM_DATAWIDTH(15) +#define SOCAM_DATAWIDTH_16 SOCAM_DATAWIDTH(16) #define SOCAM_DATAWIDTH_MASK (SOCAM_DATAWIDTH_4 | SOCAM_DATAWIDTH_8 | \ SOCAM_DATAWIDTH_9 | SOCAM_DATAWIDTH_10 | \ SOCAM_DATAWIDTH_15 | SOCAM_DATAWIDTH_16) -static inline unsigned long soc_camera_bus_param_compatible( - unsigned long camera_flags, unsigned long bus_flags) -{ - unsigned long common_flags, hsync, vsync, pclk, data, buswidth, mode; - unsigned long mipi; - - common_flags = camera_flags & bus_flags; - - hsync = common_flags & (SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW); - vsync = common_flags & (SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW); - pclk = common_flags & (SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING); - data = common_flags & (SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_LOW); - mode = common_flags & (SOCAM_MASTER | SOCAM_SLAVE); - buswidth = common_flags & SOCAM_DATAWIDTH_MASK; - mipi = common_flags & SOCAM_MIPI; - - return ((!hsync || !vsync || !pclk || !data || !mode || !buswidth) && !mipi) ? 0 : - common_flags; -} - static inline void soc_camera_limit_side(int *start, int *length, unsigned int start_min, unsigned int length_min, unsigned int length_max) -- cgit v1.2.3 From ebc087d0905c41d7fe450866eb1afd1f661cba76 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 31 Aug 2011 06:51:10 -0300 Subject: [media] V4L: add a new videobuf2 buffer state VB2_BUF_STATE_PREPARED This patch prepares for a better separation of the buffer preparation stage. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/videobuf2-core.c | 61 +++++++++++++++++++++++------------- include/media/videobuf2-core.h | 2 ++ 2 files changed, 41 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index 3f5c7a38e6e8..b88b5b08792b 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c @@ -366,6 +366,7 @@ static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b) b->flags |= V4L2_BUF_FLAG_DONE; break; case VB2_BUF_STATE_DEQUEUED: + case VB2_BUF_STATE_PREPARED: /* nothing */ break; } @@ -832,6 +833,33 @@ static void __enqueue_in_driver(struct vb2_buffer *vb) q->ops->buf_queue(vb); } +static int __buf_prepare(struct vb2_buffer *vb, struct v4l2_buffer *b) +{ + struct vb2_queue *q = vb->vb2_queue; + int ret; + + switch (q->memory) { + case V4L2_MEMORY_MMAP: + ret = __qbuf_mmap(vb, b); + break; + case V4L2_MEMORY_USERPTR: + ret = __qbuf_userptr(vb, b); + break; + default: + WARN(1, "Invalid queue type\n"); + ret = -EINVAL; + } + + if (!ret) + ret = call_qop(q, buf_prepare, vb); + if (ret) + dprintk(1, "qbuf: buffer preparation failed: %d\n", ret); + else + vb->state = VB2_BUF_STATE_PREPARED; + + return ret; +} + /** * vb2_qbuf() - Queue a buffer from userspace * @q: videobuf2 queue @@ -841,8 +869,8 @@ static void __enqueue_in_driver(struct vb2_buffer *vb) * Should be called from vidioc_qbuf ioctl handler of a driver. * This function: * 1) verifies the passed buffer, - * 2) calls buf_prepare callback in the driver (if provided), in which - * driver-specific buffer initialization can be performed, + * 2) if necessary, calls buf_prepare callback in the driver (if provided), in + * which driver-specific buffer initialization can be performed, * 3) if streaming is on, queues the buffer in driver by the means of buf_queue * callback for processing. * @@ -852,7 +880,7 @@ static void __enqueue_in_driver(struct vb2_buffer *vb) int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) { struct vb2_buffer *vb; - int ret = 0; + int ret; if (q->fileio) { dprintk(1, "qbuf: file io in progress\n"); @@ -881,29 +909,18 @@ int vb2_qbuf(struct vb2_queue *q, struct v4l2_buffer *b) return -EINVAL; } - if (vb->state != VB2_BUF_STATE_DEQUEUED) { + switch (vb->state) { + case VB2_BUF_STATE_DEQUEUED: + ret = __buf_prepare(vb, b); + if (ret) + return ret; + case VB2_BUF_STATE_PREPARED: + break; + default: dprintk(1, "qbuf: buffer already in use\n"); return -EINVAL; } - if (q->memory == V4L2_MEMORY_MMAP) - ret = __qbuf_mmap(vb, b); - else if (q->memory == V4L2_MEMORY_USERPTR) - ret = __qbuf_userptr(vb, b); - else { - WARN(1, "Invalid queue type\n"); - return -EINVAL; - } - - if (ret) - return ret; - - ret = call_qop(q, buf_prepare, vb); - if (ret) { - dprintk(1, "qbuf: buffer preparation failed\n"); - return ret; - } - /* * Add to the queued buffers list, a buffer will stay on it until * dequeued in dqbuf. diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index ea55c08eddfb..dbd10acc8e80 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -105,6 +105,7 @@ enum vb2_fileio_flags { /** * enum vb2_buffer_state - current video buffer state * @VB2_BUF_STATE_DEQUEUED: buffer under userspace control + * @VB2_BUF_STATE_PREPARED: buffer prepared in videobuf and by the driver * @VB2_BUF_STATE_QUEUED: buffer queued in videobuf, but not in driver * @VB2_BUF_STATE_ACTIVE: buffer queued in driver and possibly used * in a hardware operation @@ -116,6 +117,7 @@ enum vb2_fileio_flags { */ enum vb2_buffer_state { VB2_BUF_STATE_DEQUEUED, + VB2_BUF_STATE_PREPARED, VB2_BUF_STATE_QUEUED, VB2_BUF_STATE_ACTIVE, VB2_BUF_STATE_DONE, -- cgit v1.2.3 From 2150158b31a3290cc883cf6dea4f5d6803b6b811 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 28 Sep 2011 11:34:06 -0300 Subject: [media] V4L: add two new ioctl()s for multi-size videobuffer management A possibility to preallocate and initialise buffers of different sizes in V4L2 is required for an efficient implementation of a snapshot mode. This patch adds two new ioctl()s: VIDIOC_CREATE_BUFS and VIDIOC_PREPARE_BUF and defines respective data structures. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-compat-ioctl32.c | 67 +++++++++++++++++++++++++++---- drivers/media/video/v4l2-ioctl.c | 36 +++++++++++++++++ include/linux/videodev2.h | 18 +++++++++ include/media/v4l2-ioctl.h | 2 + 4 files changed, 115 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index 61979b70f388..e77e0cfc9312 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -159,11 +159,16 @@ struct v4l2_format32 { } fmt; }; -static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up) +struct v4l2_create_buffers32 { + __u32 index; /* output: buffers index...index + count - 1 have been created */ + __u32 count; + enum v4l2_memory memory; + struct v4l2_format32 format; /* filled in by the user, plane sizes calculated by the driver */ + __u32 reserved[8]; +}; + +static int __get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up) { - if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32)) || - get_user(kp->type, &up->type)) - return -EFAULT; switch (kp->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_OUTPUT: @@ -192,11 +197,24 @@ static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user } } -static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up) +static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up) +{ + if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_format32)) || + get_user(kp->type, &up->type)) + return -EFAULT; + return __get_v4l2_format32(kp, up); +} + +static int get_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up) +{ + if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_create_buffers32)) || + copy_from_user(kp, up, offsetof(struct v4l2_create_buffers32, format.fmt))) + return -EFAULT; + return __get_v4l2_format32(&kp->format, &up->format); +} + +static int __put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up) { - if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32)) || - put_user(kp->type, &up->type)) - return -EFAULT; switch (kp->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_OUTPUT: @@ -225,6 +243,22 @@ static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user } } +static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user *up) +{ + if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_format32)) || + put_user(kp->type, &up->type)) + return -EFAULT; + return __put_v4l2_format32(kp, up); +} + +static int put_v4l2_create32(struct v4l2_create_buffers *kp, struct v4l2_create_buffers32 __user *up) +{ + if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_create_buffers32)) || + copy_to_user(up, kp, offsetof(struct v4l2_create_buffers32, format.fmt))) + return -EFAULT; + return __put_v4l2_format32(&kp->format, &up->format); +} + struct v4l2_standard32 { __u32 index; __u32 id[2]; /* __u64 would get the alignment wrong */ @@ -702,6 +736,8 @@ static int put_v4l2_event32(struct v4l2_event *kp, struct v4l2_event32 __user *u #define VIDIOC_S_EXT_CTRLS32 _IOWR('V', 72, struct v4l2_ext_controls32) #define VIDIOC_TRY_EXT_CTRLS32 _IOWR('V', 73, struct v4l2_ext_controls32) #define VIDIOC_DQEVENT32 _IOR ('V', 89, struct v4l2_event32) +#define VIDIOC_CREATE_BUFS32 _IOWR('V', 92, struct v4l2_create_buffers32) +#define VIDIOC_PREPARE_BUF32 _IOWR('V', 93, struct v4l2_buffer32) #define VIDIOC_OVERLAY32 _IOW ('V', 14, s32) #define VIDIOC_STREAMON32 _IOW ('V', 18, s32) @@ -721,6 +757,7 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar struct v4l2_standard v2s; struct v4l2_ext_controls v2ecs; struct v4l2_event v2ev; + struct v4l2_create_buffers v2crt; unsigned long vx; int vi; } karg; @@ -751,6 +788,8 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar case VIDIOC_S_INPUT32: cmd = VIDIOC_S_INPUT; break; case VIDIOC_G_OUTPUT32: cmd = VIDIOC_G_OUTPUT; break; case VIDIOC_S_OUTPUT32: cmd = VIDIOC_S_OUTPUT; break; + case VIDIOC_CREATE_BUFS32: cmd = VIDIOC_CREATE_BUFS; break; + case VIDIOC_PREPARE_BUF32: cmd = VIDIOC_PREPARE_BUF; break; } switch (cmd) { @@ -775,6 +814,12 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar compatible_arg = 0; break; + case VIDIOC_CREATE_BUFS: + err = get_v4l2_create32(&karg.v2crt, up); + compatible_arg = 0; + break; + + case VIDIOC_PREPARE_BUF: case VIDIOC_QUERYBUF: case VIDIOC_QBUF: case VIDIOC_DQBUF: @@ -860,6 +905,10 @@ static long do_video_ioctl(struct file *file, unsigned int cmd, unsigned long ar err = put_v4l2_format32(&karg.v2f, up); break; + case VIDIOC_CREATE_BUFS: + err = put_v4l2_create32(&karg.v2crt, up); + break; + case VIDIOC_QUERYBUF: case VIDIOC_QBUF: case VIDIOC_DQBUF: @@ -959,6 +1008,8 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) case VIDIOC_DQEVENT32: case VIDIOC_SUBSCRIBE_EVENT: case VIDIOC_UNSUBSCRIBE_EVENT: + case VIDIOC_CREATE_BUFS32: + case VIDIOC_PREPARE_BUF32: ret = do_video_ioctl(file, cmd, arg); break; diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 24fd43322150..e1da8fc9dd2f 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -273,6 +273,8 @@ static const char *v4l2_ioctls[] = { [_IOC_NR(VIDIOC_DQEVENT)] = "VIDIOC_DQEVENT", [_IOC_NR(VIDIOC_SUBSCRIBE_EVENT)] = "VIDIOC_SUBSCRIBE_EVENT", [_IOC_NR(VIDIOC_UNSUBSCRIBE_EVENT)] = "VIDIOC_UNSUBSCRIBE_EVENT", + [_IOC_NR(VIDIOC_CREATE_BUFS)] = "VIDIOC_CREATE_BUFS", + [_IOC_NR(VIDIOC_PREPARE_BUF)] = "VIDIOC_PREPARE_BUF", }; #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) @@ -2104,6 +2106,40 @@ static long __video_do_ioctl(struct file *file, dbgarg(cmd, "type=0x%8.8x", sub->type); break; } + case VIDIOC_CREATE_BUFS: + { + struct v4l2_create_buffers *create = arg; + + if (!ops->vidioc_create_bufs) + break; + if (ret_prio) { + ret = ret_prio; + break; + } + ret = check_fmt(ops, create->format.type); + if (ret) + break; + + ret = ops->vidioc_create_bufs(file, fh, create); + + dbgarg(cmd, "count=%d @ %d\n", create->count, create->index); + break; + } + case VIDIOC_PREPARE_BUF: + { + struct v4l2_buffer *b = arg; + + if (!ops->vidioc_prepare_buf) + break; + ret = check_fmt(ops, b->type); + if (ret) + break; + + ret = ops->vidioc_prepare_buf(file, fh, b); + + dbgarg(cmd, "index=%d", b->index); + break; + } default: if (!ops->vidioc_default) break; diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 225560c1a10f..cd512f07beed 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -653,6 +653,10 @@ struct v4l2_buffer { #define V4L2_BUF_FLAG_ERROR 0x0040 #define V4L2_BUF_FLAG_TIMECODE 0x0100 /* timecode field is valid */ #define V4L2_BUF_FLAG_INPUT 0x0200 /* input field is valid */ +#define V4L2_BUF_FLAG_PREPARED 0x0400 /* Buffer is prepared for queuing */ +/* Cache handling flags */ +#define V4L2_BUF_FLAG_NO_CACHE_INVALIDATE 0x0800 +#define V4L2_BUF_FLAG_NO_CACHE_CLEAN 0x1000 /* * O V E R L A Y P R E V I E W @@ -2138,6 +2142,15 @@ struct v4l2_dbg_chip_ident { __u32 revision; /* chip revision, chip specific */ } __attribute__ ((packed)); +/* VIDIOC_CREATE_BUFS */ +struct v4l2_create_buffers { + __u32 index; /* output: buffers index...index + count - 1 have been created */ + __u32 count; + enum v4l2_memory memory; + struct v4l2_format format; /* "type" is used always, the rest if sizeimage == 0 */ + __u32 reserved[8]; +}; + /* * I O C T L C O D E S F O R V I D E O D E V I C E S * @@ -2228,6 +2241,11 @@ struct v4l2_dbg_chip_ident { #define VIDIOC_SUBSCRIBE_EVENT _IOW('V', 90, struct v4l2_event_subscription) #define VIDIOC_UNSUBSCRIBE_EVENT _IOW('V', 91, struct v4l2_event_subscription) +/* Experimental, the below two ioctls may change over the next couple of kernel + versions */ +#define VIDIOC_CREATE_BUFS _IOWR('V', 92, struct v4l2_create_buffers) +#define VIDIOC_PREPARE_BUF _IOWR('V', 93, struct v4l2_buffer) + /* Reminder: when adding new ioctls please add support for them to drivers/media/video/v4l2-compat-ioctl32.c as well! */ diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index dd9f1e7b8ff7..4d1c74ad4c84 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -122,6 +122,8 @@ struct v4l2_ioctl_ops { int (*vidioc_qbuf) (struct file *file, void *fh, struct v4l2_buffer *b); int (*vidioc_dqbuf) (struct file *file, void *fh, struct v4l2_buffer *b); + int (*vidioc_create_bufs)(struct file *file, void *fh, struct v4l2_create_buffers *b); + int (*vidioc_prepare_buf)(struct file *file, void *fh, struct v4l2_buffer *b); int (*vidioc_overlay) (struct file *file, void *fh, unsigned int i); int (*vidioc_g_fbuf) (struct file *file, void *fh, -- cgit v1.2.3 From fc714e70dd063e6887d09872ac6158b0c20cc817 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 24 Aug 2011 10:30:21 -0300 Subject: [media] V4L: vb2: prepare to support multi-size buffers In preparation for the forthcoming VIDIOC_CREATE_BUFS ioctl add a "const struct v4l2_format *" argument to the .queue_setup() vb2 operation. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/atmel-isi.c | 6 +++--- drivers/media/video/marvell-ccic/mcam-core.c | 3 ++- drivers/media/video/mem2mem_testdev.c | 7 ++++--- drivers/media/video/mx3_camera.c | 1 + drivers/media/video/pwc/pwc-if.c | 6 +++--- drivers/media/video/s5p-fimc/fimc-capture.c | 6 +++--- drivers/media/video/s5p-fimc/fimc-core.c | 6 +++--- drivers/media/video/s5p-mfc/s5p_mfc_dec.c | 7 ++++--- drivers/media/video/s5p-mfc/s5p_mfc_enc.c | 5 +++-- drivers/media/video/s5p-tv/mixer_video.c | 4 ++-- drivers/media/video/sh_mobile_ceu_camera.c | 1 + drivers/media/video/videobuf2-core.c | 6 +++--- drivers/media/video/vivi.c | 6 +++--- include/media/videobuf2-core.h | 6 +++--- 14 files changed, 38 insertions(+), 32 deletions(-) (limited to 'include') diff --git a/drivers/media/video/atmel-isi.c b/drivers/media/video/atmel-isi.c index 06f6595a1c9a..8c775c59e120 100644 --- a/drivers/media/video/atmel-isi.c +++ b/drivers/media/video/atmel-isi.c @@ -249,9 +249,9 @@ static int atmel_isi_wait_status(struct atmel_isi *isi, int wait_reset) /* ------------------------------------------------------------------ Videobuf operations ------------------------------------------------------------------*/ -static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, - unsigned int *nplanes, unsigned int sizes[], - void *alloc_ctxs[]) +static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) { struct soc_camera_device *icd = soc_camera_from_vb2q(vq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); diff --git a/drivers/media/video/marvell-ccic/mcam-core.c b/drivers/media/video/marvell-ccic/mcam-core.c index 1141b976dff4..80ec64d2d6d8 100644 --- a/drivers/media/video/marvell-ccic/mcam-core.c +++ b/drivers/media/video/marvell-ccic/mcam-core.c @@ -883,7 +883,8 @@ static int mcam_read_setup(struct mcam_camera *cam) * Videobuf2 interface code. */ -static int mcam_vb_queue_setup(struct vb2_queue *vq, unsigned int *nbufs, +static int mcam_vb_queue_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, unsigned int *nbufs, unsigned int *num_planes, unsigned int sizes[], void *alloc_ctxs[]) { diff --git a/drivers/media/video/mem2mem_testdev.c b/drivers/media/video/mem2mem_testdev.c index 9594b52f8605..12897e8a3314 100644 --- a/drivers/media/video/mem2mem_testdev.c +++ b/drivers/media/video/mem2mem_testdev.c @@ -738,9 +738,10 @@ static const struct v4l2_ioctl_ops m2mtest_ioctl_ops = { * Queue operations */ -static int m2mtest_queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, - unsigned int *nplanes, unsigned int sizes[], - void *alloc_ctxs[]) +static int m2mtest_queue_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) { struct m2mtest_ctx *ctx = vb2_get_drv_priv(vq); struct m2mtest_q_data *q_data; diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c index f4ef6b993ff1..24c2fe0714c5 100644 --- a/drivers/media/video/mx3_camera.c +++ b/drivers/media/video/mx3_camera.c @@ -191,6 +191,7 @@ static void mx3_cam_dma_done(void *arg) * Calculate the __buffer__ (not data) size and number of buffers. */ static int mx3_videobuf_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, unsigned int *count, unsigned int *num_planes, unsigned int sizes[], void *alloc_ctxs[]) { diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index 360be226718d..01ff643e682d 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -744,9 +744,9 @@ static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma) /***************************************************************************/ /* Videobuf2 operations */ -static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, - unsigned int *nplanes, unsigned int sizes[], - void *alloc_ctxs[]) +static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) { struct pwc_device *pdev = vb2_get_drv_priv(vq); diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 931f469604b0..c8d91b0cd9bd 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -246,9 +246,9 @@ static unsigned int get_plane_size(struct fimc_frame *fr, unsigned int plane) return fr->f_width * fr->f_height * fr->fmt->depth[plane] / 8; } -static int queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, - unsigned int *num_planes, unsigned int sizes[], - void *allocators[]) +static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *allocators[]) { struct fimc_ctx *ctx = vq->drv_priv; struct fimc_fmt *fmt = ctx->d_frame.fmt; diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index 6c1c9cb55378..19ca6db38b2f 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -670,9 +670,9 @@ static void fimc_job_abort(void *priv) fimc_m2m_shutdown(priv); } -static int fimc_queue_setup(struct vb2_queue *vq, unsigned int *num_buffers, - unsigned int *num_planes, unsigned int sizes[], - void *allocators[]) +static int fimc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *allocators[]) { struct fimc_ctx *ctx = vb2_get_drv_priv(vq); struct fimc_frame *f; diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_dec.c b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c index bfbe08432050..725634d9736d 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_dec.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc_dec.c @@ -744,9 +744,10 @@ static const struct v4l2_ioctl_ops s5p_mfc_dec_ioctl_ops = { .vidioc_g_crop = vidioc_g_crop, }; -static int s5p_mfc_queue_setup(struct vb2_queue *vq, unsigned int *buf_count, - unsigned int *plane_count, unsigned int psize[], - void *allocators[]) +static int s5p_mfc_queue_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, unsigned int *buf_count, + unsigned int *plane_count, unsigned int psize[], + void *allocators[]) { struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv); diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c index 4c90e53bd964..ecef127dbc66 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c @@ -1513,8 +1513,9 @@ static int check_vb_with_fmt(struct s5p_mfc_fmt *fmt, struct vb2_buffer *vb) } static int s5p_mfc_queue_setup(struct vb2_queue *vq, - unsigned int *buf_count, unsigned int *plane_count, - unsigned int psize[], void *allocators[]) + const struct v4l2_format *fmt, + unsigned int *buf_count, unsigned int *plane_count, + unsigned int psize[], void *allocators[]) { struct s5p_mfc_ctx *ctx = fh_to_ctx(vq->drv_priv); diff --git a/drivers/media/video/s5p-tv/mixer_video.c b/drivers/media/video/s5p-tv/mixer_video.c index 4917e2c2b321..e16d3a4bc1dc 100644 --- a/drivers/media/video/s5p-tv/mixer_video.c +++ b/drivers/media/video/s5p-tv/mixer_video.c @@ -727,8 +727,8 @@ static const struct v4l2_file_operations mxr_fops = { .unlocked_ioctl = video_ioctl2, }; -static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, - unsigned int *nplanes, unsigned int sizes[], +static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt, + unsigned int *nbuffers, unsigned int *nplanes, unsigned int sizes[], void *alloc_ctxs[]) { struct mxr_layer *layer = vb2_get_drv_priv(vq); diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 5c8ddd821df6..0cb19689cfe8 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -193,6 +193,7 @@ static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev) * Videobuf operations */ static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq, + const struct v4l2_format *fmt, unsigned int *count, unsigned int *num_planes, unsigned int sizes[], void *alloc_ctxs[]) { diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index 82b51be3ca72..f04f27d68cec 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c @@ -530,7 +530,7 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) * Ask the driver how many buffers and planes per buffer it requires. * Driver also sets the size and allocator context for each plane. */ - ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes, + ret = call_qop(q, queue_setup, q, NULL, &num_buffers, &num_planes, q->plane_sizes, q->alloc_ctx); if (ret) return ret; @@ -549,8 +549,8 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) unsigned int orig_num_buffers; orig_num_buffers = num_buffers = ret; - ret = call_qop(q, queue_setup, q, &num_buffers, &num_planes, - q->plane_sizes, q->alloc_ctx); + ret = call_qop(q, queue_setup, q, NULL, &num_buffers, + &num_planes, q->plane_sizes, q->alloc_ctx); if (ret) goto free_mem; diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index 7cf94c09d99a..7d754fbcccbf 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -650,9 +650,9 @@ static void vivi_stop_generating(struct vivi_dev *dev) /* ------------------------------------------------------------------ Videobuf operations ------------------------------------------------------------------*/ -static int queue_setup(struct vb2_queue *vq, unsigned int *nbuffers, - unsigned int *nplanes, unsigned int sizes[], - void *alloc_ctxs[]) +static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) { struct vivi_dev *dev = vb2_get_drv_priv(vq); unsigned long size; diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index dbd10acc8e80..692e35c232a9 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -218,9 +218,9 @@ struct vb2_buffer { * pre-queued buffers before calling STREAMON */ struct vb2_ops { - int (*queue_setup)(struct vb2_queue *q, unsigned int *num_buffers, - unsigned int *num_planes, unsigned int sizes[], - void *alloc_ctxs[]); + int (*queue_setup)(struct vb2_queue *q, const struct v4l2_format *fmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *alloc_ctxs[]); void (*wait_prepare)(struct vb2_queue *q); void (*wait_finish)(struct vb2_queue *q); -- cgit v1.2.3 From 2d86401c2cbfce9f99b08ba168bdb60b2eb7796e Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 28 Sep 2011 09:23:02 -0300 Subject: [media] V4L: vb2: add support for buffers of different sizes on a single queue The two recently added ioctl()s VIDIOC_CREATE_BUFS and VIDIOC_PREPARE_BUF allow user-space applications to allocate video buffers of different sizes and hand them over to the driver for fast switching between different frame formats. This patch adds support for buffers of different sizes on the same buffer-queue to vb2. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/videobuf2-core.c | 294 +++++++++++++++++++++++++++++------ include/media/videobuf2-core.h | 35 +++-- 2 files changed, 270 insertions(+), 59 deletions(-) (limited to 'include') diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index f04f27d68cec..9005dc9991ab 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c @@ -38,7 +38,8 @@ module_param(debug, int, 0644); (((q)->ops->op) ? ((q)->ops->op(args)) : 0) #define V4L2_BUFFER_STATE_FLAGS (V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | \ - V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR) + V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR | \ + V4L2_BUF_FLAG_PREPARED) /** * __vb2_buf_mem_alloc() - allocate video memory for the given buffer @@ -109,13 +110,22 @@ static void __vb2_buf_userptr_put(struct vb2_buffer *vb) * __setup_offsets() - setup unique offsets ("cookies") for every plane in * every buffer on the queue */ -static void __setup_offsets(struct vb2_queue *q) +static void __setup_offsets(struct vb2_queue *q, unsigned int n) { unsigned int buffer, plane; struct vb2_buffer *vb; - unsigned long off = 0; + unsigned long off; - for (buffer = 0; buffer < q->num_buffers; ++buffer) { + if (q->num_buffers) { + struct v4l2_plane *p; + vb = q->bufs[q->num_buffers - 1]; + p = &vb->v4l2_planes[vb->num_planes - 1]; + off = PAGE_ALIGN(p->m.mem_offset + p->length); + } else { + off = 0; + } + + for (buffer = q->num_buffers; buffer < q->num_buffers + n; ++buffer) { vb = q->bufs[buffer]; if (!vb) continue; @@ -161,7 +171,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory, vb->state = VB2_BUF_STATE_DEQUEUED; vb->vb2_queue = q; vb->num_planes = num_planes; - vb->v4l2_buf.index = buffer; + vb->v4l2_buf.index = q->num_buffers + buffer; vb->v4l2_buf.type = q->type; vb->v4l2_buf.memory = memory; @@ -189,15 +199,13 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory, } } - q->bufs[buffer] = vb; + q->bufs[q->num_buffers + buffer] = vb; } - q->num_buffers = buffer; - - __setup_offsets(q); + __setup_offsets(q, buffer); dprintk(1, "Allocated %d buffers, %d plane(s) each\n", - q->num_buffers, num_planes); + buffer, num_planes); return buffer; } @@ -205,12 +213,13 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory, /** * __vb2_free_mem() - release all video buffer memory for a given queue */ -static void __vb2_free_mem(struct vb2_queue *q) +static void __vb2_free_mem(struct vb2_queue *q, unsigned int buffers) { unsigned int buffer; struct vb2_buffer *vb; - for (buffer = 0; buffer < q->num_buffers; ++buffer) { + for (buffer = q->num_buffers - buffers; buffer < q->num_buffers; + ++buffer) { vb = q->bufs[buffer]; if (!vb) continue; @@ -224,17 +233,18 @@ static void __vb2_free_mem(struct vb2_queue *q) } /** - * __vb2_queue_free() - free the queue - video memory and related information - * and return the queue to an uninitialized state. Might be called even if the - * queue has already been freed. + * __vb2_queue_free() - free buffers at the end of the queue - video memory and + * related information, if no buffers are left return the queue to an + * uninitialized state. Might be called even if the queue has already been freed. */ -static void __vb2_queue_free(struct vb2_queue *q) +static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers) { unsigned int buffer; /* Call driver-provided cleanup function for each buffer, if provided */ if (q->ops->buf_cleanup) { - for (buffer = 0; buffer < q->num_buffers; ++buffer) { + for (buffer = q->num_buffers - buffers; buffer < q->num_buffers; + ++buffer) { if (NULL == q->bufs[buffer]) continue; q->ops->buf_cleanup(q->bufs[buffer]); @@ -242,23 +252,25 @@ static void __vb2_queue_free(struct vb2_queue *q) } /* Release video buffer memory */ - __vb2_free_mem(q); + __vb2_free_mem(q, buffers); /* Free videobuf buffers */ - for (buffer = 0; buffer < q->num_buffers; ++buffer) { + for (buffer = q->num_buffers - buffers; buffer < q->num_buffers; + ++buffer) { kfree(q->bufs[buffer]); q->bufs[buffer] = NULL; } - q->num_buffers = 0; - q->memory = 0; + q->num_buffers -= buffers; + if (!q->num_buffers) + q->memory = 0; } /** * __verify_planes_array() - verify that the planes array passed in struct * v4l2_buffer from userspace can be safely used */ -static int __verify_planes_array(struct vb2_buffer *vb, struct v4l2_buffer *b) +static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer *b) { /* Is memory for copying plane information present? */ if (NULL == b->m.planes) { @@ -318,7 +330,7 @@ static bool __buffers_in_use(struct vb2_queue *q) static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b) { struct vb2_queue *q = vb->vb2_queue; - int ret = 0; + int ret; /* Copy back data such as timestamp, flags, input, etc. */ memcpy(b, &vb->v4l2_buf, offsetof(struct v4l2_buffer, m)); @@ -365,8 +377,10 @@ static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b) case VB2_BUF_STATE_DONE: b->flags |= V4L2_BUF_FLAG_DONE; break; - case VB2_BUF_STATE_DEQUEUED: case VB2_BUF_STATE_PREPARED: + b->flags |= V4L2_BUF_FLAG_PREPARED; + break; + case VB2_BUF_STATE_DEQUEUED: /* nothing */ break; } @@ -374,7 +388,7 @@ static int __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b) if (__buffer_in_use(q, vb)) b->flags |= V4L2_BUF_FLAG_MAPPED; - return ret; + return 0; } /** @@ -460,7 +474,7 @@ static int __verify_mmap_ops(struct vb2_queue *q) */ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) { - unsigned int num_buffers, num_planes; + unsigned int num_buffers, allocated_buffers, num_planes = 0; int ret = 0; if (q->fileio) { @@ -508,7 +522,7 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) return -EBUSY; } - __vb2_queue_free(q); + __vb2_queue_free(q, q->num_buffers); /* * In case of REQBUFS(0) return immediately without calling @@ -542,43 +556,167 @@ int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req) return -ENOMEM; } + allocated_buffers = ret; + /* * Check if driver can handle the allocated number of buffers. */ - if (ret < num_buffers) { - unsigned int orig_num_buffers; + if (allocated_buffers < num_buffers) { + num_buffers = allocated_buffers; - orig_num_buffers = num_buffers = ret; ret = call_qop(q, queue_setup, q, NULL, &num_buffers, &num_planes, q->plane_sizes, q->alloc_ctx); - if (ret) - goto free_mem; - if (orig_num_buffers < num_buffers) { + if (!ret && allocated_buffers < num_buffers) ret = -ENOMEM; - goto free_mem; - } /* - * Ok, driver accepted smaller number of buffers. + * Either the driver has accepted a smaller number of buffers, + * or .queue_setup() returned an error */ - ret = num_buffers; + } + + q->num_buffers = allocated_buffers; + + if (ret < 0) { + __vb2_queue_free(q, allocated_buffers); + return ret; } /* * Return the number of successfully allocated buffers * to the userspace. */ - req->count = ret; + req->count = allocated_buffers; return 0; - -free_mem: - __vb2_queue_free(q); - return ret; } EXPORT_SYMBOL_GPL(vb2_reqbufs); +/** + * vb2_create_bufs() - Allocate buffers and any required auxiliary structs + * @q: videobuf2 queue + * @create: creation parameters, passed from userspace to vidioc_create_bufs + * handler in driver + * + * Should be called from vidioc_create_bufs ioctl handler of a driver. + * This function: + * 1) verifies parameter sanity + * 2) calls the .queue_setup() queue operation + * 3) performs any necessary memory allocations + * + * The return values from this function are intended to be directly returned + * from vidioc_create_bufs handler in driver. + */ +int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create) +{ + unsigned int num_planes = 0, num_buffers, allocated_buffers; + int ret = 0; + + if (q->fileio) { + dprintk(1, "%s(): file io in progress\n", __func__); + return -EBUSY; + } + + if (create->memory != V4L2_MEMORY_MMAP + && create->memory != V4L2_MEMORY_USERPTR) { + dprintk(1, "%s(): unsupported memory type\n", __func__); + return -EINVAL; + } + + if (create->format.type != q->type) { + dprintk(1, "%s(): requested type is incorrect\n", __func__); + return -EINVAL; + } + + /* + * Make sure all the required memory ops for given memory type + * are available. + */ + if (create->memory == V4L2_MEMORY_MMAP && __verify_mmap_ops(q)) { + dprintk(1, "%s(): MMAP for current setup unsupported\n", __func__); + return -EINVAL; + } + + if (create->memory == V4L2_MEMORY_USERPTR && __verify_userptr_ops(q)) { + dprintk(1, "%s(): USERPTR for current setup unsupported\n", __func__); + return -EINVAL; + } + + if (q->num_buffers == VIDEO_MAX_FRAME) { + dprintk(1, "%s(): maximum number of buffers already allocated\n", + __func__); + return -ENOBUFS; + } + + create->index = q->num_buffers; + + if (!q->num_buffers) { + memset(q->plane_sizes, 0, sizeof(q->plane_sizes)); + memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx)); + q->memory = create->memory; + } + + num_buffers = min(create->count, VIDEO_MAX_FRAME - q->num_buffers); + + /* + * Ask the driver, whether the requested number of buffers, planes per + * buffer and their sizes are acceptable + */ + ret = call_qop(q, queue_setup, q, &create->format, &num_buffers, + &num_planes, q->plane_sizes, q->alloc_ctx); + if (ret) + return ret; + + /* Finally, allocate buffers and video memory */ + ret = __vb2_queue_alloc(q, create->memory, num_buffers, + num_planes); + if (ret < 0) { + dprintk(1, "Memory allocation failed with error: %d\n", ret); + return ret; + } + + allocated_buffers = ret; + + /* + * Check if driver can handle the so far allocated number of buffers. + */ + if (ret < num_buffers) { + num_buffers = ret; + + /* + * q->num_buffers contains the total number of buffers, that the + * queue driver has set up + */ + ret = call_qop(q, queue_setup, q, &create->format, &num_buffers, + &num_planes, q->plane_sizes, q->alloc_ctx); + + if (!ret && allocated_buffers < num_buffers) + ret = -ENOMEM; + + /* + * Either the driver has accepted a smaller number of buffers, + * or .queue_setup() returned an error + */ + } + + q->num_buffers += allocated_buffers; + + if (ret < 0) { + __vb2_queue_free(q, allocated_buffers); + return ret; + } + + /* + * Return the number of successfully allocated buffers + * to the userspace. + */ + create->count = allocated_buffers; + + return 0; +} +EXPORT_SYMBOL_GPL(vb2_create_bufs); + /** * vb2_plane_vaddr() - Return a kernel virtual address of a given plane * @vb: vb2_buffer to which the plane in question belongs to @@ -663,7 +801,7 @@ EXPORT_SYMBOL_GPL(vb2_buffer_done); * __fill_vb2_buffer() - fill a vb2_buffer with information provided in * a v4l2_buffer by the userspace */ -static int __fill_vb2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b, +static int __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b, struct v4l2_plane *v4l2_planes) { unsigned int plane; @@ -727,7 +865,7 @@ static int __fill_vb2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b, /** * __qbuf_userptr() - handle qbuf of a USERPTR buffer */ -static int __qbuf_userptr(struct vb2_buffer *vb, struct v4l2_buffer *b) +static int __qbuf_userptr(struct vb2_buffer *vb, const struct v4l2_buffer *b) { struct v4l2_plane planes[VIDEO_MAX_PLANES]; struct vb2_queue *q = vb->vb2_queue; @@ -816,7 +954,7 @@ err: /** * __qbuf_mmap() - handle qbuf of an MMAP buffer */ -static int __qbuf_mmap(struct vb2_buffer *vb, struct v4l2_buffer *b) +static int __qbuf_mmap(struct vb2_buffer *vb, const struct v4l2_buffer *b) { return __fill_vb2_buffer(vb, b, vb->v4l2_planes); } @@ -833,7 +971,7 @@ static void __enqueue_in_driver(struct vb2_buffer *vb) q->ops->buf_queue(vb); } -static int __buf_prepare(struct vb2_buffer *vb, struct v4l2_buffer *b) +static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) { struct vb2_queue *q = vb->vb2_queue; int ret; @@ -860,6 +998,68 @@ static int __buf_prepare(struct vb2_buffer *vb, struct v4l2_buffer *b) return ret; } +/** + * vb2_prepare_buf() - Pass ownership of a buffer from userspace to the kernel + * @q: videobuf2 queue + * @b: buffer structure passed from userspace to vidioc_prepare_buf + * handler in driver + * + * Should be called from vidioc_prepare_buf ioctl handler of a driver. + * This function: + * 1) verifies the passed buffer, + * 2) calls buf_prepare callback in the driver (if provided), in which + * driver-specific buffer initialization can be performed, + * + * The return values from this function are intended to be directly returned + * from vidioc_prepare_buf handler in driver. + */ +int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b) +{ + struct vb2_buffer *vb; + int ret; + + if (q->fileio) { + dprintk(1, "%s(): file io in progress\n", __func__); + return -EBUSY; + } + + if (b->type != q->type) { + dprintk(1, "%s(): invalid buffer type\n", __func__); + return -EINVAL; + } + + if (b->index >= q->num_buffers) { + dprintk(1, "%s(): buffer index out of range\n", __func__); + return -EINVAL; + } + + vb = q->bufs[b->index]; + if (NULL == vb) { + /* Should never happen */ + dprintk(1, "%s(): buffer is NULL\n", __func__); + return -EINVAL; + } + + if (b->memory != q->memory) { + dprintk(1, "%s(): invalid memory type\n", __func__); + return -EINVAL; + } + + if (vb->state != VB2_BUF_STATE_DEQUEUED) { + dprintk(1, "%s(): invalid buffer state %d\n", __func__, vb->state); + return -EINVAL; + } + + ret = __buf_prepare(vb, b); + if (ret < 0) + return ret; + + __fill_v4l2_buffer(vb, b); + + return 0; +} +EXPORT_SYMBOL_GPL(vb2_prepare_buf); + /** * vb2_qbuf() - Queue a buffer from userspace * @q: videobuf2 queue @@ -1484,7 +1684,7 @@ void vb2_queue_release(struct vb2_queue *q) { __vb2_cleanup_fileio(q); __vb2_queue_cancel(q); - __vb2_queue_free(q); + __vb2_queue_free(q, q->num_buffers); } EXPORT_SYMBOL_GPL(vb2_queue_release); diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 692e35c232a9..55c57d3d3e63 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -169,13 +169,21 @@ struct vb2_buffer { /** * struct vb2_ops - driver-specific callbacks * - * @queue_setup: called from a VIDIOC_REQBUFS handler, before - * memory allocation; driver should return the required - * number of buffers in num_buffers, the required number - * of planes per buffer in num_planes; the size of each - * plane should be set in the sizes[] array and optional - * per-plane allocator specific context in alloc_ctxs[] - * array + * @queue_setup: called from VIDIOC_REQBUFS and VIDIOC_CREATE_BUFS + * handlers before memory allocation, or, if + * *num_planes != 0, after the allocation to verify a + * smaller number of buffers. Driver should return + * the required number of buffers in *num_buffers, the + * required number of planes per buffer in *num_planes; the + * size of each plane should be set in the sizes[] array + * and optional per-plane allocator specific context in the + * alloc_ctxs[] array. When called from VIDIOC_REQBUFS, + * fmt == NULL, the driver has to use the currently + * configured format and *num_buffers is the total number + * of buffers, that are being allocated. When called from + * VIDIOC_CREATE_BUFS, fmt != NULL and it describes the + * target frame format. In this case *num_buffers are being + * allocated additionally to q->num_buffers. * @wait_prepare: release any locks taken while calling vb2 functions; * it is called before an ioctl needs to wait for a new * buffer to arrive; required to avoid a deadlock in @@ -188,11 +196,11 @@ struct vb2_buffer { * perform additional buffer-related initialization; * initialization failure (return != 0) will prevent * queue setup from completing successfully; optional - * @buf_prepare: called every time the buffer is queued from userspace; - * drivers may perform any initialization required before - * each hardware operation in this callback; - * if an error is returned, the buffer will not be queued - * in driver; optional + * @buf_prepare: called every time the buffer is queued from userspace + * and from the VIDIOC_PREPARE_BUF ioctl; drivers may + * perform any initialization required before each hardware + * operation in this callback; if an error is returned, the + * buffer will not be queued in driver; optional * @buf_finish: called before every dequeue of the buffer back to * userspace; drivers may perform any operations required * before userspace accesses the buffer; optional @@ -300,6 +308,9 @@ int vb2_wait_for_all_buffers(struct vb2_queue *q); int vb2_querybuf(struct vb2_queue *q, struct v4l2_buffer *b); int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req); +int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create); +int vb2_prepare_buf(struct vb2_queue *q, struct v4l2_buffer *b); + int vb2_queue_init(struct vb2_queue *q); void vb2_queue_release(struct vb2_queue *q); -- cgit v1.2.3 From ee02da64558f04fb30c2462fdeabdfafc87a9799 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 6 Sep 2011 12:36:39 -0300 Subject: [media] soc_camera: add control handler support The soc_camera framework is switched over to use the control framework. After this patch none of the controls in subdevs or host drivers are available, until those drivers are also converted to the control framework. Signed-off-by: Hans Verkuil [g.liakhovetski@gmx.de: moved code around, fixed problems] Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/soc_camera.c | 94 ++++++++-------------------------------- include/media/soc_camera.h | 2 + 2 files changed, 21 insertions(+), 75 deletions(-) (limited to 'include') diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index ac23916552db..b56f4b78273d 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -512,6 +512,7 @@ static int soc_camera_open(struct file *file) if (ret < 0) goto einitvb; } + v4l2_ctrl_handler_setup(&icd->ctrl_handler); } file->private_data = icd; @@ -781,78 +782,6 @@ static int soc_camera_streamoff(struct file *file, void *priv, return 0; } -static int soc_camera_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - int i; - - WARN_ON(priv != file->private_data); - - if (!qc->id) - return -EINVAL; - - /* First check host controls */ - for (i = 0; i < ici->ops->num_controls; i++) - if (qc->id == ici->ops->controls[i].id) { - memcpy(qc, &(ici->ops->controls[i]), - sizeof(*qc)); - return 0; - } - - if (!icd->ops) - return -EINVAL; - - /* Then device controls */ - for (i = 0; i < icd->ops->num_controls; i++) - if (qc->id == icd->ops->controls[i].id) { - memcpy(qc, &(icd->ops->controls[i]), - sizeof(*qc)); - return 0; - } - - return -EINVAL; -} - -static int soc_camera_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - int ret; - - WARN_ON(priv != file->private_data); - - if (ici->ops->get_ctrl) { - ret = ici->ops->get_ctrl(icd, ctrl); - if (ret != -ENOIOCTLCMD) - return ret; - } - - return v4l2_subdev_call(sd, core, g_ctrl, ctrl); -} - -static int soc_camera_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct soc_camera_device *icd = file->private_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - int ret; - - WARN_ON(priv != file->private_data); - - if (ici->ops->set_ctrl) { - ret = ici->ops->set_ctrl(icd, ctrl); - if (ret != -ENOIOCTLCMD) - return ret; - } - - return v4l2_subdev_call(sd, core, s_ctrl, ctrl); -} - static int soc_camera_cropcap(struct file *file, void *fh, struct v4l2_cropcap *a) { @@ -1055,6 +984,17 @@ static int soc_camera_probe(struct soc_camera_device *icd) dev_info(icd->pdev, "Probing %s\n", dev_name(icd->pdev)); + /* + * Currently the subdev with the largest number of controls (13) is + * ov6550. So let's pick 16 as a hint for the control handler. Note + * that this is a hint only: too large and you waste some memory, too + * small and there is a (very) small performance hit when looking up + * controls in the internal hash. + */ + ret = v4l2_ctrl_handler_init(&icd->ctrl_handler, 16); + if (ret < 0) + return ret; + ret = regulator_bulk_get(icd->pdev, icl->num_regulators, icl->regulators); if (ret < 0) @@ -1108,6 +1048,9 @@ static int soc_camera_probe(struct soc_camera_device *icd) sd = soc_camera_to_subdev(icd); sd->grp_id = (long)icd; + if (v4l2_ctrl_add_handler(&icd->ctrl_handler, sd->ctrl_handler)) + goto ectrl; + /* At this point client .probe() should have run already */ ret = soc_camera_init_user_formats(icd); if (ret < 0) @@ -1146,6 +1089,7 @@ evidstart: mutex_unlock(&icd->video_lock); soc_camera_free_user_formats(icd); eiufmt: +ectrl: if (icl->board_info) { soc_camera_free_i2c(icd); } else { @@ -1162,6 +1106,7 @@ eadd: epower: regulator_bulk_free(icl->num_regulators, icl->regulators); ereg: + v4l2_ctrl_handler_free(&icd->ctrl_handler); return ret; } @@ -1176,6 +1121,7 @@ static int soc_camera_remove(struct soc_camera_device *icd) BUG_ON(!icd->parent); + v4l2_ctrl_handler_free(&icd->ctrl_handler); if (vdev) { video_unregister_device(vdev); icd->vdev = NULL; @@ -1381,9 +1327,6 @@ static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = { .vidioc_dqbuf = soc_camera_dqbuf, .vidioc_streamon = soc_camera_streamon, .vidioc_streamoff = soc_camera_streamoff, - .vidioc_queryctrl = soc_camera_queryctrl, - .vidioc_g_ctrl = soc_camera_g_ctrl, - .vidioc_s_ctrl = soc_camera_s_ctrl, .vidioc_cropcap = soc_camera_cropcap, .vidioc_g_crop = soc_camera_g_crop, .vidioc_s_crop = soc_camera_s_crop, @@ -1412,6 +1355,7 @@ static int video_dev_create(struct soc_camera_device *icd) vdev->ioctl_ops = &soc_camera_ioctl_ops; vdev->release = video_device_release; vdev->tvnorms = V4L2_STD_UNKNOWN; + vdev->ctrl_handler = &icd->ctrl_handler; vdev->lock = &icd->video_lock; icd->vdev = vdev; diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 1864e2242d45..2e15e17130da 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -19,6 +19,7 @@ #include #include #include +#include #include struct file; @@ -40,6 +41,7 @@ struct soc_camera_device { struct soc_camera_sense *sense; /* See comment in struct definition */ struct soc_camera_ops *ops; struct video_device *vdev; + struct v4l2_ctrl_handler ctrl_handler; const struct soc_camera_format_xlate *current_fmt; struct soc_camera_format_xlate *user_formats; int num_user_formats; -- cgit v1.2.3 From d34bfcd2a1e5f6be5ae81030b7a6193094632955 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 5 Sep 2011 17:07:47 -0300 Subject: [media] sh_mobile_ceu_camera: implement the control handler And since this is the last and only host driver that uses controls, also remove the now obsolete control fields from soc_camera.h. Signed-off-by: Hans Verkuil [g.liakhovetski@gmx.de: moved code around, fixed problems] Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/sh_mobile_ceu_camera.c | 91 +++++++++++++----------------- include/media/soc_camera.h | 4 -- 2 files changed, 38 insertions(+), 57 deletions(-) (limited to 'include') diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 0cb19689cfe8..5d5781bd1447 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -951,6 +951,38 @@ static bool sh_mobile_ceu_packing_supported(const struct soc_mbus_pixelfmt *fmt) static int client_g_rect(struct v4l2_subdev *sd, struct v4l2_rect *rect); +static struct soc_camera_device *ctrl_to_icd(struct v4l2_ctrl *ctrl) +{ + return container_of(ctrl->handler, struct soc_camera_device, + ctrl_handler); +} + +static int sh_mobile_ceu_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct soc_camera_device *icd = ctrl_to_icd(ctrl); + struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + struct sh_mobile_ceu_dev *pcdev = ici->priv; + + switch (ctrl->id) { + case V4L2_CID_SHARPNESS: + switch (icd->current_fmt->host_fmt->fourcc) { + case V4L2_PIX_FMT_NV12: + case V4L2_PIX_FMT_NV21: + case V4L2_PIX_FMT_NV16: + case V4L2_PIX_FMT_NV61: + ceu_write(pcdev, CLFCR, !ctrl->val); + return 0; + } + break; + } + + return -EINVAL; +} + +static const struct v4l2_ctrl_ops sh_mobile_ceu_ctrl_ops = { + .s_ctrl = sh_mobile_ceu_s_ctrl, +}; + static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int idx, struct soc_camera_format_xlate *xlate) { @@ -987,6 +1019,12 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, unsigned int struct v4l2_rect rect; int shift = 0; + /* Add our control */ + v4l2_ctrl_new_std(&icd->ctrl_handler, &sh_mobile_ceu_ctrl_ops, + V4L2_CID_SHARPNESS, 0, 1, 1, 0); + if (icd->ctrl_handler.error) + return icd->ctrl_handler.error; + /* FIXME: subwindow is lost between close / open */ /* Cache current client geometry */ @@ -1915,55 +1953,6 @@ static int sh_mobile_ceu_init_videobuf(struct vb2_queue *q, return vb2_queue_init(q); } -static int sh_mobile_ceu_get_ctrl(struct soc_camera_device *icd, - struct v4l2_control *ctrl) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - u32 val; - - switch (ctrl->id) { - case V4L2_CID_SHARPNESS: - val = ceu_read(pcdev, CLFCR); - ctrl->value = val ^ 1; - return 0; - } - return -ENOIOCTLCMD; -} - -static int sh_mobile_ceu_set_ctrl(struct soc_camera_device *icd, - struct v4l2_control *ctrl) -{ - struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - struct sh_mobile_ceu_dev *pcdev = ici->priv; - - switch (ctrl->id) { - case V4L2_CID_SHARPNESS: - switch (icd->current_fmt->host_fmt->fourcc) { - case V4L2_PIX_FMT_NV12: - case V4L2_PIX_FMT_NV21: - case V4L2_PIX_FMT_NV16: - case V4L2_PIX_FMT_NV61: - ceu_write(pcdev, CLFCR, !ctrl->value); - return 0; - } - return -EINVAL; - } - return -ENOIOCTLCMD; -} - -static const struct v4l2_queryctrl sh_mobile_ceu_controls[] = { - { - .id = V4L2_CID_SHARPNESS, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Low-pass filter", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, -}; - static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { .owner = THIS_MODULE, .add = sh_mobile_ceu_add_device, @@ -1975,14 +1964,10 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { .set_livecrop = sh_mobile_ceu_set_livecrop, .set_fmt = sh_mobile_ceu_set_fmt, .try_fmt = sh_mobile_ceu_try_fmt, - .set_ctrl = sh_mobile_ceu_set_ctrl, - .get_ctrl = sh_mobile_ceu_get_ctrl, .poll = sh_mobile_ceu_poll, .querycap = sh_mobile_ceu_querycap, .set_bus_param = sh_mobile_ceu_set_bus_param, .init_videobuf2 = sh_mobile_ceu_init_videobuf, - .controls = sh_mobile_ceu_controls, - .num_controls = ARRAY_SIZE(sh_mobile_ceu_controls), }; struct bus_wait { diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 2e15e17130da..d41b8bd74445 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -96,14 +96,10 @@ struct soc_camera_host_ops { int (*reqbufs)(struct soc_camera_device *, struct v4l2_requestbuffers *); int (*querycap)(struct soc_camera_host *, struct v4l2_capability *); int (*set_bus_param)(struct soc_camera_device *, __u32); - int (*get_ctrl)(struct soc_camera_device *, struct v4l2_control *); - int (*set_ctrl)(struct soc_camera_device *, struct v4l2_control *); int (*get_parm)(struct soc_camera_device *, struct v4l2_streamparm *); int (*set_parm)(struct soc_camera_device *, struct v4l2_streamparm *); int (*enum_fsizes)(struct soc_camera_device *, struct v4l2_frmsizeenum *); unsigned int (*poll)(struct file *, poll_table *); - const struct v4l2_queryctrl *controls; - int num_controls; }; #define SOCAM_SENSOR_INVERT_PCLK (1 << 0) -- cgit v1.2.3 From 0934d94a52423fac35922c2e29d72a43db7ddd48 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 8 Sep 2011 13:16:56 -0300 Subject: [media] soc_camera: remove the now obsolete struct soc_camera_ops Signed-off-by: Hans Verkuil [g.liakhovetski@gmx.de: mt9m001 hunk moved to an earlier patch] Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/imx074.c | 1 - drivers/media/video/mt9t112.c | 2 -- drivers/media/video/ov5642.c | 1 - drivers/media/video/soc_camera_platform.c | 2 -- drivers/media/video/tw9910.c | 1 - include/media/soc_camera.h | 18 ------------------ 6 files changed, 25 deletions(-) (limited to 'include') diff --git a/drivers/media/video/imx074.c b/drivers/media/video/imx074.c index 20756e03dbb1..3f5d4de8b91b 100644 --- a/drivers/media/video/imx074.c +++ b/drivers/media/video/imx074.c @@ -437,7 +437,6 @@ static int imx074_probe(struct i2c_client *client, v4l2_i2c_subdev_init(&priv->subdev, client, &imx074_subdev_ops); - icd->ops = NULL; priv->fmt = &imx074_colour_fmts[0]; ret = imx074_video_probe(icd, client); diff --git a/drivers/media/video/mt9t112.c b/drivers/media/video/mt9t112.c index 25cdcb90768a..b8da7fe9a6bd 100644 --- a/drivers/media/video/mt9t112.c +++ b/drivers/media/video/mt9t112.c @@ -1095,8 +1095,6 @@ static int mt9t112_probe(struct i2c_client *client, v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops); - icd->ops = NULL; - ret = mt9t112_camera_probe(icd, client); if (ret) kfree(priv); diff --git a/drivers/media/video/ov5642.c b/drivers/media/video/ov5642.c index b36d42bfeab2..163a6f7ff8ef 100644 --- a/drivers/media/video/ov5642.c +++ b/drivers/media/video/ov5642.c @@ -942,7 +942,6 @@ static int ov5642_probe(struct i2c_client *client, v4l2_i2c_subdev_init(&priv->subdev, client, &ov5642_subdev_ops); - icd->ops = NULL; priv->fmt = &ov5642_colour_fmts[0]; ret = ov5642_video_probe(icd, client); diff --git a/drivers/media/video/soc_camera_platform.c b/drivers/media/video/soc_camera_platform.c index c8f6b188496d..4402a8a74f7a 100644 --- a/drivers/media/video/soc_camera_platform.c +++ b/drivers/media/video/soc_camera_platform.c @@ -150,8 +150,6 @@ static int soc_camera_platform_probe(struct platform_device *pdev) /* Set the control device reference */ icd->control = &pdev->dev; - icd->ops = NULL; - ici = to_soc_camera_host(icd->parent); v4l2_subdev_init(&priv->subdev, &platform_subdev_ops); diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c index 40cc1494b377..2fddd1fbe529 100644 --- a/drivers/media/video/tw9910.c +++ b/drivers/media/video/tw9910.c @@ -921,7 +921,6 @@ static int tw9910_probe(struct i2c_client *client, v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops); - icd->ops = NULL; icd->iface = icl->bus_id; ret = tw9910_video_probe(icd, client); diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index d41b8bd74445..6398ff0f08ab 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -39,7 +39,6 @@ struct soc_camera_device { unsigned char iface; /* Host number */ unsigned char devnum; /* Device number per host */ struct soc_camera_sense *sense; /* See comment in struct definition */ - struct soc_camera_ops *ops; struct video_device *vdev; struct v4l2_ctrl_handler ctrl_handler; const struct soc_camera_format_xlate *current_fmt; @@ -192,11 +191,6 @@ struct soc_camera_format_xlate { const struct soc_mbus_pixelfmt *host_fmt; }; -struct soc_camera_ops { - const struct v4l2_queryctrl *controls; - int num_controls; -}; - #define SOCAM_SENSE_PCLK_CHANGED (1 << 0) /** @@ -223,18 +217,6 @@ struct soc_camera_sense { unsigned long pixel_clock; }; -static inline struct v4l2_queryctrl const *soc_camera_find_qctrl( - struct soc_camera_ops *ops, int id) -{ - int i; - - for (i = 0; i < ops->num_controls; i++) - if (ops->controls[i].id == id) - return &ops->controls[i]; - - return NULL; -} - #define SOCAM_DATAWIDTH(x) BIT((x) - 1) #define SOCAM_DATAWIDTH_4 SOCAM_DATAWIDTH(4) #define SOCAM_DATAWIDTH_8 SOCAM_DATAWIDTH(8) -- cgit v1.2.3 From 09362ec25c3f42d00a4008d0622bfbca68e540f5 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 7 Sep 2011 18:07:23 -0300 Subject: [media] V4L: docbook documentation for struct v4l2_create_buffers Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-compat-ioctl32.c | 13 +++++++++++-- include/linux/videodev2.h | 14 +++++++++++--- 2 files changed, 22 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index e77e0cfc9312..c68531b88279 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -159,11 +159,20 @@ struct v4l2_format32 { } fmt; }; +/** + * struct v4l2_create_buffers32 - VIDIOC_CREATE_BUFS32 argument + * @index: on return, index of the first created buffer + * @count: entry: number of requested buffers, + * return: number of created buffers + * @memory: buffer memory type + * @format: frame format, for which buffers are requested + * @reserved: future extensions + */ struct v4l2_create_buffers32 { - __u32 index; /* output: buffers index...index + count - 1 have been created */ + __u32 index; __u32 count; enum v4l2_memory memory; - struct v4l2_format32 format; /* filled in by the user, plane sizes calculated by the driver */ + struct v4l2_format32 format; __u32 reserved[8]; }; diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index cd512f07beed..66945a6f628d 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -2142,12 +2142,20 @@ struct v4l2_dbg_chip_ident { __u32 revision; /* chip revision, chip specific */ } __attribute__ ((packed)); -/* VIDIOC_CREATE_BUFS */ +/** + * struct v4l2_create_buffers - VIDIOC_CREATE_BUFS argument + * @index: on return, index of the first created buffer + * @count: entry: number of requested buffers, + * return: number of created buffers + * @memory: buffer memory type + * @format: frame format, for which buffers are requested + * @reserved: future extensions + */ struct v4l2_create_buffers { - __u32 index; /* output: buffers index...index + count - 1 have been created */ + __u32 index; __u32 count; enum v4l2_memory memory; - struct v4l2_format format; /* "type" is used always, the rest if sizeimage == 0 */ + struct v4l2_format format; __u32 reserved[8]; }; -- cgit v1.2.3 From 14178aa57ce6ac4f05b4df8ea9e010486ce83a76 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 21 Sep 2011 15:16:30 -0300 Subject: [media] V4L: soc-camera: start removing struct soc_camera_device from client drivers Remove most trivial uses of struct soc_camera_device from most client drivers, abstracting some of them inside inline functions. Next steps will eliminate remaining uses and modify inline functions to not use struct soc_camera_device. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/imx074.c | 17 ++++----------- drivers/media/video/mt9m001.c | 39 ++++++++++------------------------ drivers/media/video/mt9m111.c | 24 +++++---------------- drivers/media/video/mt9t031.c | 22 +++++++------------- drivers/media/video/mt9t112.c | 45 ++++++++++++++-------------------------- drivers/media/video/mt9v022.c | 44 ++++++++++----------------------------- drivers/media/video/ov2640.c | 27 +++++++----------------- drivers/media/video/ov5642.c | 17 ++++----------- drivers/media/video/ov6650.c | 20 +++++------------- drivers/media/video/ov772x.c | 31 +++++++++------------------ drivers/media/video/ov9640.c | 21 ++++--------------- drivers/media/video/ov9740.c | 21 ++++--------------- drivers/media/video/rj54n1cb0c.c | 27 ++++++------------------ drivers/media/video/tw9910.c | 32 +++++++++------------------- include/media/soc_camera.h | 25 ++++++++++++++++++---- 15 files changed, 126 insertions(+), 286 deletions(-) (limited to 'include') diff --git a/drivers/media/video/imx074.c b/drivers/media/video/imx074.c index 3f5d4de8b91b..4f3ce7fa87df 100644 --- a/drivers/media/video/imx074.c +++ b/drivers/media/video/imx074.c @@ -298,8 +298,7 @@ static struct v4l2_subdev_ops imx074_subdev_ops = { .video = &imx074_subdev_video_ops, }; -static int imx074_video_probe(struct soc_camera_device *icd, - struct i2c_client *client) +static int imx074_video_probe(struct i2c_client *client) { int ret; u16 id; @@ -409,17 +408,10 @@ static int imx074_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct imx074 *priv; - struct soc_camera_device *icd = client->dev.platform_data; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); int ret; - if (!icd) { - dev_err(&client->dev, "IMX074: missing soc-camera data!\n"); - return -EINVAL; - } - - icl = to_soc_camera_link(icd); if (!icl) { dev_err(&client->dev, "IMX074: missing platform data!\n"); return -EINVAL; @@ -439,7 +431,7 @@ static int imx074_probe(struct i2c_client *client, priv->fmt = &imx074_colour_fmts[0]; - ret = imx074_video_probe(icd, client); + ret = imx074_video_probe(client); if (ret < 0) { kfree(priv); return ret; @@ -451,8 +443,7 @@ static int imx074_probe(struct i2c_client *client, static int imx074_remove(struct i2c_client *client) { struct imx074 *priv = to_imx074(client); - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); if (icl->free_bus) icl->free_bus(icl); diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c index 42bb3c89cfe6..58cdcedf53de 100644 --- a/drivers/media/video/mt9m001.c +++ b/drivers/media/video/mt9m001.c @@ -205,7 +205,7 @@ static int mt9m001_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) /* * The caller provides a supported format, as verified per - * call to icd->try_fmt() + * call to .try_mbus_fmt() */ if (!ret) ret = reg_write(client, MT9M001_COLUMN_START, rect.left); @@ -474,19 +474,14 @@ static int mt9m001_s_ctrl(struct v4l2_ctrl *ctrl) * Interface active, can use i2c. If it fails, it can indeed mean, that * this wasn't our capture interface, so, we wait for the right one */ -static int mt9m001_video_probe(struct soc_camera_device *icd, +static int mt9m001_video_probe(struct soc_camera_link *icl, struct i2c_client *client) { struct mt9m001 *mt9m001 = to_mt9m001(client); - struct soc_camera_link *icl = to_soc_camera_link(icd); s32 data; unsigned long flags; int ret; - /* We must have a parent by now. And it cannot be a wrong one. */ - BUG_ON(!icd->parent || - to_soc_camera_host(icd->parent)->nr != icd->iface); - /* Enable the chip */ data = reg_write(client, MT9M001_CHIP_ENABLE, 1); dev_dbg(&client->dev, "write: %d\n", data); @@ -544,12 +539,8 @@ static int mt9m001_video_probe(struct soc_camera_device *icd, return v4l2_ctrl_handler_setup(&mt9m001->hdl); } -static void mt9m001_video_remove(struct soc_camera_device *icd) +static void mt9m001_video_remove(struct soc_camera_link *icl) { - struct soc_camera_link *icl = to_soc_camera_link(icd); - - dev_dbg(icd->pdev, "Video removed: %p, %p\n", - icd->parent, icd->vdev); if (icl->free_bus) icl->free_bus(icl); } @@ -594,8 +585,7 @@ static int mt9m001_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); /* MT9M001 has all capture_format parameters fixed */ cfg->flags = V4L2_MBUS_PCLK_SAMPLE_FALLING | @@ -610,9 +600,9 @@ static int mt9m001_g_mbus_config(struct v4l2_subdev *sd, static int mt9m001_s_mbus_config(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg) { - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl = to_soc_camera_link(icd); + const struct i2c_client *client = v4l2_get_subdevdata(sd); + struct soc_camera_device *icd = soc_camera_from_i2c(client); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); /* * Cannot use icd->current_fmt->host_fmt->bits_per_sample, because that * is the number of bits, that the host has to sample, not the number of @@ -658,17 +648,10 @@ static int mt9m001_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct mt9m001 *mt9m001; - struct soc_camera_device *icd = client->dev.platform_data; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); int ret; - if (!icd) { - dev_err(&client->dev, "MT9M001: missing soc-camera data!\n"); - return -EINVAL; - } - - icl = to_soc_camera_link(icd); if (!icl) { dev_err(&client->dev, "MT9M001 driver needs platform data\n"); return -EINVAL; @@ -716,7 +699,7 @@ static int mt9m001_probe(struct i2c_client *client, mt9m001->rect.width = MT9M001_MAX_WIDTH; mt9m001->rect.height = MT9M001_MAX_HEIGHT; - ret = mt9m001_video_probe(icd, client); + ret = mt9m001_video_probe(icl, client); if (ret) { v4l2_ctrl_handler_free(&mt9m001->hdl); kfree(mt9m001); @@ -728,11 +711,11 @@ static int mt9m001_probe(struct i2c_client *client, static int mt9m001_remove(struct i2c_client *client) { struct mt9m001 *mt9m001 = to_mt9m001(client); - struct soc_camera_device *icd = client->dev.platform_data; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); v4l2_device_unregister_subdev(&mt9m001->subdev); v4l2_ctrl_handler_free(&mt9m001->hdl); - mt9m001_video_remove(icd); + mt9m001_video_remove(icl); kfree(mt9m001); return 0; diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c index 8cacbf016848..9feeb0cb6722 100644 --- a/drivers/media/video/mt9m111.c +++ b/drivers/media/video/mt9m111.c @@ -784,19 +784,12 @@ static int mt9m111_init(struct mt9m111 *mt9m111) * Interface active, can use i2c. If it fails, it can indeed mean, that * this wasn't our capture interface, so, we wait for the right one */ -static int mt9m111_video_probe(struct soc_camera_device *icd, - struct i2c_client *client) +static int mt9m111_video_probe(struct i2c_client *client) { struct mt9m111 *mt9m111 = to_mt9m111(client); s32 data; int ret; - /* We must have a parent by now. And it cannot be a wrong one. */ - BUG_ON(!icd->parent || - to_soc_camera_host(icd->parent)->nr != icd->iface); - - mt9m111->lastpage = -1; - data = reg_read(CHIP_VERSION); switch (data) { @@ -883,8 +876,7 @@ static int mt9m111_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_HIGH | @@ -915,17 +907,10 @@ static int mt9m111_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct mt9m111 *mt9m111; - struct soc_camera_device *icd = client->dev.platform_data; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); int ret; - if (!icd) { - dev_err(&client->dev, "mt9m111: soc-camera data missing!\n"); - return -EINVAL; - } - - icl = to_soc_camera_link(icd); if (!icl) { dev_err(&client->dev, "mt9m111: driver needs platform data\n"); return -EINVAL; @@ -968,8 +953,9 @@ static int mt9m111_probe(struct i2c_client *client, mt9m111->rect.width = MT9M111_MAX_WIDTH; mt9m111->rect.height = MT9M111_MAX_HEIGHT; mt9m111->fmt = &mt9m111_colour_fmts[0]; + mt9m111->lastpage = -1; - ret = mt9m111_video_probe(icd, client); + ret = mt9m111_video_probe(client); if (ret) { v4l2_ctrl_handler_free(&mt9m111->hdl); kfree(mt9m111); diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c index 7ce37990a446..95cd602ae4ec 100644 --- a/drivers/media/video/mt9t031.c +++ b/drivers/media/video/mt9t031.c @@ -265,7 +265,7 @@ static int mt9t031_set_params(struct i2c_client *client, /* * The caller provides a supported format, as guaranteed by - * icd->try_fmt_cap(), soc_camera_s_crop() and soc_camera_cropcap() + * .try_mbus_fmt(), soc_camera_s_crop() and soc_camera_cropcap() */ if (ret >= 0) ret = reg_write(client, MT9T031_COLUMN_START, rect->left); @@ -573,8 +573,7 @@ static int mt9t031_runtime_suspend(struct device *dev) static int mt9t031_runtime_resume(struct device *dev) { struct video_device *vdev = to_video_device(dev); - struct soc_camera_device *icd = dev_get_drvdata(vdev->parent); - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct v4l2_subdev *sd = soc_camera_vdev_to_subdev(vdev); struct i2c_client *client = v4l2_get_subdevdata(sd); struct mt9t031 *mt9t031 = to_mt9t031(client); @@ -684,8 +683,7 @@ static int mt9t031_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | V4L2_MBUS_HSYNC_ACTIVE_HIGH | @@ -700,8 +698,7 @@ static int mt9t031_s_mbus_config(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); if (soc_camera_apply_board_flags(icl, cfg) & V4L2_MBUS_PCLK_SAMPLE_FALLING) @@ -737,16 +734,13 @@ static int mt9t031_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct mt9t031 *mt9t031; - struct soc_camera_device *icd = client->dev.platform_data; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); int ret; - if (icd) { - struct soc_camera_link *icl = to_soc_camera_link(icd); - if (!icl) { - dev_err(&client->dev, "MT9T031 driver needs platform data\n"); - return -EINVAL; - } + if (!icl) { + dev_err(&client->dev, "MT9T031 driver needs platform data\n"); + return -EINVAL; } if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) { diff --git a/drivers/media/video/mt9t112.c b/drivers/media/video/mt9t112.c index b8da7fe9a6bd..5b045a1097c6 100644 --- a/drivers/media/video/mt9t112.c +++ b/drivers/media/video/mt9t112.c @@ -89,7 +89,6 @@ struct mt9t112_priv { struct v4l2_subdev subdev; struct mt9t112_camera_info *info; struct i2c_client *client; - struct soc_camera_device icd; struct v4l2_rect frame; const struct mt9t112_format *format; int model; @@ -306,38 +305,38 @@ static int mt9t112_clock_info(const struct i2c_client *client, u32 ext) n = (n >> 8) & 0x003f; enable = ((6000 > ext) || (54000 < ext)) ? "X" : ""; - dev_info(&client->dev, "EXTCLK : %10u K %s\n", ext, enable); + dev_dbg(&client->dev, "EXTCLK : %10u K %s\n", ext, enable); vco = 2 * m * ext / (n+1); enable = ((384000 > vco) || (768000 < vco)) ? "X" : ""; - dev_info(&client->dev, "VCO : %10u K %s\n", vco, enable); + dev_dbg(&client->dev, "VCO : %10u K %s\n", vco, enable); clk = vco / (p1+1) / (p2+1); enable = (96000 < clk) ? "X" : ""; - dev_info(&client->dev, "PIXCLK : %10u K %s\n", clk, enable); + dev_dbg(&client->dev, "PIXCLK : %10u K %s\n", clk, enable); clk = vco / (p3+1); enable = (768000 < clk) ? "X" : ""; - dev_info(&client->dev, "MIPICLK : %10u K %s\n", clk, enable); + dev_dbg(&client->dev, "MIPICLK : %10u K %s\n", clk, enable); clk = vco / (p6+1); enable = (96000 < clk) ? "X" : ""; - dev_info(&client->dev, "MCU CLK : %10u K %s\n", clk, enable); + dev_dbg(&client->dev, "MCU CLK : %10u K %s\n", clk, enable); clk = vco / (p5+1); enable = (54000 < clk) ? "X" : ""; - dev_info(&client->dev, "SOC CLK : %10u K %s\n", clk, enable); + dev_dbg(&client->dev, "SOC CLK : %10u K %s\n", clk, enable); clk = vco / (p4+1); enable = (70000 < clk) ? "X" : ""; - dev_info(&client->dev, "Sensor CLK : %10u K %s\n", clk, enable); + dev_dbg(&client->dev, "Sensor CLK : %10u K %s\n", clk, enable); clk = vco / (p7+1); - dev_info(&client->dev, "External sensor : %10u K\n", clk); + dev_dbg(&client->dev, "External sensor : %10u K\n", clk); clk = ext / (n+1); enable = ((2000 > clk) || (24000 < clk)) ? "X" : ""; - dev_info(&client->dev, "PFD : %10u K %s\n", clk, enable); + dev_dbg(&client->dev, "PFD : %10u K %s\n", clk, enable); return 0; } @@ -982,8 +981,7 @@ static int mt9t112_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | V4L2_MBUS_DATA_ACTIVE_HIGH | @@ -998,8 +996,7 @@ static int mt9t112_s_mbus_config(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); struct mt9t112_priv *priv = to_mt9t112(client); if (soc_camera_apply_board_flags(icl, cfg) & V4L2_MBUS_PCLK_SAMPLE_RISING) @@ -1029,17 +1026,12 @@ static struct v4l2_subdev_ops mt9t112_subdev_ops = { .video = &mt9t112_subdev_video_ops, }; -static int mt9t112_camera_probe(struct soc_camera_device *icd, - struct i2c_client *client) +static int mt9t112_camera_probe(struct i2c_client *client) { struct mt9t112_priv *priv = to_mt9t112(client); const char *devname; int chipid; - /* We must have a parent by now. And it cannot be a wrong one. */ - BUG_ON(!icd->parent || - to_soc_camera_host(icd->parent)->nr != icd->iface); - /* * check and show chip ID */ @@ -1068,8 +1060,7 @@ static int mt9t112_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct mt9t112_priv *priv; - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); struct v4l2_rect rect = { .width = VGA_WIDTH, .height = VGA_HEIGHT, @@ -1078,15 +1069,11 @@ static int mt9t112_probe(struct i2c_client *client, }; int ret; - if (!icd) { - dev_err(&client->dev, "mt9t112: missing soc-camera data!\n"); + if (!icl || !icl->priv) { + dev_err(&client->dev, "mt9t112: missing platform data!\n"); return -EINVAL; } - icl = to_soc_camera_link(icd); - if (!icl || !icl->priv) - return -EINVAL; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; @@ -1095,7 +1082,7 @@ static int mt9t112_probe(struct i2c_client *client, v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops); - ret = mt9t112_camera_probe(icd, client); + ret = mt9t112_camera_probe(client); if (ret) kfree(priv); diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c index 7e2aeda21752..72b179b2ad6c 100644 --- a/drivers/media/video/mt9v022.c +++ b/drivers/media/video/mt9v022.c @@ -332,7 +332,7 @@ static int mt9v022_s_fmt(struct v4l2_subdev *sd, /* * The caller provides a supported format, as verified per call to - * icd->try_fmt(), datawidth is from our supported format list + * .try_mbus_fmt(), datawidth is from our supported format list */ switch (mf->code) { case V4L2_MBUS_FMT_Y8_1X8: @@ -562,19 +562,14 @@ static int mt9v022_s_ctrl(struct v4l2_ctrl *ctrl) * Interface active, can use i2c. If it fails, it can indeed mean, that * this wasn't our capture interface, so, we wait for the right one */ -static int mt9v022_video_probe(struct soc_camera_device *icd, - struct i2c_client *client) +static int mt9v022_video_probe(struct i2c_client *client) { struct mt9v022 *mt9v022 = to_mt9v022(client); - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); s32 data; int ret; unsigned long flags; - /* We must have a parent by now. And it cannot be a wrong one. */ - BUG_ON(!icd->parent || - to_soc_camera_host(icd->parent)->nr != icd->iface); - /* Read out the chip version register */ data = reg_read(client, MT9V022_CHIP_VERSION); @@ -648,16 +643,6 @@ ei2c: return ret; } -static void mt9v022_video_remove(struct soc_camera_device *icd) -{ - struct soc_camera_link *icl = to_soc_camera_link(icd); - - dev_dbg(icd->pdev, "Video removed: %p, %p\n", - icd->parent, icd->vdev); - if (icl->free_bus) - icl->free_bus(icl); -} - static int mt9v022_g_skip_top_lines(struct v4l2_subdev *sd, u32 *lines) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -698,8 +683,7 @@ static int mt9v022_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_SLAVE | V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | @@ -730,8 +714,8 @@ static int mt9v022_s_mbus_config(struct v4l2_subdev *sd, int ret; u16 pixclk = 0; - dev_info(icd->pdev, "set %d: %s, %dbps\n", icd->current_fmt->code, - icd->current_fmt->host_fmt->name, bps); + dev_dbg(icd->pdev, "set %d: %s, %dbps\n", icd->current_fmt->code, + icd->current_fmt->host_fmt->name, bps); if (icl->set_bus_param) { ret = icl->set_bus_param(icl, 1 << (bps - 1)); @@ -798,17 +782,10 @@ static int mt9v022_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct mt9v022 *mt9v022; - struct soc_camera_device *icd = client->dev.platform_data; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl; int ret; - if (!icd) { - dev_err(&client->dev, "MT9V022: missing soc-camera data!\n"); - return -EINVAL; - } - - icl = to_soc_camera_link(icd); if (!icl) { dev_err(&client->dev, "MT9V022 driver needs platform data\n"); return -EINVAL; @@ -868,7 +845,7 @@ static int mt9v022_probe(struct i2c_client *client, mt9v022->rect.width = MT9V022_MAX_WIDTH; mt9v022->rect.height = MT9V022_MAX_HEIGHT; - ret = mt9v022_video_probe(icd, client); + ret = mt9v022_video_probe(client); if (ret) { v4l2_ctrl_handler_free(&mt9v022->hdl); kfree(mt9v022); @@ -880,10 +857,11 @@ static int mt9v022_probe(struct i2c_client *client, static int mt9v022_remove(struct i2c_client *client) { struct mt9v022 *mt9v022 = to_mt9v022(client); - struct soc_camera_device *icd = client->dev.platform_data; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); v4l2_device_unregister_subdev(&mt9v022->subdev); - mt9v022_video_remove(icd); + if (icl->free_bus) + icl->free_bus(icl); v4l2_ctrl_handler_free(&mt9v022->hdl); kfree(mt9v022); diff --git a/drivers/media/video/ov2640.c b/drivers/media/video/ov2640.c index 981767f2d8ea..d37a5cceebb6 100644 --- a/drivers/media/video/ov2640.c +++ b/drivers/media/video/ov2640.c @@ -942,18 +942,13 @@ static int ov2640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) return 0; } -static int ov2640_video_probe(struct soc_camera_device *icd, - struct i2c_client *client) +static int ov2640_video_probe(struct i2c_client *client) { struct ov2640_priv *priv = to_ov2640(client); u8 pid, ver, midh, midl; const char *devname; int ret; - /* We must have a parent by now. And it cannot be a wrong one. */ - BUG_ON(!icd->parent || - to_soc_camera_host(icd->parent)->nr != icd->iface); - /* * check and show product ID and manufacturer ID */ @@ -1001,8 +996,7 @@ static int ov2640_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | @@ -1035,18 +1029,11 @@ static struct v4l2_subdev_ops ov2640_subdev_ops = { static int ov2640_probe(struct i2c_client *client, const struct i2c_device_id *did) { - struct ov2640_priv *priv; - struct soc_camera_device *icd = client->dev.platform_data; - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl; - int ret; - - if (!icd) { - dev_err(&adapter->dev, "OV2640: missing soc-camera data!\n"); - return -EINVAL; - } + struct ov2640_priv *priv; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + int ret; - icl = to_soc_camera_link(icd); if (!icl) { dev_err(&adapter->dev, "OV2640: Missing platform_data for driver\n"); @@ -1080,7 +1067,7 @@ static int ov2640_probe(struct i2c_client *client, return err; } - ret = ov2640_video_probe(icd, client); + ret = ov2640_video_probe(client); if (ret) { v4l2_ctrl_handler_free(&priv->hdl); kfree(priv); diff --git a/drivers/media/video/ov5642.c b/drivers/media/video/ov5642.c index 163a6f7ff8ef..2a26602fc86d 100644 --- a/drivers/media/video/ov5642.c +++ b/drivers/media/video/ov5642.c @@ -889,8 +889,7 @@ static struct v4l2_subdev_ops ov5642_subdev_ops = { .video = &ov5642_subdev_video_ops, }; -static int ov5642_video_probe(struct soc_camera_device *icd, - struct i2c_client *client) +static int ov5642_video_probe(struct i2c_client *client) { int ret; u8 id_high, id_low; @@ -921,16 +920,9 @@ static int ov5642_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov5642 *priv; - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); int ret; - if (!icd) { - dev_err(&client->dev, "OV5642: missing soc-camera data!\n"); - return -EINVAL; - } - - icl = to_soc_camera_link(icd); if (!icl) { dev_err(&client->dev, "OV5642: missing platform data!\n"); return -EINVAL; @@ -944,7 +936,7 @@ static int ov5642_probe(struct i2c_client *client, priv->fmt = &ov5642_colour_fmts[0]; - ret = ov5642_video_probe(icd, client); + ret = ov5642_video_probe(client); if (ret < 0) goto error; @@ -958,8 +950,7 @@ error: static int ov5642_remove(struct i2c_client *client) { struct ov5642 *priv = to_ov5642(client); - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); if (icl->free_bus) icl->free_bus(icl); diff --git a/drivers/media/video/ov6650.c b/drivers/media/video/ov6650.c index efa45132c992..f060eaaf916f 100644 --- a/drivers/media/video/ov6650.c +++ b/drivers/media/video/ov6650.c @@ -820,8 +820,7 @@ static int ov6650_prog_dflt(struct i2c_client *client) return ret; } -static int ov6650_video_probe(struct soc_camera_device *icd, - struct i2c_client *client) +static int ov6650_video_probe(struct i2c_client *client) { u8 pidh, pidl, midh, midl; int ret = 0; @@ -875,8 +874,7 @@ static int ov6650_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); cfg->flags = V4L2_MBUS_MASTER | V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | @@ -894,8 +892,7 @@ static int ov6650_s_mbus_config(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); unsigned long flags = soc_camera_apply_board_flags(icl, cfg); int ret; @@ -948,16 +945,9 @@ static int ov6650_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov6650 *priv; - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); int ret; - if (!icd) { - dev_err(&client->dev, "Missing soc-camera data!\n"); - return -EINVAL; - } - - icl = to_soc_camera_link(icd); if (!icl) { dev_err(&client->dev, "Missing platform_data for driver\n"); return -EINVAL; @@ -1020,7 +1010,7 @@ static int ov6650_probe(struct i2c_client *client, priv->code = V4L2_MBUS_FMT_YUYV8_2X8; priv->colorspace = V4L2_COLORSPACE_JPEG; - ret = ov6650_video_probe(icd, client); + ret = ov6650_video_probe(client); if (!ret) ret = v4l2_ctrl_handler_setup(&priv->hdl); diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c index 9b540421e2cb..a2146c30771e 100644 --- a/drivers/media/video/ov772x.c +++ b/drivers/media/video/ov772x.c @@ -953,17 +953,12 @@ static int ov772x_try_fmt(struct v4l2_subdev *sd, return 0; } -static int ov772x_video_probe(struct soc_camera_device *icd, - struct i2c_client *client) +static int ov772x_video_probe(struct i2c_client *client) { struct ov772x_priv *priv = to_ov772x(client); u8 pid, ver; const char *devname; - /* We must have a parent by now. And it cannot be a wrong one. */ - BUG_ON(!icd->parent || - to_soc_camera_host(icd->parent)->nr != icd->iface); - /* * check and show product ID and manufacturer ID */ @@ -1021,8 +1016,7 @@ static int ov772x_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | @@ -1056,20 +1050,15 @@ static struct v4l2_subdev_ops ov772x_subdev_ops = { static int ov772x_probe(struct i2c_client *client, const struct i2c_device_id *did) { - struct ov772x_priv *priv; - struct soc_camera_device *icd = client->dev.platform_data; - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl; - int ret; - - if (!icd) { - dev_err(&client->dev, "OV772X: missing soc-camera data!\n"); - return -EINVAL; - } + struct ov772x_priv *priv; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + int ret; - icl = to_soc_camera_link(icd); - if (!icl || !icl->priv) + if (!icl || !icl->priv) { + dev_err(&client->dev, "OV772X: missing platform data!\n"); return -EINVAL; + } if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_err(&adapter->dev, @@ -1100,7 +1089,7 @@ static int ov772x_probe(struct i2c_client *client, return err; } - ret = ov772x_video_probe(icd, client); + ret = ov772x_video_probe(client); if (ret) { v4l2_ctrl_handler_free(&priv->hdl); kfree(priv); diff --git a/drivers/media/video/ov9640.c b/drivers/media/video/ov9640.c index 12d33a9c07b0..f9babf39d802 100644 --- a/drivers/media/video/ov9640.c +++ b/drivers/media/video/ov9640.c @@ -578,8 +578,7 @@ static int ov9640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) return 0; } -static int ov9640_video_probe(struct soc_camera_device *icd, - struct i2c_client *client) +static int ov9640_video_probe(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); struct ov9640_priv *priv = to_ov9640_sensor(sd); @@ -587,10 +586,6 @@ static int ov9640_video_probe(struct soc_camera_device *icd, const char *devname; int ret = 0; - /* We must have a parent by now. And it cannot be a wrong one. */ - BUG_ON(!icd->parent || - to_soc_camera_host(icd->parent)->nr != icd->iface); - /* * check and show product ID and manufacturer ID */ @@ -644,8 +639,7 @@ static int ov9640_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | @@ -678,16 +672,9 @@ static int ov9640_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov9640_priv *priv; - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); int ret; - if (!icd) { - dev_err(&client->dev, "Missing soc-camera data!\n"); - return -EINVAL; - } - - icl = to_soc_camera_link(icd); if (!icl) { dev_err(&client->dev, "Missing platform_data for driver\n"); return -EINVAL; @@ -715,7 +702,7 @@ static int ov9640_probe(struct i2c_client *client, return err; } - ret = ov9640_video_probe(icd, client); + ret = ov9640_video_probe(client); if (ret) { v4l2_ctrl_handler_free(&priv->hdl); diff --git a/drivers/media/video/ov9740.c b/drivers/media/video/ov9740.c index 3dd910dcc5b8..9558aca86818 100644 --- a/drivers/media/video/ov9740.c +++ b/drivers/media/video/ov9740.c @@ -836,18 +836,13 @@ static int ov9740_set_register(struct v4l2_subdev *sd, } #endif -static int ov9740_video_probe(struct soc_camera_device *icd, - struct i2c_client *client) +static int ov9740_video_probe(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); struct ov9740_priv *priv = to_ov9740(sd); u8 modelhi, modello; int ret; - /* We must have a parent by now. And it cannot be a wrong one. */ - BUG_ON(!icd->parent || - to_soc_camera_host(icd->parent)->nr != icd->iface); - /* * check and show product ID and manufacturer ID */ @@ -893,8 +888,7 @@ static int ov9740_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_HSYNC_ACTIVE_HIGH | @@ -940,16 +934,9 @@ static int ov9740_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov9740_priv *priv; - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); int ret; - if (!icd) { - dev_err(&client->dev, "Missing soc-camera data!\n"); - return -EINVAL; - } - - icl = to_soc_camera_link(icd); if (!icl) { dev_err(&client->dev, "Missing platform_data for driver\n"); return -EINVAL; @@ -975,7 +962,7 @@ static int ov9740_probe(struct i2c_client *client, return err; } - ret = ov9740_video_probe(icd, client); + ret = ov9740_video_probe(client); if (!ret) ret = v4l2_ctrl_handler_setup(&priv->hdl); if (ret < 0) { diff --git a/drivers/media/video/rj54n1cb0c.c b/drivers/media/video/rj54n1cb0c.c index 9a871537d233..fcb14d93a24c 100644 --- a/drivers/media/video/rj54n1cb0c.c +++ b/drivers/media/video/rj54n1cb0c.c @@ -1235,8 +1235,7 @@ static int rj54n1_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_PCLK_SAMPLE_FALLING | @@ -1252,8 +1251,7 @@ static int rj54n1_s_mbus_config(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); /* Figures 2.5-1 to 2.5-3 - default falling pixclk edge */ if (soc_camera_apply_board_flags(icl, cfg) & @@ -1285,17 +1283,12 @@ static struct v4l2_subdev_ops rj54n1_subdev_ops = { * Interface active, can use i2c. If it fails, it can indeed mean, that * this wasn't our capture interface, so, we wait for the right one */ -static int rj54n1_video_probe(struct soc_camera_device *icd, - struct i2c_client *client, +static int rj54n1_video_probe(struct i2c_client *client, struct rj54n1_pdata *priv) { int data1, data2; int ret; - /* We must have a parent by now. And it cannot be a wrong one. */ - BUG_ON(!icd->parent || - to_soc_camera_host(icd->parent)->nr != icd->iface); - /* Read out the chip version register */ data1 = reg_read(client, RJ54N1_DEV_CODE); data2 = reg_read(client, RJ54N1_DEV_CODE2); @@ -1323,18 +1316,11 @@ static int rj54n1_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct rj54n1 *rj54n1; - struct soc_camera_device *icd = client->dev.platform_data; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl; struct rj54n1_pdata *rj54n1_priv; int ret; - if (!icd) { - dev_err(&client->dev, "RJ54N1CB0C: missing soc-camera data!\n"); - return -EINVAL; - } - - icl = to_soc_camera_link(icd); if (!icl || !icl->priv) { dev_err(&client->dev, "RJ54N1CB0C: missing platform data!\n"); return -EINVAL; @@ -1382,7 +1368,7 @@ static int rj54n1_probe(struct i2c_client *client, rj54n1->tgclk_mhz = (rj54n1_priv->mclk_freq / PLL_L * PLL_N) / (clk_div.ratio_tg + 1) / (clk_div.ratio_t + 1); - ret = rj54n1_video_probe(icd, client, rj54n1_priv); + ret = rj54n1_video_probe(client, rj54n1_priv); if (ret < 0) { v4l2_ctrl_handler_free(&rj54n1->hdl); kfree(rj54n1); @@ -1394,8 +1380,7 @@ static int rj54n1_probe(struct i2c_client *client, static int rj54n1_remove(struct i2c_client *client) { struct rj54n1 *rj54n1 = to_rj54n1(client); - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); v4l2_device_unregister_subdev(&rj54n1->subdev); if (icl->free_bus) diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c index 2fddd1fbe529..5a3722b756c7 100644 --- a/drivers/media/video/tw9910.c +++ b/drivers/media/video/tw9910.c @@ -764,10 +764,6 @@ static int tw9910_video_probe(struct soc_camera_device *icd, struct tw9910_priv *priv = to_tw9910(client); s32 id; - /* We must have a parent by now. And it cannot be a wrong one. */ - BUG_ON(!icd->parent || - to_soc_camera_host(icd->parent)->nr != icd->iface); - /* * tw9910 only use 8 or 16 bit bus width */ @@ -825,8 +821,7 @@ static int tw9910_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); cfg->flags = V4L2_MBUS_PCLK_SAMPLE_RISING | V4L2_MBUS_MASTER | V4L2_MBUS_VSYNC_ACTIVE_HIGH | V4L2_MBUS_VSYNC_ACTIVE_LOW | @@ -842,8 +837,7 @@ static int tw9910_s_mbus_config(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); u8 val = VSSL_VVALID | HSSL_DVALID; unsigned long flags = soc_camera_apply_board_flags(icl, cfg); @@ -887,23 +881,19 @@ static int tw9910_probe(struct i2c_client *client, const struct i2c_device_id *did) { - struct tw9910_priv *priv; - struct tw9910_video_info *info; - struct soc_camera_device *icd = client->dev.platform_data; - struct i2c_adapter *adapter = + struct tw9910_priv *priv; + struct tw9910_video_info *info; + struct soc_camera_device *icd = client->dev.platform_data; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); - struct soc_camera_link *icl; - int ret; + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); + int ret; - if (!icd) { - dev_err(&client->dev, "TW9910: missing soc-camera data!\n"); + if (!icl || !icl->priv) { + dev_err(&client->dev, "TW9910: missing platform data!\n"); return -EINVAL; } - icl = to_soc_camera_link(icd); - if (!icl || !icl->priv) - return -EINVAL; - info = icl->priv; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { @@ -921,8 +911,6 @@ static int tw9910_probe(struct i2c_client *client, v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops); - icd->iface = icl->bus_id; - ret = tw9910_video_probe(icd, client); if (ret) kfree(priv); diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 6398ff0f08ab..67a52c729eff 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -251,18 +251,35 @@ unsigned long soc_camera_apply_board_flags(struct soc_camera_link *icl, /* This is only temporary here - until v4l2-subdev begins to link to video_device */ #include -static inline struct video_device *soc_camera_i2c_to_vdev(struct i2c_client *client) +static inline struct video_device *soc_camera_i2c_to_vdev(const struct i2c_client *client) { struct soc_camera_device *icd = client->dev.platform_data; - return icd->vdev; + return icd ? icd->vdev : NULL; } -static inline struct soc_camera_device *soc_camera_from_vb2q(struct vb2_queue *vq) +static inline struct soc_camera_link *soc_camera_i2c_to_link(const struct i2c_client *client) +{ + struct soc_camera_device *icd = client->dev.platform_data; + return icd ? to_soc_camera_link(icd) : NULL; +} + +static inline struct v4l2_subdev *soc_camera_vdev_to_subdev(const struct video_device *vdev) +{ + struct soc_camera_device *icd = dev_get_drvdata(vdev->parent); + return soc_camera_to_subdev(icd); +} + +static inline struct soc_camera_device *soc_camera_from_i2c(const struct i2c_client *client) +{ + return client->dev.platform_data; +} + +static inline struct soc_camera_device *soc_camera_from_vb2q(const struct vb2_queue *vq) { return container_of(vq, struct soc_camera_device, vb2_vidq); } -static inline struct soc_camera_device *soc_camera_from_vbq(struct videobuf_queue *vq) +static inline struct soc_camera_device *soc_camera_from_vbq(const struct videobuf_queue *vq) { return container_of(vq, struct soc_camera_device, vb_vidq); } -- cgit v1.2.3 From 443f483aa2494b93d73ba122cafdf2ef89989ed7 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 9 Sep 2011 07:06:50 -0300 Subject: [media] V4L: mt9m001, mt9v022: use internally cached pixel code Using the internally cached pixel code, instead of the one, provided by the soc-camera, removes one more use of struct soc_camera_device in these drivers. Also remove the no longer needed soc_camera_from_i2c() inline function. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/mt9m001.c | 10 ++-------- drivers/media/video/mt9v022.c | 14 ++------------ include/media/soc_camera.h | 5 ----- 3 files changed, 4 insertions(+), 25 deletions(-) (limited to 'include') diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c index 58cdcedf53de..63ae5c61c9bf 100644 --- a/drivers/media/video/mt9m001.c +++ b/drivers/media/video/mt9m001.c @@ -601,15 +601,9 @@ static int mt9m001_s_mbus_config(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg) { const struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_device *icd = soc_camera_from_i2c(client); struct soc_camera_link *icl = soc_camera_i2c_to_link(client); - /* - * Cannot use icd->current_fmt->host_fmt->bits_per_sample, because that - * is the number of bits, that the host has to sample, not the number of - * bits, that we have to send. See mx3_camera.c for an example of 10-bit - * formats being truncated to 8 bits by the host. - */ - unsigned int bps = soc_mbus_get_fmtdesc(icd->current_fmt->code)->bits_per_sample; + struct mt9m001 *mt9m001 = to_mt9m001(client); + unsigned int bps = soc_mbus_get_fmtdesc(mt9m001->fmt->code)->bits_per_sample; if (icl->set_bus_param) return icl->set_bus_param(icl, 1 << (bps - 1)); diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c index 72b179b2ad6c..b6a29f7de82c 100644 --- a/drivers/media/video/mt9v022.c +++ b/drivers/media/video/mt9v022.c @@ -700,23 +700,13 @@ static int mt9v022_s_mbus_config(struct v4l2_subdev *sd, const struct v4l2_mbus_config *cfg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_device *icd = client->dev.platform_data; - struct soc_camera_link *icl = to_soc_camera_link(icd); + struct soc_camera_link *icl = soc_camera_i2c_to_link(client); struct mt9v022 *mt9v022 = to_mt9v022(client); unsigned long flags = soc_camera_apply_board_flags(icl, cfg); - /* - * Cannot use icd->current_fmt->host_fmt->bits_per_sample, because that - * is the number of bits, that the host has to sample, not the number of - * bits, that we have to send. See mx3_camera.c for an example of 10-bit - * formats being truncated to 8 bits by the host. - */ - unsigned int bps = soc_mbus_get_fmtdesc(icd->current_fmt->code)->bits_per_sample; + unsigned int bps = soc_mbus_get_fmtdesc(mt9v022->fmt->code)->bits_per_sample; int ret; u16 pixclk = 0; - dev_dbg(icd->pdev, "set %d: %s, %dbps\n", icd->current_fmt->code, - icd->current_fmt->host_fmt->name, bps); - if (icl->set_bus_param) { ret = icl->set_bus_param(icl, 1 << (bps - 1)); if (ret) diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index 67a52c729eff..dac57598ee57 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -269,11 +269,6 @@ static inline struct v4l2_subdev *soc_camera_vdev_to_subdev(const struct video_d return soc_camera_to_subdev(icd); } -static inline struct soc_camera_device *soc_camera_from_i2c(const struct i2c_client *client) -{ - return client->dev.platform_data; -} - static inline struct soc_camera_device *soc_camera_from_vb2q(const struct vb2_queue *vq) { return container_of(vq, struct soc_camera_device, vb2_vidq); -- cgit v1.2.3 From 3e0ec41c5c5ee14e27f65e28d4a616de34f59a97 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 13 Sep 2011 08:07:55 -0300 Subject: [media] V4L: dynamically allocate video_device nodes in subdevices Currently only very few drivers actually use video_device nodes, embedded in struct v4l2_subdev. Allocate these nodes dynamically for those drivers to save memory for the rest. Signed-off-by: Guennadi Liakhovetski Tested-by: Sylwester Nawrocki Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-device.c | 36 +++++++++++++++++++++++++++++++----- include/media/v4l2-subdev.h | 4 ++-- 2 files changed, 33 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c index e6a2c3b302d4..9fc0ae8a526a 100644 --- a/drivers/media/video/v4l2-device.c +++ b/drivers/media/video/v4l2-device.c @@ -21,6 +21,7 @@ #include #include #include +#include #if defined(CONFIG_SPI) #include #endif @@ -193,6 +194,13 @@ int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev, } EXPORT_SYMBOL_GPL(v4l2_device_register_subdev); +static void v4l2_device_release_subdev_node(struct video_device *vdev) +{ + struct v4l2_subdev *sd = video_get_drvdata(vdev); + sd->devnode = NULL; + kfree(vdev); +} + int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev) { struct video_device *vdev; @@ -206,22 +214,40 @@ int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev) if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE)) continue; - vdev = &sd->devnode; + vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); + if (!vdev) { + err = -ENOMEM; + goto clean_up; + } + + video_set_drvdata(vdev, sd); strlcpy(vdev->name, sd->name, sizeof(vdev->name)); vdev->v4l2_dev = v4l2_dev; vdev->fops = &v4l2_subdev_fops; - vdev->release = video_device_release_empty; + vdev->release = v4l2_device_release_subdev_node; vdev->ctrl_handler = sd->ctrl_handler; err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1, sd->owner); - if (err < 0) - return err; + if (err < 0) { + kfree(vdev); + goto clean_up; + } #if defined(CONFIG_MEDIA_CONTROLLER) sd->entity.v4l.major = VIDEO_MAJOR; sd->entity.v4l.minor = vdev->minor; #endif + sd->devnode = vdev; } return 0; + +clean_up: + list_for_each_entry(sd, &v4l2_dev->subdevs, list) { + if (!sd->devnode) + break; + video_unregister_device(sd->devnode); + } + + return err; } EXPORT_SYMBOL_GPL(v4l2_device_register_subdev_nodes); @@ -247,7 +273,7 @@ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd) if (v4l2_dev->mdev) media_device_unregister_entity(&sd->entity); #endif - video_unregister_device(&sd->devnode); + video_unregister_device(sd->devnode); module_put(sd->owner); } EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev); diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 257da1a30f66..5dd049a7437d 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -534,13 +534,13 @@ struct v4l2_subdev { void *dev_priv; void *host_priv; /* subdev device node */ - struct video_device devnode; + struct video_device *devnode; }; #define media_entity_to_v4l2_subdev(ent) \ container_of(ent, struct v4l2_subdev, entity) #define vdev_to_v4l2_subdev(vdev) \ - container_of(vdev, struct v4l2_subdev, devnode) + video_get_drvdata(vdev) /* * Used for storing subdev information per file handle -- cgit v1.2.3 From 1a99b972a86ba9c3984c042f7f641458ad4812d0 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 9 Sep 2011 13:10:02 -0300 Subject: [media] V4L: add .g_std() core V4L2 subdevice operation VIDIOC_G_STD can return the current TV-norm to the user in one of two ways: if an .vidioc_g_std() ioctl operation is provided by the driver, it is called, otherwise the value ot the .current_norm field of struct video_device is returned. Since subdevice drivers currently have no access to struct video_device objects, the only way to provide this information to the user is by implementing a .g_std() method. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-subdev.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include') diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 5dd049a7437d..f0f3358d1b1b 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -158,6 +158,7 @@ struct v4l2_subdev_core_ops { int (*s_ext_ctrls)(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls); int (*try_ext_ctrls)(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls); int (*querymenu)(struct v4l2_subdev *sd, struct v4l2_querymenu *qm); + int (*g_std)(struct v4l2_subdev *sd, v4l2_std_id *norm); int (*s_std)(struct v4l2_subdev *sd, v4l2_std_id norm); long (*ioctl)(struct v4l2_subdev *sd, unsigned int cmd, void *arg); #ifdef CONFIG_VIDEO_ADV_DEBUG -- cgit v1.2.3 From 2f0babb7e43278247df512263581c4738afa4cbc Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Fri, 9 Sep 2011 13:39:20 -0300 Subject: [media] V4L: soc-camera: make (almost) all client drivers re-usable outside of the framework The most important change in this patch is direct linking to struct soc_camera_link via the client->dev.platform_data pointer. This makes most of the soc-camera client drivers also usable outside of the soc-camera framework. After this change all what is needed for these drivers to function are inclusions of soc-camera headers for some convenience macros, suitably configured platform data, which is anyway always required, and loaded soc-camera core module for library functions. If desired, these library functions can be made generic in the future and moved to a more neutral location. The only two client drivers, that still depend on soc-camera are: mt9t031: it uses struct video_device for its PM. Since no hardware is available, alternative methods cannot be tested. ov6650: it uses struct soc_camera_device to pass its sense data back to the bridge driver. A generic v4l2-subdevice approach should be developed to perform this. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/mt9t031.c | 29 +++++++++++++++++++---- drivers/media/video/ov6650.c | 2 +- drivers/media/video/soc_camera.c | 11 ++++++++- drivers/media/video/tw9910.c | 50 ++++++++++++++++++++++++---------------- include/media/soc_camera.h | 6 ++--- 5 files changed, 68 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c index 95cd602ae4ec..0226486d59be 100644 --- a/drivers/media/video/mt9t031.c +++ b/drivers/media/video/mt9t031.c @@ -21,6 +21,13 @@ #include #include +/* + * ATTENTION: this driver still cannot be used outside of the soc-camera + * framework because of its PM implementation, using the video_device node. + * If hardware becomes available for testing, alternative PM approaches shall + * be considered and tested. + */ + /* * mt9t031 i2c address 0x5d * The platform has to define i2c_board_info and link to it from @@ -606,6 +613,19 @@ static struct device_type mt9t031_dev_type = { .pm = &mt9t031_dev_pm_ops, }; +static int mt9t031_s_power(struct v4l2_subdev *sd, int on) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct video_device *vdev = soc_camera_i2c_to_vdev(client); + + if (on) + vdev->dev.type = &mt9t031_dev_type; + else + vdev->dev.type = NULL; + + return 0; +} + /* * Interface active, can use i2c. If it fails, it can indeed mean, that * this wasn't our capture interface, so, we wait for the right one @@ -613,7 +633,6 @@ static struct device_type mt9t031_dev_type = { static int mt9t031_video_probe(struct i2c_client *client) { struct mt9t031 *mt9t031 = to_mt9t031(client); - struct video_device *vdev = soc_camera_i2c_to_vdev(client); s32 data; int ret; @@ -637,12 +656,11 @@ static int mt9t031_video_probe(struct i2c_client *client) dev_info(&client->dev, "Detected a MT9T031 chip ID %x\n", data); ret = mt9t031_idle(client); - if (ret < 0) { + if (ret < 0) dev_err(&client->dev, "Failed to initialise the camera\n"); - } else { - vdev->dev.type = &mt9t031_dev_type; + else v4l2_ctrl_handler_setup(&mt9t031->hdl); - } + return ret; } @@ -663,6 +681,7 @@ static const struct v4l2_ctrl_ops mt9t031_ctrl_ops = { static struct v4l2_subdev_core_ops mt9t031_subdev_core_ops = { .g_chip_ident = mt9t031_g_chip_ident, + .s_power = mt9t031_s_power, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = mt9t031_g_register, .s_register = mt9t031_s_register, diff --git a/drivers/media/video/ov6650.c b/drivers/media/video/ov6650.c index f060eaaf916f..eb296f9e69f4 100644 --- a/drivers/media/video/ov6650.c +++ b/drivers/media/video/ov6650.c @@ -541,7 +541,7 @@ static u8 to_clkrc(struct v4l2_fract *timeperframe, static int ov6650_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_device *icd = client->dev.platform_data; + struct soc_camera_device *icd = (struct soc_camera_device *)sd->grp_id; struct soc_camera_sense *sense = icd->sense; struct ov6650 *priv = to_ov6650(client); bool half_scale = !is_unscaled_ok(mf->width, mf->height, &priv->rect); diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index 9a62ed08d86a..b72580c38957 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -249,6 +249,14 @@ static int soc_camera_s_std(struct file *file, void *priv, v4l2_std_id *a) return v4l2_subdev_call(sd, core, s_std, *a); } +static int soc_camera_g_std(struct file *file, void *priv, v4l2_std_id *a) +{ + struct soc_camera_device *icd = file->private_data; + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + + return v4l2_subdev_call(sd, core, g_std, a); +} + static int soc_camera_enum_fsizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize) { @@ -977,7 +985,7 @@ static int soc_camera_init_i2c(struct soc_camera_device *icd, goto ei2cga; } - icl->board_info->platform_data = icd; + icl->board_info->platform_data = icl; subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap, icl->board_info, NULL); @@ -1376,6 +1384,7 @@ static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = { .vidioc_g_input = soc_camera_g_input, .vidioc_s_input = soc_camera_s_input, .vidioc_s_std = soc_camera_s_std, + .vidioc_g_std = soc_camera_g_std, .vidioc_enum_framesizes = soc_camera_enum_fsizes, .vidioc_reqbufs = soc_camera_reqbufs, .vidioc_querybuf = soc_camera_querybuf, diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c index 5a3722b756c7..efce5371915e 100644 --- a/drivers/media/video/tw9910.c +++ b/drivers/media/video/tw9910.c @@ -230,6 +230,7 @@ struct tw9910_priv { struct v4l2_subdev subdev; struct tw9910_video_info *info; const struct tw9910_scale_ctrl *scale; + v4l2_std_id norm; u32 revision; }; @@ -421,12 +422,11 @@ static int tw9910_power(struct i2c_client *client, int enable) return tw9910_mask_set(client, ACNTL2, ACNTL2_PDN_MASK, acntl2); } -static const struct tw9910_scale_ctrl *tw9910_select_norm(struct soc_camera_device *icd, +static const struct tw9910_scale_ctrl *tw9910_select_norm(v4l2_std_id norm, u32 width, u32 height) { const struct tw9910_scale_ctrl *scale; const struct tw9910_scale_ctrl *ret = NULL; - v4l2_std_id norm = icd->vdev->current_norm; __u32 diff = 0xffffffff, tmp; int size, i; @@ -495,14 +495,27 @@ static int tw9910_s_stream(struct v4l2_subdev *sd, int enable) return tw9910_power(client, enable); } +static int tw9910_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct tw9910_priv *priv = to_tw9910(client); + + *norm = priv->norm; + + return 0; +} + static int tw9910_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) { - int ret = -EINVAL; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct tw9910_priv *priv = to_tw9910(client); - if (norm & (V4L2_STD_NTSC | V4L2_STD_PAL)) - ret = 0; + if (!(norm & (V4L2_STD_NTSC | V4L2_STD_PAL))) + return -EINVAL; - return ret; + priv->norm = norm; + + return 0; } static int tw9910_g_chip_ident(struct v4l2_subdev *sd, @@ -557,14 +570,13 @@ static int tw9910_set_frame(struct v4l2_subdev *sd, u32 *width, u32 *height) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct tw9910_priv *priv = to_tw9910(client); - struct soc_camera_device *icd = client->dev.platform_data; int ret = -EINVAL; u8 val; /* * select suitable norm */ - priv->scale = tw9910_select_norm(icd, *width, *height); + priv->scale = tw9910_select_norm(priv->norm, *width, *height); if (!priv->scale) goto tw9910_set_fmt_error; @@ -642,11 +654,11 @@ tw9910_set_fmt_error: static int tw9910_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_device *icd = client->dev.platform_data; + struct tw9910_priv *priv = to_tw9910(client); a->c.left = 0; a->c.top = 0; - if (icd->vdev->current_norm & V4L2_STD_NTSC) { + if (priv->norm & V4L2_STD_NTSC) { a->c.width = 640; a->c.height = 480; } else { @@ -661,11 +673,11 @@ static int tw9910_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) static int tw9910_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_device *icd = client->dev.platform_data; + struct tw9910_priv *priv = to_tw9910(client); a->bounds.left = 0; a->bounds.top = 0; - if (icd->vdev->current_norm & V4L2_STD_NTSC) { + if (priv->norm & V4L2_STD_NTSC) { a->bounds.width = 640; a->bounds.height = 480; } else { @@ -732,7 +744,7 @@ static int tw9910_try_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct soc_camera_device *icd = client->dev.platform_data; + struct tw9910_priv *priv = to_tw9910(client); const struct tw9910_scale_ctrl *scale; if (V4L2_FIELD_ANY == mf->field) { @@ -748,7 +760,7 @@ static int tw9910_try_fmt(struct v4l2_subdev *sd, /* * select suitable norm */ - scale = tw9910_select_norm(icd, mf->width, mf->height); + scale = tw9910_select_norm(priv->norm, mf->width, mf->height); if (!scale) return -EINVAL; @@ -758,8 +770,7 @@ static int tw9910_try_fmt(struct v4l2_subdev *sd, return 0; } -static int tw9910_video_probe(struct soc_camera_device *icd, - struct i2c_client *client) +static int tw9910_video_probe(struct i2c_client *client) { struct tw9910_priv *priv = to_tw9910(client); s32 id; @@ -792,8 +803,7 @@ static int tw9910_video_probe(struct soc_camera_device *icd, dev_info(&client->dev, "tw9910 Product ID %0x:%0x\n", id, priv->revision); - icd->vdev->tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL; - icd->vdev->current_norm = V4L2_STD_NTSC; + priv->norm = V4L2_STD_NTSC; return 0; } @@ -801,6 +811,7 @@ static int tw9910_video_probe(struct soc_camera_device *icd, static struct v4l2_subdev_core_ops tw9910_subdev_core_ops = { .g_chip_ident = tw9910_g_chip_ident, .s_std = tw9910_s_std, + .g_std = tw9910_g_std, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = tw9910_g_register, .s_register = tw9910_s_register, @@ -883,7 +894,6 @@ static int tw9910_probe(struct i2c_client *client, { struct tw9910_priv *priv; struct tw9910_video_info *info; - struct soc_camera_device *icd = client->dev.platform_data; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); struct soc_camera_link *icl = soc_camera_i2c_to_link(client); @@ -911,7 +921,7 @@ static int tw9910_probe(struct i2c_client *client, v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops); - ret = tw9910_video_probe(icd, client); + ret = tw9910_video_probe(client); if (ret) kfree(priv); diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index dac57598ee57..b1377b931eb7 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -253,14 +253,14 @@ unsigned long soc_camera_apply_board_flags(struct soc_camera_link *icl, #include static inline struct video_device *soc_camera_i2c_to_vdev(const struct i2c_client *client) { - struct soc_camera_device *icd = client->dev.platform_data; + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct soc_camera_device *icd = (struct soc_camera_device *)sd->grp_id; return icd ? icd->vdev : NULL; } static inline struct soc_camera_link *soc_camera_i2c_to_link(const struct i2c_client *client) { - struct soc_camera_device *icd = client->dev.platform_data; - return icd ? to_soc_camera_link(icd) : NULL; + return client->dev.platform_data; } static inline struct v4l2_subdev *soc_camera_vdev_to_subdev(const struct video_device *vdev) -- cgit v1.2.3 From d26a6635b24210791cf4b71fd861738270c8cc3c Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Sun, 4 Sep 2011 19:08:54 -0300 Subject: [media] v4l: Add AUTO option for the V4L2_CID_POWER_LINE_FREQUENCY control V4L2_CID_POWER_LINE_FREQUENCY control allows applications to instruct a driver what is the power line frequency so an appropriate filter can be used by the device to cancel flicker by compensating the light intensity ripple. Currently in the menu we have entries for 50 Hz and 60 Hz and for entirely disabling the anti-flicker filter. However some devices are capable of automatically detecting the frequency, so add V4L2_CID_POWER_LINE_FREQUENCY_AUTO entry for them. Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/media/v4l/controls.xml | 5 +++-- drivers/media/video/v4l2-ctrls.c | 1 + include/linux/videodev2.h | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index 23fdf79f8cf3..3bc5ee8b2c74 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -232,8 +232,9 @@ control is deprecated. New drivers and applications should use the Enables a power line frequency filter to avoid flicker. Possible values for enum v4l2_power_line_frequency are: V4L2_CID_POWER_LINE_FREQUENCY_DISABLED (0), -V4L2_CID_POWER_LINE_FREQUENCY_50HZ (1) and -V4L2_CID_POWER_LINE_FREQUENCY_60HZ (2). +V4L2_CID_POWER_LINE_FREQUENCY_50HZ (1), +V4L2_CID_POWER_LINE_FREQUENCY_60HZ (2) and +V4L2_CID_POWER_LINE_FREQUENCY_AUTO (3). V4L2_CID_HUE_AUTO diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index fc8666ae408f..5552f8137571 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -210,6 +210,7 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "Disabled", "50 Hz", "60 Hz", + "Auto", NULL }; static const char * const camera_exposure_auto[] = { diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 66945a6f628d..4b752d5ee80e 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1169,6 +1169,7 @@ enum v4l2_power_line_frequency { V4L2_CID_POWER_LINE_FREQUENCY_DISABLED = 0, V4L2_CID_POWER_LINE_FREQUENCY_50HZ = 1, V4L2_CID_POWER_LINE_FREQUENCY_60HZ = 2, + V4L2_CID_POWER_LINE_FREQUENCY_AUTO = 3, }; #define V4L2_CID_HUE_AUTO (V4L2_CID_BASE+25) #define V4L2_CID_WHITE_BALANCE_TEMPERATURE (V4L2_CID_BASE+26) -- cgit v1.2.3 From bfa8dd3a05248457fce18712e7bc0499030b3100 Mon Sep 17 00:00:00 2001 From: Sylwester Nawrocki Date: Tue, 4 Oct 2011 14:05:58 -0300 Subject: [media] v4l: Add v4l2 subdev driver for S5K6AAFX sensor This driver exposes preview mode operation of the S5K6AAFX sensor with embedded SoC ISP. The native capture (snapshot) operation mode is not supported. Following controls are available: manual/auto exposure and gain, power line frequency (anti-flicker), saturation, sharpness, brightness, contrast, white balance temperature, color effects, horizontal/vertical image flip, frame interval, auto white balance. RGB component gains are currently exposed through private controls. Reviewed-by: Sakari Ailus Signed-off-by: Sylwester Nawrocki Signed-off-by: Kyungmin Park Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/Kconfig | 7 + drivers/media/video/Makefile | 1 + drivers/media/video/s5k6aa.c | 1680 ++++++++++++++++++++++++++++++++++++++++++ include/media/s5k6aa.h | 51 ++ 4 files changed, 1739 insertions(+) create mode 100644 drivers/media/video/s5k6aa.c create mode 100644 include/media/s5k6aa.h (limited to 'include') diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index d471d1e5a744..b303a3f8a9f8 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -517,6 +517,13 @@ config VIDEO_NOON010PC30 source "drivers/media/video/m5mols/Kconfig" +config VIDEO_S5K6AA + tristate "Samsung S5K6AAFX sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + ---help--- + This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M + camera sensor with an embedded SoC image signal processor. + comment "Flash devices" config VIDEO_ADP1653 diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index faba1e333110..117f9c4b4cb9 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -72,6 +72,7 @@ obj-$(CONFIG_VIDEO_MT9V032) += mt9v032.o obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/ +obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o diff --git a/drivers/media/video/s5k6aa.c b/drivers/media/video/s5k6aa.c new file mode 100644 index 000000000000..2446736b7871 --- /dev/null +++ b/drivers/media/video/s5k6aa.c @@ -0,0 +1,1680 @@ +/* + * Driver for Samsung S5K6AAFX SXGA 1/6" 1.3M CMOS Image Sensor + * with embedded SoC ISP. + * + * Copyright (C) 2011, Samsung Electronics Co., Ltd. + * Sylwester Nawrocki + * + * Based on a driver authored by Dongsoo Nathaniel Kim. + * Copyright (C) 2009, Dongsoo Nathaniel Kim + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static int debug; +module_param(debug, int, 0644); + +#define DRIVER_NAME "S5K6AA" + +/* The token to indicate array termination */ +#define S5K6AA_TERM 0xffff +#define S5K6AA_OUT_WIDTH_DEF 640 +#define S5K6AA_OUT_HEIGHT_DEF 480 +#define S5K6AA_WIN_WIDTH_MAX 1280 +#define S5K6AA_WIN_HEIGHT_MAX 1024 +#define S5K6AA_WIN_WIDTH_MIN 8 +#define S5K6AA_WIN_HEIGHT_MIN 8 + +/* + * H/W register Interface (0xD0000000 - 0xD0000FFF) + */ +#define AHB_MSB_ADDR_PTR 0xfcfc +#define GEN_REG_OFFSH 0xd000 +#define REG_CMDWR_ADDRH 0x0028 +#define REG_CMDWR_ADDRL 0x002a +#define REG_CMDRD_ADDRH 0x002c +#define REG_CMDRD_ADDRL 0x002e +#define REG_CMDBUF0_ADDR 0x0f12 +#define REG_CMDBUF1_ADDR 0x0f10 + +/* + * Host S/W Register interface (0x70000000 - 0x70002000) + * The value of the two most significant address bytes is 0x7000, + * (HOST_SWIF_OFFS_H). The register addresses below specify 2 LSBs. + */ +#define HOST_SWIF_OFFSH 0x7000 + +/* Initialization parameters */ +/* Master clock frequency in KHz */ +#define REG_I_INCLK_FREQ_L 0x01b8 +#define REG_I_INCLK_FREQ_H 0x01ba +#define MIN_MCLK_FREQ_KHZ 6000U +#define MAX_MCLK_FREQ_KHZ 27000U +#define REG_I_USE_NPVI_CLOCKS 0x01c6 +#define REG_I_USE_NMIPI_CLOCKS 0x01c8 + +/* Clock configurations, n = 0..2. REG_I_* frequency unit is 4 kHz. */ +#define REG_I_OPCLK_4KHZ(n) ((n) * 6 + 0x01cc) +#define REG_I_MIN_OUTRATE_4KHZ(n) ((n) * 6 + 0x01ce) +#define REG_I_MAX_OUTRATE_4KHZ(n) ((n) * 6 + 0x01d0) +#define SYS_PLL_OUT_FREQ (48000000 / 4000) +#define PCLK_FREQ_MIN (24000000 / 4000) +#define PCLK_FREQ_MAX (48000000 / 4000) +#define REG_I_INIT_PARAMS_UPDATED 0x01e0 +#define REG_I_ERROR_INFO 0x01e2 + +/* General purpose parameters */ +#define REG_USER_BRIGHTNESS 0x01e4 +#define REG_USER_CONTRAST 0x01e6 +#define REG_USER_SATURATION 0x01e8 +#define REG_USER_SHARPBLUR 0x01ea + +#define REG_G_SPEC_EFFECTS 0x01ee +#define REG_G_ENABLE_PREV 0x01f0 +#define REG_G_ENABLE_PREV_CHG 0x01f2 +#define REG_G_NEW_CFG_SYNC 0x01f8 +#define REG_G_PREVZOOM_IN_WIDTH 0x020a +#define REG_G_PREVZOOM_IN_HEIGHT 0x020c +#define REG_G_PREVZOOM_IN_XOFFS 0x020e +#define REG_G_PREVZOOM_IN_YOFFS 0x0210 +#define REG_G_INPUTS_CHANGE_REQ 0x021a +#define REG_G_ACTIVE_PREV_CFG 0x021c +#define REG_G_PREV_CFG_CHG 0x021e +#define REG_G_PREV_OPEN_AFTER_CH 0x0220 +#define REG_G_PREV_CFG_ERROR 0x0222 + +/* Preview control section. n = 0...4. */ +#define PREG(n, x) ((n) * 0x26 + x) +#define REG_P_OUT_WIDTH(n) PREG(n, 0x0242) +#define REG_P_OUT_HEIGHT(n) PREG(n, 0x0244) +#define REG_P_FMT(n) PREG(n, 0x0246) +#define REG_P_MAX_OUT_RATE(n) PREG(n, 0x0248) +#define REG_P_MIN_OUT_RATE(n) PREG(n, 0x024a) +#define REG_P_PVI_MASK(n) PREG(n, 0x024c) +#define REG_P_CLK_INDEX(n) PREG(n, 0x024e) +#define REG_P_FR_RATE_TYPE(n) PREG(n, 0x0250) +#define FR_RATE_DYNAMIC 0 +#define FR_RATE_FIXED 1 +#define FR_RATE_FIXED_ACCURATE 2 +#define REG_P_FR_RATE_Q_TYPE(n) PREG(n, 0x0252) +#define FR_RATE_Q_BEST_FRRATE 1 /* Binning enabled */ +#define FR_RATE_Q_BEST_QUALITY 2 /* Binning disabled */ +/* Frame period in 0.1 ms units */ +#define REG_P_MAX_FR_TIME(n) PREG(n, 0x0254) +#define REG_P_MIN_FR_TIME(n) PREG(n, 0x0256) +/* Conversion to REG_P_[MAX/MIN]_FR_TIME value; __t: time in us */ +#define US_TO_FR_TIME(__t) ((__t) / 100) +#define S5K6AA_MIN_FR_TIME 33300 /* us */ +#define S5K6AA_MAX_FR_TIME 650000 /* us */ +#define S5K6AA_MAX_HIGHRES_FR_TIME 666 /* x100 us */ +/* The below 5 registers are for "device correction" values */ +#define REG_P_COLORTEMP(n) PREG(n, 0x025e) +#define REG_P_PREV_MIRROR(n) PREG(n, 0x0262) + +/* Extended image property controls */ +/* Exposure time in 10 us units */ +#define REG_SF_USR_EXPOSURE_L 0x03c6 +#define REG_SF_USR_EXPOSURE_H 0x03c8 +#define REG_SF_USR_EXPOSURE_CHG 0x03ca +#define REG_SF_USR_TOT_GAIN 0x03cc +#define REG_SF_USR_TOT_GAIN_CHG 0x03ce +#define REG_SF_RGAIN 0x03d0 +#define REG_SF_RGAIN_CHG 0x03d2 +#define REG_SF_GGAIN 0x03d4 +#define REG_SF_GGAIN_CHG 0x03d6 +#define REG_SF_BGAIN 0x03d8 +#define REG_SF_BGAIN_CHG 0x03da +#define REG_SF_FLICKER_QUANT 0x03dc +#define REG_SF_FLICKER_QUANT_CHG 0x03de + +/* Output interface (parallel/MIPI) setup */ +#define REG_OIF_EN_MIPI_LANES 0x03fa +#define REG_OIF_EN_PACKETS 0x03fc +#define REG_OIF_CFG_CHG 0x03fe + +/* Auto-algorithms enable mask */ +#define REG_DBG_AUTOALG_EN 0x0400 +#define AALG_ALL_EN_MASK (1 << 0) +#define AALG_AE_EN_MASK (1 << 1) +#define AALG_DIVLEI_EN_MASK (1 << 2) +#define AALG_WB_EN_MASK (1 << 3) +#define AALG_FLICKER_EN_MASK (1 << 5) +#define AALG_FIT_EN_MASK (1 << 6) +#define AALG_WRHW_EN_MASK (1 << 7) + +/* Firmware revision information */ +#define REG_FW_APIVER 0x012e +#define S5K6AAFX_FW_APIVER 0x0001 +#define REG_FW_REVISION 0x0130 + +/* For now we use only one user configuration register set */ +#define S5K6AA_MAX_PRESETS 1 + +static const char * const s5k6aa_supply_names[] = { + "vdd_core", /* Digital core supply 1.5V (1.4V to 1.6V) */ + "vdda", /* Analog power supply 2.8V (2.6V to 3.0V) */ + "vdd_reg", /* Regulator input power 1.8V (1.7V to 1.9V) + or 2.8V (2.6V to 3.0) */ + "vddio", /* I/O supply 1.8V (1.65V to 1.95V) + or 2.8V (2.5V to 3.1V) */ +}; +#define S5K6AA_NUM_SUPPLIES ARRAY_SIZE(s5k6aa_supply_names) + +enum s5k6aa_gpio_id { + STBY, + RST, + GPIO_NUM, +}; + +struct s5k6aa_regval { + u16 addr; + u16 val; +}; + +struct s5k6aa_pixfmt { + enum v4l2_mbus_pixelcode code; + u32 colorspace; + /* REG_P_FMT(x) register value */ + u16 reg_p_fmt; +}; + +struct s5k6aa_preset { + /* output pixel format and resolution */ + struct v4l2_mbus_framefmt mbus_fmt; + u8 clk_id; + u8 index; +}; + +struct s5k6aa_ctrls { + struct v4l2_ctrl_handler handler; + /* Auto / manual white balance cluster */ + struct v4l2_ctrl *awb; + struct v4l2_ctrl *gain_red; + struct v4l2_ctrl *gain_blue; + struct v4l2_ctrl *gain_green; + /* Mirror cluster */ + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + /* Auto exposure / manual exposure and gain cluster */ + struct v4l2_ctrl *auto_exp; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *gain; +}; + +struct s5k6aa_interval { + u16 reg_fr_time; + struct v4l2_fract interval; + /* Maximum rectangle for the interval */ + struct v4l2_frmsize_discrete size; +}; + +struct s5k6aa { + struct v4l2_subdev sd; + struct media_pad pad; + + enum v4l2_mbus_type bus_type; + u8 mipi_lanes; + + int (*s_power)(int enable); + struct regulator_bulk_data supplies[S5K6AA_NUM_SUPPLIES]; + struct s5k6aa_gpio gpio[GPIO_NUM]; + + /* external master clock frequency */ + unsigned long mclk_frequency; + /* ISP internal master clock frequency */ + u16 clk_fop; + /* output pixel clock frequency range */ + u16 pclk_fmin; + u16 pclk_fmax; + + unsigned int inv_hflip:1; + unsigned int inv_vflip:1; + + /* protects the struct members below */ + struct mutex lock; + + /* sensor matrix scan window */ + struct v4l2_rect ccd_rect; + + struct s5k6aa_ctrls ctrls; + struct s5k6aa_preset presets[S5K6AA_MAX_PRESETS]; + struct s5k6aa_preset *preset; + const struct s5k6aa_interval *fiv; + + unsigned int streaming:1; + unsigned int apply_cfg:1; + unsigned int apply_crop:1; + unsigned int power; +}; + +static struct s5k6aa_regval s5k6aa_analog_config[] = { + /* Analog settings */ + { 0x112a, 0x0000 }, { 0x1132, 0x0000 }, + { 0x113e, 0x0000 }, { 0x115c, 0x0000 }, + { 0x1164, 0x0000 }, { 0x1174, 0x0000 }, + { 0x1178, 0x0000 }, { 0x077a, 0x0000 }, + { 0x077c, 0x0000 }, { 0x077e, 0x0000 }, + { 0x0780, 0x0000 }, { 0x0782, 0x0000 }, + { 0x0784, 0x0000 }, { 0x0786, 0x0000 }, + { 0x0788, 0x0000 }, { 0x07a2, 0x0000 }, + { 0x07a4, 0x0000 }, { 0x07a6, 0x0000 }, + { 0x07a8, 0x0000 }, { 0x07b6, 0x0000 }, + { 0x07b8, 0x0002 }, { 0x07ba, 0x0004 }, + { 0x07bc, 0x0004 }, { 0x07be, 0x0005 }, + { 0x07c0, 0x0005 }, { S5K6AA_TERM, 0 }, +}; + +/* TODO: Add RGB888 and Bayer format */ +static const struct s5k6aa_pixfmt s5k6aa_formats[] = { + { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_JPEG, 5 }, + /* range 16-240 */ + { V4L2_MBUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_REC709, 6 }, + { V4L2_MBUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_JPEG, 0 }, +}; + +static const struct s5k6aa_interval s5k6aa_intervals[] = { + { 1000, {10000, 1000000}, {1280, 1024} }, /* 10 fps */ + { 666, {15000, 1000000}, {1280, 1024} }, /* 15 fps */ + { 500, {20000, 1000000}, {1280, 720} }, /* 20 fps */ + { 400, {25000, 1000000}, {640, 480} }, /* 25 fps */ + { 333, {33300, 1000000}, {640, 480} }, /* 30 fps */ +}; + +#define S5K6AA_INTERVAL_DEF_INDEX 1 /* 15 fps */ + +static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) +{ + return &container_of(ctrl->handler, struct s5k6aa, ctrls.handler)->sd; +} + +static inline struct s5k6aa *to_s5k6aa(struct v4l2_subdev *sd) +{ + return container_of(sd, struct s5k6aa, sd); +} + +/* Set initial values for all preview presets */ +static void s5k6aa_presets_data_init(struct s5k6aa *s5k6aa) +{ + struct s5k6aa_preset *preset = &s5k6aa->presets[0]; + int i; + + for (i = 0; i < S5K6AA_MAX_PRESETS; i++) { + preset->mbus_fmt.width = S5K6AA_OUT_WIDTH_DEF; + preset->mbus_fmt.height = S5K6AA_OUT_HEIGHT_DEF; + preset->mbus_fmt.code = s5k6aa_formats[0].code; + preset->index = i; + preset->clk_id = 0; + preset++; + } + + s5k6aa->fiv = &s5k6aa_intervals[S5K6AA_INTERVAL_DEF_INDEX]; + s5k6aa->preset = &s5k6aa->presets[0]; +} + +static int s5k6aa_i2c_read(struct i2c_client *client, u16 addr, u16 *val) +{ + u8 wbuf[2] = {addr >> 8, addr & 0xFF}; + struct i2c_msg msg[2]; + u8 rbuf[2]; + int ret; + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].len = 2; + msg[0].buf = wbuf; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = 2; + msg[1].buf = rbuf; + + ret = i2c_transfer(client->adapter, msg, 2); + *val = be16_to_cpu(*((u16 *)rbuf)); + + v4l2_dbg(3, debug, client, "i2c_read: 0x%04X : 0x%04x\n", addr, *val); + + return ret == 2 ? 0 : ret; +} + +static int s5k6aa_i2c_write(struct i2c_client *client, u16 addr, u16 val) +{ + u8 buf[4] = {addr >> 8, addr & 0xFF, val >> 8, val & 0xFF}; + + int ret = i2c_master_send(client, buf, 4); + v4l2_dbg(3, debug, client, "i2c_write: 0x%04X : 0x%04x\n", addr, val); + + return ret == 4 ? 0 : ret; +} + +/* The command register write, assumes Command_Wr_addH = 0x7000. */ +static int s5k6aa_write(struct i2c_client *c, u16 addr, u16 val) +{ + int ret = s5k6aa_i2c_write(c, REG_CMDWR_ADDRL, addr); + if (ret) + return ret; + return s5k6aa_i2c_write(c, REG_CMDBUF0_ADDR, val); +} + +/* The command register read, assumes Command_Rd_addH = 0x7000. */ +static int s5k6aa_read(struct i2c_client *client, u16 addr, u16 *val) +{ + int ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRL, addr); + if (ret) + return ret; + return s5k6aa_i2c_read(client, REG_CMDBUF0_ADDR, val); +} + +static int s5k6aa_write_array(struct v4l2_subdev *sd, + const struct s5k6aa_regval *msg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u16 addr_incr = 0; + int ret = 0; + + while (msg->addr != S5K6AA_TERM) { + if (addr_incr != 2) + ret = s5k6aa_i2c_write(client, REG_CMDWR_ADDRL, + msg->addr); + if (ret) + break; + ret = s5k6aa_i2c_write(client, REG_CMDBUF0_ADDR, msg->val); + if (ret) + break; + /* Assume that msg->addr is always less than 0xfffc */ + addr_incr = (msg + 1)->addr - msg->addr; + msg++; + } + + return ret; +} + +/* Configure the AHB high address bytes for GTG registers access */ +static int s5k6aa_set_ahb_address(struct i2c_client *client) +{ + int ret = s5k6aa_i2c_write(client, AHB_MSB_ADDR_PTR, GEN_REG_OFFSH); + if (ret) + return ret; + ret = s5k6aa_i2c_write(client, REG_CMDRD_ADDRH, HOST_SWIF_OFFSH); + if (ret) + return ret; + return s5k6aa_i2c_write(client, REG_CMDWR_ADDRH, HOST_SWIF_OFFSH); +} + +/** + * s5k6aa_configure_pixel_clock - apply ISP main clock/PLL configuration + * + * Configure the internal ISP PLL for the required output frequency. + * Locking: called with s5k6aa.lock mutex held. + */ +static int s5k6aa_configure_pixel_clocks(struct s5k6aa *s5k6aa) +{ + struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); + unsigned long fmclk = s5k6aa->mclk_frequency / 1000; + u16 status; + int ret; + + if (WARN(fmclk < MIN_MCLK_FREQ_KHZ || fmclk > MAX_MCLK_FREQ_KHZ, + "Invalid clock frequency: %ld\n", fmclk)) + return -EINVAL; + + s5k6aa->pclk_fmin = PCLK_FREQ_MIN; + s5k6aa->pclk_fmax = PCLK_FREQ_MAX; + s5k6aa->clk_fop = SYS_PLL_OUT_FREQ; + + /* External input clock frequency in kHz */ + ret = s5k6aa_write(c, REG_I_INCLK_FREQ_H, fmclk >> 16); + if (!ret) + ret = s5k6aa_write(c, REG_I_INCLK_FREQ_L, fmclk & 0xFFFF); + if (!ret) + ret = s5k6aa_write(c, REG_I_USE_NPVI_CLOCKS, 1); + /* Internal PLL frequency */ + if (!ret) + ret = s5k6aa_write(c, REG_I_OPCLK_4KHZ(0), s5k6aa->clk_fop); + if (!ret) + ret = s5k6aa_write(c, REG_I_MIN_OUTRATE_4KHZ(0), + s5k6aa->pclk_fmin); + if (!ret) + ret = s5k6aa_write(c, REG_I_MAX_OUTRATE_4KHZ(0), + s5k6aa->pclk_fmax); + if (!ret) + ret = s5k6aa_write(c, REG_I_INIT_PARAMS_UPDATED, 1); + if (!ret) + ret = s5k6aa_read(c, REG_I_ERROR_INFO, &status); + + return ret ? ret : (status ? -EINVAL : 0); +} + +/* Set horizontal and vertical image flipping */ +static int s5k6aa_set_mirror(struct s5k6aa *s5k6aa, int horiz_flip) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + int index = s5k6aa->preset->index; + + unsigned int vflip = s5k6aa->ctrls.vflip->val ^ s5k6aa->inv_vflip; + unsigned int flip = (horiz_flip ^ s5k6aa->inv_hflip) | (vflip << 1); + + return s5k6aa_write(client, REG_P_PREV_MIRROR(index), flip); +} + +/* Configure auto/manual white balance and R/G/B gains */ +static int s5k6aa_set_awb(struct s5k6aa *s5k6aa, int awb) +{ + struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); + struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls; + u16 reg; + + int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, ®); + + if (!ret && !awb) { + ret = s5k6aa_write(c, REG_SF_RGAIN, ctrls->gain_red->val); + if (!ret) + ret = s5k6aa_write(c, REG_SF_RGAIN_CHG, 1); + if (ret) + return ret; + + ret = s5k6aa_write(c, REG_SF_GGAIN, ctrls->gain_green->val); + if (!ret) + ret = s5k6aa_write(c, REG_SF_GGAIN_CHG, 1); + if (ret) + return ret; + + ret = s5k6aa_write(c, REG_SF_BGAIN, ctrls->gain_blue->val); + if (!ret) + ret = s5k6aa_write(c, REG_SF_BGAIN_CHG, 1); + } + if (!ret) { + reg = awb ? reg | AALG_WB_EN_MASK : reg & ~AALG_WB_EN_MASK; + ret = s5k6aa_write(c, REG_DBG_AUTOALG_EN, reg); + } + + return ret; +} + +/* Program FW with exposure time, 'exposure' in us units */ +static int s5k6aa_set_user_exposure(struct i2c_client *client, int exposure) +{ + unsigned int time = exposure / 10; + + int ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_L, time & 0xffff); + if (!ret) + ret = s5k6aa_write(client, REG_SF_USR_EXPOSURE_H, time >> 16); + if (ret) + return ret; + return s5k6aa_write(client, REG_SF_USR_EXPOSURE_CHG, 1); +} + +static int s5k6aa_set_user_gain(struct i2c_client *client, int gain) +{ + int ret = s5k6aa_write(client, REG_SF_USR_TOT_GAIN, gain); + if (ret) + return ret; + return s5k6aa_write(client, REG_SF_USR_TOT_GAIN_CHG, 1); +} + +/* Set auto/manual exposure and total gain */ +static int s5k6aa_set_auto_exposure(struct s5k6aa *s5k6aa, int value) +{ + struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); + unsigned int exp_time = s5k6aa->ctrls.exposure->val; + u16 auto_alg; + + int ret = s5k6aa_read(c, REG_DBG_AUTOALG_EN, &auto_alg); + if (ret) + return ret; + + v4l2_dbg(1, debug, c, "man_exp: %d, auto_exp: %d, a_alg: 0x%x\n", + exp_time, value, auto_alg); + + if (value == V4L2_EXPOSURE_AUTO) { + auto_alg |= AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK; + } else { + ret = s5k6aa_set_user_exposure(c, exp_time); + if (ret) + return ret; + ret = s5k6aa_set_user_gain(c, s5k6aa->ctrls.gain->val); + if (ret) + return ret; + auto_alg &= ~(AALG_AE_EN_MASK | AALG_DIVLEI_EN_MASK); + } + + return s5k6aa_write(c, REG_DBG_AUTOALG_EN, auto_alg); +} + +static int s5k6aa_set_anti_flicker(struct s5k6aa *s5k6aa, int value) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + u16 auto_alg; + int ret; + + ret = s5k6aa_read(client, REG_DBG_AUTOALG_EN, &auto_alg); + if (ret) + return ret; + + if (value == V4L2_CID_POWER_LINE_FREQUENCY_AUTO) { + auto_alg |= AALG_FLICKER_EN_MASK; + } else { + auto_alg &= ~AALG_FLICKER_EN_MASK; + /* The V4L2_CID_LINE_FREQUENCY control values match + * the register values */ + ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT, value); + if (ret) + return ret; + ret = s5k6aa_write(client, REG_SF_FLICKER_QUANT_CHG, 1); + if (ret) + return ret; + } + + return s5k6aa_write(client, REG_DBG_AUTOALG_EN, auto_alg); +} + +static int s5k6aa_set_colorfx(struct s5k6aa *s5k6aa, int val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + static const struct v4l2_control colorfx[] = { + { V4L2_COLORFX_NONE, 0 }, + { V4L2_COLORFX_BW, 1 }, + { V4L2_COLORFX_NEGATIVE, 2 }, + { V4L2_COLORFX_SEPIA, 3 }, + { V4L2_COLORFX_SKY_BLUE, 4 }, + { V4L2_COLORFX_SKETCH, 5 }, + }; + int i; + + for (i = 0; i < ARRAY_SIZE(colorfx); i++) { + if (colorfx[i].id == val) + return s5k6aa_write(client, REG_G_SPEC_EFFECTS, + colorfx[i].value); + } + return -EINVAL; +} + +static int s5k6aa_preview_config_status(struct i2c_client *client) +{ + u16 error = 0; + int ret = s5k6aa_read(client, REG_G_PREV_CFG_ERROR, &error); + + v4l2_dbg(1, debug, client, "error: 0x%x (%d)\n", error, ret); + return ret ? ret : (error ? -EINVAL : 0); +} + +static int s5k6aa_get_pixfmt_index(struct s5k6aa *s5k6aa, + struct v4l2_mbus_framefmt *mf) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(s5k6aa_formats); i++) + if (mf->colorspace == s5k6aa_formats[i].colorspace && + mf->code == s5k6aa_formats[i].code) + return i; + return 0; +} + +static int s5k6aa_set_output_framefmt(struct s5k6aa *s5k6aa, + struct s5k6aa_preset *preset) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + int fmt_index = s5k6aa_get_pixfmt_index(s5k6aa, &preset->mbus_fmt); + int ret; + + ret = s5k6aa_write(client, REG_P_OUT_WIDTH(preset->index), + preset->mbus_fmt.width); + if (!ret) + ret = s5k6aa_write(client, REG_P_OUT_HEIGHT(preset->index), + preset->mbus_fmt.height); + if (!ret) + ret = s5k6aa_write(client, REG_P_FMT(preset->index), + s5k6aa_formats[fmt_index].reg_p_fmt); + return ret; +} + +static int s5k6aa_set_input_params(struct s5k6aa *s5k6aa) +{ + struct i2c_client *c = v4l2_get_subdevdata(&s5k6aa->sd); + struct v4l2_rect *r = &s5k6aa->ccd_rect; + int ret; + + ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_WIDTH, r->width); + if (!ret) + ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_HEIGHT, r->height); + if (!ret) + ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_XOFFS, r->left); + if (!ret) + ret = s5k6aa_write(c, REG_G_PREVZOOM_IN_YOFFS, r->top); + if (!ret) + ret = s5k6aa_write(c, REG_G_INPUTS_CHANGE_REQ, 1); + if (!ret) + s5k6aa->apply_crop = 0; + + return ret; +} + +/** + * s5k6aa_configure_video_bus - configure the video output interface + * @bus_type: video bus type: parallel or MIPI-CSI + * @nlanes: number of MIPI lanes to be used (MIPI-CSI only) + * + * Note: Only parallel bus operation has been tested. + */ +static int s5k6aa_configure_video_bus(struct s5k6aa *s5k6aa, + enum v4l2_mbus_type bus_type, int nlanes) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + u16 cfg = 0; + int ret; + + /* + * TODO: The sensor is supposed to support BT.601 and BT.656 + * but there is nothing indicating how to switch between both + * in the datasheet. For now default BT.601 interface is assumed. + */ + if (bus_type == V4L2_MBUS_CSI2) + cfg = nlanes; + else if (bus_type != V4L2_MBUS_PARALLEL) + return -EINVAL; + + ret = s5k6aa_write(client, REG_OIF_EN_MIPI_LANES, cfg); + if (ret) + return ret; + return s5k6aa_write(client, REG_OIF_CFG_CHG, 1); +} + +/* This function should be called when switching to new user configuration set*/ +static int s5k6aa_new_config_sync(struct i2c_client *client, int timeout, + int cid) +{ + unsigned long end = jiffies + msecs_to_jiffies(timeout); + u16 reg = 1; + int ret; + + ret = s5k6aa_write(client, REG_G_ACTIVE_PREV_CFG, cid); + if (!ret) + ret = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1); + if (!ret) + ret = s5k6aa_write(client, REG_G_NEW_CFG_SYNC, 1); + if (timeout == 0) + return ret; + + while (ret >= 0 && time_is_after_jiffies(end)) { + ret = s5k6aa_read(client, REG_G_NEW_CFG_SYNC, ®); + if (!reg) + return 0; + usleep_range(1000, 5000); + } + return ret ? ret : -ETIMEDOUT; +} + +/** + * s5k6aa_set_prev_config - write user preview register set + * + * Configure output resolution and color fromat, pixel clock + * frequency range, device frame rate type and frame period range. + */ +static int s5k6aa_set_prev_config(struct s5k6aa *s5k6aa, + struct s5k6aa_preset *preset) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + int idx = preset->index; + u16 frame_rate_q; + int ret; + + if (s5k6aa->fiv->reg_fr_time >= S5K6AA_MAX_HIGHRES_FR_TIME) + frame_rate_q = FR_RATE_Q_BEST_FRRATE; + else + frame_rate_q = FR_RATE_Q_BEST_QUALITY; + + ret = s5k6aa_set_output_framefmt(s5k6aa, preset); + if (!ret) + ret = s5k6aa_write(client, REG_P_MAX_OUT_RATE(idx), + s5k6aa->pclk_fmax); + if (!ret) + ret = s5k6aa_write(client, REG_P_MIN_OUT_RATE(idx), + s5k6aa->pclk_fmin); + if (!ret) + ret = s5k6aa_write(client, REG_P_CLK_INDEX(idx), + preset->clk_id); + if (!ret) + ret = s5k6aa_write(client, REG_P_FR_RATE_TYPE(idx), + FR_RATE_DYNAMIC); + if (!ret) + ret = s5k6aa_write(client, REG_P_FR_RATE_Q_TYPE(idx), + frame_rate_q); + if (!ret) + ret = s5k6aa_write(client, REG_P_MAX_FR_TIME(idx), + s5k6aa->fiv->reg_fr_time + 33); + if (!ret) + ret = s5k6aa_write(client, REG_P_MIN_FR_TIME(idx), + s5k6aa->fiv->reg_fr_time - 33); + if (!ret) + ret = s5k6aa_new_config_sync(client, 250, idx); + if (!ret) + ret = s5k6aa_preview_config_status(client); + if (!ret) + s5k6aa->apply_cfg = 0; + + v4l2_dbg(1, debug, client, "Frame interval: %d +/- 3.3ms. (%d)\n", + s5k6aa->fiv->reg_fr_time, ret); + return ret; +} + +/** + * s5k6aa_initialize_isp - basic ISP MCU initialization + * + * Configure AHB addresses for registers read/write; configure PLLs for + * required output pixel clock. The ISP power supply needs to be already + * enabled, with an optional H/W reset. + * Locking: called with s5k6aa.lock mutex held. + */ +static int s5k6aa_initialize_isp(struct v4l2_subdev *sd) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + int ret; + + s5k6aa->apply_crop = 1; + s5k6aa->apply_cfg = 1; + msleep(100); + + ret = s5k6aa_set_ahb_address(client); + if (ret) + return ret; + ret = s5k6aa_configure_video_bus(s5k6aa, s5k6aa->bus_type, + s5k6aa->mipi_lanes); + if (ret) + return ret; + ret = s5k6aa_write_array(sd, s5k6aa_analog_config); + if (ret) + return ret; + msleep(20); + + return s5k6aa_configure_pixel_clocks(s5k6aa); +} + +static int s5k6aa_gpio_set_value(struct s5k6aa *priv, int id, u32 val) +{ + if (!gpio_is_valid(priv->gpio[id].gpio)) + return 0; + gpio_set_value(priv->gpio[id].gpio, !!val); + return 1; +} + +static int s5k6aa_gpio_assert(struct s5k6aa *priv, int id) +{ + return s5k6aa_gpio_set_value(priv, id, priv->gpio[id].level); +} + +static int s5k6aa_gpio_deassert(struct s5k6aa *priv, int id) +{ + return s5k6aa_gpio_set_value(priv, id, !priv->gpio[id].level); +} + +static int __s5k6aa_power_on(struct s5k6aa *s5k6aa) +{ + int ret; + + ret = regulator_bulk_enable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); + if (ret) + return ret; + if (s5k6aa_gpio_deassert(s5k6aa, STBY)) + usleep_range(150, 200); + + if (s5k6aa->s_power) + ret = s5k6aa->s_power(1); + usleep_range(4000, 4000); + + if (s5k6aa_gpio_deassert(s5k6aa, RST)) + msleep(20); + + return ret; +} + +static int __s5k6aa_power_off(struct s5k6aa *s5k6aa) +{ + int ret; + + if (s5k6aa_gpio_assert(s5k6aa, RST)) + usleep_range(100, 150); + + if (s5k6aa->s_power) { + ret = s5k6aa->s_power(0); + if (ret) + return ret; + } + if (s5k6aa_gpio_assert(s5k6aa, STBY)) + usleep_range(50, 100); + s5k6aa->streaming = 0; + + return regulator_bulk_disable(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); +} + +/* + * V4L2 subdev core and video operations + */ +static int s5k6aa_set_power(struct v4l2_subdev *sd, int on) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + int ret = 0; + + mutex_lock(&s5k6aa->lock); + + if (!on == s5k6aa->power) { + if (on) { + ret = __s5k6aa_power_on(s5k6aa); + if (!ret) + ret = s5k6aa_initialize_isp(sd); + } else { + ret = __s5k6aa_power_off(s5k6aa); + } + + if (!ret) + s5k6aa->power += on ? 1 : -1; + } + + mutex_unlock(&s5k6aa->lock); + + if (!on || ret || s5k6aa->power != 1) + return ret; + + return v4l2_ctrl_handler_setup(sd->ctrl_handler); +} + +static int __s5k6aa_stream(struct s5k6aa *s5k6aa, int enable) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + int ret = 0; + + ret = s5k6aa_write(client, REG_G_ENABLE_PREV, enable); + if (!ret) + ret = s5k6aa_write(client, REG_G_ENABLE_PREV_CHG, 1); + if (!ret) + s5k6aa->streaming = enable; + + return ret; +} + +static int s5k6aa_s_stream(struct v4l2_subdev *sd, int on) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + int ret = 0; + + mutex_lock(&s5k6aa->lock); + + if (s5k6aa->streaming == !on) { + if (!ret && s5k6aa->apply_cfg) + ret = s5k6aa_set_prev_config(s5k6aa, s5k6aa->preset); + if (s5k6aa->apply_crop) + ret = s5k6aa_set_input_params(s5k6aa); + if (!ret) + ret = __s5k6aa_stream(s5k6aa, !!on); + } + mutex_unlock(&s5k6aa->lock); + + return ret; +} + +static int s5k6aa_g_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + + mutex_lock(&s5k6aa->lock); + fi->interval = s5k6aa->fiv->interval; + mutex_unlock(&s5k6aa->lock); + + return 0; +} + +static int __s5k6aa_set_frame_interval(struct s5k6aa *s5k6aa, + struct v4l2_subdev_frame_interval *fi) +{ + struct v4l2_mbus_framefmt *mbus_fmt = &s5k6aa->preset->mbus_fmt; + const struct s5k6aa_interval *fiv = &s5k6aa_intervals[0]; + unsigned int err, min_err = UINT_MAX; + unsigned int i, fr_time; + + if (fi->interval.denominator == 0) + return -EINVAL; + + fr_time = fi->interval.numerator * 10000 / fi->interval.denominator; + + for (i = 0; i < ARRAY_SIZE(s5k6aa_intervals); i++) { + const struct s5k6aa_interval *iv = &s5k6aa_intervals[i]; + + if (mbus_fmt->width > iv->size.width || + mbus_fmt->height > iv->size.height) + continue; + + err = abs(iv->reg_fr_time - fr_time); + if (err < min_err) { + fiv = iv; + min_err = err; + } + } + s5k6aa->fiv = fiv; + + v4l2_dbg(1, debug, &s5k6aa->sd, "Changed frame interval to %d us\n", + fiv->reg_fr_time * 100); + return 0; +} + +static int s5k6aa_s_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_frame_interval *fi) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + int ret; + + v4l2_dbg(1, debug, sd, "Setting %d/%d frame interval\n", + fi->interval.numerator, fi->interval.denominator); + + mutex_lock(&s5k6aa->lock); + ret = __s5k6aa_set_frame_interval(s5k6aa, fi); + s5k6aa->apply_cfg = 1; + + mutex_unlock(&s5k6aa->lock); + return ret; +} + +/* + * V4L2 subdev pad level and video operations + */ +static int s5k6aa_enum_frame_interval(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_interval_enum *fie) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + const struct s5k6aa_interval *fi; + int ret = 0; + + if (fie->index > ARRAY_SIZE(s5k6aa_intervals)) + return -EINVAL; + + v4l_bound_align_image(&fie->width, S5K6AA_WIN_WIDTH_MIN, + S5K6AA_WIN_WIDTH_MAX, 1, + &fie->height, S5K6AA_WIN_HEIGHT_MIN, + S5K6AA_WIN_HEIGHT_MAX, 1, 0); + + mutex_lock(&s5k6aa->lock); + fi = &s5k6aa_intervals[fie->index]; + if (fie->width > fi->size.width || fie->height > fi->size.height) + ret = -EINVAL; + else + fie->interval = fi->interval; + mutex_unlock(&s5k6aa->lock); + + return ret; +} + +static int s5k6aa_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + if (code->index >= ARRAY_SIZE(s5k6aa_formats)) + return -EINVAL; + + code->code = s5k6aa_formats[code->index].code; + return 0; +} + +static int s5k6aa_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_frame_size_enum *fse) +{ + int i = ARRAY_SIZE(s5k6aa_formats); + + if (fse->index > 0) + return -EINVAL; + + while (--i) + if (fse->code == s5k6aa_formats[i].code) + break; + + fse->code = s5k6aa_formats[i].code; + fse->min_width = S5K6AA_WIN_WIDTH_MIN; + fse->max_width = S5K6AA_WIN_WIDTH_MAX; + fse->max_height = S5K6AA_WIN_HEIGHT_MIN; + fse->min_height = S5K6AA_WIN_HEIGHT_MAX; + + return 0; +} + +static struct v4l2_rect * +__s5k6aa_get_crop_rect(struct s5k6aa *s5k6aa, struct v4l2_subdev_fh *fh, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_ACTIVE) + return &s5k6aa->ccd_rect; + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_crop(fh, 0); + + return NULL; +} + +static void s5k6aa_try_format(struct s5k6aa *s5k6aa, + struct v4l2_mbus_framefmt *mf) +{ + unsigned int index; + + v4l_bound_align_image(&mf->width, S5K6AA_WIN_WIDTH_MIN, + S5K6AA_WIN_WIDTH_MAX, 1, + &mf->height, S5K6AA_WIN_HEIGHT_MIN, + S5K6AA_WIN_HEIGHT_MAX, 1, 0); + + if (mf->colorspace != V4L2_COLORSPACE_JPEG && + mf->colorspace != V4L2_COLORSPACE_REC709) + mf->colorspace = V4L2_COLORSPACE_JPEG; + + index = s5k6aa_get_pixfmt_index(s5k6aa, mf); + + mf->colorspace = s5k6aa_formats[index].colorspace; + mf->code = s5k6aa_formats[index].code; + mf->field = V4L2_FIELD_NONE; +} + +static int s5k6aa_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + struct v4l2_mbus_framefmt *mf; + + memset(fmt->reserved, 0, sizeof(fmt->reserved)); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(fh, 0); + fmt->format = *mf; + return 0; + } + + mutex_lock(&s5k6aa->lock); + fmt->format = s5k6aa->preset->mbus_fmt; + mutex_unlock(&s5k6aa->lock); + + return 0; +} + +static int s5k6aa_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + struct s5k6aa_preset *preset = s5k6aa->preset; + struct v4l2_mbus_framefmt *mf; + struct v4l2_rect *crop; + int ret = 0; + + mutex_lock(&s5k6aa->lock); + s5k6aa_try_format(s5k6aa, &fmt->format); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(fh, fmt->pad); + crop = v4l2_subdev_get_try_crop(fh, 0); + } else { + if (s5k6aa->streaming) { + ret = -EBUSY; + } else { + mf = &preset->mbus_fmt; + crop = &s5k6aa->ccd_rect; + s5k6aa->apply_cfg = 1; + } + } + + if (ret == 0) { + struct v4l2_subdev_frame_interval fiv = { + .interval = {0, 1} + }; + + *mf = fmt->format; + /* + * Make sure the crop window is valid, i.e. its size is + * greater than the output window, as the ISP supports + * only down-scaling. + */ + crop->width = clamp_t(unsigned int, crop->width, mf->width, + S5K6AA_WIN_WIDTH_MAX); + crop->height = clamp_t(unsigned int, crop->height, mf->height, + S5K6AA_WIN_HEIGHT_MAX); + crop->left = clamp_t(unsigned int, crop->left, 0, + S5K6AA_WIN_WIDTH_MAX - crop->width); + crop->top = clamp_t(unsigned int, crop->top, 0, + S5K6AA_WIN_HEIGHT_MAX - crop->height); + + /* Reset to minimum possible frame interval */ + ret = __s5k6aa_set_frame_interval(s5k6aa, &fiv); + } + mutex_unlock(&s5k6aa->lock); + + return ret; +} + +static int s5k6aa_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + struct v4l2_rect *rect; + + memset(crop->reserved, 0, sizeof(crop->reserved)); + mutex_lock(&s5k6aa->lock); + + rect = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which); + if (rect) + crop->rect = *rect; + + mutex_unlock(&s5k6aa->lock); + + v4l2_dbg(1, debug, sd, "Current crop rectangle: (%d,%d)/%dx%d\n", + rect->left, rect->top, rect->width, rect->height); + + return 0; +} + +static int s5k6aa_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_crop *crop) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + struct v4l2_mbus_framefmt *mf; + unsigned int max_x, max_y; + struct v4l2_rect *crop_r; + + mutex_lock(&s5k6aa->lock); + crop_r = __s5k6aa_get_crop_rect(s5k6aa, fh, crop->which); + + if (crop->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + mf = &s5k6aa->preset->mbus_fmt; + s5k6aa->apply_crop = 1; + } else { + mf = v4l2_subdev_get_try_format(fh, 0); + } + v4l_bound_align_image(&crop->rect.width, mf->width, + S5K6AA_WIN_WIDTH_MAX, 1, + &crop->rect.height, mf->height, + S5K6AA_WIN_HEIGHT_MAX, 1, 0); + + max_x = (S5K6AA_WIN_WIDTH_MAX - crop->rect.width) & ~1; + max_y = (S5K6AA_WIN_HEIGHT_MAX - crop->rect.height) & ~1; + + crop->rect.left = clamp_t(unsigned int, crop->rect.left, 0, max_x); + crop->rect.top = clamp_t(unsigned int, crop->rect.top, 0, max_y); + + *crop_r = crop->rect; + + mutex_unlock(&s5k6aa->lock); + + v4l2_dbg(1, debug, sd, "Set crop rectangle: (%d,%d)/%dx%d\n", + crop_r->left, crop_r->top, crop_r->width, crop_r->height); + + return 0; +} + +static const struct v4l2_subdev_pad_ops s5k6aa_pad_ops = { + .enum_mbus_code = s5k6aa_enum_mbus_code, + .enum_frame_size = s5k6aa_enum_frame_size, + .enum_frame_interval = s5k6aa_enum_frame_interval, + .get_fmt = s5k6aa_get_fmt, + .set_fmt = s5k6aa_set_fmt, + .get_crop = s5k6aa_get_crop, + .set_crop = s5k6aa_set_crop, +}; + +static const struct v4l2_subdev_video_ops s5k6aa_video_ops = { + .g_frame_interval = s5k6aa_g_frame_interval, + .s_frame_interval = s5k6aa_s_frame_interval, + .s_stream = s5k6aa_s_stream, +}; + +/* + * V4L2 subdev controls + */ + +static int s5k6aa_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct v4l2_subdev *sd = ctrl_to_sd(ctrl); + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + int idx, err = 0; + + v4l2_dbg(1, debug, sd, "ctrl: 0x%x, value: %d\n", ctrl->id, ctrl->val); + + mutex_lock(&s5k6aa->lock); + /* + * If the device is not powered up by the host driver do + * not apply any controls to H/W at this time. Instead + * the controls will be restored right after power-up. + */ + if (s5k6aa->power == 0) + goto unlock; + idx = s5k6aa->preset->index; + + switch (ctrl->id) { + case V4L2_CID_AUTO_WHITE_BALANCE: + err = s5k6aa_set_awb(s5k6aa, ctrl->val); + break; + + case V4L2_CID_BRIGHTNESS: + err = s5k6aa_write(client, REG_USER_BRIGHTNESS, ctrl->val); + break; + + case V4L2_CID_COLORFX: + err = s5k6aa_set_colorfx(s5k6aa, ctrl->val); + break; + + case V4L2_CID_CONTRAST: + err = s5k6aa_write(client, REG_USER_CONTRAST, ctrl->val); + break; + + case V4L2_CID_EXPOSURE_AUTO: + err = s5k6aa_set_auto_exposure(s5k6aa, ctrl->val); + break; + + case V4L2_CID_HFLIP: + err = s5k6aa_set_mirror(s5k6aa, ctrl->val); + if (err) + break; + err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1); + break; + + case V4L2_CID_POWER_LINE_FREQUENCY: + err = s5k6aa_set_anti_flicker(s5k6aa, ctrl->val); + break; + + case V4L2_CID_SATURATION: + err = s5k6aa_write(client, REG_USER_SATURATION, ctrl->val); + break; + + case V4L2_CID_SHARPNESS: + err = s5k6aa_write(client, REG_USER_SHARPBLUR, ctrl->val); + break; + + case V4L2_CID_WHITE_BALANCE_TEMPERATURE: + err = s5k6aa_write(client, REG_P_COLORTEMP(idx), ctrl->val); + if (err) + break; + err = s5k6aa_write(client, REG_G_PREV_CFG_CHG, 1); + break; + } +unlock: + mutex_unlock(&s5k6aa->lock); + return err; +} + +static const struct v4l2_ctrl_ops s5k6aa_ctrl_ops = { + .s_ctrl = s5k6aa_s_ctrl, +}; + +static int s5k6aa_log_status(struct v4l2_subdev *sd) +{ + v4l2_ctrl_handler_log_status(sd->ctrl_handler, sd->name); + return 0; +} + +#define V4L2_CID_RED_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1001) +#define V4L2_CID_GREEN_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1002) +#define V4L2_CID_BLUE_GAIN (V4L2_CTRL_CLASS_CAMERA | 0x1003) + +static const struct v4l2_ctrl_config s5k6aa_ctrls[] = { + { + .ops = &s5k6aa_ctrl_ops, + .id = V4L2_CID_RED_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Red", + .min = 0, + .max = 256, + .def = 127, + .step = 1, + }, { + .ops = &s5k6aa_ctrl_ops, + .id = V4L2_CID_GREEN_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Green", + .min = 0, + .max = 256, + .def = 127, + .step = 1, + }, { + .ops = &s5k6aa_ctrl_ops, + .id = V4L2_CID_BLUE_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain, Blue", + .min = 0, + .max = 256, + .def = 127, + .step = 1, + }, +}; + +static int s5k6aa_initialize_ctrls(struct s5k6aa *s5k6aa) +{ + const struct v4l2_ctrl_ops *ops = &s5k6aa_ctrl_ops; + struct s5k6aa_ctrls *ctrls = &s5k6aa->ctrls; + struct v4l2_ctrl_handler *hdl = &ctrls->handler; + + int ret = v4l2_ctrl_handler_init(hdl, 16); + if (ret) + return ret; + /* Auto white balance cluster */ + ctrls->awb = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_AUTO_WHITE_BALANCE, + 0, 1, 1, 1); + ctrls->gain_red = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[0], NULL); + ctrls->gain_green = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[1], NULL); + ctrls->gain_blue = v4l2_ctrl_new_custom(hdl, &s5k6aa_ctrls[2], NULL); + v4l2_ctrl_auto_cluster(4, &ctrls->awb, 0, false); + + ctrls->hflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HFLIP, 0, 1, 1, 0); + ctrls->vflip = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_cluster(2, &ctrls->hflip); + + ctrls->auto_exp = v4l2_ctrl_new_std_menu(hdl, ops, + V4L2_CID_EXPOSURE_AUTO, + V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_AUTO); + /* Exposure time: x 1 us */ + ctrls->exposure = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_EXPOSURE, + 0, 6000000U, 1, 100000U); + /* Total gain: 256 <=> 1x */ + ctrls->gain = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAIN, + 0, 256, 1, 256); + v4l2_ctrl_auto_cluster(3, &ctrls->auto_exp, 0, false); + + v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_AUTO, 0, + V4L2_CID_POWER_LINE_FREQUENCY_AUTO); + + v4l2_ctrl_new_std_menu(hdl, ops, V4L2_CID_COLORFX, + V4L2_COLORFX_SKY_BLUE, ~0x6f, V4L2_COLORFX_NONE); + + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_WHITE_BALANCE_TEMPERATURE, + 0, 256, 1, 0); + + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SATURATION, -127, 127, 1, 0); + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -127, 127, 1, 0); + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_CONTRAST, -127, 127, 1, 0); + v4l2_ctrl_new_std(hdl, ops, V4L2_CID_SHARPNESS, -127, 127, 1, 0); + + if (hdl->error) { + ret = hdl->error; + v4l2_ctrl_handler_free(hdl); + return ret; + } + + s5k6aa->sd.ctrl_handler = hdl; + return 0; +} + +/* + * V4L2 subdev internal operations + */ +static int s5k6aa_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct v4l2_mbus_framefmt *format = v4l2_subdev_get_try_format(fh, 0); + struct v4l2_rect *crop = v4l2_subdev_get_try_crop(fh, 0); + + format->colorspace = s5k6aa_formats[0].colorspace; + format->code = s5k6aa_formats[0].code; + format->width = S5K6AA_OUT_WIDTH_DEF; + format->height = S5K6AA_OUT_HEIGHT_DEF; + format->field = V4L2_FIELD_NONE; + + crop->width = S5K6AA_WIN_WIDTH_MAX; + crop->height = S5K6AA_WIN_HEIGHT_MAX; + crop->left = 0; + crop->top = 0; + + return 0; +} + +int s5k6aa_check_fw_revision(struct s5k6aa *s5k6aa) +{ + struct i2c_client *client = v4l2_get_subdevdata(&s5k6aa->sd); + u16 api_ver = 0, fw_rev = 0; + + int ret = s5k6aa_set_ahb_address(client); + + if (!ret) + ret = s5k6aa_read(client, REG_FW_APIVER, &api_ver); + if (!ret) + ret = s5k6aa_read(client, REG_FW_REVISION, &fw_rev); + if (ret) { + v4l2_err(&s5k6aa->sd, "FW revision check failed!\n"); + return ret; + } + + v4l2_info(&s5k6aa->sd, "FW API ver.: 0x%X, FW rev.: 0x%X\n", + api_ver, fw_rev); + + return api_ver == S5K6AAFX_FW_APIVER ? 0 : -ENODEV; +} + +static int s5k6aa_registered(struct v4l2_subdev *sd) +{ + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + int ret; + + mutex_lock(&s5k6aa->lock); + ret = __s5k6aa_power_on(s5k6aa); + if (!ret) { + msleep(100); + ret = s5k6aa_check_fw_revision(s5k6aa); + __s5k6aa_power_off(s5k6aa); + } + mutex_unlock(&s5k6aa->lock); + + return ret; +} + +static const struct v4l2_subdev_internal_ops s5k6aa_subdev_internal_ops = { + .registered = s5k6aa_registered, + .open = s5k6aa_open, +}; + +static const struct v4l2_subdev_core_ops s5k6aa_core_ops = { + .s_power = s5k6aa_set_power, + .log_status = s5k6aa_log_status, +}; + +static const struct v4l2_subdev_ops s5k6aa_subdev_ops = { + .core = &s5k6aa_core_ops, + .pad = &s5k6aa_pad_ops, + .video = &s5k6aa_video_ops, +}; + +/* + * GPIO setup + */ +static int s5k6aa_configure_gpio(int nr, int val, const char *name) +{ + unsigned long flags = val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; + int ret; + + if (!gpio_is_valid(nr)) + return 0; + ret = gpio_request_one(nr, flags, name); + if (!ret) + gpio_export(nr, 0); + return ret; +} + +static void s5k6aa_free_gpios(struct s5k6aa *s5k6aa) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(s5k6aa->gpio); i++) { + if (!gpio_is_valid(s5k6aa->gpio[i].gpio)) + continue; + gpio_free(s5k6aa->gpio[i].gpio); + s5k6aa->gpio[i].gpio = -EINVAL; + } +} + +static int s5k6aa_configure_gpios(struct s5k6aa *s5k6aa, + const struct s5k6aa_platform_data *pdata) +{ + const struct s5k6aa_gpio *gpio = &pdata->gpio_stby; + int ret; + + s5k6aa->gpio[STBY].gpio = -EINVAL; + s5k6aa->gpio[RST].gpio = -EINVAL; + + ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_STBY"); + if (ret) { + s5k6aa_free_gpios(s5k6aa); + return ret; + } + s5k6aa->gpio[STBY] = *gpio; + if (gpio_is_valid(gpio->gpio)) + gpio_set_value(gpio->gpio, 0); + + gpio = &pdata->gpio_reset; + ret = s5k6aa_configure_gpio(gpio->gpio, gpio->level, "S5K6AA_RST"); + if (ret) { + s5k6aa_free_gpios(s5k6aa); + return ret; + } + s5k6aa->gpio[RST] = *gpio; + if (gpio_is_valid(gpio->gpio)) + gpio_set_value(gpio->gpio, 0); + + return 0; +} + +static int s5k6aa_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct s5k6aa_platform_data *pdata = client->dev.platform_data; + struct v4l2_subdev *sd; + struct s5k6aa *s5k6aa; + int i, ret; + + if (pdata == NULL) { + dev_err(&client->dev, "Platform data not specified\n"); + return -EINVAL; + } + + if (pdata->mclk_frequency == 0) { + dev_err(&client->dev, "MCLK frequency not specified\n"); + return -EINVAL; + } + + s5k6aa = kzalloc(sizeof(*s5k6aa), GFP_KERNEL); + if (!s5k6aa) + return -ENOMEM; + + mutex_init(&s5k6aa->lock); + + s5k6aa->mclk_frequency = pdata->mclk_frequency; + s5k6aa->bus_type = pdata->bus_type; + s5k6aa->mipi_lanes = pdata->nlanes; + s5k6aa->s_power = pdata->set_power; + s5k6aa->inv_hflip = pdata->horiz_flip; + s5k6aa->inv_vflip = pdata->vert_flip; + + sd = &s5k6aa->sd; + strlcpy(sd->name, DRIVER_NAME, sizeof(sd->name)); + v4l2_i2c_subdev_init(sd, client, &s5k6aa_subdev_ops); + + sd->internal_ops = &s5k6aa_subdev_internal_ops; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + + s5k6aa->pad.flags = MEDIA_PAD_FL_SOURCE; + sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; + ret = media_entity_init(&sd->entity, 1, &s5k6aa->pad, 0); + if (ret) + goto out_err1; + + ret = s5k6aa_configure_gpios(s5k6aa, pdata); + if (ret) + goto out_err2; + + for (i = 0; i < S5K6AA_NUM_SUPPLIES; i++) + s5k6aa->supplies[i].supply = s5k6aa_supply_names[i]; + + ret = regulator_bulk_get(&client->dev, S5K6AA_NUM_SUPPLIES, + s5k6aa->supplies); + if (ret) { + dev_err(&client->dev, "Failed to get regulators\n"); + goto out_err3; + } + + ret = s5k6aa_initialize_ctrls(s5k6aa); + if (ret) + goto out_err4; + + s5k6aa_presets_data_init(s5k6aa); + + s5k6aa->ccd_rect.width = S5K6AA_WIN_WIDTH_MAX; + s5k6aa->ccd_rect.height = S5K6AA_WIN_HEIGHT_MAX; + s5k6aa->ccd_rect.left = 0; + s5k6aa->ccd_rect.top = 0; + + return 0; + +out_err4: + regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); +out_err3: + s5k6aa_free_gpios(s5k6aa); +out_err2: + media_entity_cleanup(&s5k6aa->sd.entity); +out_err1: + kfree(s5k6aa); + return ret; +} + +static int s5k6aa_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct s5k6aa *s5k6aa = to_s5k6aa(sd); + + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(sd->ctrl_handler); + media_entity_cleanup(&sd->entity); + regulator_bulk_free(S5K6AA_NUM_SUPPLIES, s5k6aa->supplies); + s5k6aa_free_gpios(s5k6aa); + kfree(s5k6aa); + + return 0; +} + +static const struct i2c_device_id s5k6aa_id[] = { + { DRIVER_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, s5k6aa_id); + + +static struct i2c_driver s5k6aa_i2c_driver = { + .driver = { + .name = DRIVER_NAME + }, + .probe = s5k6aa_probe, + .remove = s5k6aa_remove, + .id_table = s5k6aa_id, +}; + +static int __init s5k6aa_init(void) +{ + return i2c_add_driver(&s5k6aa_i2c_driver); +} + +static void __exit s5k6aa_exit(void) +{ + i2c_del_driver(&s5k6aa_i2c_driver); +} + +module_init(s5k6aa_init); +module_exit(s5k6aa_exit); + +MODULE_DESCRIPTION("Samsung S5K6AA(FX) SXGA camera driver"); +MODULE_AUTHOR("Sylwester Nawrocki "); +MODULE_LICENSE("GPL"); diff --git a/include/media/s5k6aa.h b/include/media/s5k6aa.h new file mode 100644 index 000000000000..ba34f7055e55 --- /dev/null +++ b/include/media/s5k6aa.h @@ -0,0 +1,51 @@ +/* + * S5K6AAFX camera sensor driver header + * + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef S5K6AA_H +#define S5K6AA_H + +#include + +/** + * struct s5k6aa_gpio - data structure describing a GPIO + * @gpio: GPIO number + * @level: indicates active state of the @gpio + */ +struct s5k6aa_gpio { + int gpio; + int level; +}; + +/** + * struct s5k6aa_platform_data - s5k6aa driver platform data + * @set_power: an additional callback to the board code, called + * after enabling the regulators and before switching + * the sensor off + * @mclk_frequency: sensor's master clock frequency in Hz + * @gpio_reset: GPIO driving RESET pin + * @gpio_stby: GPIO driving STBY pin + * @nlanes: maximum number of MIPI-CSI lanes used + * @horiz_flip: default horizontal image flip value, non zero to enable + * @vert_flip: default vertical image flip value, non zero to enable + */ + +struct s5k6aa_platform_data { + int (*set_power)(int enable); + unsigned long mclk_frequency; + struct s5k6aa_gpio gpio_reset; + struct s5k6aa_gpio gpio_stby; + enum v4l2_mbus_type bus_type; + u8 nlanes; + u8 horiz_flip; + u8 vert_flip; +}; + +#endif /* S5K6AA_H */ -- cgit v1.2.3 From 6f524ec156ba31a18425fad9dd1287be0701d9d1 Mon Sep 17 00:00:00 2001 From: Scott Jiang <[scott.jiang.linux@gmail.com]> Date: Wed, 21 Sep 2011 09:25:23 -0300 Subject: [media] vb2: add vb2_get_unmapped_area in vb2 core no mmu system needs get_unmapped_area file operations to do mmap Signed-off-by: Scott Jiang Signed-off-by: Marek Szyprowski Signed-off-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/videobuf2-core.c | 31 +++++++++++++++++++++++++++++++ include/media/videobuf2-core.h | 7 +++++++ 2 files changed, 38 insertions(+) (limited to 'include') diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index 9005dc9991ab..979e544388cb 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c @@ -1567,6 +1567,37 @@ int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma) } EXPORT_SYMBOL_GPL(vb2_mmap); +#ifndef CONFIG_MMU +unsigned long vb2_get_unmapped_area(struct vb2_queue *q, + unsigned long addr, + unsigned long len, + unsigned long pgoff, + unsigned long flags) +{ + unsigned long off = pgoff << PAGE_SHIFT; + struct vb2_buffer *vb; + unsigned int buffer, plane; + int ret; + + if (q->memory != V4L2_MEMORY_MMAP) { + dprintk(1, "Queue is not currently set up for mmap\n"); + return -EINVAL; + } + + /* + * Find the plane corresponding to the offset passed by userspace. + */ + ret = __find_plane_by_offset(q, off, &buffer, &plane); + if (ret) + return ret; + + vb = q->bufs[buffer]; + + return (unsigned long)vb2_plane_vaddr(vb, plane); +} +EXPORT_SYMBOL_GPL(vb2_get_unmapped_area); +#endif + static int __vb2_init_fileio(struct vb2_queue *q, int read); static int __vb2_cleanup_fileio(struct vb2_queue *q); diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 55c57d3d3e63..a15d1f1b319e 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -322,6 +322,13 @@ int vb2_streamon(struct vb2_queue *q, enum v4l2_buf_type type); int vb2_streamoff(struct vb2_queue *q, enum v4l2_buf_type type); int vb2_mmap(struct vb2_queue *q, struct vm_area_struct *vma); +#ifndef CONFIG_MMU +unsigned long vb2_get_unmapped_area(struct vb2_queue *q, + unsigned long addr, + unsigned long len, + unsigned long pgoff, + unsigned long flags); +#endif unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait); size_t vb2_read(struct vb2_queue *q, char __user *data, size_t count, loff_t *ppos, int nonblock); -- cgit v1.2.3