diff options
author | Josh Wu <josh.wu@atmel.com> | 2015-02-10 04:31:35 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@osg.samsung.com> | 2015-04-02 17:36:10 -0400 |
commit | 4e65172f7bd20fcbfa87453d1e5711ad129d4216 (patch) | |
tree | 9677ed62b6148c6d6c0ebd95034fecd9d8c96303 | |
parent | 87838addf6143430061823c334792185b56ede47 (diff) |
[media] media: ov2640: add primary dt support
Add device tree support for ov2640.
In device tree, user needs to provide the master clock (xvclk).
User can add the reset/pwdn pins if they have.
Cc: devicetree@vger.kernel.org
Signed-off-by: Josh Wu <josh.wu@atmel.com>
Acked-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
-rw-r--r-- | drivers/media/i2c/soc_camera/ov2640.c | 90 |
1 files changed, 83 insertions, 7 deletions
diff --git a/drivers/media/i2c/soc_camera/ov2640.c b/drivers/media/i2c/soc_camera/ov2640.c index 16adbccdfa6c..42d9ba356d6d 100644 --- a/drivers/media/i2c/soc_camera/ov2640.c +++ b/drivers/media/i2c/soc_camera/ov2640.c | |||
@@ -18,6 +18,8 @@ | |||
18 | #include <linux/i2c.h> | 18 | #include <linux/i2c.h> |
19 | #include <linux/slab.h> | 19 | #include <linux/slab.h> |
20 | #include <linux/delay.h> | 20 | #include <linux/delay.h> |
21 | #include <linux/gpio.h> | ||
22 | #include <linux/of_gpio.h> | ||
21 | #include <linux/v4l2-mediabus.h> | 23 | #include <linux/v4l2-mediabus.h> |
22 | #include <linux/videodev2.h> | 24 | #include <linux/videodev2.h> |
23 | 25 | ||
@@ -283,6 +285,10 @@ struct ov2640_priv { | |||
283 | u32 cfmt_code; | 285 | u32 cfmt_code; |
284 | struct v4l2_clk *clk; | 286 | struct v4l2_clk *clk; |
285 | const struct ov2640_win_size *win; | 287 | const struct ov2640_win_size *win; |
288 | |||
289 | struct soc_camera_subdev_desc ssdd_dt; | ||
290 | struct gpio_desc *resetb_gpio; | ||
291 | struct gpio_desc *pwdn_gpio; | ||
286 | }; | 292 | }; |
287 | 293 | ||
288 | /* | 294 | /* |
@@ -1038,6 +1044,63 @@ static struct v4l2_subdev_ops ov2640_subdev_ops = { | |||
1038 | .video = &ov2640_subdev_video_ops, | 1044 | .video = &ov2640_subdev_video_ops, |
1039 | }; | 1045 | }; |
1040 | 1046 | ||
1047 | /* OF probe functions */ | ||
1048 | static int ov2640_hw_power(struct device *dev, int on) | ||
1049 | { | ||
1050 | struct i2c_client *client = to_i2c_client(dev); | ||
1051 | struct ov2640_priv *priv = to_ov2640(client); | ||
1052 | |||
1053 | dev_dbg(&client->dev, "%s: %s the camera\n", | ||
1054 | __func__, on ? "ENABLE" : "DISABLE"); | ||
1055 | |||
1056 | if (priv->pwdn_gpio) | ||
1057 | gpiod_direction_output(priv->pwdn_gpio, !on); | ||
1058 | |||
1059 | return 0; | ||
1060 | } | ||
1061 | |||
1062 | static int ov2640_hw_reset(struct device *dev) | ||
1063 | { | ||
1064 | struct i2c_client *client = to_i2c_client(dev); | ||
1065 | struct ov2640_priv *priv = to_ov2640(client); | ||
1066 | |||
1067 | if (priv->resetb_gpio) { | ||
1068 | /* Active the resetb pin to perform a reset pulse */ | ||
1069 | gpiod_direction_output(priv->resetb_gpio, 1); | ||
1070 | usleep_range(3000, 5000); | ||
1071 | gpiod_direction_output(priv->resetb_gpio, 0); | ||
1072 | } | ||
1073 | |||
1074 | return 0; | ||
1075 | } | ||
1076 | |||
1077 | static int ov2640_probe_dt(struct i2c_client *client, | ||
1078 | struct ov2640_priv *priv) | ||
1079 | { | ||
1080 | /* Request the reset GPIO deasserted */ | ||
1081 | priv->resetb_gpio = devm_gpiod_get_optional(&client->dev, "resetb", | ||
1082 | GPIOD_OUT_LOW); | ||
1083 | if (!priv->resetb_gpio) | ||
1084 | dev_dbg(&client->dev, "resetb gpio is not assigned!\n"); | ||
1085 | else if (IS_ERR(priv->resetb_gpio)) | ||
1086 | return PTR_ERR(priv->resetb_gpio); | ||
1087 | |||
1088 | /* Request the power down GPIO asserted */ | ||
1089 | priv->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "pwdn", | ||
1090 | GPIOD_OUT_HIGH); | ||
1091 | if (!priv->pwdn_gpio) | ||
1092 | dev_dbg(&client->dev, "pwdn gpio is not assigned!\n"); | ||
1093 | else if (IS_ERR(priv->pwdn_gpio)) | ||
1094 | return PTR_ERR(priv->pwdn_gpio); | ||
1095 | |||
1096 | /* Initialize the soc_camera_subdev_desc */ | ||
1097 | priv->ssdd_dt.power = ov2640_hw_power; | ||
1098 | priv->ssdd_dt.reset = ov2640_hw_reset; | ||
1099 | client->dev.platform_data = &priv->ssdd_dt; | ||
1100 | |||
1101 | return 0; | ||
1102 | } | ||
1103 | |||
1041 | /* | 1104 | /* |
1042 | * i2c_driver functions | 1105 | * i2c_driver functions |
1043 | */ | 1106 | */ |
@@ -1049,12 +1112,6 @@ static int ov2640_probe(struct i2c_client *client, | |||
1049 | struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); | 1112 | struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); |
1050 | int ret; | 1113 | int ret; |
1051 | 1114 | ||
1052 | if (!ssdd) { | ||
1053 | dev_err(&adapter->dev, | ||
1054 | "OV2640: Missing platform_data for driver\n"); | ||
1055 | return -EINVAL; | ||
1056 | } | ||
1057 | |||
1058 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { | 1115 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { |
1059 | dev_err(&adapter->dev, | 1116 | dev_err(&adapter->dev, |
1060 | "OV2640: I2C-Adapter doesn't support SMBUS\n"); | 1117 | "OV2640: I2C-Adapter doesn't support SMBUS\n"); |
@@ -1068,10 +1125,22 @@ static int ov2640_probe(struct i2c_client *client, | |||
1068 | return -ENOMEM; | 1125 | return -ENOMEM; |
1069 | } | 1126 | } |
1070 | 1127 | ||
1071 | priv->clk = v4l2_clk_get(&client->dev, "mclk"); | 1128 | priv->clk = v4l2_clk_get(&client->dev, "xvclk"); |
1072 | if (IS_ERR(priv->clk)) | 1129 | if (IS_ERR(priv->clk)) |
1073 | return -EPROBE_DEFER; | 1130 | return -EPROBE_DEFER; |
1074 | 1131 | ||
1132 | if (!ssdd && !client->dev.of_node) { | ||
1133 | dev_err(&client->dev, "Missing platform_data for driver\n"); | ||
1134 | ret = -EINVAL; | ||
1135 | goto err_clk; | ||
1136 | } | ||
1137 | |||
1138 | if (!ssdd) { | ||
1139 | ret = ov2640_probe_dt(client, priv); | ||
1140 | if (ret) | ||
1141 | goto err_clk; | ||
1142 | } | ||
1143 | |||
1075 | v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops); | 1144 | v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops); |
1076 | v4l2_ctrl_handler_init(&priv->hdl, 2); | 1145 | v4l2_ctrl_handler_init(&priv->hdl, 2); |
1077 | v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops, | 1146 | v4l2_ctrl_new_std(&priv->hdl, &ov2640_ctrl_ops, |
@@ -1120,9 +1189,16 @@ static const struct i2c_device_id ov2640_id[] = { | |||
1120 | }; | 1189 | }; |
1121 | MODULE_DEVICE_TABLE(i2c, ov2640_id); | 1190 | MODULE_DEVICE_TABLE(i2c, ov2640_id); |
1122 | 1191 | ||
1192 | static const struct of_device_id ov2640_of_match[] = { | ||
1193 | {.compatible = "ovti,ov2640", }, | ||
1194 | {}, | ||
1195 | }; | ||
1196 | MODULE_DEVICE_TABLE(of, ov2640_of_match); | ||
1197 | |||
1123 | static struct i2c_driver ov2640_i2c_driver = { | 1198 | static struct i2c_driver ov2640_i2c_driver = { |
1124 | .driver = { | 1199 | .driver = { |
1125 | .name = "ov2640", | 1200 | .name = "ov2640", |
1201 | .of_match_table = of_match_ptr(ov2640_of_match), | ||
1126 | }, | 1202 | }, |
1127 | .probe = ov2640_probe, | 1203 | .probe = ov2640_probe, |
1128 | .remove = ov2640_remove, | 1204 | .remove = ov2640_remove, |