summaryrefslogtreecommitdiffstats
path: root/drivers/media/i2c/ov2740.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/i2c/ov2740.c')
-rw-r--r--drivers/media/i2c/ov2740.c214
1 files changed, 135 insertions, 79 deletions
diff --git a/drivers/media/i2c/ov2740.c b/drivers/media/i2c/ov2740.c
index bd0d45b0d43f..b41a90c2aed5 100644
--- a/drivers/media/i2c/ov2740.c
+++ b/drivers/media/i2c/ov2740.c
@@ -37,7 +37,7 @@
/* Exposure controls from sensor */
#define OV2740_REG_EXPOSURE 0x3500
-#define OV2740_EXPOSURE_MIN 8
+#define OV2740_EXPOSURE_MIN 4
#define OV2740_EXPOSURE_MAX_MARGIN 8
#define OV2740_EXPOSURE_STEP 1
@@ -71,9 +71,10 @@
#define OV2740_REG_OTP_CUSTOMER 0x7010
struct nvm_data {
- char *nvm_buffer;
+ struct i2c_client *client;
struct nvmem_device *nvmem;
struct regmap *regmap;
+ char *nvm_buffer;
};
enum {
@@ -335,6 +336,9 @@ struct ov2740 {
/* Streaming on/off */
bool streaming;
+
+ /* NVM data inforamtion */
+ struct nvm_data *nvm;
};
static inline struct ov2740 *to_ov2740(struct v4l2_subdev *subdev)
@@ -594,13 +598,112 @@ static void ov2740_update_pad_format(const struct ov2740_mode *mode,
fmt->field = V4L2_FIELD_NONE;
}
+static int ov2740_load_otp_data(struct nvm_data *nvm)
+{
+ struct i2c_client *client;
+ struct ov2740 *ov2740;
+ u32 isp_ctrl00 = 0;
+ u32 isp_ctrl01 = 0;
+ int ret;
+
+ if (!nvm)
+ return -EINVAL;
+
+ if (nvm->nvm_buffer)
+ return 0;
+
+ client = nvm->client;
+ ov2740 = to_ov2740(i2c_get_clientdata(client));
+
+ nvm->nvm_buffer = kzalloc(CUSTOMER_USE_OTP_SIZE, GFP_KERNEL);
+ if (!nvm->nvm_buffer)
+ return -ENOMEM;
+
+ ret = ov2740_read_reg(ov2740, OV2740_REG_ISP_CTRL00, 1, &isp_ctrl00);
+ if (ret) {
+ dev_err(&client->dev, "failed to read ISP CTRL00\n");
+ goto err;
+ }
+
+ ret = ov2740_read_reg(ov2740, OV2740_REG_ISP_CTRL01, 1, &isp_ctrl01);
+ if (ret) {
+ dev_err(&client->dev, "failed to read ISP CTRL01\n");
+ goto err;
+ }
+
+ /* Clear bit 5 of ISP CTRL00 */
+ ret = ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL00, 1,
+ isp_ctrl00 & ~BIT(5));
+ if (ret) {
+ dev_err(&client->dev, "failed to set ISP CTRL00\n");
+ goto err;
+ }
+
+ /* Clear bit 7 of ISP CTRL01 */
+ ret = ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL01, 1,
+ isp_ctrl01 & ~BIT(7));
+ if (ret) {
+ dev_err(&client->dev, "failed to set ISP CTRL01\n");
+ goto err;
+ }
+
+ ret = ov2740_write_reg(ov2740, OV2740_REG_MODE_SELECT, 1,
+ OV2740_MODE_STREAMING);
+ if (ret) {
+ dev_err(&client->dev, "failed to set streaming mode\n");
+ goto err;
+ }
+
+ /*
+ * Users are not allowed to access OTP-related registers and memory
+ * during the 20 ms period after streaming starts (0x100 = 0x01).
+ */
+ msleep(20);
+
+ ret = regmap_bulk_read(nvm->regmap, OV2740_REG_OTP_CUSTOMER,
+ nvm->nvm_buffer, CUSTOMER_USE_OTP_SIZE);
+ if (ret) {
+ dev_err(&client->dev, "failed to read OTP data, ret %d\n", ret);
+ goto err;
+ }
+
+ ret = ov2740_write_reg(ov2740, OV2740_REG_MODE_SELECT, 1,
+ OV2740_MODE_STANDBY);
+ if (ret) {
+ dev_err(&client->dev, "failed to set streaming mode\n");
+ goto err;
+ }
+
+ ret = ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL01, 1, isp_ctrl01);
+ if (ret) {
+ dev_err(&client->dev, "failed to set ISP CTRL01\n");
+ goto err;
+ }
+
+ ret = ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL00, 1, isp_ctrl00);
+ if (ret) {
+ dev_err(&client->dev, "failed to set ISP CTRL00\n");
+ goto err;
+ }
+
+ return 0;
+err:
+ kfree(nvm->nvm_buffer);
+ nvm->nvm_buffer = NULL;
+
+ return ret;
+}
+
static int ov2740_start_streaming(struct ov2740 *ov2740)
{
struct i2c_client *client = v4l2_get_subdevdata(&ov2740->sd);
+ struct nvm_data *nvm = ov2740->nvm;
const struct ov2740_reg_list *reg_list;
int link_freq_index;
int ret = 0;
+ ov2740_load_otp_data(nvm);
+
link_freq_index = ov2740->cur_mode->link_freq_index;
reg_list = &link_freq_configs[link_freq_index].reg_list;
ret = ov2740_write_reg_list(ov2740, reg_list);
@@ -674,8 +777,7 @@ static int ov2740_set_stream(struct v4l2_subdev *sd, int enable)
static int __maybe_unused ov2740_suspend(struct device *dev)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct ov2740 *ov2740 = to_ov2740(sd);
mutex_lock(&ov2740->mutex);
@@ -689,8 +791,7 @@ static int __maybe_unused ov2740_suspend(struct device *dev)
static int __maybe_unused ov2740_resume(struct device *dev)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct v4l2_subdev *sd = i2c_get_clientdata(client);
+ struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct ov2740 *ov2740 = to_ov2740(sd);
int ret = 0;
@@ -932,96 +1033,52 @@ static int ov2740_remove(struct i2c_client *client)
return 0;
}
-static int ov2740_load_otp_data(struct i2c_client *client, struct nvm_data *nvm)
+static int ov2740_nvmem_read(void *priv, unsigned int off, void *val,
+ size_t count)
{
- struct ov2740 *ov2740 = to_ov2740(i2c_get_clientdata(client));
- u32 isp_ctrl00 = 0;
- u32 isp_ctrl01 = 0;
- int ret;
-
- ret = ov2740_read_reg(ov2740, OV2740_REG_ISP_CTRL00, 1, &isp_ctrl00);
- if (ret) {
- dev_err(&client->dev, "failed to read ISP CTRL00\n");
- goto exit;
- }
- ret = ov2740_read_reg(ov2740, OV2740_REG_ISP_CTRL01, 1, &isp_ctrl01);
- if (ret) {
- dev_err(&client->dev, "failed to read ISP CTRL01\n");
- goto exit;
- }
-
- /* Clear bit 5 of ISP CTRL00 */
- ret = ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL00, 1,
- isp_ctrl00 & ~BIT(5));
- if (ret) {
- dev_err(&client->dev, "failed to write ISP CTRL00\n");
- goto exit;
- }
+ struct nvm_data *nvm = priv;
+ struct v4l2_subdev *sd = i2c_get_clientdata(nvm->client);
+ struct device *dev = &nvm->client->dev;
+ struct ov2740 *ov2740 = to_ov2740(sd);
+ int ret = 0;
- /* Clear bit 7 of ISP CTRL01 */
- ret = ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL01, 1,
- isp_ctrl01 & ~BIT(7));
- if (ret) {
- dev_err(&client->dev, "failed to write ISP CTRL01\n");
- goto exit;
- }
+ mutex_lock(&ov2740->mutex);
- ret = ov2740_write_reg(ov2740, OV2740_REG_MODE_SELECT, 1,
- OV2740_MODE_STREAMING);
- if (ret) {
- dev_err(&client->dev, "failed to start streaming\n");
+ if (nvm->nvm_buffer) {
+ memcpy(val, nvm->nvm_buffer + off, count);
goto exit;
}
- /*
- * Users are not allowed to access OTP-related registers and memory
- * during the 20 ms period after streaming starts (0x100 = 0x01).
- */
- msleep(20);
-
- ret = regmap_bulk_read(nvm->regmap, OV2740_REG_OTP_CUSTOMER,
- nvm->nvm_buffer, CUSTOMER_USE_OTP_SIZE);
- if (ret) {
- dev_err(&client->dev, "failed to read OTP data, ret %d\n", ret);
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(dev);
goto exit;
}
- ov2740_write_reg(ov2740, OV2740_REG_MODE_SELECT, 1,
- OV2740_MODE_STANDBY);
- ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL01, 1, isp_ctrl01);
- ov2740_write_reg(ov2740, OV2740_REG_ISP_CTRL00, 1, isp_ctrl00);
+ ret = ov2740_load_otp_data(nvm);
+ if (!ret)
+ memcpy(val, nvm->nvm_buffer + off, count);
+ pm_runtime_put(dev);
exit:
+ mutex_unlock(&ov2740->mutex);
return ret;
}
-static int ov2740_nvmem_read(void *priv, unsigned int off, void *val,
- size_t count)
-{
- struct nvm_data *nvm = priv;
-
- memcpy(val, nvm->nvm_buffer + off, count);
-
- return 0;
-}
-
-static int ov2740_register_nvmem(struct i2c_client *client)
+static int ov2740_register_nvmem(struct i2c_client *client,
+ struct ov2740 *ov2740)
{
struct nvm_data *nvm;
struct regmap_config regmap_config = { };
struct nvmem_config nvmem_config = { };
struct regmap *regmap;
struct device *dev = &client->dev;
- int ret = 0;
+ int ret;
nvm = devm_kzalloc(dev, sizeof(*nvm), GFP_KERNEL);
if (!nvm)
return -ENOMEM;
- nvm->nvm_buffer = devm_kzalloc(dev, CUSTOMER_USE_OTP_SIZE, GFP_KERNEL);
- if (!nvm->nvm_buffer)
- return -ENOMEM;
-
regmap_config.val_bits = 8;
regmap_config.reg_bits = 16;
regmap_config.disable_locking = true;
@@ -1030,12 +1087,7 @@ static int ov2740_register_nvmem(struct i2c_client *client)
return PTR_ERR(regmap);
nvm->regmap = regmap;
-
- ret = ov2740_load_otp_data(client, nvm);
- if (ret) {
- dev_err(dev, "failed to load OTP data, ret %d\n", ret);
- return ret;
- }
+ nvm->client = client;
nvmem_config.name = dev_name(dev);
nvmem_config.dev = dev;
@@ -1053,7 +1105,11 @@ static int ov2740_register_nvmem(struct i2c_client *client)
nvm->nvmem = devm_nvmem_register(dev, &nvmem_config);
- return PTR_ERR_OR_ZERO(nvm->nvmem);
+ ret = PTR_ERR_OR_ZERO(nvm->nvmem);
+ if (!ret)
+ ov2740->nvm = nvm;
+
+ return ret;
}
static int ov2740_probe(struct i2c_client *client)
@@ -1105,7 +1161,7 @@ static int ov2740_probe(struct i2c_client *client)
goto probe_error_media_entity_cleanup;
}
- ret = ov2740_register_nvmem(client);
+ ret = ov2740_register_nvmem(client, ov2740);
if (ret)
dev_warn(&client->dev, "register nvmem failed, ret %d\n", ret);