forked from mirrors/linux
		
	[media] soc-camera: switch I2C subdevice drivers to use v4l2-clk
Instead of centrally enabling and disabling subdevice master clocks in soc-camera core, let subdevice drivers do that themselves, using the V4L2 clock API and soc-camera convenience wrappers. Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de> Acked-by: Hans Verkuil <hans.verkuil@cisco.com> Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
		
							parent
							
								
									e9e310491b
								
							
						
					
					
						commit
						9aea470b39
					
				
					 18 changed files with 362 additions and 79 deletions
				
			
		|  | @ -18,6 +18,7 @@ | |||
| #include <linux/module.h> | ||||
| 
 | ||||
| #include <media/soc_camera.h> | ||||
| #include <media/v4l2-clk.h> | ||||
| #include <media/v4l2-subdev.h> | ||||
| 
 | ||||
| /* IMX074 registers */ | ||||
|  | @ -76,6 +77,7 @@ struct imx074_datafmt { | |||
| struct imx074 { | ||||
| 	struct v4l2_subdev		subdev; | ||||
| 	const struct imx074_datafmt	*fmt; | ||||
| 	struct v4l2_clk			*clk; | ||||
| }; | ||||
| 
 | ||||
| static const struct imx074_datafmt imx074_colour_fmts[] = { | ||||
|  | @ -254,8 +256,9 @@ static int imx074_s_power(struct v4l2_subdev *sd, int on) | |||
| { | ||||
| 	struct i2c_client *client = v4l2_get_subdevdata(sd); | ||||
| 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); | ||||
| 	struct imx074 *priv = to_imx074(client); | ||||
| 
 | ||||
| 	return soc_camera_set_power(&client->dev, ssdd, on); | ||||
| 	return soc_camera_set_power(&client->dev, ssdd, priv->clk, on); | ||||
| } | ||||
| 
 | ||||
| static int imx074_g_mbus_config(struct v4l2_subdev *sd, | ||||
|  | @ -412,6 +415,7 @@ static int imx074_probe(struct i2c_client *client, | |||
| 	struct imx074 *priv; | ||||
| 	struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); | ||||
| 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (!ssdd) { | ||||
| 		dev_err(&client->dev, "IMX074: missing platform data!\n"); | ||||
|  | @ -432,13 +436,23 @@ static int imx074_probe(struct i2c_client *client, | |||
| 
 | ||||
| 	priv->fmt	= &imx074_colour_fmts[0]; | ||||
| 
 | ||||
| 	return imx074_video_probe(client); | ||||
| 	priv->clk = v4l2_clk_get(&client->dev, "mclk"); | ||||
| 	if (IS_ERR(priv->clk)) | ||||
| 		return PTR_ERR(priv->clk); | ||||
| 
 | ||||
| 	ret = imx074_video_probe(client); | ||||
| 	if (ret < 0) | ||||
| 		v4l2_clk_put(priv->clk); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int imx074_remove(struct i2c_client *client) | ||||
| { | ||||
| 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); | ||||
| 	struct imx074 *priv = to_imx074(client); | ||||
| 
 | ||||
| 	v4l2_clk_put(priv->clk); | ||||
| 	if (ssdd->free_bus) | ||||
| 		ssdd->free_bus(ssdd); | ||||
| 
 | ||||
|  |  | |||
|  | @ -16,6 +16,7 @@ | |||
| 
 | ||||
| #include <media/soc_camera.h> | ||||
| #include <media/soc_mediabus.h> | ||||
| #include <media/v4l2-clk.h> | ||||
| #include <media/v4l2-subdev.h> | ||||
| #include <media/v4l2-ctrls.h> | ||||
| 
 | ||||
|  | @ -93,6 +94,7 @@ struct mt9m001 { | |||
| 		struct v4l2_ctrl *exposure; | ||||
| 	}; | ||||
| 	struct v4l2_rect rect;	/* Sensor window */ | ||||
| 	struct v4l2_clk *clk; | ||||
| 	const struct mt9m001_datafmt *fmt; | ||||
| 	const struct mt9m001_datafmt *fmts; | ||||
| 	int num_fmts; | ||||
|  | @ -355,8 +357,9 @@ static int mt9m001_s_power(struct v4l2_subdev *sd, int on) | |||
| { | ||||
| 	struct i2c_client *client = v4l2_get_subdevdata(sd); | ||||
| 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); | ||||
| 	struct mt9m001 *mt9m001 = to_mt9m001(client); | ||||
| 
 | ||||
| 	return soc_camera_set_power(&client->dev, ssdd, on); | ||||
| 	return soc_camera_set_power(&client->dev, ssdd, mt9m001->clk, on); | ||||
| } | ||||
| 
 | ||||
| static int mt9m001_g_volatile_ctrl(struct v4l2_ctrl *ctrl) | ||||
|  | @ -681,9 +684,18 @@ static int mt9m001_probe(struct i2c_client *client, | |||
| 	mt9m001->rect.width	= MT9M001_MAX_WIDTH; | ||||
| 	mt9m001->rect.height	= MT9M001_MAX_HEIGHT; | ||||
| 
 | ||||
| 	mt9m001->clk = v4l2_clk_get(&client->dev, "mclk"); | ||||
| 	if (IS_ERR(mt9m001->clk)) { | ||||
| 		ret = PTR_ERR(mt9m001->clk); | ||||
| 		goto eclkget; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = mt9m001_video_probe(ssdd, client); | ||||
| 	if (ret) | ||||
| 	if (ret) { | ||||
| 		v4l2_clk_put(mt9m001->clk); | ||||
| eclkget: | ||||
| 		v4l2_ctrl_handler_free(&mt9m001->hdl); | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
|  | @ -693,6 +705,7 @@ static int mt9m001_remove(struct i2c_client *client) | |||
| 	struct mt9m001 *mt9m001 = to_mt9m001(client); | ||||
| 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); | ||||
| 
 | ||||
| 	v4l2_clk_put(mt9m001->clk); | ||||
| 	v4l2_device_unregister_subdev(&mt9m001->subdev); | ||||
| 	v4l2_ctrl_handler_free(&mt9m001->hdl); | ||||
| 	mt9m001_video_remove(ssdd); | ||||
|  |  | |||
|  | @ -17,6 +17,7 @@ | |||
| #include <linux/module.h> | ||||
| 
 | ||||
| #include <media/soc_camera.h> | ||||
| #include <media/v4l2-clk.h> | ||||
| #include <media/v4l2-common.h> | ||||
| #include <media/v4l2-ctrls.h> | ||||
| 
 | ||||
|  | @ -206,6 +207,7 @@ struct mt9m111 { | |||
| 	struct v4l2_ctrl *gain; | ||||
| 	struct mt9m111_context *ctx; | ||||
| 	struct v4l2_rect rect;	/* cropping rectangle */ | ||||
| 	struct v4l2_clk *clk; | ||||
| 	int width;		/* output */ | ||||
| 	int height;		/* sizes */ | ||||
| 	struct mutex power_lock; /* lock to protect power_count */ | ||||
|  | @ -775,14 +777,14 @@ static int mt9m111_power_on(struct mt9m111 *mt9m111) | |||
| 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); | ||||
| 	int ret; | ||||
| 
 | ||||
| 	ret = soc_camera_power_on(&client->dev, ssdd); | ||||
| 	ret = soc_camera_power_on(&client->dev, ssdd, mt9m111->clk); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = mt9m111_resume(mt9m111); | ||||
| 	if (ret < 0) { | ||||
| 		dev_err(&client->dev, "Failed to resume the sensor: %d\n", ret); | ||||
| 		soc_camera_power_off(&client->dev, ssdd); | ||||
| 		soc_camera_power_off(&client->dev, ssdd, mt9m111->clk); | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
|  | @ -794,7 +796,7 @@ static void mt9m111_power_off(struct mt9m111 *mt9m111) | |||
| 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); | ||||
| 
 | ||||
| 	mt9m111_suspend(mt9m111); | ||||
| 	soc_camera_power_off(&client->dev, ssdd); | ||||
| 	soc_camera_power_off(&client->dev, ssdd, mt9m111->clk); | ||||
| } | ||||
| 
 | ||||
| static int mt9m111_s_power(struct v4l2_subdev *sd, int on) | ||||
|  | @ -973,9 +975,18 @@ static int mt9m111_probe(struct i2c_client *client, | |||
| 	mt9m111->lastpage	= -1; | ||||
| 	mutex_init(&mt9m111->power_lock); | ||||
| 
 | ||||
| 	mt9m111->clk = v4l2_clk_get(&client->dev, "mclk"); | ||||
| 	if (IS_ERR(mt9m111->clk)) { | ||||
| 		ret = PTR_ERR(mt9m111->clk); | ||||
| 		goto eclkget; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = mt9m111_video_probe(client); | ||||
| 	if (ret) | ||||
| 	if (ret) { | ||||
| 		v4l2_clk_put(mt9m111->clk); | ||||
| eclkget: | ||||
| 		v4l2_ctrl_handler_free(&mt9m111->hdl); | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
|  | @ -984,6 +995,7 @@ static int mt9m111_remove(struct i2c_client *client) | |||
| { | ||||
| 	struct mt9m111 *mt9m111 = to_mt9m111(client); | ||||
| 
 | ||||
| 	v4l2_clk_put(mt9m111->clk); | ||||
| 	v4l2_device_unregister_subdev(&mt9m111->subdev); | ||||
| 	v4l2_ctrl_handler_free(&mt9m111->hdl); | ||||
| 
 | ||||
|  |  | |||
|  | @ -18,6 +18,7 @@ | |||
| #include <linux/module.h> | ||||
| 
 | ||||
| #include <media/soc_camera.h> | ||||
| #include <media/v4l2-clk.h> | ||||
| #include <media/v4l2-subdev.h> | ||||
| #include <media/v4l2-ctrls.h> | ||||
| 
 | ||||
|  | @ -75,6 +76,7 @@ struct mt9t031 { | |||
| 		struct v4l2_ctrl *exposure; | ||||
| 	}; | ||||
| 	struct v4l2_rect rect;	/* Sensor window */ | ||||
| 	struct v4l2_clk *clk; | ||||
| 	u16 xskip; | ||||
| 	u16 yskip; | ||||
| 	unsigned int total_h; | ||||
|  | @ -585,16 +587,17 @@ static int mt9t031_s_power(struct v4l2_subdev *sd, int on) | |||
| 	struct i2c_client *client = v4l2_get_subdevdata(sd); | ||||
| 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); | ||||
| 	struct video_device *vdev = soc_camera_i2c_to_vdev(client); | ||||
| 	struct mt9t031 *mt9t031 = to_mt9t031(client); | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (on) { | ||||
| 		ret = soc_camera_power_on(&client->dev, ssdd); | ||||
| 		ret = soc_camera_power_on(&client->dev, ssdd, mt9t031->clk); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 		vdev->dev.type = &mt9t031_dev_type; | ||||
| 	} else { | ||||
| 		vdev->dev.type = NULL; | ||||
| 		soc_camera_power_off(&client->dev, ssdd); | ||||
| 		soc_camera_power_off(&client->dev, ssdd, mt9t031->clk); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
|  | @ -785,9 +788,18 @@ static int mt9t031_probe(struct i2c_client *client, | |||
| 	mt9t031->xskip = 1; | ||||
| 	mt9t031->yskip = 1; | ||||
| 
 | ||||
| 	mt9t031->clk = v4l2_clk_get(&client->dev, "mclk"); | ||||
| 	if (IS_ERR(mt9t031->clk)) { | ||||
| 		ret = PTR_ERR(mt9t031->clk); | ||||
| 		goto eclkget; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = mt9t031_video_probe(client); | ||||
| 	if (ret) | ||||
| 	if (ret) { | ||||
| 		v4l2_clk_put(mt9t031->clk); | ||||
| eclkget: | ||||
| 		v4l2_ctrl_handler_free(&mt9t031->hdl); | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
|  | @ -796,6 +808,7 @@ static int mt9t031_remove(struct i2c_client *client) | |||
| { | ||||
| 	struct mt9t031 *mt9t031 = to_mt9t031(client); | ||||
| 
 | ||||
| 	v4l2_clk_put(mt9t031->clk); | ||||
| 	v4l2_device_unregister_subdev(&mt9t031->subdev); | ||||
| 	v4l2_ctrl_handler_free(&mt9t031->hdl); | ||||
| 
 | ||||
|  |  | |||
|  | @ -27,6 +27,7 @@ | |||
| 
 | ||||
| #include <media/mt9t112.h> | ||||
| #include <media/soc_camera.h> | ||||
| #include <media/v4l2-clk.h> | ||||
| #include <media/v4l2-common.h> | ||||
| 
 | ||||
| /* you can check PLL/clock info */ | ||||
|  | @ -89,6 +90,7 @@ struct mt9t112_priv { | |||
| 	struct mt9t112_camera_info	*info; | ||||
| 	struct i2c_client		*client; | ||||
| 	struct v4l2_rect		 frame; | ||||
| 	struct v4l2_clk			*clk; | ||||
| 	const struct mt9t112_format	*format; | ||||
| 	int				 num_formats; | ||||
| 	u32				 flags; | ||||
|  | @ -768,8 +770,9 @@ static int mt9t112_s_power(struct v4l2_subdev *sd, int on) | |||
| { | ||||
| 	struct i2c_client *client = v4l2_get_subdevdata(sd); | ||||
| 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); | ||||
| 	struct mt9t112_priv *priv = to_mt9t112(client); | ||||
| 
 | ||||
| 	return soc_camera_set_power(&client->dev, ssdd, on); | ||||
| 	return soc_camera_set_power(&client->dev, ssdd, priv->clk, on); | ||||
| } | ||||
| 
 | ||||
| static struct v4l2_subdev_core_ops mt9t112_subdev_core_ops = { | ||||
|  | @ -1092,16 +1095,29 @@ static int mt9t112_probe(struct i2c_client *client, | |||
| 
 | ||||
| 	v4l2_i2c_subdev_init(&priv->subdev, client, &mt9t112_subdev_ops); | ||||
| 
 | ||||
| 	priv->clk = v4l2_clk_get(&client->dev, "mclk"); | ||||
| 	if (IS_ERR(priv->clk)) | ||||
| 		return PTR_ERR(priv->clk); | ||||
| 
 | ||||
| 	ret = mt9t112_camera_probe(client); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	/* Cannot fail: using the default supported pixel code */ | ||||
| 	mt9t112_set_params(priv, &rect, V4L2_MBUS_FMT_UYVY8_2X8); | ||||
| 	if (!ret) | ||||
| 		mt9t112_set_params(priv, &rect, V4L2_MBUS_FMT_UYVY8_2X8); | ||||
| 	else | ||||
| 		v4l2_clk_put(priv->clk); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int mt9t112_remove(struct i2c_client *client) | ||||
| { | ||||
| 	struct mt9t112_priv *priv = to_mt9t112(client); | ||||
| 
 | ||||
| 	v4l2_clk_put(priv->clk); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct i2c_device_id mt9t112_id[] = { | ||||
| 	{ "mt9t112", 0 }, | ||||
| 	{ } | ||||
|  | @ -1113,6 +1129,7 @@ static struct i2c_driver mt9t112_i2c_driver = { | |||
| 		.name = "mt9t112", | ||||
| 	}, | ||||
| 	.probe    = mt9t112_probe, | ||||
| 	.remove   = mt9t112_remove, | ||||
| 	.id_table = mt9t112_id, | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -19,6 +19,7 @@ | |||
| #include <media/soc_camera.h> | ||||
| #include <media/soc_mediabus.h> | ||||
| #include <media/v4l2-subdev.h> | ||||
| #include <media/v4l2-clk.h> | ||||
| #include <media/v4l2-ctrls.h> | ||||
| 
 | ||||
| /*
 | ||||
|  | @ -153,6 +154,7 @@ struct mt9v022 { | |||
| 	struct v4l2_ctrl *hblank; | ||||
| 	struct v4l2_ctrl *vblank; | ||||
| 	struct v4l2_rect rect;	/* Sensor window */ | ||||
| 	struct v4l2_clk *clk; | ||||
| 	const struct mt9v022_datafmt *fmt; | ||||
| 	const struct mt9v022_datafmt *fmts; | ||||
| 	const struct mt9v02x_register *reg; | ||||
|  | @ -498,8 +500,9 @@ static int mt9v022_s_power(struct v4l2_subdev *sd, int on) | |||
| { | ||||
| 	struct i2c_client *client = v4l2_get_subdevdata(sd); | ||||
| 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); | ||||
| 	struct mt9v022 *mt9v022 = to_mt9v022(client); | ||||
| 
 | ||||
| 	return soc_camera_set_power(&client->dev, ssdd, on); | ||||
| 	return soc_camera_set_power(&client->dev, ssdd, mt9v022->clk, on); | ||||
| } | ||||
| 
 | ||||
| static int mt9v022_g_volatile_ctrl(struct v4l2_ctrl *ctrl) | ||||
|  | @ -936,9 +939,18 @@ static int mt9v022_probe(struct i2c_client *client, | |||
| 	mt9v022->rect.width	= MT9V022_MAX_WIDTH; | ||||
| 	mt9v022->rect.height	= MT9V022_MAX_HEIGHT; | ||||
| 
 | ||||
| 	mt9v022->clk = v4l2_clk_get(&client->dev, "mclk"); | ||||
| 	if (IS_ERR(mt9v022->clk)) { | ||||
| 		ret = PTR_ERR(mt9v022->clk); | ||||
| 		goto eclkget; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = mt9v022_video_probe(client); | ||||
| 	if (ret) | ||||
| 	if (ret) { | ||||
| 		v4l2_clk_put(mt9v022->clk); | ||||
| eclkget: | ||||
| 		v4l2_ctrl_handler_free(&mt9v022->hdl); | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
|  | @ -948,6 +960,7 @@ static int mt9v022_remove(struct i2c_client *client) | |||
| 	struct mt9v022 *mt9v022 = to_mt9v022(client); | ||||
| 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); | ||||
| 
 | ||||
| 	v4l2_clk_put(mt9v022->clk); | ||||
| 	v4l2_device_unregister_subdev(&mt9v022->subdev); | ||||
| 	if (ssdd->free_bus) | ||||
| 		ssdd->free_bus(ssdd); | ||||
|  |  | |||
|  | @ -22,6 +22,7 @@ | |||
| #include <linux/videodev2.h> | ||||
| 
 | ||||
| #include <media/soc_camera.h> | ||||
| #include <media/v4l2-clk.h> | ||||
| #include <media/v4l2-subdev.h> | ||||
| #include <media/v4l2-ctrls.h> | ||||
| 
 | ||||
|  | @ -302,6 +303,7 @@ struct ov2640_priv { | |||
| 	struct v4l2_subdev		subdev; | ||||
| 	struct v4l2_ctrl_handler	hdl; | ||||
| 	enum v4l2_mbus_pixelcode	cfmt_code; | ||||
| 	struct v4l2_clk			*clk; | ||||
| 	const struct ov2640_win_size	*win; | ||||
| }; | ||||
| 
 | ||||
|  | @ -758,8 +760,9 @@ static int ov2640_s_power(struct v4l2_subdev *sd, int on) | |||
| { | ||||
| 	struct i2c_client *client = v4l2_get_subdevdata(sd); | ||||
| 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); | ||||
| 	struct ov2640_priv *priv = to_ov2640(client); | ||||
| 
 | ||||
| 	return soc_camera_set_power(&client->dev, ssdd, on); | ||||
| 	return soc_camera_set_power(&client->dev, ssdd, priv->clk, on); | ||||
| } | ||||
| 
 | ||||
| /* Select the nearest higher resolution for capture */ | ||||
|  | @ -1097,11 +1100,20 @@ static int ov2640_probe(struct i2c_client *client, | |||
| 	if (priv->hdl.error) | ||||
| 		return priv->hdl.error; | ||||
| 
 | ||||
| 	priv->clk = v4l2_clk_get(&client->dev, "mclk"); | ||||
| 	if (IS_ERR(priv->clk)) { | ||||
| 		ret = PTR_ERR(priv->clk); | ||||
| 		goto eclkget; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = ov2640_video_probe(client); | ||||
| 	if (ret) | ||||
| 	if (ret) { | ||||
| 		v4l2_clk_put(priv->clk); | ||||
| eclkget: | ||||
| 		v4l2_ctrl_handler_free(&priv->hdl); | ||||
| 	else | ||||
| 	} else { | ||||
| 		dev_info(&adapter->dev, "OV2640 Probed\n"); | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
|  | @ -1110,6 +1122,7 @@ static int ov2640_remove(struct i2c_client *client) | |||
| { | ||||
| 	struct ov2640_priv       *priv = to_ov2640(client); | ||||
| 
 | ||||
| 	v4l2_clk_put(priv->clk); | ||||
| 	v4l2_device_unregister_subdev(&priv->subdev); | ||||
| 	v4l2_ctrl_handler_free(&priv->hdl); | ||||
| 	return 0; | ||||
|  |  | |||
|  | @ -24,6 +24,7 @@ | |||
| #include <linux/v4l2-mediabus.h> | ||||
| 
 | ||||
| #include <media/soc_camera.h> | ||||
| #include <media/v4l2-clk.h> | ||||
| #include <media/v4l2-subdev.h> | ||||
| 
 | ||||
| /* OV5642 registers */ | ||||
|  | @ -609,6 +610,7 @@ struct ov5642 { | |||
| 	struct v4l2_subdev		subdev; | ||||
| 	const struct ov5642_datafmt	*fmt; | ||||
| 	struct v4l2_rect                crop_rect; | ||||
| 	struct v4l2_clk			*clk; | ||||
| 
 | ||||
| 	/* blanking information */ | ||||
| 	int total_width; | ||||
|  | @ -917,12 +919,13 @@ static int ov5642_s_power(struct v4l2_subdev *sd, int on) | |||
| { | ||||
| 	struct i2c_client *client = v4l2_get_subdevdata(sd); | ||||
| 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); | ||||
| 	struct ov5642 *priv = to_ov5642(client); | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (!on) | ||||
| 		return soc_camera_power_off(&client->dev, ssdd); | ||||
| 		return soc_camera_power_off(&client->dev, ssdd, priv->clk); | ||||
| 
 | ||||
| 	ret = soc_camera_power_on(&client->dev, ssdd); | ||||
| 	ret = soc_camera_power_on(&client->dev, ssdd, priv->clk); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 
 | ||||
|  | @ -1002,6 +1005,7 @@ static int ov5642_probe(struct i2c_client *client, | |||
| { | ||||
| 	struct ov5642 *priv; | ||||
| 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (!ssdd) { | ||||
| 		dev_err(&client->dev, "OV5642: missing platform data!\n"); | ||||
|  | @ -1023,13 +1027,23 @@ static int ov5642_probe(struct i2c_client *client, | |||
| 	priv->total_width = OV5642_DEFAULT_WIDTH + BLANKING_EXTRA_WIDTH; | ||||
| 	priv->total_height = BLANKING_MIN_HEIGHT; | ||||
| 
 | ||||
| 	return ov5642_video_probe(client); | ||||
| 	priv->clk = v4l2_clk_get(&client->dev, "mclk"); | ||||
| 	if (IS_ERR(priv->clk)) | ||||
| 		return PTR_ERR(priv->clk); | ||||
| 
 | ||||
| 	ret = ov5642_video_probe(client); | ||||
| 	if (ret < 0) | ||||
| 		v4l2_clk_put(priv->clk); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int ov5642_remove(struct i2c_client *client) | ||||
| { | ||||
| 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); | ||||
| 	struct ov5642 *priv = to_ov5642(client); | ||||
| 
 | ||||
| 	v4l2_clk_put(priv->clk); | ||||
| 	if (ssdd->free_bus) | ||||
| 		ssdd->free_bus(ssdd); | ||||
| 
 | ||||
|  |  | |||
|  | @ -32,6 +32,7 @@ | |||
| #include <linux/module.h> | ||||
| 
 | ||||
| #include <media/soc_camera.h> | ||||
| #include <media/v4l2-clk.h> | ||||
| #include <media/v4l2-ctrls.h> | ||||
| 
 | ||||
| /* Register definitions */ | ||||
|  | @ -195,6 +196,7 @@ struct ov6650 { | |||
| 		struct v4l2_ctrl *blue; | ||||
| 		struct v4l2_ctrl *red; | ||||
| 	}; | ||||
| 	struct v4l2_clk		*clk; | ||||
| 	bool			half_scale;	/* scale down output by 2 */ | ||||
| 	struct v4l2_rect	rect;		/* sensor cropping window */ | ||||
| 	unsigned long		pclk_limit;	/* from host */ | ||||
|  | @ -425,8 +427,9 @@ static int ov6650_s_power(struct v4l2_subdev *sd, int on) | |||
| { | ||||
| 	struct i2c_client *client = v4l2_get_subdevdata(sd); | ||||
| 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); | ||||
| 	struct ov6650 *priv = to_ov6650(client); | ||||
| 
 | ||||
| 	return soc_camera_set_power(&client->dev, ssdd, on); | ||||
| 	return soc_camera_set_power(&client->dev, ssdd, priv->clk, on); | ||||
| } | ||||
| 
 | ||||
| static int ov6650_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) | ||||
|  | @ -1013,9 +1016,18 @@ static int ov6650_probe(struct i2c_client *client, | |||
| 	priv->code	  = V4L2_MBUS_FMT_YUYV8_2X8; | ||||
| 	priv->colorspace  = V4L2_COLORSPACE_JPEG; | ||||
| 
 | ||||
| 	priv->clk = v4l2_clk_get(&client->dev, "mclk"); | ||||
| 	if (IS_ERR(priv->clk)) { | ||||
| 		ret = PTR_ERR(priv->clk); | ||||
| 		goto eclkget; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = ov6650_video_probe(client); | ||||
| 	if (ret) | ||||
| 	if (ret) { | ||||
| 		v4l2_clk_put(priv->clk); | ||||
| eclkget: | ||||
| 		v4l2_ctrl_handler_free(&priv->hdl); | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
|  | @ -1024,6 +1036,7 @@ static int ov6650_remove(struct i2c_client *client) | |||
| { | ||||
| 	struct ov6650 *priv = to_ov6650(client); | ||||
| 
 | ||||
| 	v4l2_clk_put(priv->clk); | ||||
| 	v4l2_device_unregister_subdev(&priv->subdev); | ||||
| 	v4l2_ctrl_handler_free(&priv->hdl); | ||||
| 	return 0; | ||||
|  |  | |||
|  | @ -26,6 +26,7 @@ | |||
| 
 | ||||
| #include <media/ov772x.h> | ||||
| #include <media/soc_camera.h> | ||||
| #include <media/v4l2-clk.h> | ||||
| #include <media/v4l2-ctrls.h> | ||||
| #include <media/v4l2-subdev.h> | ||||
| 
 | ||||
|  | @ -395,6 +396,7 @@ struct ov772x_win_size { | |||
| struct ov772x_priv { | ||||
| 	struct v4l2_subdev                subdev; | ||||
| 	struct v4l2_ctrl_handler	  hdl; | ||||
| 	struct v4l2_clk			 *clk; | ||||
| 	struct ov772x_camera_info        *info; | ||||
| 	const struct ov772x_color_format *cfmt; | ||||
| 	const struct ov772x_win_size     *win; | ||||
|  | @ -655,8 +657,9 @@ static int ov772x_s_power(struct v4l2_subdev *sd, int on) | |||
| { | ||||
| 	struct i2c_client *client = v4l2_get_subdevdata(sd); | ||||
| 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); | ||||
| 	struct ov772x_priv *priv = to_ov772x(sd); | ||||
| 
 | ||||
| 	return soc_camera_set_power(&client->dev, ssdd, on); | ||||
| 	return soc_camera_set_power(&client->dev, ssdd, priv->clk, on); | ||||
| } | ||||
| 
 | ||||
| static const struct ov772x_win_size *ov772x_select_win(u32 width, u32 height) | ||||
|  | @ -1072,13 +1075,22 @@ static int ov772x_probe(struct i2c_client *client, | |||
| 	if (priv->hdl.error) | ||||
| 		return priv->hdl.error; | ||||
| 
 | ||||
| 	priv->clk = v4l2_clk_get(&client->dev, "mclk"); | ||||
| 	if (IS_ERR(priv->clk)) { | ||||
| 		ret = PTR_ERR(priv->clk); | ||||
| 		goto eclkget; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = ov772x_video_probe(priv); | ||||
| 	if (ret < 0) { | ||||
| 		v4l2_clk_put(priv->clk); | ||||
| eclkget: | ||||
| 		v4l2_ctrl_handler_free(&priv->hdl); | ||||
| 	} else { | ||||
| 		priv->cfmt = &ov772x_cfmts[0]; | ||||
| 		priv->win = &ov772x_win_sizes[0]; | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
|  | @ -1086,6 +1098,7 @@ static int ov772x_remove(struct i2c_client *client) | |||
| { | ||||
| 	struct ov772x_priv *priv = to_ov772x(i2c_get_clientdata(client)); | ||||
| 
 | ||||
| 	v4l2_clk_put(priv->clk); | ||||
| 	v4l2_device_unregister_subdev(&priv->subdev); | ||||
| 	v4l2_ctrl_handler_free(&priv->hdl); | ||||
| 	return 0; | ||||
|  |  | |||
|  | @ -28,6 +28,7 @@ | |||
| #include <linux/videodev2.h> | ||||
| 
 | ||||
| #include <media/soc_camera.h> | ||||
| #include <media/v4l2-clk.h> | ||||
| #include <media/v4l2-common.h> | ||||
| #include <media/v4l2-ctrls.h> | ||||
| 
 | ||||
|  | @ -324,8 +325,9 @@ static int ov9640_s_power(struct v4l2_subdev *sd, int on) | |||
| { | ||||
| 	struct i2c_client *client = v4l2_get_subdevdata(sd); | ||||
| 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); | ||||
| 	struct ov9640_priv *priv = to_ov9640_sensor(sd); | ||||
| 
 | ||||
| 	return soc_camera_set_power(&client->dev, ssdd, on); | ||||
| 	return soc_camera_set_power(&client->dev, ssdd, priv->clk, on); | ||||
| } | ||||
| 
 | ||||
| /* select nearest higher resolution for capture */ | ||||
|  | @ -700,10 +702,18 @@ static int ov9640_probe(struct i2c_client *client, | |||
| 	if (priv->hdl.error) | ||||
| 		return priv->hdl.error; | ||||
| 
 | ||||
| 	ret = ov9640_video_probe(client); | ||||
| 	priv->clk = v4l2_clk_get(&client->dev, "mclk"); | ||||
| 	if (IS_ERR(priv->clk)) { | ||||
| 		ret = PTR_ERR(priv->clk); | ||||
| 		goto eclkget; | ||||
| 	} | ||||
| 
 | ||||
| 	if (ret) | ||||
| 	ret = ov9640_video_probe(client); | ||||
| 	if (ret) { | ||||
| 		v4l2_clk_put(priv->clk); | ||||
| eclkget: | ||||
| 		v4l2_ctrl_handler_free(&priv->hdl); | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
|  | @ -713,6 +723,7 @@ static int ov9640_remove(struct i2c_client *client) | |||
| 	struct v4l2_subdev *sd = i2c_get_clientdata(client); | ||||
| 	struct ov9640_priv *priv = to_ov9640_sensor(sd); | ||||
| 
 | ||||
| 	v4l2_clk_put(priv->clk); | ||||
| 	v4l2_device_unregister_subdev(&priv->subdev); | ||||
| 	v4l2_ctrl_handler_free(&priv->hdl); | ||||
| 	return 0; | ||||
|  |  | |||
|  | @ -199,6 +199,7 @@ struct ov9640_reg { | |||
| struct ov9640_priv { | ||||
| 	struct v4l2_subdev		subdev; | ||||
| 	struct v4l2_ctrl_handler	hdl; | ||||
| 	struct v4l2_clk			*clk; | ||||
| 
 | ||||
| 	int				model; | ||||
| 	int				revision; | ||||
|  |  | |||
|  | @ -17,6 +17,7 @@ | |||
| #include <linux/v4l2-mediabus.h> | ||||
| 
 | ||||
| #include <media/soc_camera.h> | ||||
| #include <media/v4l2-clk.h> | ||||
| #include <media/v4l2-ctrls.h> | ||||
| 
 | ||||
| #define to_ov9740(sd)		container_of(sd, struct ov9740_priv, subdev) | ||||
|  | @ -195,6 +196,7 @@ struct ov9740_reg { | |||
| struct ov9740_priv { | ||||
| 	struct v4l2_subdev		subdev; | ||||
| 	struct v4l2_ctrl_handler	hdl; | ||||
| 	struct v4l2_clk			*clk; | ||||
| 
 | ||||
| 	u16				model; | ||||
| 	u8				revision; | ||||
|  | @ -778,7 +780,7 @@ static int ov9740_s_power(struct v4l2_subdev *sd, int on) | |||
| 	int ret; | ||||
| 
 | ||||
| 	if (on) { | ||||
| 		ret = soc_camera_power_on(&client->dev, ssdd); | ||||
| 		ret = soc_camera_power_on(&client->dev, ssdd, priv->clk); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 
 | ||||
|  | @ -792,7 +794,7 @@ static int ov9740_s_power(struct v4l2_subdev *sd, int on) | |||
| 			priv->current_enable = true; | ||||
| 		} | ||||
| 
 | ||||
| 		soc_camera_power_off(&client->dev, ssdd); | ||||
| 		soc_camera_power_off(&client->dev, ssdd, priv->clk); | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
|  | @ -958,9 +960,18 @@ static int ov9740_probe(struct i2c_client *client, | |||
| 	if (priv->hdl.error) | ||||
| 		return priv->hdl.error; | ||||
| 
 | ||||
| 	priv->clk = v4l2_clk_get(&client->dev, "mclk"); | ||||
| 	if (IS_ERR(priv->clk)) { | ||||
| 		ret = PTR_ERR(priv->clk); | ||||
| 		goto eclkget; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = ov9740_video_probe(client); | ||||
| 	if (ret < 0) | ||||
| 	if (ret < 0) { | ||||
| 		v4l2_clk_put(priv->clk); | ||||
| eclkget: | ||||
| 		v4l2_ctrl_handler_free(&priv->hdl); | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
|  | @ -969,6 +980,7 @@ static int ov9740_remove(struct i2c_client *client) | |||
| { | ||||
| 	struct ov9740_priv *priv = i2c_get_clientdata(client); | ||||
| 
 | ||||
| 	v4l2_clk_put(priv->clk); | ||||
| 	v4l2_device_unregister_subdev(&priv->subdev); | ||||
| 	v4l2_ctrl_handler_free(&priv->hdl); | ||||
| 	return 0; | ||||
|  |  | |||
|  | @ -17,6 +17,7 @@ | |||
| 
 | ||||
| #include <media/rj54n1cb0c.h> | ||||
| #include <media/soc_camera.h> | ||||
| #include <media/v4l2-clk.h> | ||||
| #include <media/v4l2-subdev.h> | ||||
| #include <media/v4l2-ctrls.h> | ||||
| 
 | ||||
|  | @ -150,6 +151,7 @@ struct rj54n1_clock_div { | |||
| struct rj54n1 { | ||||
| 	struct v4l2_subdev subdev; | ||||
| 	struct v4l2_ctrl_handler hdl; | ||||
| 	struct v4l2_clk *clk; | ||||
| 	struct rj54n1_clock_div clk_div; | ||||
| 	const struct rj54n1_datafmt *fmt; | ||||
| 	struct v4l2_rect rect;	/* Sensor window */ | ||||
|  | @ -1158,8 +1160,9 @@ static int rj54n1_s_power(struct v4l2_subdev *sd, int on) | |||
| { | ||||
| 	struct i2c_client *client = v4l2_get_subdevdata(sd); | ||||
| 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); | ||||
| 	struct rj54n1 *rj54n1 = to_rj54n1(client); | ||||
| 
 | ||||
| 	return soc_camera_set_power(&client->dev, ssdd, on); | ||||
| 	return soc_camera_set_power(&client->dev, ssdd, rj54n1->clk, on); | ||||
| } | ||||
| 
 | ||||
| static int rj54n1_s_ctrl(struct v4l2_ctrl *ctrl) | ||||
|  | @ -1355,9 +1358,18 @@ 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); | ||||
| 
 | ||||
| 	rj54n1->clk = v4l2_clk_get(&client->dev, "mclk"); | ||||
| 	if (IS_ERR(rj54n1->clk)) { | ||||
| 		ret = PTR_ERR(rj54n1->clk); | ||||
| 		goto eclkget; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = rj54n1_video_probe(client, rj54n1_priv); | ||||
| 	if (ret < 0) | ||||
| 	if (ret < 0) { | ||||
| 		v4l2_clk_put(rj54n1->clk); | ||||
| eclkget: | ||||
| 		v4l2_ctrl_handler_free(&rj54n1->hdl); | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
|  | @ -1367,6 +1379,7 @@ static int rj54n1_remove(struct i2c_client *client) | |||
| 	struct rj54n1 *rj54n1 = to_rj54n1(client); | ||||
| 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); | ||||
| 
 | ||||
| 	v4l2_clk_put(rj54n1->clk); | ||||
| 	v4l2_device_unregister_subdev(&rj54n1->subdev); | ||||
| 	if (ssdd->free_bus) | ||||
| 		ssdd->free_bus(ssdd); | ||||
|  |  | |||
|  | @ -27,6 +27,7 @@ | |||
| 
 | ||||
| #include <media/soc_camera.h> | ||||
| #include <media/tw9910.h> | ||||
| #include <media/v4l2-clk.h> | ||||
| #include <media/v4l2-subdev.h> | ||||
| 
 | ||||
| #define GET_ID(val)  ((val & 0xF8) >> 3) | ||||
|  | @ -227,6 +228,7 @@ struct tw9910_scale_ctrl { | |||
| 
 | ||||
| struct tw9910_priv { | ||||
| 	struct v4l2_subdev		subdev; | ||||
| 	struct v4l2_clk			*clk; | ||||
| 	struct tw9910_video_info	*info; | ||||
| 	const struct tw9910_scale_ctrl	*scale; | ||||
| 	v4l2_std_id			norm; | ||||
|  | @ -558,8 +560,9 @@ static int tw9910_s_power(struct v4l2_subdev *sd, int on) | |||
| { | ||||
| 	struct i2c_client *client = v4l2_get_subdevdata(sd); | ||||
| 	struct soc_camera_subdev_desc *ssdd = soc_camera_i2c_to_desc(client); | ||||
| 	struct tw9910_priv *priv = to_tw9910(client); | ||||
| 
 | ||||
| 	return soc_camera_set_power(&client->dev, ssdd, on); | ||||
| 	return soc_camera_set_power(&client->dev, ssdd, priv->clk, on); | ||||
| } | ||||
| 
 | ||||
| static int tw9910_set_frame(struct v4l2_subdev *sd, u32 *width, u32 *height) | ||||
|  | @ -899,6 +902,7 @@ static int tw9910_probe(struct i2c_client *client, | |||
| 	struct i2c_adapter		*adapter = | ||||
| 		to_i2c_adapter(client->dev.parent); | ||||
| 	struct soc_camera_subdev_desc	*ssdd = soc_camera_i2c_to_desc(client); | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (!ssdd || !ssdd->drv_priv) { | ||||
| 		dev_err(&client->dev, "TW9910: missing platform data!\n"); | ||||
|  | @ -922,7 +926,22 @@ static int tw9910_probe(struct i2c_client *client, | |||
| 
 | ||||
| 	v4l2_i2c_subdev_init(&priv->subdev, client, &tw9910_subdev_ops); | ||||
| 
 | ||||
| 	return tw9910_video_probe(client); | ||||
| 	priv->clk = v4l2_clk_get(&client->dev, "mclk"); | ||||
| 	if (IS_ERR(priv->clk)) | ||||
| 		return PTR_ERR(priv->clk); | ||||
| 
 | ||||
| 	ret = tw9910_video_probe(client); | ||||
| 	if (ret < 0) | ||||
| 		v4l2_clk_put(priv->clk); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static int tw9910_remove(struct i2c_client *client) | ||||
| { | ||||
| 	struct tw9910_priv *priv = to_tw9910(client); | ||||
| 	v4l2_clk_put(priv->clk); | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static const struct i2c_device_id tw9910_id[] = { | ||||
|  | @ -936,6 +955,7 @@ static struct i2c_driver tw9910_i2c_driver = { | |||
| 		.name = "tw9910", | ||||
| 	}, | ||||
| 	.probe    = tw9910_probe, | ||||
| 	.remove   = tw9910_remove, | ||||
| 	.id_table = tw9910_id, | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -30,6 +30,7 @@ | |||
| #include <linux/vmalloc.h> | ||||
| 
 | ||||
| #include <media/soc_camera.h> | ||||
| #include <media/v4l2-clk.h> | ||||
| #include <media/v4l2-common.h> | ||||
| #include <media/v4l2-ioctl.h> | ||||
| #include <media/v4l2-dev.h> | ||||
|  | @ -50,13 +51,19 @@ static LIST_HEAD(hosts); | |||
| static LIST_HEAD(devices); | ||||
| static DEFINE_MUTEX(list_lock);		/* Protects the list of hosts */ | ||||
| 
 | ||||
| int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd) | ||||
| int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd, | ||||
| 			struct v4l2_clk *clk) | ||||
| { | ||||
| 	int ret = regulator_bulk_enable(ssdd->num_regulators, | ||||
| 	int ret = clk ? v4l2_clk_enable(clk) : 0; | ||||
| 	if (ret < 0) { | ||||
| 		dev_err(dev, "Cannot enable clock\n"); | ||||
| 		return ret; | ||||
| 	} | ||||
| 	ret = regulator_bulk_enable(ssdd->num_regulators, | ||||
| 					ssdd->regulators); | ||||
| 	if (ret < 0) { | ||||
| 		dev_err(dev, "Cannot enable regulators\n"); | ||||
| 		return ret; | ||||
| 		goto eregenable;; | ||||
| 	} | ||||
| 
 | ||||
| 	if (ssdd->power) { | ||||
|  | @ -64,16 +71,25 @@ int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd) | |||
| 		if (ret < 0) { | ||||
| 			dev_err(dev, | ||||
| 				"Platform failed to power-on the camera.\n"); | ||||
| 			regulator_bulk_disable(ssdd->num_regulators, | ||||
| 					       ssdd->regulators); | ||||
| 			goto epwron; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| epwron: | ||||
| 	regulator_bulk_disable(ssdd->num_regulators, | ||||
| 			       ssdd->regulators); | ||||
| eregenable: | ||||
| 	if (clk) | ||||
| 		v4l2_clk_disable(clk); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| EXPORT_SYMBOL(soc_camera_power_on); | ||||
| 
 | ||||
| int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd) | ||||
| int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd, | ||||
| 			 struct v4l2_clk *clk) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	int err; | ||||
|  | @ -94,6 +110,9 @@ int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd | |||
| 		ret = ret ? : err; | ||||
| 	} | ||||
| 
 | ||||
| 	if (clk) | ||||
| 		v4l2_clk_disable(clk); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| EXPORT_SYMBOL(soc_camera_power_off); | ||||
|  | @ -512,9 +531,11 @@ static int soc_camera_add_device(struct soc_camera_device *icd) | |||
| 	if (ici->icd) | ||||
| 		return -EBUSY; | ||||
| 
 | ||||
| 	ret = ici->ops->clock_start(ici); | ||||
| 	if (ret < 0) | ||||
| 		return ret; | ||||
| 	if (!icd->clk) { | ||||
| 		ret = ici->ops->clock_start(ici); | ||||
| 		if (ret < 0) | ||||
| 			return ret; | ||||
| 	} | ||||
| 
 | ||||
| 	if (ici->ops->add) { | ||||
| 		ret = ici->ops->add(icd); | ||||
|  | @ -527,7 +548,8 @@ static int soc_camera_add_device(struct soc_camera_device *icd) | |||
| 	return 0; | ||||
| 
 | ||||
| eadd: | ||||
| 	ici->ops->clock_stop(ici); | ||||
| 	if (!icd->clk) | ||||
| 		ici->ops->clock_stop(ici); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
|  | @ -540,7 +562,8 @@ static void soc_camera_remove_device(struct soc_camera_device *icd) | |||
| 
 | ||||
| 	if (ici->ops->remove) | ||||
| 		ici->ops->remove(icd); | ||||
| 	ici->ops->clock_stop(ici); | ||||
| 	if (!icd->clk) | ||||
| 		ici->ops->clock_stop(ici); | ||||
| 	ici->icd = NULL; | ||||
| } | ||||
| 
 | ||||
|  | @ -1094,6 +1117,57 @@ static void scan_add_host(struct soc_camera_host *ici) | |||
| 	mutex_unlock(&list_lock); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * It is invalid to call v4l2_clk_enable() after a successful probing | ||||
|  * asynchronously outside of V4L2 operations, i.e. with .host_lock not held. | ||||
|  */ | ||||
| static int soc_camera_clk_enable(struct v4l2_clk *clk) | ||||
| { | ||||
| 	struct soc_camera_device *icd = clk->priv; | ||||
| 	struct soc_camera_host *ici; | ||||
| 
 | ||||
| 	if (!icd || !icd->parent) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	ici = to_soc_camera_host(icd->parent); | ||||
| 
 | ||||
| 	if (!try_module_get(ici->ops->owner)) | ||||
| 		return -ENODEV; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * If a different client is currently being probed, the host will tell | ||||
| 	 * you to go | ||||
| 	 */ | ||||
| 	return ici->ops->clock_start(ici); | ||||
| } | ||||
| 
 | ||||
| static void soc_camera_clk_disable(struct v4l2_clk *clk) | ||||
| { | ||||
| 	struct soc_camera_device *icd = clk->priv; | ||||
| 	struct soc_camera_host *ici; | ||||
| 
 | ||||
| 	if (!icd || !icd->parent) | ||||
| 		return; | ||||
| 
 | ||||
| 	ici = to_soc_camera_host(icd->parent); | ||||
| 
 | ||||
| 	ici->ops->clock_stop(ici); | ||||
| 
 | ||||
| 	module_put(ici->ops->owner); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Eventually, it would be more logical to make the respective host the clock | ||||
|  * owner, but then we would have to copy this struct for each ici. Besides, it | ||||
|  * would introduce the circular dependency problem, unless we port all client | ||||
|  * drivers to release the clock, when not in use. | ||||
|  */ | ||||
| static const struct v4l2_clk_ops soc_camera_clk_ops = { | ||||
| 	.owner = THIS_MODULE, | ||||
| 	.enable = soc_camera_clk_enable, | ||||
| 	.disable = soc_camera_clk_disable, | ||||
| }; | ||||
| 
 | ||||
| #ifdef CONFIG_I2C_BOARDINFO | ||||
| static int soc_camera_init_i2c(struct soc_camera_device *icd, | ||||
| 			       struct soc_camera_desc *sdesc) | ||||
|  | @ -1103,19 +1177,32 @@ static int soc_camera_init_i2c(struct soc_camera_device *icd, | |||
| 	struct soc_camera_host_desc *shd = &sdesc->host_desc; | ||||
| 	struct i2c_adapter *adap = i2c_get_adapter(shd->i2c_adapter_id); | ||||
| 	struct v4l2_subdev *subdev; | ||||
| 	char clk_name[V4L2_SUBDEV_NAME_SIZE]; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	if (!adap) { | ||||
| 		dev_err(icd->pdev, "Cannot get I2C adapter #%d. No driver?\n", | ||||
| 			shd->i2c_adapter_id); | ||||
| 		goto ei2cga; | ||||
| 		return -ENODEV; | ||||
| 	} | ||||
| 
 | ||||
| 	shd->board_info->platform_data = &sdesc->subdev_desc; | ||||
| 
 | ||||
| 	snprintf(clk_name, sizeof(clk_name), "%d-%04x", | ||||
| 		 shd->i2c_adapter_id, shd->board_info->addr); | ||||
| 
 | ||||
| 	icd->clk = v4l2_clk_register(&soc_camera_clk_ops, clk_name, "mclk", icd); | ||||
| 	if (IS_ERR(icd->clk)) { | ||||
| 		ret = PTR_ERR(icd->clk); | ||||
| 		goto eclkreg; | ||||
| 	} | ||||
| 
 | ||||
| 	subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap, | ||||
| 				shd->board_info, NULL); | ||||
| 	if (!subdev) | ||||
| 	if (!subdev) { | ||||
| 		ret = -ENODEV; | ||||
| 		goto ei2cnd; | ||||
| 	} | ||||
| 
 | ||||
| 	client = v4l2_get_subdevdata(subdev); | ||||
| 
 | ||||
|  | @ -1124,9 +1211,11 @@ static int soc_camera_init_i2c(struct soc_camera_device *icd, | |||
| 
 | ||||
| 	return 0; | ||||
| ei2cnd: | ||||
| 	v4l2_clk_unregister(icd->clk); | ||||
| 	icd->clk = NULL; | ||||
| eclkreg: | ||||
| 	i2c_put_adapter(adap); | ||||
| ei2cga: | ||||
| 	return -ENODEV; | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static void soc_camera_free_i2c(struct soc_camera_device *icd) | ||||
|  | @ -1139,6 +1228,8 @@ static void soc_camera_free_i2c(struct soc_camera_device *icd) | |||
| 	v4l2_device_unregister_subdev(i2c_get_clientdata(client)); | ||||
| 	i2c_unregister_device(client); | ||||
| 	i2c_put_adapter(adap); | ||||
| 	v4l2_clk_unregister(icd->clk); | ||||
| 	icd->clk = NULL; | ||||
| } | ||||
| #else | ||||
| #define soc_camera_init_i2c(icd, sdesc)	(-ENODEV) | ||||
|  | @ -1176,26 +1267,31 @@ static int soc_camera_probe(struct soc_camera_device *icd) | |||
| 	if (ssdd->reset) | ||||
| 		ssdd->reset(icd->pdev); | ||||
| 
 | ||||
| 	mutex_lock(&ici->host_lock); | ||||
| 	ret = ici->ops->clock_start(ici); | ||||
| 	mutex_unlock(&ici->host_lock); | ||||
| 	if (ret < 0) | ||||
| 		goto eadd; | ||||
| 
 | ||||
| 	/* Must have icd->vdev before registering the device */ | ||||
| 	ret = video_dev_create(icd); | ||||
| 	if (ret < 0) | ||||
| 		goto evdc; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * ..._video_start() will create a device node, video_register_device() | ||||
| 	 * itself is protected against concurrent open() calls, but we also have | ||||
| 	 * to protect our data also during client probing. | ||||
| 	 */ | ||||
| 	mutex_lock(&ici->host_lock); | ||||
| 
 | ||||
| 	/* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */ | ||||
| 	if (shd->board_info) { | ||||
| 		ret = soc_camera_init_i2c(icd, sdesc); | ||||
| 		if (ret < 0) | ||||
| 			goto eadddev; | ||||
| 			goto eadd; | ||||
| 	} else if (!shd->add_device || !shd->del_device) { | ||||
| 		ret = -EINVAL; | ||||
| 		goto eadddev; | ||||
| 		goto eadd; | ||||
| 	} else { | ||||
| 		ret = ici->ops->clock_start(ici); | ||||
| 		if (ret < 0) | ||||
| 			goto eadd; | ||||
| 
 | ||||
| 		if (shd->module_name) | ||||
| 			ret = request_module(shd->module_name); | ||||
| 
 | ||||
|  | @ -1231,13 +1327,6 @@ static int soc_camera_probe(struct soc_camera_device *icd) | |||
| 
 | ||||
| 	icd->field = V4L2_FIELD_ANY; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * ..._video_start() will create a device node, video_register_device() | ||||
| 	 * itself is protected against concurrent open() calls, but we also have | ||||
| 	 * to protect our data. | ||||
| 	 */ | ||||
| 	mutex_lock(&ici->host_lock); | ||||
| 
 | ||||
| 	ret = soc_camera_video_start(icd); | ||||
| 	if (ret < 0) | ||||
| 		goto evidstart; | ||||
|  | @ -1250,14 +1339,14 @@ static int soc_camera_probe(struct soc_camera_device *icd) | |||
| 		icd->field		= mf.field; | ||||
| 	} | ||||
| 
 | ||||
| 	ici->ops->clock_stop(ici); | ||||
| 	if (!shd->board_info) | ||||
| 		ici->ops->clock_stop(ici); | ||||
| 
 | ||||
| 	mutex_unlock(&ici->host_lock); | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| evidstart: | ||||
| 	mutex_unlock(&ici->host_lock); | ||||
| 	soc_camera_free_user_formats(icd); | ||||
| eiufmt: | ||||
| ectrl: | ||||
|  | @ -1266,16 +1355,15 @@ static int soc_camera_probe(struct soc_camera_device *icd) | |||
| 	} else { | ||||
| 		shd->del_device(icd); | ||||
| 		module_put(control->driver->owner); | ||||
| 	} | ||||
| enodrv: | ||||
| eadddev: | ||||
| 		ici->ops->clock_stop(ici); | ||||
| 	} | ||||
| eadd: | ||||
| 	video_device_release(icd->vdev); | ||||
| 	icd->vdev = NULL; | ||||
| evdc: | ||||
| 	mutex_lock(&ici->host_lock); | ||||
| 	ici->ops->clock_stop(ici); | ||||
| 	mutex_unlock(&ici->host_lock); | ||||
| eadd: | ||||
| evdc: | ||||
| 	v4l2_ctrl_handler_free(&icd->ctrl_handler); | ||||
| 	return ret; | ||||
| } | ||||
|  |  | |||
|  | @ -54,7 +54,7 @@ static int soc_camera_platform_s_power(struct v4l2_subdev *sd, int on) | |||
| { | ||||
| 	struct soc_camera_platform_info *p = v4l2_get_subdevdata(sd); | ||||
| 
 | ||||
| 	return soc_camera_set_power(p->icd->control, &p->icd->sdesc->subdev_desc, on); | ||||
| 	return soc_camera_set_power(p->icd->control, &p->icd->sdesc->subdev_desc, NULL, on); | ||||
| } | ||||
| 
 | ||||
| static struct v4l2_subdev_core_ops platform_subdev_core_ops = { | ||||
|  |  | |||
|  | @ -49,6 +49,7 @@ struct soc_camera_device { | |||
| 	/* soc_camera.c private count. Only accessed with .host_lock held */ | ||||
| 	int use_count; | ||||
| 	struct file *streamer;		/* stream owner */ | ||||
| 	struct v4l2_clk *clk; | ||||
| 	union { | ||||
| 		struct videobuf_queue vb_vidq; | ||||
| 		struct vb2_queue vb2_vidq; | ||||
|  | @ -325,14 +326,16 @@ static inline void soc_camera_limit_side(int *start, int *length, | |||
| unsigned long soc_camera_apply_board_flags(struct soc_camera_subdev_desc *ssdd, | ||||
| 					   const struct v4l2_mbus_config *cfg); | ||||
| 
 | ||||
| int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd); | ||||
| int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd); | ||||
| int soc_camera_power_on(struct device *dev, struct soc_camera_subdev_desc *ssdd, | ||||
| 			struct v4l2_clk *clk); | ||||
| int soc_camera_power_off(struct device *dev, struct soc_camera_subdev_desc *ssdd, | ||||
| 			 struct v4l2_clk *clk); | ||||
| 
 | ||||
| static inline int soc_camera_set_power(struct device *dev, | ||||
| 				struct soc_camera_subdev_desc *ssdd, bool on) | ||||
| 		struct soc_camera_subdev_desc *ssdd, struct v4l2_clk *clk, bool on) | ||||
| { | ||||
| 	return on ? soc_camera_power_on(dev, ssdd) | ||||
| 		  : soc_camera_power_off(dev, ssdd); | ||||
| 	return on ? soc_camera_power_on(dev, ssdd, clk) | ||||
| 		  : soc_camera_power_off(dev, ssdd, clk); | ||||
| } | ||||
| 
 | ||||
| /* This is only temporary here - until v4l2-subdev begins to link to video_device */ | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Guennadi Liakhovetski
						Guennadi Liakhovetski