#include <linux/module.h>
#include <media/soc_camera.h>
+#include <media/v4l2-clk.h>
#include <media/v4l2-subdev.h>
/* IMX074 registers */
struct imx074 {
struct v4l2_subdev subdev;
const struct imx074_datafmt *fmt;
+ struct v4l2_clk *clk;
};
static const struct imx074_datafmt imx074_colour_fmts[] = {
{
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,
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");
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);
#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>
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;
{
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)
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;
}
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);
#include <linux/module.h>
#include <media/soc_camera.h>
+#include <media/v4l2-clk.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
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 */
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;
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)
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;
}
{
struct mt9m111 *mt9m111 = to_mt9m111(client);
+ v4l2_clk_put(mt9m111->clk);
v4l2_device_unregister_subdev(&mt9m111->subdev);
v4l2_ctrl_handler_free(&mt9m111->hdl);
#include <linux/module.h>
#include <media/soc_camera.h>
+#include <media/v4l2-clk.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-ctrls.h>
struct v4l2_ctrl *exposure;
};
struct v4l2_rect rect; /* Sensor window */
+ struct v4l2_clk *clk;
u16 xskip;
u16 yskip;
unsigned int total_h;
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;
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;
}
{
struct mt9t031 *mt9t031 = to_mt9t031(client);
+ v4l2_clk_put(mt9t031->clk);
v4l2_device_unregister_subdev(&mt9t031->subdev);
v4l2_ctrl_handler_free(&mt9t031->hdl);
#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 */
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;
{
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 = {
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 },
{ }
.name = "mt9t112",
},
.probe = mt9t112_probe,
+ .remove = mt9t112_remove,
.id_table = mt9t112_id,
};
#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>
/*
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;
{
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)
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;
}
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);
#include <linux/videodev2.h>
#include <media/soc_camera.h>
+#include <media/v4l2-clk.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-ctrls.h>
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;
};
{
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 */
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;
}
{
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;
#include <linux/v4l2-mediabus.h>
#include <media/soc_camera.h>
+#include <media/v4l2-clk.h>
#include <media/v4l2-subdev.h>
/* OV5642 registers */
struct v4l2_subdev subdev;
const struct ov5642_datafmt *fmt;
struct v4l2_rect crop_rect;
+ struct v4l2_clk *clk;
/* blanking information */
int total_width;
{
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;
{
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");
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);
#include <linux/module.h>
#include <media/soc_camera.h>
+#include <media/v4l2-clk.h>
#include <media/v4l2-ctrls.h>
/* Register definitions */
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 */
{
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)
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;
}
{
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;
#include <media/ov772x.h>
#include <media/soc_camera.h>
+#include <media/v4l2-clk.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-subdev.h>
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;
{
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)
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;
}
{
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;
#include <linux/videodev2.h>
#include <media/soc_camera.h>
+#include <media/v4l2-clk.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ctrls.h>
{
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 */
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;
}
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;
struct ov9640_priv {
struct v4l2_subdev subdev;
struct v4l2_ctrl_handler hdl;
+ struct v4l2_clk *clk;
int model;
int revision;
#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)
struct ov9740_priv {
struct v4l2_subdev subdev;
struct v4l2_ctrl_handler hdl;
+ struct v4l2_clk *clk;
u16 model;
u8 revision;
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;
priv->current_enable = true;
}
- soc_camera_power_off(&client->dev, ssdd);
+ soc_camera_power_off(&client->dev, ssdd, priv->clk);
}
return 0;
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;
}
{
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;
#include <media/rj54n1cb0c.h>
#include <media/soc_camera.h>
+#include <media/v4l2-clk.h>
#include <media/v4l2-subdev.h>
#include <media/v4l2-ctrls.h>
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 */
{
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)
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;
}
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);
#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)
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;
{
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)
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");
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[] = {
.name = "tw9910",
},
.probe = tw9910_probe,
+ .remove = tw9910_remove,
.id_table = tw9910_id,
};
#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>
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) {
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;
ret = ret ? : err;
}
+ if (clk)
+ v4l2_clk_disable(clk);
+
return ret;
}
EXPORT_SYMBOL(soc_camera_power_off);
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);
return 0;
eadd:
- ici->ops->clock_stop(ici);
+ if (!icd->clk)
+ ici->ops->clock_stop(ici);
return ret;
}
if (ici->ops->remove)
ici->ops->remove(icd);
- ici->ops->clock_stop(ici);
+ if (!icd->clk)
+ ici->ops->clock_stop(ici);
ici->icd = NULL;
}
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)
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);
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)
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)
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);
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;
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:
} 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;
}
{
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 = {
/* 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;
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 */