From 0cdfbc8e6b3b8b89874f688bd5f856a382831e40 Mon Sep 17 00:00:00 2001 From: Zefa Chen Date: Sat, 21 Jun 2025 11:48:23 +0800 Subject: [PATCH 01/28] media: i2c: ox03c10 skip group hold before streaming use group hold before streaming will make register effect delay one frmae Change-Id: Ia28b9981d38e3fe5132a62da277495e61c7dd052 Signed-off-by: Zefa Chen --- drivers/media/i2c/ox03c10.c | 65 ++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/drivers/media/i2c/ox03c10.c b/drivers/media/i2c/ox03c10.c index 22068f144e43..da19d4a111e2 100644 --- a/drivers/media/i2c/ox03c10.c +++ b/drivers/media/i2c/ox03c10.c @@ -5399,9 +5399,10 @@ static int ox03c10_set_hdrae(struct ox03c10 *ox03c10, dev_dbg(&ox03c10->client->dev, "l_again 0x%x l_dgain 0x%x, m_again 0x%x m_dgain 0x%x, s_again 0x%x s_dgain 0x%x\n", l_again, l_dgain, m_again, m_dgain, s_again, s_dgain); - ret |= ox03c10_write_reg(ox03c10->client, OX03C10_GROUP_UPDATE_ADDRESS, - OX03C10_REG_VALUE_08BIT, - OX03C10_GROUP_UPDATE_START_DATA); + if (ox03c10->streaming) + ret |= ox03c10_write_reg(ox03c10->client, OX03C10_GROUP_UPDATE_ADDRESS, + OX03C10_REG_VALUE_08BIT, + OX03C10_GROUP_UPDATE_START_DATA); // dcg exposure ret |= ox03c10_write_reg(ox03c10->client, OX03C10_REG_EXPOSURE_DCG_H, OX03C10_REG_VALUE_16BIT, @@ -5507,12 +5508,14 @@ static int ox03c10_set_hdrae(struct ox03c10 *ox03c10, (s_dgain << 6) & 0xc0); } - ret |= ox03c10_write_reg(ox03c10->client, OX03C10_GROUP_UPDATE_ADDRESS, - OX03C10_REG_VALUE_08BIT, - OX03C10_GROUP_UPDATE_END_DATA); - ret |= ox03c10_write_reg(ox03c10->client, OX03C10_GROUP_UPDATE_ADDRESS, - OX03C10_REG_VALUE_08BIT, - OX03C10_GROUP_UPDATE_LAUNCH); + if (ox03c10->streaming) { + ret |= ox03c10_write_reg(ox03c10->client, OX03C10_GROUP_UPDATE_ADDRESS, + OX03C10_REG_VALUE_08BIT, + OX03C10_GROUP_UPDATE_END_DATA); + ret |= ox03c10_write_reg(ox03c10->client, OX03C10_GROUP_UPDATE_ADDRESS, + OX03C10_REG_VALUE_08BIT, + OX03C10_GROUP_UPDATE_LAUNCH); + } return ret; } @@ -6421,19 +6424,22 @@ static int ox03c10_set_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_EXPOSURE: if (ox03c10->cur_mode->hdr_mode != NO_HDR) break; - ret |= ox03c10_write_reg(ox03c10->client, OX03C10_GROUP_UPDATE_ADDRESS, - OX03C10_REG_VALUE_08BIT, - OX03C10_GROUP_UPDATE_START_DATA); + if (ox03c10->streaming) + ret |= ox03c10_write_reg(ox03c10->client, OX03C10_GROUP_UPDATE_ADDRESS, + OX03C10_REG_VALUE_08BIT, + OX03C10_GROUP_UPDATE_START_DATA); ret |= ox03c10_write_reg(ox03c10->client, OX03C10_REG_EXPOSURE_DCG_H, OX03C10_REG_VALUE_16BIT, ctrl->val); - ret |= ox03c10_write_reg(ox03c10->client, OX03C10_GROUP_UPDATE_ADDRESS, - OX03C10_REG_VALUE_08BIT, - OX03C10_GROUP_UPDATE_END_DATA); - ret |= ox03c10_write_reg(ox03c10->client, OX03C10_GROUP_UPDATE_ADDRESS, - OX03C10_REG_VALUE_08BIT, - OX03C10_GROUP_UPDATE_LAUNCH); + if (ox03c10->streaming) { + ret |= ox03c10_write_reg(ox03c10->client, OX03C10_GROUP_UPDATE_ADDRESS, + OX03C10_REG_VALUE_08BIT, + OX03C10_GROUP_UPDATE_END_DATA); + ret |= ox03c10_write_reg(ox03c10->client, OX03C10_GROUP_UPDATE_ADDRESS, + OX03C10_REG_VALUE_08BIT, + OX03C10_GROUP_UPDATE_LAUNCH); + } break; case V4L2_CID_ANALOGUE_GAIN: if (ox03c10->cur_mode->hdr_mode != NO_HDR) @@ -6464,9 +6470,10 @@ static int ox03c10_set_ctrl(struct v4l2_ctrl *ctrl) __func__, ctrl->val); break; } - ret |= ox03c10_write_reg(ox03c10->client, OX03C10_GROUP_UPDATE_ADDRESS, - OX03C10_REG_VALUE_08BIT, - OX03C10_GROUP1_UPDATE_START_DATA); + if (ox03c10->streaming) + ret |= ox03c10_write_reg(ox03c10->client, OX03C10_GROUP_UPDATE_ADDRESS, + OX03C10_REG_VALUE_08BIT, + OX03C10_GROUP1_UPDATE_START_DATA); // lcg real gain ret |= ox03c10_write_reg(ox03c10->client, @@ -6478,14 +6485,14 @@ static int ox03c10_set_ctrl(struct v4l2_ctrl *ctrl) OX03C10_REG_DGAIN_LCG_H, OX03C10_REG_VALUE_24BIT, (dgain << 6) & 0xfffc0); - - ret |= ox03c10_write_reg(ox03c10->client, OX03C10_GROUP_UPDATE_ADDRESS, - OX03C10_REG_VALUE_08BIT, - OX03C10_GROUP1_UPDATE_END_DATA); - ret |= ox03c10_write_reg(ox03c10->client, OX03C10_GROUP_UPDATE_ADDRESS, - OX03C10_REG_VALUE_08BIT, - OX03C10_GROUP1_UPDATE_LAUNCH); - + if (ox03c10->streaming) { + ret |= ox03c10_write_reg(ox03c10->client, OX03C10_GROUP_UPDATE_ADDRESS, + OX03C10_REG_VALUE_08BIT, + OX03C10_GROUP1_UPDATE_END_DATA); + ret |= ox03c10_write_reg(ox03c10->client, OX03C10_GROUP_UPDATE_ADDRESS, + OX03C10_REG_VALUE_08BIT, + OX03C10_GROUP1_UPDATE_LAUNCH); + } dev_err(&client->dev, "%s set gain val:0x%x ret:%d again:0x%x, dgain:0x%x", __func__, ctrl->val, ret, again, dgain); From a0cd643d5638350be059db25e9e2a677a2477d34 Mon Sep 17 00:00:00 2001 From: Zefa Chen Date: Sat, 21 Jun 2025 16:43:40 +0800 Subject: [PATCH 02/28] include: uapi: rk-camera-module.h add RKMODULE_SET_EXPAND_SINGLE_MODE Change-Id: I93da3cdabee3a9b5465c70aa7e80f4854c1316b8 Signed-off-by: Zefa Chen --- include/uapi/linux/rk-camera-module.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/uapi/linux/rk-camera-module.h b/include/uapi/linux/rk-camera-module.h index d1c5a63b0bad..77944de3b9e9 100644 --- a/include/uapi/linux/rk-camera-module.h +++ b/include/uapi/linux/rk-camera-module.h @@ -232,6 +232,9 @@ #define RKMODULE_GET_ERROR_INFO \ _IOR('V', BASE_VIDIOC_PRIVATE + 56, struct rkmodule_error_info) +#define RKMODULE_SET_EXPAND_SINGLE_MODE \ + _IOW('V', BASE_VIDIOC_PRIVATE + 57, __u32) + struct rkmodule_i2cdev_info { __u8 slave_addr; } __attribute__ ((packed)); @@ -995,4 +998,12 @@ struct rkmodule_error_info { __u8 detail[256]; }; +enum rkmodule_expand_single_mode { + EXPAND_SINGLE_LCG, + EXPAND_SINGLE_HCG, + EXPAND_SINGLE_VS, + EXPAND_SINGLE_SPD, + EXPAND_SINGLE_LOFIC, +}; + #endif /* _UAPI_RKMODULE_CAMERA_H */ From b7f90ab741c704cb44640bb9fc23f72c8693bf6c Mon Sep 17 00:00:00 2001 From: Zefa Chen Date: Sat, 21 Jun 2025 16:44:19 +0800 Subject: [PATCH 03/28] media: i2c: ox03c10 support linear mode of lcg/hcg/vs/spd Change-Id: I9b3cd6330cb16fba6b951cf806badf2ab5a29a18 Signed-off-by: Zefa Chen --- drivers/media/i2c/ox03c10.c | 1291 ++++++----------------------------- 1 file changed, 213 insertions(+), 1078 deletions(-) diff --git a/drivers/media/i2c/ox03c10.c b/drivers/media/i2c/ox03c10.c index da19d4a111e2..02d4c8b75f1f 100644 --- a/drivers/media/i2c/ox03c10.c +++ b/drivers/media/i2c/ox03c10.c @@ -183,12 +183,14 @@ struct ox03c10_mode { u32 bpp; u32 mipi_freq_idx; const struct regval *reg_list; + const struct regval *linear_reg_list; u32 hdr_mode; struct rkmodule_hdr_compr *hdr_compr; u32 hdr_operating_mode; u32 hdr_ratio; u32 vc[PAD_MAX]; u32 exp_mode; + u32 single_mode; }; struct ox03c10 { @@ -3830,1076 +3832,7 @@ static const struct regval ox03c10_1920x1080_30fps_HDR3_LFM_PWL16_mipi996[] = { {REG_NULL, 0xff}, }; -static const struct regval ox03c10_1920x1080_30fps_linear_raw10_mipi996[] = { - {0x0103, 0x01}, - {0x0107, 0x01}, - {0x4d5a, 0x1c}, - {0x4d09, 0xff}, - {0x4d09, 0xdf}, - {0x3208, 0x04}, - {0x4620, 0x04}, - {0x3208, 0x14}, - {0x3208, 0x05}, - {0x4620, 0x04}, - {0x3208, 0x15}, - {0x3208, 0x02}, - {0x3507, 0x00}, - {0x3208, 0x12}, - {0x3208, 0xa2}, - {0x0301, 0xc8}, - {0x0303, 0x01}, - {0x0304, 0x01}, - {0x0305, 0x2c}, - {0x0306, 0x04}, - {0x0307, 0x01}, - {0x0316, 0x00}, - {0x0317, 0x00}, - {0x0318, 0x00}, - {0x0323, 0x05}, - {0x0324, 0x01}, - {0x0325, 0x2c}, - {0x0400, 0xe0}, - {0x0401, 0x80}, - {0x0403, 0xde}, - {0x0404, 0x34}, - {0x0405, 0x3b}, - {0x0406, 0xde}, - {0x0407, 0x08}, - {0x0408, 0xe0}, - {0x0409, 0x7f}, - {0x040a, 0xde}, - {0x040b, 0x34}, - {0x040c, 0x47}, - {0x040d, 0xd8}, - {0x040e, 0x08}, - {0x2803, 0xfe}, - {0x280b, 0x00}, - {0x280c, 0x79}, - {0x3001, 0x03}, - {0x3002, 0xf8}, - {0x3005, 0x80}, - {0x3007, 0x01}, - {0x3008, 0x80}, - {0x3012, 0x41}, - {0x3020, 0x05}, - {0x3700, 0x28}, - {0x3701, 0x15}, - {0x3702, 0x19}, - {0x3703, 0x23}, - {0x3704, 0x0a}, - {0x3705, 0x00}, - {0x3706, 0x3e}, - {0x3707, 0x0d}, - {0x3708, 0x50}, - {0x3709, 0x5a}, - {0x370a, 0x00}, - {0x370b, 0x96}, - {0x3711, 0x11}, - {0x3712, 0x13}, - {0x3717, 0x02}, - {0x3718, 0x73}, - {0x372c, 0x40}, - {0x3733, 0x01}, - {0x3738, 0x36}, - {0x3739, 0x36}, - {0x373a, 0x25}, - {0x373b, 0x25}, - {0x373f, 0x21}, - {0x3740, 0x21}, - {0x3741, 0x21}, - {0x3742, 0x21}, - {0x3747, 0x28}, - {0x3748, 0x28}, - {0x3749, 0x19}, - {0x3755, 0x1a}, - {0x3756, 0x0a}, - {0x3757, 0x1c}, - {0x3765, 0x19}, - {0x3766, 0x05}, - {0x3767, 0x05}, - {0x3768, 0x13}, - {0x376c, 0x07}, - {0x3778, 0x20}, - {0x377c, 0xc8}, - {0x3781, 0x02}, - {0x3783, 0x02}, - {0x379c, 0x58}, - {0x379e, 0x00}, - {0x379f, 0x00}, - {0x37a0, 0x00}, - {0x37bc, 0x22}, - {0x37c0, 0x01}, - {0x37c4, 0x3e}, - {0x37c5, 0x3e}, - {0x37c6, 0x2a}, - {0x37c7, 0x28}, - {0x37c8, 0x02}, - {0x37c9, 0x12}, - {0x37cb, 0x29}, - {0x37cd, 0x29}, - {0x37d2, 0x00}, - {0x37d3, 0x73}, - {0x37d6, 0x00}, - {0x37d7, 0x6b}, - {0x37dc, 0x00}, - {0x37df, 0x54}, - {0x37e2, 0x00}, - {0x37e3, 0x00}, - {0x37f8, 0x00}, - {0x37f9, 0x01}, - {0x37fa, 0x00}, - {0x37fb, 0x19}, - {0x3c03, 0x01}, - {0x3c04, 0x01}, - {0x3c06, 0x21}, - {0x3c08, 0x01}, - {0x3c09, 0x01}, - {0x3c0a, 0x01}, - {0x3c0b, 0x21}, - {0x3c13, 0x21}, - {0x3c14, 0x82}, - {0x3c16, 0x13}, - {0x3c21, 0x00}, - {0x3c22, 0xf3}, - {0x3c37, 0x12}, - {0x3c38, 0x31}, - {0x3c3c, 0x00}, - {0x3c3d, 0x03}, - {0x3c44, 0x16}, - {0x3c5c, 0x8a}, - {0x3c5f, 0x03}, - {0x3c61, 0x80}, - {0x3c6f, 0x2b}, - {0x3c70, 0x5f}, - {0x3c71, 0x2c}, - {0x3c72, 0x2c}, - {0x3c73, 0x2c}, - {0x3c76, 0x12}, - {0x3182, 0x12}, - {0x320e, 0x00}, - {0x320f, 0x00}, - {0x3211, 0x61}, - {0x3215, 0xcd}, - {0x3219, 0x08}, - {0x3506, 0x30}, - {0x350a, 0x01}, - {0x350b, 0x00}, - {0x350c, 0x00}, - {0x3586, 0x60}, - {0x358a, 0x01}, - {0x358b, 0x00}, - {0x358c, 0x00}, - {0x3541, 0x00}, - {0x3542, 0x04}, - {0x3548, 0x04}, - {0x3549, 0x40}, - {0x354a, 0x01}, - {0x354b, 0x00}, - {0x354c, 0x00}, - {0x35c1, 0x00}, - {0x35c2, 0x02}, - {0x35c6, 0xa0}, - {0x3600, 0x8f}, - {0x3605, 0x16}, - {0x3609, 0xf0}, - {0x360a, 0x01}, - {0x360e, 0x1d}, - {0x360f, 0x10}, - {0x3610, 0x70}, - {0x3611, 0x3a}, - {0x3612, 0x28}, - {0x361a, 0x29}, - {0x361b, 0x6c}, - {0x361c, 0x0b}, - {0x361d, 0x00}, - {0x361e, 0xfc}, - {0x362a, 0x00}, - {0x364d, 0x0f}, - {0x364e, 0x18}, - {0x364f, 0x12}, - {0x3653, 0x1c}, - {0x3654, 0x00}, - {0x3655, 0x1f}, - {0x3656, 0x1f}, - {0x3657, 0x0c}, - {0x3658, 0x0a}, - {0x3659, 0x14}, - {0x365a, 0x18}, - {0x365b, 0x14}, - {0x365c, 0x10}, - {0x365e, 0x12}, - {0x3674, 0x08}, - {0x3677, 0x3a}, - {0x3678, 0x3a}, - {0x3679, 0x19}, - {0x3802, 0x00}, - {0x3803, 0x04}, - {0x3806, 0x05}, - {0x3807, 0x0b}, - {0x3808, 0x07}, - {0x3809, 0x80}, - {0x380a, 0x05}, - {0x380b, 0x00}, - {0x380c, 0x04}, - {0x380d, 0xd3}, - {0x380e, 0x02}, - {0x380f, 0xae}, - {0x3810, 0x00}, - {0x3811, 0x08}, - {0x3812, 0x00}, - {0x3813, 0x04}, - {0x3816, 0x01}, - {0x3817, 0x01}, - {0x381c, 0x18}, - {0x381e, 0x01}, - {0x381f, 0x01}, - {0x3820, 0x00}, - {0x3821, 0x19}, - {0x3832, 0x00}, - {0x3834, 0x00}, - {0x384c, 0x02}, - {0x384d, 0x53}, - {0x3850, 0x00}, - {0x3851, 0x42}, - {0x3852, 0x00}, - {0x3853, 0x40}, - {0x3858, 0x04}, - {0x388c, 0x02}, - {0x388d, 0x71}, - {0x3b40, 0x05}, - {0x3b41, 0x40}, - {0x3b42, 0x00}, - {0x3b43, 0x90}, - {0x3b44, 0x00}, - {0x3b45, 0x20}, - {0x3b46, 0x00}, - {0x3b47, 0x20}, - {0x3b48, 0x19}, - {0x3b49, 0x12}, - {0x3b4a, 0x16}, - {0x3b4b, 0x2e}, - {0x3b4c, 0x00}, - {0x3b4d, 0x00}, - {0x3b86, 0x00}, - {0x3b87, 0x34}, - {0x3b88, 0x00}, - {0x3b89, 0x08}, - {0x3b8a, 0x05}, - {0x3b8b, 0x00}, - {0x3b8c, 0x07}, - {0x3b8d, 0x80}, - {0x3b8e, 0x00}, - {0x3b8f, 0x00}, - {0x3b92, 0x05}, - {0x3b93, 0x00}, - {0x3b94, 0x07}, - {0x3b95, 0x80}, - {0x3b9e, 0x09}, - {0x3d82, 0x73}, - {0x3d85, 0x05}, - {0x3d8a, 0x03}, - {0x3d8b, 0xff}, - {0x3d99, 0x00}, - {0x3d9a, 0x9f}, - {0x3d9b, 0x00}, - {0x3d9c, 0xa0}, - {0x3da4, 0x00}, - {0x3da7, 0x50}, - {0x420e, 0xff}, - {0x420f, 0xff}, - {0x4210, 0xff}, - {0x4211, 0xff}, - {0x421e, 0x02}, - {0x421f, 0x45}, - {0x4220, 0xe1}, - {0x4221, 0x05}, - {0x4301, 0x0f}, - {0x4307, 0x03}, - {0x4308, 0x13}, - {0x430a, 0x53}, - {0x430d, 0x93}, - {0x430f, 0x57}, - {0x4310, 0x95}, - {0x4311, 0x16}, - {0x4316, 0x00}, - {0x4317, 0x08}, - {0x4319, 0x09}, - {0x431a, 0x00}, - {0x431b, 0x22}, - {0x431d, 0x2a}, - {0x431e, 0x11}, - {0x431f, 0x30}, - {0x4320, 0x59}, - {0x4323, 0x80}, - {0x4324, 0x00}, - {0x4503, 0x4e}, - {0x4505, 0x00}, - {0x4509, 0x00}, - {0x450a, 0x00}, - {0x4580, 0xf8}, - {0x4583, 0x07}, - {0x4584, 0x6a}, - {0x4585, 0x08}, - {0x4586, 0x05}, - {0x4587, 0x04}, - {0x4588, 0x73}, - {0x4589, 0x05}, - {0x458a, 0x1f}, - {0x458b, 0x02}, - {0x458c, 0xdc}, - {0x458d, 0x03}, - {0x458e, 0x02}, - {0x4597, 0x07}, - {0x4598, 0x40}, - {0x4599, 0x0e}, - {0x459a, 0x0e}, - {0x459b, 0xfb}, - {0x459c, 0xf3}, - {0x4602, 0x00}, - {0x4603, 0x13}, - {0x4604, 0x00}, - {0x4609, 0x0a}, - {0x460a, 0x00}, - {0x4610, 0x00}, - {0x4611, 0x70}, - {0x4612, 0x00}, - {0x4613, 0x0c}, - {0x4614, 0x00}, - {0x4615, 0x70}, - {0x4616, 0x00}, - {0x4617, 0x0c}, - {0x4800, 0x04}, - {0x480a, 0x22}, - {0x4813, 0xe4}, - {0x4814, 0x2a}, - {0x4837, 0x0d}, - {0x484b, 0x47}, - {0x484f, 0x40}, - {0x4887, 0x51}, - {0x4d00, 0x4a}, - {0x4d01, 0x18}, - {0x4d05, 0xff}, - {0x4d06, 0x88}, - {0x4d08, 0x63}, - {0x4d09, 0xdf}, - {0x4d15, 0x7d}, - {0x4d1a, 0x20}, - {0x4d30, 0x0a}, - {0x4d31, 0x00}, - {0x4d34, 0x7d}, - {0x4d3c, 0x7d}, - {0x4f00, 0x3f}, - {0x4f01, 0xff}, - {0x4f02, 0xff}, - {0x4f03, 0x2c}, - {0x4f04, 0xe0}, - {0x6a00, 0x00}, - {0x6a01, 0x20}, - {0x6a02, 0x00}, - {0x6a03, 0x20}, - {0x6a04, 0x02}, - {0x6a05, 0x80}, - {0x6a06, 0x01}, - {0x6a07, 0xe0}, - {0x6a08, 0xcf}, - {0x6a09, 0x01}, - {0x6a0a, 0x40}, - {0x6a20, 0x00}, - {0x6a21, 0x02}, - {0x6a22, 0x00}, - {0x6a23, 0x00}, - {0x6a24, 0x00}, - {0x6a25, 0xf0}, - {0x6a26, 0x00}, - {0x6a27, 0x00}, - {0x6a28, 0x00}, - {0x5000, 0x8f}, - {0x5001, 0x65}, - {0x5002, 0x7f}, - {0x5003, 0x6a}, - {0x5004, 0x3e}, - {0x5005, 0x1e}, - {0x5006, 0x1e}, - {0x5007, 0x1e}, - {0x5008, 0x00}, - {0x500c, 0x00}, - {0x502c, 0x00}, - {0x502e, 0x00}, - {0x502f, 0x00}, - {0x504b, 0x00}, - {0x5053, 0x00}, - {0x505b, 0x00}, - {0x5063, 0x00}, - {0x5070, 0x00}, - {0x5074, 0x04}, - {0x507a, 0x00}, - {0x507b, 0x00}, - {0x5500, 0x02}, - {0x5700, 0x02}, - {0x5900, 0x02}, - {0x6007, 0x04}, - {0x6008, 0x05}, - {0x6009, 0x02}, - {0x600b, 0x08}, - {0x600c, 0x07}, - {0x600d, 0x88}, - {0x6016, 0x00}, - {0x6027, 0x04}, - {0x6028, 0x05}, - {0x6029, 0x02}, - {0x602b, 0x08}, - {0x602c, 0x07}, - {0x602d, 0x88}, - {0x6047, 0x04}, - {0x6048, 0x05}, - {0x6049, 0x02}, - {0x604b, 0x08}, - {0x604c, 0x07}, - {0x604d, 0x88}, - {0x6067, 0x04}, - {0x6068, 0x05}, - {0x6069, 0x02}, - {0x606b, 0x08}, - {0x606c, 0x07}, - {0x606d, 0x88}, - {0x6087, 0x04}, - {0x6088, 0x05}, - {0x6089, 0x02}, - {0x608b, 0x08}, - {0x608c, 0x07}, - {0x608d, 0x88}, - {0x5e00, 0x02}, - {0x5e01, 0x0b}, - {0x5e02, 0x00}, - {0x5e03, 0x00}, - {0x5e04, 0x00}, - {0x5e05, 0x0b}, - {0x5e06, 0x0c}, - {0x5e07, 0x0c}, - {0x5e08, 0x0c}, - {0x5e09, 0x0c}, - {0x5e0a, 0x0c}, - {0x5e0b, 0x0d}, - {0x5e0c, 0x0d}, - {0x5e0d, 0x0d}, - {0x5e0e, 0x0d}, - {0x5e0f, 0x0d}, - {0x5e10, 0x0d}, - {0x5e11, 0x0d}, - {0x5e12, 0x0e}, - {0x5e13, 0x0e}, - {0x5e14, 0x0e}, - {0x5e15, 0x0e}, - {0x5e16, 0x0e}, - {0x5e17, 0x0e}, - {0x5e18, 0x0e}, - {0x5e19, 0x10}, - {0x5e1a, 0x11}, - {0x5e1b, 0x11}, - {0x5e1c, 0x12}, - {0x5e1d, 0x12}, - {0x5e1e, 0x14}, - {0x5e1f, 0x15}, - {0x5e20, 0x16}, - {0x5e21, 0x17}, - {0x5e22, 0x00}, - {0x5e23, 0x08}, - {0x5e26, 0x00}, - {0x5e27, 0x00}, - {0x5e29, 0x00}, - {0x5e2a, 0x00}, - {0x5e2c, 0x00}, - {0x5e2d, 0x00}, - {0x5e2f, 0x03}, - {0x5e30, 0xff}, - {0x5e32, 0x04}, - {0x5e33, 0x00}, - {0x5e34, 0x00}, - {0x5e35, 0x04}, - {0x5e36, 0x00}, - {0x5e37, 0x00}, - {0x5e38, 0x04}, - {0x5e39, 0x00}, - {0x5e3a, 0x00}, - {0x5e3b, 0x04}, - {0x5e3c, 0x00}, - {0x5e3d, 0x00}, - {0x5e3e, 0x04}, - {0x5e3f, 0x00}, - {0x5e40, 0x00}, - {0x5e41, 0x06}, - {0x5e42, 0x00}, - {0x5e43, 0x00}, - {0x5e44, 0x06}, - {0x5e45, 0x00}, - {0x5e46, 0x00}, - {0x5e47, 0x06}, - {0x5e48, 0x00}, - {0x5e49, 0x00}, - {0x5e4a, 0x06}, - {0x5e4b, 0x00}, - {0x5e4c, 0x00}, - {0x5e4d, 0x06}, - {0x5e4e, 0x00}, - {0x5e50, 0x06}, - {0x5e51, 0x00}, - {0x5e53, 0x06}, - {0x5e54, 0x00}, - {0x5e56, 0x08}, - {0x5e57, 0x00}, - {0x5e59, 0x08}, - {0x5e5a, 0x00}, - {0x5e5c, 0x08}, - {0x5e5d, 0x00}, - {0x5e5f, 0x08}, - {0x5e60, 0x00}, - {0x5e62, 0x08}, - {0x5e63, 0x00}, - {0x5e65, 0x08}, - {0x5e66, 0x00}, - {0x5e68, 0x08}, - {0x5e69, 0x00}, - {0x5e6b, 0x16}, - {0x5e6c, 0x00}, - {0x5e6e, 0x20}, - {0x5e6f, 0x00}, - {0x5e71, 0x18}, - {0x5e72, 0x00}, - {0x5e74, 0x18}, - {0x5e75, 0x00}, - {0x5e77, 0x17}, - {0x5e78, 0xff}, - {0x5e7a, 0x00}, - {0x5e7b, 0x00}, - {0x5e7d, 0x00}, - {0x5e7e, 0x00}, - {0x5e80, 0x00}, - {0x5e81, 0x00}, - {0x5e83, 0x00}, - {0x5e84, 0x00}, - {0x5f00, 0x02}, - {0x5f01, 0x08}, - {0x5f02, 0x09}, - {0x5f03, 0x0a}, - {0x5f04, 0x0b}, - {0x5f05, 0x0c}, - {0x5f06, 0x0c}, - {0x5f07, 0x0c}, - {0x5f08, 0x0c}, - {0x5f09, 0x0c}, - {0x5f0a, 0x0d}, - {0x5f0b, 0x0d}, - {0x5f0c, 0x0d}, - {0x5f0d, 0x0d}, - {0x5f0e, 0x0d}, - {0x5f0f, 0x0e}, - {0x5f10, 0x0e}, - {0x5f11, 0x0e}, - {0x5f12, 0x0e}, - {0x5f13, 0x0f}, - {0x5f14, 0x0f}, - {0x5f15, 0x10}, - {0x5f16, 0x11}, - {0x5f17, 0x11}, - {0x5f18, 0x12}, - {0x5f19, 0x12}, - {0x5f1a, 0x13}, - {0x5f1b, 0x13}, - {0x5f1c, 0x14}, - {0x5f1d, 0x14}, - {0x5f1e, 0x16}, - {0x5f1f, 0x16}, - {0x5f20, 0x16}, - {0x5f21, 0x08}, - {0x5f22, 0x00}, - {0x5f23, 0x01}, - {0x5f26, 0x02}, - {0x5f27, 0x00}, - {0x5f29, 0x02}, - {0x5f2a, 0x00}, - {0x5f2c, 0x02}, - {0x5f2d, 0x00}, - {0x5f2f, 0x02}, - {0x5f30, 0x00}, - {0x5f32, 0x02}, - {0x5f33, 0x00}, - {0x5f34, 0x00}, - {0x5f35, 0x02}, - {0x5f36, 0x00}, - {0x5f37, 0x00}, - {0x5f38, 0x02}, - {0x5f39, 0x00}, - {0x5f3a, 0x00}, - {0x5f3b, 0x02}, - {0x5f3c, 0x00}, - {0x5f3d, 0x00}, - {0x5f3e, 0x02}, - {0x5f3f, 0x00}, - {0x5f40, 0x00}, - {0x5f41, 0x02}, - {0x5f42, 0x00}, - {0x5f43, 0x00}, - {0x5f44, 0x02}, - {0x5f45, 0x00}, - {0x5f46, 0x00}, - {0x5f47, 0x04}, - {0x5f48, 0x00}, - {0x5f49, 0x00}, - {0x5f4a, 0x04}, - {0x5f4b, 0x00}, - {0x5f4c, 0x00}, - {0x5f4d, 0x04}, - {0x5f4e, 0x00}, - {0x5f50, 0x04}, - {0x5f51, 0x00}, - {0x5f53, 0x04}, - {0x5f54, 0x00}, - {0x5f56, 0x04}, - {0x5f57, 0x00}, - {0x5f59, 0x04}, - {0x5f5a, 0x00}, - {0x5f5c, 0x04}, - {0x5f5d, 0x00}, - {0x5f5f, 0x08}, - {0x5f60, 0x00}, - {0x5f62, 0x08}, - {0x5f63, 0x00}, - {0x5f65, 0x08}, - {0x5f66, 0x00}, - {0x5f68, 0x08}, - {0x5f69, 0x00}, - {0x5f6b, 0x08}, - {0x5f6c, 0x00}, - {0x5f6e, 0x10}, - {0x5f6f, 0x00}, - {0x5f71, 0x10}, - {0x5f72, 0x00}, - {0x5f74, 0x10}, - {0x5f75, 0x00}, - {0x5f77, 0x10}, - {0x5f78, 0x00}, - {0x5f7a, 0x20}, - {0x5f7b, 0x00}, - {0x5f7d, 0x20}, - {0x5f7e, 0x00}, - {0x5f80, 0x20}, - {0x5f81, 0x00}, - {0x5f83, 0x00}, - {0x5f84, 0xff}, - {0x5240, 0x0f}, - {0x5243, 0x00}, - {0x5244, 0x00}, - {0x5245, 0x00}, - {0x5246, 0x00}, - {0x5247, 0x00}, - {0x5248, 0x00}, - {0x5249, 0x00}, - {0x5440, 0x0f}, - {0x5443, 0x00}, - {0x5445, 0x00}, - {0x5447, 0x00}, - {0x5448, 0x00}, - {0x5449, 0x00}, - {0x5640, 0x0f}, - {0x5642, 0x00}, - {0x5643, 0x00}, - {0x5644, 0x00}, - {0x5645, 0x00}, - {0x5646, 0x00}, - {0x5647, 0x00}, - {0x5649, 0x00}, - {0x5840, 0x0f}, - {0x5842, 0x00}, - {0x5843, 0x00}, - {0x5845, 0x00}, - {0x5846, 0x00}, - {0x5847, 0x00}, - {0x5848, 0x00}, - {0x5849, 0x00}, - {0x4001, 0x2b}, - {0x4008, 0x02}, - {0x4009, 0x03}, - {0x4018, 0x12}, - {0x4022, 0x40}, - {0x4023, 0x20}, - {0x4026, 0x00}, - {0x4027, 0x40}, - {0x4028, 0x00}, - {0x4029, 0x40}, - {0x402a, 0x00}, - {0x402b, 0x40}, - {0x402c, 0x00}, - {0x402d, 0x40}, - {0x405e, 0x00}, - {0x405f, 0x00}, - {0x4060, 0x00}, - {0x4061, 0x00}, - {0x4062, 0x00}, - {0x4063, 0x00}, - {0x4064, 0x00}, - {0x4065, 0x00}, - {0x4066, 0x00}, - {0x4067, 0x00}, - {0x4068, 0x00}, - {0x4069, 0x00}, - {0x406a, 0x00}, - {0x406b, 0x00}, - {0x406c, 0x00}, - {0x406d, 0x00}, - {0x406e, 0x00}, - {0x406f, 0x00}, - {0x4070, 0x00}, - {0x4071, 0x00}, - {0x4072, 0x00}, - {0x4073, 0x00}, - {0x4074, 0x00}, - {0x4075, 0x00}, - {0x4076, 0x00}, - {0x4077, 0x00}, - {0x4078, 0x00}, - {0x4079, 0x00}, - {0x407a, 0x00}, - {0x407b, 0x00}, - {0x407c, 0x00}, - {0x407d, 0x00}, - {0x407e, 0xcc}, - {0x407f, 0x18}, - {0x4080, 0xff}, - {0x4081, 0xff}, - {0x4082, 0x01}, - {0x4083, 0x53}, - {0x4084, 0x01}, - {0x4085, 0x2b}, - {0x4086, 0x00}, - {0x4087, 0xb3}, - {0x4640, 0x40}, - {0x4641, 0x11}, - {0x4642, 0x0e}, - {0x4643, 0xee}, - {0x4646, 0x0f}, - {0x4648, 0x00}, - {0x4649, 0x03}, - {0x4f04, 0xf8}, - {0x4d09, 0xff}, - {0x4d09, 0xdf}, - {0x5003, 0x7a}, - {0x5b80, 0x08}, - {0x5c00, 0x08}, - {0x5c80, 0x00}, - {0x5bbe, 0x12}, - {0x5c3e, 0x12}, - {0x5cbe, 0x12}, - {0x5b8a, 0x80}, - {0x5b8b, 0x80}, - {0x5b8c, 0x80}, - {0x5b8d, 0x80}, - {0x5b8e, 0x60}, - {0x5b8f, 0x80}, - {0x5b90, 0x80}, - {0x5b91, 0x80}, - {0x5b92, 0x80}, - {0x5b93, 0x20}, - {0x5b94, 0x80}, - {0x5b95, 0x80}, - {0x5b96, 0x80}, - {0x5b97, 0x20}, - {0x5b98, 0x00}, - {0x5b99, 0x80}, - {0x5b9a, 0x40}, - {0x5b9b, 0x20}, - {0x5b9c, 0x00}, - {0x5b9d, 0x00}, - {0x5b9e, 0x80}, - {0x5b9f, 0x00}, - {0x5ba0, 0x00}, - {0x5ba1, 0x00}, - {0x5ba2, 0x00}, - {0x5ba3, 0x00}, - {0x5ba4, 0x00}, - {0x5ba5, 0x00}, - {0x5ba6, 0x00}, - {0x5ba7, 0x00}, - {0x5ba8, 0x02}, - {0x5ba9, 0x00}, - {0x5baa, 0x02}, - {0x5bab, 0x76}, - {0x5bac, 0x03}, - {0x5bad, 0x08}, - {0x5bae, 0x00}, - {0x5baf, 0x80}, - {0x5bb0, 0x00}, - {0x5bb1, 0xc0}, - {0x5bb2, 0x01}, - {0x5bb3, 0x00}, - {0x5c0a, 0x80}, - {0x5c0b, 0x80}, - {0x5c0c, 0x80}, - {0x5c0d, 0x80}, - {0x5c0e, 0x60}, - {0x5c0f, 0x80}, - {0x5c10, 0x80}, - {0x5c11, 0x80}, - {0x5c12, 0x60}, - {0x5c13, 0x20}, - {0x5c14, 0x80}, - {0x5c15, 0x80}, - {0x5c16, 0x80}, - {0x5c17, 0x20}, - {0x5c18, 0x00}, - {0x5c19, 0x80}, - {0x5c1a, 0x40}, - {0x5c1b, 0x20}, - {0x5c1c, 0x00}, - {0x5c1d, 0x00}, - {0x5c1e, 0x80}, - {0x5c1f, 0x00}, - {0x5c20, 0x00}, - {0x5c21, 0x00}, - {0x5c22, 0x00}, - {0x5c23, 0x00}, - {0x5c24, 0x00}, - {0x5c25, 0x00}, - {0x5c26, 0x00}, - {0x5c27, 0x00}, - {0x5c28, 0x02}, - {0x5c29, 0x00}, - {0x5c2a, 0x02}, - {0x5c2b, 0x76}, - {0x5c2c, 0x03}, - {0x5c2d, 0x08}, - {0x5c2e, 0x00}, - {0x5c2f, 0x80}, - {0x5c30, 0x00}, - {0x5c31, 0xc0}, - {0x5c32, 0x01}, - {0x5c33, 0x00}, - {0x5c8a, 0x80}, - {0x5c8b, 0x80}, - {0x5c8c, 0x80}, - {0x5c8d, 0x80}, - {0x5c8e, 0x80}, - {0x5c8f, 0x80}, - {0x5c90, 0x80}, - {0x5c91, 0x80}, - {0x5c92, 0x80}, - {0x5c93, 0x60}, - {0x5c94, 0x80}, - {0x5c95, 0x80}, - {0x5c96, 0x80}, - {0x5c97, 0x60}, - {0x5c98, 0x40}, - {0x5c99, 0x80}, - {0x5c9a, 0x80}, - {0x5c9b, 0x80}, - {0x5c9c, 0x40}, - {0x5c9d, 0x00}, - {0x5c9e, 0x80}, - {0x5c9f, 0x80}, - {0x5ca0, 0x80}, - {0x5ca1, 0x20}, - {0x5ca2, 0x00}, - {0x5ca3, 0x80}, - {0x5ca4, 0x80}, - {0x5ca5, 0x00}, - {0x5ca6, 0x00}, - {0x5ca7, 0x00}, - {0x5ca8, 0x01}, - {0x5ca9, 0x00}, - {0x5caa, 0x02}, - {0x5cab, 0x00}, - {0x5cac, 0x03}, - {0x5cad, 0x08}, - {0x5cae, 0x01}, - {0x5caf, 0x00}, - {0x5cb0, 0x02}, - {0x5cb1, 0x00}, - {0x5cb2, 0x03}, - {0x5cb3, 0x08}, - {0x5be7, 0x80}, - {0x5bc9, 0x80}, - {0x5bca, 0x80}, - {0x5bcb, 0x80}, - {0x5bcc, 0x80}, - {0x5bcd, 0x80}, - {0x5bce, 0x80}, - {0x5bcf, 0x80}, - {0x5bd0, 0x80}, - {0x5bd1, 0x80}, - {0x5bd2, 0x20}, - {0x5bd3, 0x80}, - {0x5bd4, 0x40}, - {0x5bd5, 0x20}, - {0x5bd6, 0x00}, - {0x5bd7, 0x00}, - {0x5bd8, 0x00}, - {0x5bd9, 0x00}, - {0x5bda, 0x00}, - {0x5bdb, 0x00}, - {0x5bdc, 0x00}, - {0x5bdd, 0x00}, - {0x5bde, 0x00}, - {0x5bdf, 0x00}, - {0x5be0, 0x00}, - {0x5be1, 0x00}, - {0x5be2, 0x00}, - {0x5be3, 0x00}, - {0x5be4, 0x00}, - {0x5be5, 0x00}, - {0x5be6, 0x00}, - {0x5c49, 0x80}, - {0x5c4a, 0x80}, - {0x5c4b, 0x80}, - {0x5c4c, 0x80}, - {0x5c4d, 0x40}, - {0x5c4e, 0x80}, - {0x5c4f, 0x80}, - {0x5c50, 0x80}, - {0x5c51, 0x60}, - {0x5c52, 0x20}, - {0x5c53, 0x80}, - {0x5c54, 0x80}, - {0x5c55, 0x80}, - {0x5c56, 0x20}, - {0x5c57, 0x00}, - {0x5c58, 0x80}, - {0x5c59, 0x40}, - {0x5c5a, 0x20}, - {0x5c5b, 0x00}, - {0x5c5c, 0x00}, - {0x5c5d, 0x80}, - {0x5c5e, 0x00}, - {0x5c5f, 0x00}, - {0x5c60, 0x00}, - {0x5c61, 0x00}, - {0x5c62, 0x00}, - {0x5c63, 0x00}, - {0x5c64, 0x00}, - {0x5c65, 0x00}, - {0x5c66, 0x00}, - {0x5cc9, 0x80}, - {0x5cca, 0x80}, - {0x5ccb, 0x80}, - {0x5ccc, 0x80}, - {0x5ccd, 0x80}, - {0x5cce, 0x80}, - {0x5ccf, 0x80}, - {0x5cd0, 0x80}, - {0x5cd1, 0x80}, - {0x5cd2, 0x60}, - {0x5cd3, 0x80}, - {0x5cd4, 0x80}, - {0x5cd5, 0x80}, - {0x5cd6, 0x60}, - {0x5cd7, 0x40}, - {0x5cd8, 0x80}, - {0x5cd9, 0x80}, - {0x5cda, 0x80}, - {0x5cdb, 0x40}, - {0x5cdc, 0x20}, - {0x5cdd, 0x80}, - {0x5cde, 0x80}, - {0x5cdf, 0x80}, - {0x5ce0, 0x20}, - {0x5ce1, 0x00}, - {0x5ce2, 0x80}, - {0x5ce3, 0x80}, - {0x5ce4, 0x80}, - {0x5ce5, 0x00}, - {0x5ce6, 0x00}, - {0x5d74, 0x01}, - {0x5d75, 0x00}, - {0x5d1f, 0x81}, - {0x5d11, 0x00}, - {0x5d12, 0x10}, - {0x5d13, 0x10}, - {0x5d15, 0x05}, - {0x5d16, 0x05}, - {0x5d17, 0x05}, - {0x5d08, 0x03}, - {0x5d09, 0xb6}, - {0x5d0a, 0x03}, - {0x5d0b, 0xb6}, - {0x5d18, 0x03}, - {0x5d19, 0xb6}, - {0x5d62, 0x01}, - {0x5d40, 0x02}, - {0x5d41, 0x01}, - {0x5d63, 0x1f}, - {0x5d64, 0x00}, - {0x5d65, 0x80}, - {0x5d56, 0x00}, - {0x5d57, 0x20}, - {0x5d58, 0x00}, - {0x5d59, 0x20}, - {0x5d5a, 0x00}, - {0x5d5b, 0x0c}, - {0x5d5c, 0x02}, - {0x5d5d, 0x40}, - {0x5d5e, 0x02}, - {0x5d5f, 0x40}, - {0x5d60, 0x03}, - {0x5d61, 0x40}, - {0x5d4a, 0x02}, - {0x5d4b, 0x40}, - {0x5d4c, 0x02}, - {0x5d4d, 0x40}, - {0x5d4e, 0x02}, - {0x5d4f, 0x40}, - {0x5d50, 0x18}, - {0x5d51, 0x80}, - {0x5d52, 0x18}, - {0x5d53, 0x80}, - {0x5d54, 0x18}, - {0x5d55, 0x80}, - {0x5d46, 0x20}, - {0x5d47, 0x00}, - {0x5d48, 0x22}, - {0x5d49, 0x00}, - {0x5d42, 0x20}, - {0x5d43, 0x00}, - {0x5d44, 0x22}, - {0x5d45, 0x00}, - {0x5004, 0x1e}, - {0x4221, 0x03}, - {0x380e, 0x02}, - {0x380f, 0xae}, - {0x380c, 0x04}, - {0x380d, 0x47}, - {0x384c, 0x02}, - {0x384d, 0x0d}, - {0x388c, 0x02}, - {0x388d, 0x2b}, - {0x420e, 0x54}, - {0x420f, 0xa0}, - {0x4210, 0xca}, - {0x4211, 0xf2}, - {0x507a, 0x5f}, - {0x507b, 0x46}, - {0x3802, 0x00}, - {0x3803, 0x68}, - {0x3806, 0x04}, - {0x3807, 0xa7}, - {0x380a, 0x04}, - {0x380b, 0x38}, - {0x3812, 0x00}, - {0x3813, 0x04}, - {0x380c, 0x08}, - {0x380d, 0x8e}, - {0x384c, 0x04}, - {0x384d, 0x1a}, - {0x388c, 0x04}, - {0x388d, 0x56}, - {0x0304, 0x00}, - {0x0305, 0xf9}, - {0x4837, 0x10}, - {0x0408, 0x78}, - {0x0409, 0x00}, - {0x040a, 0xd1}, - {0x040b, 0x1e}, - {0x040c, 0x2e}, - {0x040d, 0x44}, - {0x040e, 0x0c}, - +static const struct regval ox03c10_1920x1080_30fps_linear_lcg_raw10_mipi996[] = { {0x4221, 0x05}, {0x5002, 0x3f}, {0x5003, 0x2a}, @@ -4915,6 +3848,54 @@ static const struct regval ox03c10_1920x1080_30fps_linear_raw10_mipi996[] = { {REG_NULL, 0xff}, }; +static const struct regval ox03c10_1920x1080_30fps_linear_hcg_raw10_mipi996[] = { + {0x4221, 0x05}, + {0x5002, 0x3f}, + {0x5003, 0x2a}, + {0x502c, 0x0f}, + {0x504b, 0x04}, + {0x5053, 0x03}, + {0x505b, 0x02}, + {0x5063, 0x01}, + {0x5074, 0x49}, + {0x4319, 0x43}, + {0x431a, 0x01}, + + {REG_NULL, 0xff}, +}; + +static const struct regval ox03c10_1920x1080_30fps_linear_vs_raw10_mipi996[] = { + {0x4221, 0x05}, + {0x5002, 0x3f}, + {0x5003, 0x2a}, + {0x502c, 0x0f}, + {0x504b, 0x04}, + {0x5053, 0x03}, + {0x505b, 0x02}, + {0x5063, 0x01}, + {0x5074, 0x79}, + {0x4319, 0x43}, + {0x431a, 0x01}, + + {REG_NULL, 0xff}, +}; + +static const struct regval ox03c10_1920x1080_30fps_linear_spd_raw10_mipi996[] = { + {0x4221, 0x05}, + {0x5002, 0x3f}, + {0x5003, 0x2a}, + {0x502c, 0x0f}, + {0x504b, 0x04}, + {0x5053, 0x03}, + {0x505b, 0x02}, + {0x5063, 0x01}, + {0x5074, 0x69}, + {0x4319, 0x43}, + {0x431a, 0x01}, + + {REG_NULL, 0xff}, +}; + static struct rkmodule_hdr_compr ox03c10_hdr_compr_12 = { .point = 30, .src_bit = 20, @@ -4957,13 +3938,78 @@ static const struct ox03c10_mode supported_modes[] = { .exp_def = 0x0200, .hts_def = 0x10fe, .vts_def = 0x02ae * 2, - .reg_list = ox03c10_1920x1080_30fps_linear_raw10_mipi996, + .reg_list = ox03c10_1920x1080_30fps_HDR3_LFM_PWL16_mipi996, + .linear_reg_list = ox03c10_1920x1080_30fps_linear_lcg_raw10_mipi996, .hdr_mode = NO_HDR, .hdr_compr = NULL, .bpp = 10, .mipi_freq_idx = 1, .vc[PAD0] = 0, .exp_mode = EXP_NORMAL, + .single_mode = EXPAND_SINGLE_LCG, + }, + { + .bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10, + .width = 1920, + .height = 1080, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x0200, + .hts_def = 0x10fe, + .vts_def = 0x02ae * 2, + .reg_list = ox03c10_1920x1080_30fps_HDR3_LFM_PWL16_mipi996, + .linear_reg_list = ox03c10_1920x1080_30fps_linear_hcg_raw10_mipi996, + .hdr_mode = NO_HDR, + .hdr_compr = NULL, + .bpp = 10, + .mipi_freq_idx = 1, + .vc[PAD0] = 0, + .exp_mode = EXP_NORMAL, + .single_mode = EXPAND_SINGLE_HCG, + }, + { + .bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10, + .width = 1920, + .height = 1080, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x0200, + .hts_def = 0x10fe, + .vts_def = 0x02ae * 2, + .reg_list = ox03c10_1920x1080_30fps_HDR3_LFM_PWL16_mipi996, + .linear_reg_list = ox03c10_1920x1080_30fps_linear_vs_raw10_mipi996, + .hdr_mode = NO_HDR, + .hdr_compr = NULL, + .bpp = 10, + .mipi_freq_idx = 1, + .vc[PAD0] = 0, + .exp_mode = EXP_NORMAL, + .single_mode = EXPAND_SINGLE_VS, + }, + { + .bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10, + .width = 1920, + .height = 1080, + .max_fps = { + .numerator = 10000, + .denominator = 300000, + }, + .exp_def = 0x0200, + .hts_def = 0x10fe, + .vts_def = 0x02ae * 2, + .reg_list = ox03c10_1920x1080_30fps_HDR3_LFM_PWL16_mipi996, + .linear_reg_list = ox03c10_1920x1080_30fps_linear_spd_raw10_mipi996, + .hdr_mode = NO_HDR, + .hdr_compr = NULL, + .bpp = 10, + .mipi_freq_idx = 1, + .vc[PAD0] = 0, + .exp_mode = EXP_NORMAL, + .single_mode = EXPAND_SINGLE_SPD, }, { .bus_fmt = MEDIA_BUS_FMT_SBGGR12_1X12, @@ -5020,7 +4066,7 @@ static const struct ox03c10_mode supported_modes[] = { .hts_def = 0x10fe, .vts_def = 0x02ae * 2, .reg_list = ox03c10_1920x1080_30fps_HDR3_SPD_PWL12_mipi600, - .hdr_mode = NO_HDR, + .hdr_mode = HDR_COMPR, .hdr_compr = &ox03c10_hdr_compr_12, .bpp = 12, .mipi_freq_idx = 0, @@ -5767,6 +4813,33 @@ static int ox03c10_select_cmps_mode(struct ox03c10 *ox03c10, u32 cmps_mode) return ret; } +static int ox03c10_select_expand_single_mode(struct ox03c10 *ox03c10, u32 single_mode) +{ + int ret = -EINVAL; + u32 i, h, w, hdr_mode; + + w = ox03c10->cur_mode->width; + h = ox03c10->cur_mode->height; + hdr_mode = ox03c10->cur_mode->hdr_mode; + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { + if (w == supported_modes[i].width && + h == supported_modes[i].height && + supported_modes[i].hdr_mode == hdr_mode) { + if (supported_modes[i].single_mode == single_mode) { + ox03c10->cur_mode = &supported_modes[i]; + w = ox03c10->cur_mode->hts_def - ox03c10->cur_mode->width; + h = ox03c10->cur_mode->vts_def - ox03c10->cur_mode->height; + __v4l2_ctrl_modify_range(ox03c10->hblank, w, w, 1, w); + __v4l2_ctrl_modify_range(ox03c10->vblank, h, + OX03C10_VTS_MAX - ox03c10->cur_mode->height, 1, h); + ret = 0; + break; + } + } + } + return ret; +} + static long ox03c10_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { struct ox03c10 *ox03c10 = to_ox03c10(sd); @@ -5873,6 +4946,9 @@ static long ox03c10_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) case RKMODULE_SET_CMPS_MODE: ret = ox03c10_select_cmps_mode(ox03c10, *(u32 *)arg); break; + case RKMODULE_SET_EXPAND_SINGLE_MODE: + ret = ox03c10_select_expand_single_mode(ox03c10, *(u32 *)arg); + break; default: ret = -ENOIOCTLCMD; break; @@ -5900,6 +4976,7 @@ static long ox03c10_compat_ioctl32(struct v4l2_subdev *sd, struct rkmodule_wb_gain_info *wb_gain_info; struct rkmodule_blc_info *blc_info; u32 cmps_mode; + u32 single_mode; switch (cmd) { case RKMODULE_GET_MODULE_INFO: @@ -6106,6 +5183,11 @@ static long ox03c10_compat_ioctl32(struct v4l2_subdev *sd, return -EFAULT; ret = ox03c10_ioctl(sd, cmd, &cmps_mode); break; + case RKMODULE_SET_EXPAND_SINGLE_MODE: + if (copy_from_user(&single_mode, up, sizeof(u32))) + return -EFAULT; + ret = ox03c10_ioctl(sd, cmd, &single_mode); + break; default: ret = -ENOIOCTLCMD; break; @@ -6119,10 +5201,34 @@ static int __ox03c10_start_stream(struct ox03c10 *ox03c10) { int ret; - dev_info(&ox03c10->client->dev, "write: %d s cur_mode->reg_list done !\n", ox03c10->cur_mode->hdr_operating_mode); + if (ox03c10->cur_mode->hdr_mode == NO_HDR) { + if (ox03c10->cur_mode->single_mode == EXPAND_SINGLE_HCG) + dev_info(&ox03c10->client->dev, "%s mode: LINEAR_HCG\n", __func__); + else if (ox03c10->cur_mode->single_mode == EXPAND_SINGLE_LCG) + dev_info(&ox03c10->client->dev, "%s mode: LINEAR_LCG\n", __func__); + else if (ox03c10->cur_mode->single_mode == EXPAND_SINGLE_VS) + dev_info(&ox03c10->client->dev, "%s mode: LINEAR_VS\n", __func__); + else if (ox03c10->cur_mode->single_mode == EXPAND_SINGLE_SPD) + dev_info(&ox03c10->client->dev, "%s mode: LINEAR_SPD\n", __func__); + } else { + if (ox03c10->cur_mode->hdr_operating_mode == OX03C10_HDR3_DCG_VS_12BIT) + dev_info(&ox03c10->client->dev, "%s mode: HDR3_DCG_VS_12BIT\n", __func__); + else if (ox03c10->cur_mode->hdr_operating_mode == OX03C10_HDR3_DCG_SPD_12BIT) + dev_info(&ox03c10->client->dev, "%s mode: HDR3_DCG_SPD_12BIT\n", __func__); + else if (ox03c10->cur_mode->hdr_operating_mode == OX03C10_HDR3_DCG_VS_LFM_16BIT) + dev_info(&ox03c10->client->dev, "%s mode: HDR3_DCG_VS_16BIT\n", __func__); + } + ret = ox03c10_write_array(ox03c10->client, ox03c10->cur_mode->reg_list); if (ret) return ret; + + if (ox03c10->cur_mode->hdr_mode == NO_HDR) { + ret = ox03c10_write_array(ox03c10->client, ox03c10->cur_mode->linear_reg_list); + if (ret) + return ret; + } + /* In case these controls are set before streaming */ ret = __v4l2_ctrl_handler_setup(&ox03c10->ctrl_handler); if (ret) @@ -6404,6 +5510,8 @@ static int ox03c10_set_ctrl(struct v4l2_ctrl *ctrl) u32 val = 0; u32 again = 16, dgain = 1024; s64 exposure_max = 0; + u16 exp_reg; + u16 again_reg, dgain_reg; /* Propagate change of current control to all related controls */ switch (ctrl->id) { @@ -6428,8 +5536,20 @@ static int ox03c10_set_ctrl(struct v4l2_ctrl *ctrl) ret |= ox03c10_write_reg(ox03c10->client, OX03C10_GROUP_UPDATE_ADDRESS, OX03C10_REG_VALUE_08BIT, OX03C10_GROUP_UPDATE_START_DATA); + if (ox03c10->cur_mode->single_mode == EXPAND_SINGLE_LCG || + ox03c10->cur_mode->single_mode == EXPAND_SINGLE_HCG) { + exp_reg = OX03C10_REG_EXPOSURE_DCG_H; + } else if (ox03c10->cur_mode->single_mode == EXPAND_SINGLE_VS) { + exp_reg = OX03C10_REG_EXPOSURE_VS_H; + } else if (ox03c10->cur_mode->single_mode == EXPAND_SINGLE_SPD) { + exp_reg = OX03C10_REG_EXPOSURE_SPD_H; + } else { + dev_err(&client->dev, "%s cur single mode not support, pls check it", + __func__); + break; + } ret |= ox03c10_write_reg(ox03c10->client, - OX03C10_REG_EXPOSURE_DCG_H, + exp_reg, OX03C10_REG_VALUE_16BIT, ctrl->val); if (ox03c10->streaming) { @@ -6475,14 +5595,29 @@ static int ox03c10_set_ctrl(struct v4l2_ctrl *ctrl) OX03C10_REG_VALUE_08BIT, OX03C10_GROUP1_UPDATE_START_DATA); - // lcg real gain + if (ox03c10->cur_mode->single_mode == EXPAND_SINGLE_LCG) { + again_reg = OX03C10_REG_AGAIN_LCG_H; + dgain_reg = OX03C10_REG_DGAIN_LCG_H; + } else if (ox03c10->cur_mode->single_mode == EXPAND_SINGLE_HCG) { + again_reg = OX03C10_REG_AGAIN_HCG_H; + dgain_reg = OX03C10_REG_DGAIN_HCG_H; + } else if (ox03c10->cur_mode->single_mode == EXPAND_SINGLE_VS) { + again_reg = OX03C10_REG_AGAIN_VS_H; + dgain_reg = OX03C10_REG_DGAIN_VS_H; + } else if (ox03c10->cur_mode->single_mode == EXPAND_SINGLE_SPD) { + again_reg = OX03C10_REG_AGAIN_SPD_H; + dgain_reg = OX03C10_REG_DGAIN_SPD_H; + } else { + dev_err(&client->dev, "%s cur single mode not support, pls check it", + __func__); + break; + } ret |= ox03c10_write_reg(ox03c10->client, - OX03C10_REG_AGAIN_LCG_H, + again_reg, OX03C10_REG_VALUE_16BIT, (again << 4) & 0xff0); - // lcg digital gain ret |= ox03c10_write_reg(ox03c10->client, - OX03C10_REG_DGAIN_LCG_H, + dgain_reg, OX03C10_REG_VALUE_24BIT, (dgain << 6) & 0xfffc0); if (ox03c10->streaming) { From 30f24d6e0d388894a1bb41baa445e32a3da9bfcc Mon Sep 17 00:00:00 2001 From: Zefa Chen Date: Thu, 3 Jul 2025 11:27:59 +0800 Subject: [PATCH 04/28] include: uapi: rk-camera-module.h support get/set sensor lenc Change-Id: I3ae6dd52d46591e3a86716f22ab6a2827e97cb94 Signed-off-by: Zefa Chen --- include/uapi/linux/rk-camera-module.h | 33 +++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/include/uapi/linux/rk-camera-module.h b/include/uapi/linux/rk-camera-module.h index 77944de3b9e9..603700d77d20 100644 --- a/include/uapi/linux/rk-camera-module.h +++ b/include/uapi/linux/rk-camera-module.h @@ -235,6 +235,12 @@ #define RKMODULE_SET_EXPAND_SINGLE_MODE \ _IOW('V', BASE_VIDIOC_PRIVATE + 57, __u32) +#define RKMODULE_SET_LENC \ + _IOW('V', BASE_VIDIOC_PRIVATE + 58, struct rkmodule_lenc_gain) + +#define RKMODULE_GET_LENC_INFO \ + _IOR('V', BASE_VIDIOC_PRIVATE + 59, struct rkmodule_lenc_info) + struct rkmodule_i2cdev_info { __u8 slave_addr; } __attribute__ ((packed)); @@ -1006,4 +1012,31 @@ enum rkmodule_expand_single_mode { EXPAND_SINGLE_LOFIC, }; +#define RKMODULE_MAX_LENC_GROUP (4) + +struct rkmodule_lenc_gain { + __u32 g[RKMODULE_LSCDATA_LEN]; + __u32 b[RKMODULE_LSCDATA_LEN]; + __u32 r[RKMODULE_LSCDATA_LEN]; +}; + +struct rkmodule_lenc_data { + __u16 rgain; + __u16 bgain; + struct rkmodule_lenc_gain lenc_gain; +}; + +struct rkmodule_lenc_inf { + __u32 flag; + __u32 group_num; + __u32 lenc_gain_len; + struct rkmodule_lenc_data lenc_data[RKMODULE_MAX_LENC_GROUP]; +}; + +struct rkmodule_lenc_info { + __u32 bit_width; + __u32 grid_num; + __u32 reserved[8]; +}; + #endif /* _UAPI_RKMODULE_CAMERA_H */ From c710fa9ca43dc761a961ef22157c483d8b2ef1d2 Mon Sep 17 00:00:00 2001 From: Zefa Chen Date: Thu, 3 Jul 2025 11:51:07 +0800 Subject: [PATCH 05/28] media: i2c: add Lenc calibration initialization configuration Change-Id: I1dd50ed3ed8effe5aa955ff46a09baa177c32cf7 Signed-off-by: Zefa Chen --- drivers/media/i2c/ox03c10.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/drivers/media/i2c/ox03c10.c b/drivers/media/i2c/ox03c10.c index 02d4c8b75f1f..7e213282e76e 100644 --- a/drivers/media/i2c/ox03c10.c +++ b/drivers/media/i2c/ox03c10.c @@ -5200,6 +5200,10 @@ static long ox03c10_compat_ioctl32(struct v4l2_subdev *sd, static int __ox03c10_start_stream(struct ox03c10 *ox03c10) { int ret; +#ifdef OX03C10_LENC_CALIBRATION + u32 val = 0; + int i = 0; +#endif if (ox03c10->cur_mode->hdr_mode == NO_HDR) { if (ox03c10->cur_mode->single_mode == EXPAND_SINGLE_HCG) @@ -5253,7 +5257,36 @@ static int __ox03c10_start_stream(struct ox03c10 *ox03c10) return ret; } } +#ifdef OX03C10_LENC_CALIBRATION + ret = ox03c10_read_reg(ox03c10->client, 0x5003, + OX03C10_REG_VALUE_08BIT, &val); + if (ret) + return -EINVAL; + val &= ~BIT(2); + ret |= ox03c10_write_reg(ox03c10->client, 0x5003, + OX03C10_REG_VALUE_08BIT, val); + val = 0x400; + //init lcg wb gain + for (i = 0; i < 4; i++) + ret |= ox03c10_write_reg(ox03c10->client, OX03C10_REG_LCG_B_GAIN + i * 2, + OX03C10_REG_VALUE_16BIT, val); + //init hcg wb gain + for (i = 0; i < 4; i++) + ret |= ox03c10_write_reg(ox03c10->client, OX03C10_REG_HCG_B_GAIN + i * 2, + OX03C10_REG_VALUE_16BIT, val); + //init vs wb gain + for (i = 0; i < 4; i++) + ret |= ox03c10_write_reg(ox03c10->client, OX03C10_REG_VS_B_GAIN + i * 2, + OX03C10_REG_VALUE_16BIT, val); + + //init spd wb gain + for (i = 0; i < 4; i++) + ret |= ox03c10_write_reg(ox03c10->client, OX03C10_REG_SPD_B_GAIN + i * 2, + OX03C10_REG_VALUE_16BIT, val); + if (ret) + return -EINVAL; +#endif return ox03c10_write_reg(ox03c10->client, OX03C10_REG_CTRL_MODE, OX03C10_REG_VALUE_08BIT, OX03C10_MODE_STREAMING); } From 9662d2b59ccd386c922752cbe838f1d2796913f5 Mon Sep 17 00:00:00 2001 From: Zefa Chen Date: Thu, 3 Jul 2025 14:35:43 +0800 Subject: [PATCH 06/28] media: i2c: ox03c10 support get/set lenc Change-Id: I98f956be0b4e3461254bed4bce6068bfb152f592 Signed-off-by: Zefa Chen --- drivers/media/i2c/ox03c10.c | 93 +++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/drivers/media/i2c/ox03c10.c b/drivers/media/i2c/ox03c10.c index 7e213282e76e..b4618b9fe531 100644 --- a/drivers/media/i2c/ox03c10.c +++ b/drivers/media/i2c/ox03c10.c @@ -227,10 +227,12 @@ struct ox03c10 { const char *len_name; bool has_init_exp; bool has_init_wbgain; + bool has_init_lenc_gain; struct preisp_hdrae_exp_s init_hdrae_exp; struct rkmodule_wb_gain_group init_wbgain; struct rkmodule_dcg_ratio dcg_ratio; struct rkmodule_dcg_ratio spd_ratio; + struct rkmodule_lenc_gain lenc_gain; }; #define to_ox03c10(sd) container_of(sd, struct ox03c10, subdev) @@ -4840,6 +4842,41 @@ static int ox03c10_select_expand_single_mode(struct ox03c10 *ox03c10, u32 single return ret; } +static int ox03c10_set_lenc(struct ox03c10 *ox03c10, + struct rkmodule_lenc_gain *lenc_gain) +{ + int i = 0; + int ret = 0; + u32 val = 0; + + if (!ox03c10->has_init_lenc_gain && !ox03c10->streaming) { + ox03c10->lenc_gain = *lenc_gain; + ox03c10->has_init_lenc_gain = true; + dev_dbg(&ox03c10->client->dev, "ox03c10 don't stream, record lenc gain!\n"); + return ret; + } + ret = ox03c10_read_reg(ox03c10->client, 0x5003, OX03C10_REG_VALUE_08BIT, &val); + val |= BIT(2); + ret |= ox03c10_write_reg(ox03c10->client, 0x5003, + OX03C10_REG_VALUE_08BIT, val); + for (i = 0; i < 64; i++) { + ret |= ox03c10_write_reg(ox03c10->client, 0x5a20 + i, + OX03C10_REG_VALUE_08BIT, lenc_gain->g[i]); + ret |= ox03c10_write_reg(ox03c10->client, 0x5a60 + i, + OX03C10_REG_VALUE_08BIT, lenc_gain->b[i]); + ret |= ox03c10_write_reg(ox03c10->client, 0x5aa0 + i, + OX03C10_REG_VALUE_08BIT, lenc_gain->r[i]); +#ifdef DEBUG + dev_info(&ox03c10->client->dev, + "write lenc, g:0x%x,0x%x b:0x%x,0x%x r:0x%x,0x%x\n", + 0x5a20 + i, lenc_gain->g[i], + 0x5a60 + i, lenc_gain->b[i], + 0x5aa0 + i, lenc_gain->r[i]); +#endif + } + return ret; +} + static long ox03c10_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { struct ox03c10 *ox03c10 = to_ox03c10(sd); @@ -4854,6 +4891,8 @@ static long ox03c10_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) u32 *exp_mode; struct rkmodule_wb_gain_info *wb_gain_info; struct rkmodule_blc_info *blc_info; + struct rkmodule_lenc_info *lenc_info; + struct rkmodule_lenc_gain *lenc_gain; switch (cmd) { case RKMODULE_GET_MODULE_INFO: @@ -4949,6 +4988,15 @@ static long ox03c10_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) case RKMODULE_SET_EXPAND_SINGLE_MODE: ret = ox03c10_select_expand_single_mode(ox03c10, *(u32 *)arg); break; + case RKMODULE_GET_LENC_INFO: + lenc_info = (struct rkmodule_lenc_info *)arg; + lenc_info->bit_width = 10; + lenc_info->grid_num = 64; + break; + case RKMODULE_SET_LENC: + lenc_gain = (struct rkmodule_lenc_gain *)arg; + ret = ox03c10_set_lenc(ox03c10, lenc_gain); + break; default: ret = -ENOIOCTLCMD; break; @@ -4977,6 +5025,8 @@ static long ox03c10_compat_ioctl32(struct v4l2_subdev *sd, struct rkmodule_blc_info *blc_info; u32 cmps_mode; u32 single_mode; + struct rkmodule_lenc_info *lenc_info; + struct rkmodule_lenc_gain *lenc_gain; switch (cmd) { case RKMODULE_GET_MODULE_INFO: @@ -5188,6 +5238,37 @@ static long ox03c10_compat_ioctl32(struct v4l2_subdev *sd, return -EFAULT; ret = ox03c10_ioctl(sd, cmd, &single_mode); break; + case RKMODULE_GET_LENC_INFO: + lenc_info = kzalloc(sizeof(*lenc_info), GFP_KERNEL); + if (!lenc_info) { + ret = -ENOMEM; + return ret; + } + + ret = ox03c10_ioctl(sd, cmd, lenc_info); + if (!ret) { + if (copy_to_user(up, lenc_info, sizeof(*lenc_info))) { + kfree(lenc_info); + return -EFAULT; + } + } + kfree(lenc_info); + break; + case RKMODULE_SET_LENC: + lenc_gain = kzalloc(sizeof(*lenc_gain), GFP_KERNEL); + if (!lenc_gain) { + ret = -ENOMEM; + return ret; + } + + ret = ox03c10_ioctl(sd, cmd, lenc_gain); + if (!ret) { + ret = copy_to_user(up, lenc_gain, sizeof(*lenc_gain)); + if (ret) + return -EFAULT; + } + kfree(lenc_gain); + break; default: ret = -ENOIOCTLCMD; break; @@ -5257,6 +5338,16 @@ static int __ox03c10_start_stream(struct ox03c10 *ox03c10) return ret; } } + if (ox03c10->has_init_lenc_gain) { + ret = ox03c10_ioctl(&ox03c10->subdev, + RKMODULE_SET_LENC, + &ox03c10->lenc_gain); + if (ret) { + dev_err(&ox03c10->client->dev, + "init lenc_gain fail\n"); + return ret; + } + } #ifdef OX03C10_LENC_CALIBRATION ret = ox03c10_read_reg(ox03c10->client, 0x5003, OX03C10_REG_VALUE_08BIT, &val); @@ -5295,6 +5386,7 @@ static int __ox03c10_stop_stream(struct ox03c10 *ox03c10) { ox03c10->has_init_exp = false; ox03c10->has_init_wbgain = false; + ox03c10->has_init_lenc_gain = false; return ox03c10_write_reg(ox03c10->client, OX03C10_REG_CTRL_MODE, OX03C10_REG_VALUE_08BIT, OX03C10_MODE_SW_STANDBY); } @@ -5785,6 +5877,7 @@ static int ox03c10_initialize_controls(struct ox03c10 *ox03c10) ox03c10->subdev.ctrl_handler = handler; ox03c10->has_init_exp = false; ox03c10->has_init_wbgain = false; + ox03c10->has_init_lenc_gain = false; return 0; From 865a3ad68d8e10ca350d240a98be854d04d05ee1 Mon Sep 17 00:00:00 2001 From: Zefa Chen Date: Thu, 3 Jul 2025 16:09:32 +0800 Subject: [PATCH 07/28] media: i2c: 0x03c10 support check register setting crc Change-Id: I2282db13b1ebf902d1c9e30fe2086e75b4e2baeb Signed-off-by: Zefa Chen --- drivers/media/i2c/ox03c10.c | 63 +++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/drivers/media/i2c/ox03c10.c b/drivers/media/i2c/ox03c10.c index b4618b9fe531..34565e47d79a 100644 --- a/drivers/media/i2c/ox03c10.c +++ b/drivers/media/i2c/ox03c10.c @@ -25,6 +25,7 @@ #include #include #include +#include #define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x01) @@ -191,6 +192,7 @@ struct ox03c10_mode { u32 vc[PAD_MAX]; u32 exp_mode; u32 single_mode; + u32 reg_list_crc; }; struct ox03c10 { @@ -5918,6 +5920,64 @@ static int ox03c10_configure_regulators(struct ox03c10 *ox03c10) ox03c10->supplies); } +//#define OX03C10_SETTING_CRC_CHECK +#ifdef OX03C10_SETTING_CRC_CHECK +u8 *generate_byte_stream(const struct regval *regs, size_t *stream_len) +{ + size_t count = 0; + const struct regval *p = regs; + u8 *buf = NULL; + u8 *ptr = NULL; + + while (p->addr != REG_NULL) { + if (p->addr != DELAY_MS) + count += 3; + p++; + } + + buf = kmalloc(count, GFP_KERNEL); + if (!buf) + return NULL; + ptr = buf; + p = regs; + while (p->addr != REG_NULL) { + if (p->addr == DELAY_MS) { + p++; + continue; + } + *ptr++ = p->addr & 0xFF; + *ptr++ = (p->addr >> 8) & 0xFF; + *ptr++ = p->val; + p++; + } + *stream_len = count; + return buf; +} + +static void ox03c10_setting_crc_check(struct ox03c10 *ox03c10) +{ + const struct regval *regs; + size_t stream_len; + u8 *data = NULL; + int i = 0; + u32 crc; + + for (i = 0; i < ARRAY_SIZE(supported_modes); i++) { + if (supported_modes[i].hdr_mode == NO_HDR) + regs = supported_modes[i].linear_reg_list; + else + regs = supported_modes[i].reg_list; + data = generate_byte_stream(regs, &stream_len); + if (!data) + continue; + crc = crc32_le(0xFFFFFFFF, data, stream_len); + kfree(data); + dev_info(&ox03c10->client->dev, "supported_modes[%d] crc 0x%x\n", + i, crc); + } +} +#endif + static int ox03c10_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -5964,6 +6024,9 @@ static int ox03c10_probe(struct i2c_client *client, return -EINVAL; } +#ifdef OX03C10_SETTING_CRC_CHECK + ox03c10_setting_crc_check(ox03c10); +#endif ox03c10->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ox03c10->reset_gpio)) dev_warn(dev, "Failed to get reset-gpios\n"); From cb1cfc06030b7a34ba636165809a00f5591745b2 Mon Sep 17 00:00:00 2001 From: Zefa Chen Date: Mon, 7 Jul 2025 14:11:29 +0800 Subject: [PATCH 08/28] include: uapi: rk-camera-module.h add RKMODULE_SET_REG_SETTING Change-Id: Ic3728af9aeeec691e17fdacd14c0db75d5ca5e25 Signed-off-by: Zefa Chen --- include/uapi/linux/rk-camera-module.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/include/uapi/linux/rk-camera-module.h b/include/uapi/linux/rk-camera-module.h index 603700d77d20..4afdf5eb44fb 100644 --- a/include/uapi/linux/rk-camera-module.h +++ b/include/uapi/linux/rk-camera-module.h @@ -241,6 +241,9 @@ #define RKMODULE_GET_LENC_INFO \ _IOR('V', BASE_VIDIOC_PRIVATE + 59, struct rkmodule_lenc_info) +#define RKMODULE_SET_REG_SETTING \ + _IOW('V', BASE_VIDIOC_PRIVATE + 60, struct rkmodule_reg_setting) + struct rkmodule_i2cdev_info { __u8 slave_addr; } __attribute__ ((packed)); @@ -1039,4 +1042,23 @@ struct rkmodule_lenc_info { __u32 reserved[8]; }; +struct rkmodule_reg_struct { + __u32 reg_addr; + __u32 reg_val; +}; + +enum rkmodule_binning_mode { + BAYER_BINNING_2X2, + BAYER_SKIP_2X2, + QBC_BINNING_2X2, +}; + +#define RKMODULE_REG_LIST_MAX (16) +struct rkmodule_reg_setting { + __u32 setting_id; + __u32 binning_mode; + __u32 reg_num; + struct rkmodule_reg_struct reg_list[RKMODULE_REG_LIST_MAX]; +}; + #endif /* _UAPI_RKMODULE_CAMERA_H */ From a7f586033700dd097959f2dc07e6138c3e533177 Mon Sep 17 00:00:00 2001 From: Zefa Chen Date: Wed, 9 Jul 2025 16:39:27 +0800 Subject: [PATCH 09/28] media: i2c: ox03c10 support set reg_setting config Change-Id: I9950c2fdb4928cc2fc173485579665176e8c12ce Signed-off-by: Zefa Chen --- drivers/media/i2c/ox03c10.c | 58 +++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/drivers/media/i2c/ox03c10.c b/drivers/media/i2c/ox03c10.c index 34565e47d79a..2da66dca24a5 100644 --- a/drivers/media/i2c/ox03c10.c +++ b/drivers/media/i2c/ox03c10.c @@ -3951,6 +3951,7 @@ static const struct ox03c10_mode supported_modes[] = { .vc[PAD0] = 0, .exp_mode = EXP_NORMAL, .single_mode = EXPAND_SINGLE_LCG, + .reg_list_crc = 0x565b8838, }, { .bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10, @@ -3972,6 +3973,7 @@ static const struct ox03c10_mode supported_modes[] = { .vc[PAD0] = 0, .exp_mode = EXP_NORMAL, .single_mode = EXPAND_SINGLE_HCG, + .reg_list_crc = 0x318514f6, }, { .bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10, @@ -3993,6 +3995,7 @@ static const struct ox03c10_mode supported_modes[] = { .vc[PAD0] = 0, .exp_mode = EXP_NORMAL, .single_mode = EXPAND_SINGLE_VS, + .reg_list_crc = 0x99e6b1a4, }, { .bus_fmt = MEDIA_BUS_FMT_SBGGR10_1X10, @@ -4014,6 +4017,7 @@ static const struct ox03c10_mode supported_modes[] = { .vc[PAD0] = 0, .exp_mode = EXP_NORMAL, .single_mode = EXPAND_SINGLE_SPD, + .reg_list_crc = 0xfe382d6a, }, { .bus_fmt = MEDIA_BUS_FMT_SBGGR12_1X12, @@ -4034,6 +4038,7 @@ static const struct ox03c10_mode supported_modes[] = { .hdr_operating_mode = OX03C10_HDR3_DCG_VS_12BIT, .vc[PAD0] = 0, .exp_mode = EXP_HDR3_DCG_VS, + .reg_list_crc = 0xc1e4ba5d, }, { .bus_fmt = MEDIA_BUS_FMT_SBGGR16_1X16, @@ -4057,6 +4062,7 @@ static const struct ox03c10_mode supported_modes[] = { .vc[PAD2] = 2, .vc[PAD3] = 3, .exp_mode = EXP_HDR3_DCG_VS, + .reg_list_crc = 0xa9ae5cf, }, { .bus_fmt = MEDIA_BUS_FMT_SBGGR12_1X12, @@ -4077,6 +4083,7 @@ static const struct ox03c10_mode supported_modes[] = { .hdr_operating_mode = OX03C10_HDR3_DCG_SPD_12BIT, .vc[PAD0] = 0, .exp_mode = EXP_HDR3_DCG_SPD, + .reg_list_crc = 0x71fab602, }, }; @@ -4879,6 +4886,36 @@ static int ox03c10_set_lenc(struct ox03c10 *ox03c10, return ret; } +static int ox03c10_set_reg_setting(struct ox03c10 *ox03c10, + struct rkmodule_reg_setting *reg_setting) +{ + int i = 0; + int ret = 0; + + if (ox03c10->cur_mode->reg_list_crc != reg_setting->setting_id) { + dev_info(&ox03c10->client->dev, + "cis scene not match, local: 0x%x, aiq: 0x%x\n", + ox03c10->cur_mode->reg_list_crc, + reg_setting->setting_id); + return -EINVAL; + } + if (reg_setting->reg_num > RKMODULE_REG_LIST_MAX) { + dev_info(&ox03c10->client->dev, + "reg_setting reg_num %d, bigger than support\n", + reg_setting->reg_num); + return -EINVAL; + } + for (i = 0; i < reg_setting->reg_num; i++) { + ret |= ox03c10_write_reg(ox03c10->client, reg_setting->reg_list[i].reg_addr, + OX03C10_REG_VALUE_08BIT, reg_setting->reg_list[i].reg_val); + dev_dbg(&ox03c10->client->dev, + "reg_setting reg write 0x%x, 0x%x\n", + reg_setting->reg_list[i].reg_addr, + reg_setting->reg_list[i].reg_val); + } + return ret; +} + static long ox03c10_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { struct ox03c10 *ox03c10 = to_ox03c10(sd); @@ -4895,6 +4932,7 @@ static long ox03c10_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) struct rkmodule_blc_info *blc_info; struct rkmodule_lenc_info *lenc_info; struct rkmodule_lenc_gain *lenc_gain; + struct rkmodule_reg_setting *reg_setting; switch (cmd) { case RKMODULE_GET_MODULE_INFO: @@ -4999,6 +5037,10 @@ static long ox03c10_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) lenc_gain = (struct rkmodule_lenc_gain *)arg; ret = ox03c10_set_lenc(ox03c10, lenc_gain); break; + case RKMODULE_SET_REG_SETTING: + reg_setting = (struct rkmodule_reg_setting *)arg; + ret = ox03c10_set_reg_setting(ox03c10, reg_setting); + break; default: ret = -ENOIOCTLCMD; break; @@ -5029,6 +5071,7 @@ static long ox03c10_compat_ioctl32(struct v4l2_subdev *sd, u32 single_mode; struct rkmodule_lenc_info *lenc_info; struct rkmodule_lenc_gain *lenc_gain; + struct rkmodule_reg_setting *reg_setting; switch (cmd) { case RKMODULE_GET_MODULE_INFO: @@ -5271,6 +5314,21 @@ static long ox03c10_compat_ioctl32(struct v4l2_subdev *sd, } kfree(lenc_gain); break; + case RKMODULE_SET_REG_SETTING: + reg_setting = kzalloc(sizeof(*reg_setting), GFP_KERNEL); + if (!reg_setting) { + ret = -ENOMEM; + return ret; + } + + ret = ox03c10_ioctl(sd, cmd, reg_setting); + if (!ret) { + ret = copy_to_user(up, reg_setting, sizeof(*reg_setting)); + if (ret) + return -EFAULT; + } + kfree(reg_setting); + break; default: ret = -ENOIOCTLCMD; break; From 5d98715633f18aa0d2cde844de6c2d8f6cb66e4e Mon Sep 17 00:00:00 2001 From: Zefa Chen Date: Thu, 10 Jul 2025 09:15:12 +0800 Subject: [PATCH 10/28] include: uapi: rk-camera-module.h: RKMODULE_SET_BLC add more param Change-Id: Id56e2591124832631aaf3be0658a558e3c7b10cb Signed-off-by: Zefa Chen --- include/uapi/linux/rk-camera-module.h | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/include/uapi/linux/rk-camera-module.h b/include/uapi/linux/rk-camera-module.h index 4afdf5eb44fb..fb531a999bec 100644 --- a/include/uapi/linux/rk-camera-module.h +++ b/include/uapi/linux/rk-camera-module.h @@ -244,6 +244,12 @@ #define RKMODULE_SET_REG_SETTING \ _IOW('V', BASE_VIDIOC_PRIVATE + 60, struct rkmodule_reg_setting) +#define RKMODULE_REG_LIST_MAX (16) +struct rkmodule_reg_struct { + __u32 reg_addr; + __u32 reg_val; +}; + struct rkmodule_i2cdev_info { __u8 slave_addr; } __attribute__ ((packed)); @@ -976,9 +982,15 @@ enum rkmodule_blc_type { }; struct rkmodule_blc_group { + __u32 enable; __u32 group_num; enum rkmodule_blc_type blc_type[RKMODULE_MAX_BLC_GROUP]; __u32 blc[RKMODULE_MAX_BLC_GROUP]; + __u32 bkdg_sw_en; + __u32 dgbk2bkdg_thred; + __u32 bkdg2dgbk_thred; + __u32 reg_num; + struct rkmodule_reg_struct reg_list[RKMODULE_REG_LIST_MAX]; }; enum rkmodule_bayer_mode { @@ -1042,18 +1054,12 @@ struct rkmodule_lenc_info { __u32 reserved[8]; }; -struct rkmodule_reg_struct { - __u32 reg_addr; - __u32 reg_val; -}; - enum rkmodule_binning_mode { BAYER_BINNING_2X2, BAYER_SKIP_2X2, QBC_BINNING_2X2, }; -#define RKMODULE_REG_LIST_MAX (16) struct rkmodule_reg_setting { __u32 setting_id; __u32 binning_mode; From c2d1b044eeb3b71acbe9eec80d65df4b5efcedfa Mon Sep 17 00:00:00 2001 From: Zefa Chen Date: Wed, 30 Jul 2025 16:12:51 +0800 Subject: [PATCH 11/28] media: i2c: ox03c10 add more control of blc Change-Id: I6e07f4dff54600499e5db7900668971ede60d8d1 Signed-off-by: Zefa Chen --- drivers/media/i2c/ox03c10.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/drivers/media/i2c/ox03c10.c b/drivers/media/i2c/ox03c10.c index 2da66dca24a5..bd09b544a9f0 100644 --- a/drivers/media/i2c/ox03c10.c +++ b/drivers/media/i2c/ox03c10.c @@ -229,9 +229,11 @@ struct ox03c10 { const char *len_name; bool has_init_exp; bool has_init_wbgain; + bool has_init_blc; bool has_init_lenc_gain; struct preisp_hdrae_exp_s init_hdrae_exp; struct rkmodule_wb_gain_group init_wbgain; + struct rkmodule_blc_group init_blc; struct rkmodule_dcg_ratio dcg_ratio; struct rkmodule_dcg_ratio spd_ratio; struct rkmodule_lenc_gain lenc_gain; @@ -4701,6 +4703,16 @@ static int ox03c10_set_blc(struct ox03c10 *ox03c10, int i = 0; int ret = 0; + if (!blc_group->enable) + return 0; + + if (!ox03c10->has_init_blc && !ox03c10->streaming) { + ox03c10->init_blc = *blc_group; + ox03c10->has_init_blc = true; + dev_dbg(&ox03c10->client->dev, "ox03c10 don't stream, record blc!\n"); + return ret; + } + for (i = 0; i < blc_group->group_num; i++) { switch (blc_group->blc_type[i]) { case RKMODULE_HCG_BLC: @@ -4733,6 +4745,15 @@ static int ox03c10_set_blc(struct ox03c10 *ox03c10, blc_group->blc_type[i], blc_val); #endif } + if (blc_group->reg_num > RKMODULE_REG_LIST_MAX) { + dev_info(&ox03c10->client->dev, + "reg_list size error %d\n", + blc_group->reg_num); + return -EINVAL; + } + for (i = 0; i < blc_group->reg_num; i++) + ret |= ox03c10_write_reg(ox03c10->client, blc_group->reg_list[i].reg_addr, + OX03C10_REG_VALUE_16BIT, blc_group->reg_list[i].reg_val); return ret; } @@ -5398,6 +5419,16 @@ static int __ox03c10_start_stream(struct ox03c10 *ox03c10) return ret; } } + if (ox03c10->has_init_blc) { + ret = ox03c10_ioctl(&ox03c10->subdev, + RKMODULE_SET_BLC, + &ox03c10->init_blc); + if (ret) { + dev_err(&ox03c10->client->dev, + "init blc fail\n"); + return ret; + } + } if (ox03c10->has_init_lenc_gain) { ret = ox03c10_ioctl(&ox03c10->subdev, RKMODULE_SET_LENC, @@ -5446,6 +5477,7 @@ static int __ox03c10_stop_stream(struct ox03c10 *ox03c10) { ox03c10->has_init_exp = false; ox03c10->has_init_wbgain = false; + ox03c10->has_init_blc = false; ox03c10->has_init_lenc_gain = false; return ox03c10_write_reg(ox03c10->client, OX03C10_REG_CTRL_MODE, OX03C10_REG_VALUE_08BIT, OX03C10_MODE_SW_STANDBY); @@ -5937,6 +5969,7 @@ static int ox03c10_initialize_controls(struct ox03c10 *ox03c10) ox03c10->subdev.ctrl_handler = handler; ox03c10->has_init_exp = false; ox03c10->has_init_wbgain = false; + ox03c10->has_init_blc = false; ox03c10->has_init_lenc_gain = false; return 0; From f0542d8d9c94a9fedb71e78e1288949c4a1d7e4a Mon Sep 17 00:00:00 2001 From: Zefa Chen Date: Thu, 10 Jul 2025 09:38:49 +0800 Subject: [PATCH 12/28] include: uapi: rk-camera-module.h add cmd RKMODULE_SET_BAYER_MODE Change-Id: Ib7acc3dacefaebf5a2394ac6c04c660f1cb75ff0 Signed-off-by: Zefa Chen --- include/uapi/linux/rk-camera-module.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/uapi/linux/rk-camera-module.h b/include/uapi/linux/rk-camera-module.h index fb531a999bec..3bcca0e36a47 100644 --- a/include/uapi/linux/rk-camera-module.h +++ b/include/uapi/linux/rk-camera-module.h @@ -244,6 +244,9 @@ #define RKMODULE_SET_REG_SETTING \ _IOW('V', BASE_VIDIOC_PRIVATE + 60, struct rkmodule_reg_setting) +#define RKMODULE_SET_BAYER_MODE \ + _IOW('V', BASE_VIDIOC_PRIVATE + 61, struct rkmodule_bayer_param) + #define RKMODULE_REG_LIST_MAX (16) struct rkmodule_reg_struct { __u32 reg_addr; @@ -1067,4 +1070,10 @@ struct rkmodule_reg_setting { struct rkmodule_reg_struct reg_list[RKMODULE_REG_LIST_MAX]; }; +struct rkmodule_bayer_param { + __u32 bayer_mode; + __u32 reg_num; + struct rkmodule_reg_struct reg_list[RKMODULE_REG_LIST_MAX]; +}; + #endif /* _UAPI_RKMODULE_CAMERA_H */ From 719de208c4e89603147e874ecf06df2ef0f5c137 Mon Sep 17 00:00:00 2001 From: Zefa Chen Date: Wed, 30 Jul 2025 16:55:47 +0800 Subject: [PATCH 13/28] include: uapi: rk-camera-module.h add cmd RKMODULE_GET_HDR_COMPR_SINGLE_FRAME_INFO Change-Id: I7956a4d885302d344341b995a202a9b9c28c785a Signed-off-by: Zefa Chen --- include/uapi/linux/rk-camera-module.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/uapi/linux/rk-camera-module.h b/include/uapi/linux/rk-camera-module.h index 3bcca0e36a47..35bd79d19f14 100644 --- a/include/uapi/linux/rk-camera-module.h +++ b/include/uapi/linux/rk-camera-module.h @@ -247,6 +247,9 @@ #define RKMODULE_SET_BAYER_MODE \ _IOW('V', BASE_VIDIOC_PRIVATE + 61, struct rkmodule_bayer_param) +#define RKMODULE_GET_HDR_COMPR_SINGLE_FRAME_INFO \ + _IOR('V', BASE_VIDIOC_PRIVATE + 62, struct rkmodule_hdr_compr_single_frame_info) + #define RKMODULE_REG_LIST_MAX (16) struct rkmodule_reg_struct { __u32 reg_addr; @@ -1076,4 +1079,9 @@ struct rkmodule_bayer_param { struct rkmodule_reg_struct reg_list[RKMODULE_REG_LIST_MAX]; }; +struct rkmodule_hdr_compr_single_frame_info { + __u32 single_bitwidth; + __u32 reserved[8]; +}; + #endif /* _UAPI_RKMODULE_CAMERA_H */ From afe3d7cd5d8ac4052ac5e3d60498939eddf639f7 Mon Sep 17 00:00:00 2001 From: Zefa Chen Date: Wed, 30 Jul 2025 17:06:04 +0800 Subject: [PATCH 14/28] media: i2c: ox03c10 support get single frame info of hdr_compr Change-Id: Icc48f81decc71a2ecf392d8867cf91cdbb36f23a Signed-off-by: Zefa Chen --- drivers/media/i2c/ox03c10.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/media/i2c/ox03c10.c b/drivers/media/i2c/ox03c10.c index bd09b544a9f0..26b92592e239 100644 --- a/drivers/media/i2c/ox03c10.c +++ b/drivers/media/i2c/ox03c10.c @@ -4954,6 +4954,7 @@ static long ox03c10_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) struct rkmodule_lenc_info *lenc_info; struct rkmodule_lenc_gain *lenc_gain; struct rkmodule_reg_setting *reg_setting; + struct rkmodule_hdr_compr_single_frame_info *single_frame_info; switch (cmd) { case RKMODULE_GET_MODULE_INFO: @@ -5062,6 +5063,10 @@ static long ox03c10_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) reg_setting = (struct rkmodule_reg_setting *)arg; ret = ox03c10_set_reg_setting(ox03c10, reg_setting); break; + case RKMODULE_GET_HDR_COMPR_SINGLE_FRAME_INFO: + single_frame_info = (struct rkmodule_hdr_compr_single_frame_info *)arg; + single_frame_info->single_bitwidth = 10; + break; default: ret = -ENOIOCTLCMD; break; @@ -5093,6 +5098,7 @@ static long ox03c10_compat_ioctl32(struct v4l2_subdev *sd, struct rkmodule_lenc_info *lenc_info; struct rkmodule_lenc_gain *lenc_gain; struct rkmodule_reg_setting *reg_setting; + struct rkmodule_hdr_compr_single_frame_info *single_frame_info; switch (cmd) { case RKMODULE_GET_MODULE_INFO: @@ -5350,6 +5356,22 @@ static long ox03c10_compat_ioctl32(struct v4l2_subdev *sd, } kfree(reg_setting); break; + case RKMODULE_GET_HDR_COMPR_SINGLE_FRAME_INFO: + single_frame_info = kzalloc(sizeof(*single_frame_info), GFP_KERNEL); + if (!single_frame_info) { + ret = -ENOMEM; + return ret; + } + + ret = ox03c10_ioctl(sd, cmd, single_frame_info); + if (!ret) { + if (copy_to_user(up, single_frame_info, sizeof(*single_frame_info))) { + kfree(single_frame_info); + return -EFAULT; + } + } + kfree(single_frame_info); + break; default: ret = -ENOIOCTLCMD; break; From 542555f717ca4a9439144b76f331dab276295025 Mon Sep 17 00:00:00 2001 From: Zefa Chen Date: Wed, 30 Jul 2025 17:15:09 +0800 Subject: [PATCH 15/28] include: uapi: rk-camera-module.h add more exp_mode of lofic Change-Id: I88e3a704b6c123152c5696ab96a91b9b882c1c01 Signed-off-by: Zefa Chen --- include/uapi/linux/rk-camera-module.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/uapi/linux/rk-camera-module.h b/include/uapi/linux/rk-camera-module.h index 35bd79d19f14..79e5e0526eb9 100644 --- a/include/uapi/linux/rk-camera-module.h +++ b/include/uapi/linux/rk-camera-module.h @@ -561,6 +561,8 @@ enum exp_mode_e { EXP_HDR3_DCG_VS, EXP_HDR3_DCG_SPD, EXP_HDR3_STA, + EXP_HDR3_DCG_LOFIC, + EXP_HDR3_LCG_LOFIC_VS, }; struct rkmodule_hdr_cfg { From f14326d041e80656b685c23e2e82390b79d4ad55 Mon Sep 17 00:00:00 2001 From: Hu Kejun Date: Tue, 19 Nov 2024 14:36:19 +0800 Subject: [PATCH 16/28] include: rk_vcm_head: add zoom1 field Signed-off-by: Hu Kejun Change-Id: Iecdce3bcd92d14b6763999464c4526c880e9729e --- include/uapi/linux/rk_vcm_head.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/uapi/linux/rk_vcm_head.h b/include/uapi/linux/rk_vcm_head.h index 4e663ae260d8..dabac77f532d 100644 --- a/include/uapi/linux/rk_vcm_head.h +++ b/include/uapi/linux/rk_vcm_head.h @@ -100,11 +100,13 @@ struct rk_cam_set_focus { struct rk_cam_zoom_pos { __s32 zoom_pos; + __s32 zoom1_pos; __s32 focus_pos; }; struct rk_cam_set_zoom { _Bool is_need_zoom_reback; + _Bool is_need_zoom1_reback; _Bool is_need_focus_reback; __u32 setzoom_cnt; struct rk_cam_zoom_pos zoom_pos[VCMDRV_SETZOOM_MAXCNT]; From 20a2d77c3f4c8b8e71d755d9b4c7339936094525 Mon Sep 17 00:00:00 2001 From: Hu Kejun Date: Tue, 29 Jul 2025 09:10:13 +0800 Subject: [PATCH 17/28] media: spi: add ms41908 and ms41968 Change-Id: Ib2345cb0c53783e5160600d8711b49c96661d3b9 Signed-off-by: Hu Kejun --- drivers/media/spi/Kconfig | 14 + drivers/media/spi/Makefile | 2 + drivers/media/spi/ms41908.c | 3103 +++++++++++++++++++++++++++++++ drivers/media/spi/ms41968.c | 3503 +++++++++++++++++++++++++++++++++++ 4 files changed, 6622 insertions(+) create mode 100644 drivers/media/spi/ms41908.c create mode 100644 drivers/media/spi/ms41968.c diff --git a/drivers/media/spi/Kconfig b/drivers/media/spi/Kconfig index eedcd8274412..b0ad060ade9c 100644 --- a/drivers/media/spi/Kconfig +++ b/drivers/media/spi/Kconfig @@ -31,6 +31,20 @@ config VIDEO_ROCKCHIP_PREISP help Support for Pre-isp on the rockchip SoC. +config VIDEO_MS41908 + tristate "Relmon MS41908 motor driver" + depends on SPI && VIDEO_DEV + select VIDEO_V4L2_SUBDEV_API + help + Support motor driver By MS41908 for camera Lens. + +config VIDEO_MS41968 + tristate "Relmon MS41968 motor driver" + depends on SPI && VIDEO_DEV + select VIDEO_V4L2_SUBDEV_API + help + Support motor driver By MS41968 for camera Lens. + endmenu endif diff --git a/drivers/media/spi/Makefile b/drivers/media/spi/Makefile index dcc966fd1118..214aa3ee1295 100644 --- a/drivers/media/spi/Makefile +++ b/drivers/media/spi/Makefile @@ -8,3 +8,5 @@ obj-$(CONFIG_CXD2880_SPI_DRV) += cxd2880-spi.o obj-$(CONFIG_VIDEO_GS1662) += gs1662.o obj-$(CONFIG_VIDEO_ROCKCHIP_PREISP) += rk1608.o rk1608_dphy.o rk1608-objs += rk1608_dev.o rk1608_core.o +obj-$(CONFIG_VIDEO_MS41908) += ms41908.o +obj-$(CONFIG_VIDEO_MS41968) += ms41968.o diff --git a/drivers/media/spi/ms41908.c b/drivers/media/spi/ms41908.c new file mode 100644 index 000000000000..744dec24997e --- /dev/null +++ b/drivers/media/spi/ms41908.c @@ -0,0 +1,3103 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * motor driver + * + * Copyright (C) 2020 Rockchip Electronics Co., Ltd. + * + */ +//#define DEBUG +//#define READREG_DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "linux/rk_vcm_head.h" +#include +#include + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x00) + +#define DRIVER_NAME "ms41908" + +#define PSUMAB 0X24 +#define INTCTAB 0X25 +#define PSUMCD 0X29 +#define INTCTCD 0X2A + +#define START_UP_HZ_DEF (800) +#define PIRIS_MAX_STEP_DEF (80) +#define FOCUS_MAX_STEP_DEF (3060) +#define ZOOM_MAX_STEP_DEF (1520) + +#define DCIRIS_MAX_LOG 1023 + +#define VD_FZ_US 10000 + +#define PPW_DEF 0xff +#define MICRO_DEF 64 +#define PHMODE_DEF 0 +#define PPW_STOP 0x00 + +#define FOCUS_MAX_BACK_DELAY 4 +#define ZOOM_MAX_BACK_DELAY 4 +#define ZOOM1_MAX_BACK_DELAY 4 + +#define SYS_CLK_DEF_27MHZ 27 + +#define PIRIS_FINDPI_STEP 10 +#define FOCUS_FINDPI_STEP 200 +#define ZOOM_FINDPI_STEP 200 +#define ZOOM1_FINDPI_STEP 200 + +#define to_motor_dev(sd) container_of(sd, struct motor_dev, subdev) + +enum { + MOTOR_STATUS_STOPPED = 0, + MOTOR_STATUS_CW = 1, + MOTOR_STATUS_CCW = 2, +}; + +enum ext_dev_type { + TYPE_IRIS = 0, + TYPE_FOCUS = 1, + TYPE_ZOOM = 2, + TYPE_ZOOM1 = 3, +}; + +struct motor_reg_s { + u16 dt2_phmod; + u16 ppw; + u16 psum; + u16 intct; +}; + +struct reg_op_s { + struct motor_reg_s reg; + u16 tmp_psum; + bool is_used; +}; + +struct run_data_s { + u32 count; + u32 cur_count; + u32 psum; + u32 psum_last; + u32 intct; + u32 ppw; + u32 ppw_stop; + u32 phmode; + u32 micro; +}; + +struct ext_dev { + u8 type; + u32 step_max; + int last_pos; + u32 start_up_speed; + u32 move_status; + u32 reback_status; + u32 move_time_us; + u32 reback_move_time_us; + u32 backlash; + int reback; + u32 last_dir; + int min_pos; + int max_pos; + bool is_half_step_mode; + bool is_mv_tim_update; + bool is_need_update_tim; + bool is_dir_opp; + bool is_need_reback; + bool reback_ctrl; + bool is_pihigh_positive_pos; + u32 findpi_step; + struct rk_cam_vcm_tim mv_tim; + struct run_data_s run_data; + struct run_data_s reback_data; + struct completion complete; + struct reg_op_s *reg_op; + struct gpio_desc *pic_gpio; + struct gpio_desc *pia_gpio; + struct gpio_desc *pie_gpio; + int cur_back_delay; + int max_back_delay; + struct completion complete_out; + bool is_running; + + u32 default_psum; + u32 default_intct; +}; + +struct dciris_dev { + u32 last_log; + u32 max_log; + bool is_reversed_polarity; + struct gpio_desc *vd_iris_gpio; +}; + +struct motor_dev { + struct spi_device *spi; + struct v4l2_subdev subdev; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *iris_ctrl; + struct v4l2_ctrl *focus_ctrl; + struct v4l2_ctrl *zoom_ctrl; + struct v4l2_ctrl *zoom1_ctrl; + struct hrtimer timer; + struct mutex mutex; + u32 module_index; + const char *module_facing; + struct ext_dev *piris; + struct ext_dev *focus; + struct ext_dev *zoom; + struct ext_dev *zoom1; + struct ext_dev *dev0; + struct ext_dev *dev1; + bool is_use_dc_iris; + bool is_use_p_iris; + bool is_use_focus; + bool is_use_zoom; + bool is_use_zoom1; + struct gpio_desc *reset_gpio; + struct gpio_desc *vd_fz_gpio; + bool is_timer_restart; + bool is_timer_restart_bywq; + bool is_should_wait; + struct motor_work_s *wk; + u32 vd_fz_period_us; + struct reg_op_s motor_op[2]; + struct dciris_dev *dciris; + int id; + int wait_cnt; + int pi_gpio_usecnt; + u32 sys_clk; +}; + +struct motor_work_s { + struct work_struct work; + struct motor_dev *dev; +}; + +static const struct reg_op_s g_motor_op[2] = {{{0x22, 0x23, 0x24, 0x25}, 0, 0}, + {{0x27, 0x28, 0x29, 0x2a}, 0, 0}}; + +static int spi_write_reg(struct spi_device *spi, u8 reg, u16 val) +{ + int ret = 0; + u8 buf_reg = reg; + u16 buf_val = val; + + struct spi_message msg; + struct spi_transfer tx[] = { + { + .tx_buf = &buf_reg, + .len = 1, + .delay.value = 1, + .delay.unit = SPI_DELAY_UNIT_USECS, + }, { + .tx_buf = &buf_val, + .len = 2, + .delay.value = 1, + .delay.unit = SPI_DELAY_UNIT_USECS, + }, + }; + spi_message_init(&msg); + spi_message_add_tail(&tx[0], &msg); + spi_message_add_tail(&tx[1], &msg); + ret = spi_sync(spi, &msg); + return ret; +} + +static __maybe_unused int spi_read_reg(struct spi_device *spi, u8 reg, u16 *val) +{ + int ret = 0; + u8 buf_reg = reg | 0x40; + u16 buf_val = 0; + + struct spi_message msg; + struct spi_transfer tx[] = { + { + .tx_buf = &buf_reg, + .len = 1, + .delay.value = 1, + .delay.unit = SPI_DELAY_UNIT_USECS, + }, { + .rx_buf = &buf_val, + .len = 2, + .delay.value = 1, + .delay.unit = SPI_DELAY_UNIT_USECS, + }, + }; + spi_message_init(&msg); + spi_message_add_tail(&tx[0], &msg); + spi_message_add_tail(&tx[1], &msg); + ret = spi_sync(spi, &msg); + *val = buf_val; + + return ret; +} + +static int set_motor_running_status(struct motor_dev *motor, + struct ext_dev *ext_dev, + s32 pos, + bool is_need_update_tim, + bool is_should_wait, + bool is_need_reback) +{ + int ret = 0; + u32 step = 0; + u16 psum = 0; + struct run_data_s run_data = ext_dev->run_data; + u32 micro = 0; + u32 mv_cnt = 0; + int status = 0; + + if (ext_dev->move_status != MOTOR_STATUS_STOPPED) + wait_for_completion(&ext_dev->complete); + ext_dev->is_mv_tim_update = false; + + ext_dev->move_time_us = 0; + mv_cnt = abs(pos - ext_dev->last_pos); + if (is_need_reback) + mv_cnt += ext_dev->reback; + dev_dbg(&motor->spi->dev, + "dev type %d pos %d, last_pos %d, mv_cnt %d, status %d is_need_reback %d\n", + ext_dev->type, pos, ext_dev->last_pos, mv_cnt, status, is_need_reback); + if (mv_cnt == 0) { + mutex_lock(&motor->mutex); + if (is_need_update_tim) { + ext_dev->mv_tim.vcm_start_t = ns_to_kernel_old_timeval(ktime_get_ns()); + ext_dev->mv_tim.vcm_end_t = ext_dev->mv_tim.vcm_start_t; + ext_dev->is_mv_tim_update = true; + if (motor->wait_cnt == 0) { + mutex_unlock(&motor->mutex); + return 0; + } + } + if (is_should_wait) { + motor->wait_cnt++; + } else if (motor->is_timer_restart == false && motor->wait_cnt) { + motor->is_timer_restart = true; + motor->is_timer_restart_bywq = false; + hrtimer_start(&motor->timer, ktime_set(0, 0), HRTIMER_MODE_REL); + motor->wait_cnt = 0; + } else { + motor->wait_cnt = 0; + } + mutex_unlock(&motor->mutex); + return 0; + } + ext_dev->is_running = true; + reinit_completion(&ext_dev->complete); + reinit_completion(&ext_dev->complete_out); + + if (ext_dev->is_dir_opp) { + if (pos > ext_dev->last_pos) { + if (ext_dev->last_dir == MOTOR_STATUS_CW) + mv_cnt += ext_dev->backlash; + status = MOTOR_STATUS_CCW; + } else { + if (ext_dev->last_dir == MOTOR_STATUS_CCW) + mv_cnt += ext_dev->backlash; + status = MOTOR_STATUS_CW; + } + } else { + if (pos > ext_dev->last_pos) { + if (ext_dev->last_dir == MOTOR_STATUS_CCW) + mv_cnt += ext_dev->backlash; + status = MOTOR_STATUS_CW; + } else { + if (ext_dev->last_dir == MOTOR_STATUS_CW) + mv_cnt += ext_dev->backlash; + status = MOTOR_STATUS_CCW; + } + } + + if (ext_dev->is_half_step_mode) + step = mv_cnt * 4; + else + step = mv_cnt * 8; + + run_data.count = (step + run_data.psum - 1) / run_data.psum; + run_data.cur_count = run_data.count; + run_data.psum_last = step % run_data.psum; + if (run_data.psum_last == 0) + run_data.psum_last = run_data.psum; + + switch (run_data.micro) { + case 64: + micro = 0x03; + break; + case 128: + micro = 0x02; + break; + case 256: + micro = 0x00; + break; + default: + micro = 0x00; + break; + }; + if (run_data.count == 1) + psum = ((status - 1) << 8) | + (1 << 10) | + (micro << 12) | + (run_data.psum_last); + else + psum = ((status - 1) << 8) | + (1 << 10) | + (micro << 12) | + (run_data.psum); + mutex_lock(&motor->mutex); + ext_dev->is_need_update_tim = is_need_update_tim; + ext_dev->is_need_reback = is_need_reback; + ext_dev->move_time_us = (run_data.count + 1) * (motor->vd_fz_period_us + 500); + if (is_need_reback) + ext_dev->move_time_us += ext_dev->reback_move_time_us; + + ext_dev->last_pos = pos; + ext_dev->run_data = run_data; + ext_dev->move_status = status; + spi_write_reg(motor->spi, 0x20, 0x1a01); + spi_write_reg(motor->spi, ext_dev->reg_op->reg.ppw, run_data.ppw | (run_data.ppw << 8)); + spi_write_reg(motor->spi, ext_dev->reg_op->reg.psum, psum); + spi_write_reg(motor->spi, ext_dev->reg_op->reg.intct, ext_dev->run_data.intct); + ext_dev->reg_op->tmp_psum = psum; + + ext_dev->last_dir = status; + if (is_should_wait) { + motor->wait_cnt++; + } else if (motor->is_timer_restart == false) { + motor->is_timer_restart = true; + motor->is_timer_restart_bywq = false; + hrtimer_start(&motor->timer, ktime_set(0, 0), HRTIMER_MODE_REL); + motor->wait_cnt = 0; + } else { + motor->wait_cnt = 0; + } + mutex_unlock(&motor->mutex); + dev_dbg(&motor->spi->dev, + "ext_dev type %d move count %d, psum %d, psum_last %d, move_time_us %u!\n", + ext_dev->type, + ext_dev->run_data.count, + ext_dev->run_data.psum, + ext_dev->run_data.psum_last, + ext_dev->move_time_us); + + return ret; +} + +static int ms41908_dev_parse_dt(struct motor_dev *motor) +{ + struct device_node *node = motor->spi->dev.of_node; + int ret = 0; + const char *str; + int step_motor_cnt = 0; + + motor->is_use_dc_iris = + device_property_read_bool(&motor->spi->dev, "use-dc-iris"); + motor->is_use_p_iris = + device_property_read_bool(&motor->spi->dev, "use-p-iris"); + motor->is_use_focus = + device_property_read_bool(&motor->spi->dev, "use-focus"); + motor->is_use_zoom = + device_property_read_bool(&motor->spi->dev, "use-zoom"); + motor->is_use_zoom1 = + device_property_read_bool(&motor->spi->dev, "use-zoom1"); + + ret = of_property_read_u32(node, + "sys-clk", + &motor->sys_clk); + if (ret != 0) { + motor->sys_clk = SYS_CLK_DEF_27MHZ; + dev_err(&motor->spi->dev, + "failed get sys clk,use dafult value %d MHz\n", SYS_CLK_DEF_27MHZ); + } + /* get reset gpio */ + motor->reset_gpio = devm_gpiod_get(&motor->spi->dev, + "reset", GPIOD_OUT_LOW); + if (IS_ERR(motor->reset_gpio)) + dev_err(&motor->spi->dev, "Failed to get reset-gpios\n"); + + /* get vd_fz gpio */ + motor->vd_fz_gpio = devm_gpiod_get(&motor->spi->dev, + "vd_fz", GPIOD_OUT_LOW); + if (IS_ERR(motor->vd_fz_gpio)) + dev_info(&motor->spi->dev, "Failed to get vd_fz-gpios\n"); + + ret = of_property_read_u32(node, + "vd_fz-period-us", + &motor->vd_fz_period_us); + if (ret != 0) { + motor->vd_fz_period_us = VD_FZ_US; + dev_err(&motor->spi->dev, + "failed get vd_fz-period-us,use dafult value\n"); + } + + ret = of_property_read_u32(node, + "id", + &motor->id); + if (ret != 0) { + motor->id = 0; + dev_err(&motor->spi->dev, + "failed get driver id,use dafult value\n"); + } + + if (motor->is_use_dc_iris) { + motor->dciris = devm_kzalloc(&motor->spi->dev, sizeof(*motor->dciris), GFP_KERNEL); + if (!motor->dciris) { + dev_err(&motor->spi->dev, + "__line__ %d, devm_kzalloc return fail!\n", __LINE__); + return -ENOMEM; + } + /* get vd_iris gpio */ + motor->dciris->vd_iris_gpio = devm_gpiod_get(&motor->spi->dev, + "vd_iris", GPIOD_OUT_LOW); + if (IS_ERR(motor->dciris->vd_iris_gpio)) + dev_info(&motor->spi->dev, "Failed to get vd_iris-gpios\n"); + motor->dciris->is_reversed_polarity = + device_property_read_bool(&motor->spi->dev, "dc-iris-reserved-polarity"); + ret = of_property_read_u32(node, + "dc-iris-max-log", + &motor->dciris->max_log); + if (ret != 0) { + motor->dciris->max_log = DCIRIS_MAX_LOG; + dev_err(&motor->spi->dev, + "failed get dc-iris max log,use dafult value\n"); + } + } + + if (motor->is_use_p_iris) { + if (motor->is_use_dc_iris) { + dev_err(&motor->spi->dev, + "Does not support p-iris and dc-iris on the same module\n"); + return -EINVAL; + } + step_motor_cnt++; + motor->piris = devm_kzalloc(&motor->spi->dev, sizeof(*motor->piris), GFP_KERNEL); + if (!motor->piris) { + dev_err(&motor->spi->dev, + "__line__ %d, devm_kzalloc return fail!\n", __LINE__); + return -ENOMEM; + } + ret = of_property_read_string(node, "piris-used-pin", + &str); + if (ret != 0) { + dev_err(&motor->spi->dev, + "get piris-used-pin fail, please check it!\n"); + return -EINVAL; + } + if (strcmp(str, "ab") == 0) { + motor->piris->reg_op = &motor->motor_op[0]; + if (motor->piris->reg_op->is_used) { + dev_err(&motor->spi->dev, + "__line__ %d, pin already been used\n", __LINE__); + return -EINVAL; + } + motor->piris->reg_op->is_used = true; + } else if (strcmp(str, "cd") == 0) { + motor->piris->reg_op = &motor->motor_op[1]; + if (motor->piris->reg_op->is_used) { + dev_err(&motor->spi->dev, + "__line__ %d, pin already been used\n", __LINE__); + return -EINVAL; + } + motor->piris->reg_op->is_used = true; + } else { + dev_err(&motor->spi->dev, + "__line__ %d, pin require error\n", __LINE__); + return -EINVAL; + } + ret = of_property_read_u32(node, + "piris-backlash", + &motor->piris->backlash); + if (ret != 0) { + motor->piris->backlash = 0; + dev_err(&motor->spi->dev, + "failed get motor backlash,use dafult value\n"); + } + ret = of_property_read_u32(node, + "piris-start-up-speed", + &motor->piris->start_up_speed); + if (ret != 0) { + motor->piris->start_up_speed = START_UP_HZ_DEF; + dev_err(&motor->spi->dev, + "failed get motor start up speed,use dafult value\n"); + } + ret = of_property_read_u32(node, + "piris-step-max", + &motor->piris->step_max); + if (ret != 0) { + motor->piris->step_max = PIRIS_MAX_STEP_DEF; + dev_err(&motor->spi->dev, + "failed get piris pos_max,use dafult value\n"); + } + ret = of_property_read_u32(node, + "piris-ppw", + &motor->piris->run_data.ppw); + if (ret != 0 || (motor->piris->run_data.ppw > 0xff)) { + motor->piris->run_data.ppw = PPW_DEF; + dev_err(&motor->spi->dev, + "failed get piris ppw,use dafult value\n"); + } + ret = of_property_read_u32(node, + "piris-ppw-stop", + &motor->piris->run_data.ppw_stop); + if (ret != 0 || (motor->piris->run_data.ppw_stop > 0xff)) { + motor->piris->run_data.ppw_stop = PPW_STOP; + dev_err(&motor->spi->dev, + "failed get piris ppw_stop,use dafult value\n"); + } + ret = of_property_read_u32(node, + "piris-phmode", + &motor->piris->run_data.phmode); + if (ret != 0 || (motor->piris->run_data.phmode > 0x3f)) { + motor->piris->run_data.phmode = PHMODE_DEF; + dev_err(&motor->spi->dev, + "failed get piris phmode,use dafult value\n"); + } + ret = of_property_read_u32(node, + "piris-micro", + &motor->piris->run_data.micro); + if (ret != 0) { + motor->piris->run_data.micro = MICRO_DEF; + dev_err(&motor->spi->dev, + "failed get piris micro,use dafult value\n"); + } + /* get piris pi gpio */ + motor->piris->pic_gpio = devm_gpiod_get(&motor->spi->dev, + "piris_pic", GPIOD_IN); + if (IS_ERR(motor->piris->pic_gpio)) + dev_err(&motor->spi->dev, "Failed to get piris-pi-c-gpios\n"); + motor->piris->pia_gpio = devm_gpiod_get(&motor->spi->dev, + "piris_pia", GPIOD_OUT_LOW); + if (IS_ERR(motor->piris->pia_gpio)) + dev_err(&motor->spi->dev, "Failed to get piris-pi-a-gpios\n"); + motor->piris->pie_gpio = devm_gpiod_get(&motor->spi->dev, + "piris_pie", GPIOD_OUT_LOW); + if (IS_ERR(motor->piris->pie_gpio)) + dev_err(&motor->spi->dev, "Failed to get piris-pi-e-gpios\n"); + motor->piris->is_half_step_mode = + device_property_read_bool(&motor->spi->dev, "piris-1-2phase-excitation"); + motor->piris->is_dir_opp = + device_property_read_bool(&motor->spi->dev, "piris-dir-opposite"); + ret = of_property_read_s32(node, + "piris-min-pos", + &motor->piris->min_pos); + if (ret != 0) { + motor->piris->min_pos = 0; + dev_err(&motor->spi->dev, + "failed get piris min pos,use dafult value\n"); + } + ret = of_property_read_s32(node, + "piris-max-pos", + &motor->piris->max_pos); + if (ret != 0) { + motor->piris->max_pos = motor->piris->step_max; + dev_err(&motor->spi->dev, + "failed get piris max_pos pos,use dafult value\n"); + } + if (step_motor_cnt == 1) + motor->dev0 = motor->piris; + else if (step_motor_cnt == 2) + motor->dev1 = motor->piris; + ret = of_property_read_u32(node, + "piris-reback-distance", + &motor->piris->reback); + if (ret != 0) { + dev_err(&motor->spi->dev, + "failed get piris reback distance, return\n"); + return -EINVAL; + } + motor->piris->is_pihigh_positive_pos = + device_property_read_bool(&motor->spi->dev, "piris-pihigh-positive-pos"); + ret = of_property_read_u32(node, + "piris-findpi-step", + &motor->piris->findpi_step); + if (ret != 0) { + motor->piris->findpi_step = PIRIS_FINDPI_STEP; + dev_err(&motor->spi->dev, + "failed get piris find pi step\n"); + } + } + + if (motor->is_use_focus) { + step_motor_cnt++; + motor->focus = devm_kzalloc(&motor->spi->dev, sizeof(*motor->focus), GFP_KERNEL); + if (!motor->focus) { + dev_err(&motor->spi->dev, + "__line__ %d, devm_kzalloc return fail!\n", __LINE__); + return -ENOMEM; + } + ret = of_property_read_string(node, "focus-used-pin", + &str); + if (ret != 0) { + dev_err(&motor->spi->dev, + "get focus-used-pin fail, please check it!\n"); + return -EINVAL; + } + if (strcmp(str, "ab") == 0) { + motor->focus->reg_op = &motor->motor_op[0]; + if (motor->focus->reg_op->is_used) { + dev_err(&motor->spi->dev, + "__line__ %d, pin already been used\n", __LINE__); + return -EINVAL; + } + motor->focus->reg_op->is_used = true; + } else if (strcmp(str, "cd") == 0) { + motor->focus->reg_op = &motor->motor_op[1]; + if (motor->focus->reg_op->is_used) { + dev_err(&motor->spi->dev, + "__line__ %d, pin already been used\n", __LINE__); + return -EINVAL; + } + motor->focus->reg_op->is_used = true; + } else { + dev_err(&motor->spi->dev, + "__line__ %d, pin require error\n", __LINE__); + return -EINVAL; + } + ret = of_property_read_u32(node, + "focus-backlash", + &motor->focus->backlash); + if (ret != 0) { + motor->focus->backlash = 0; + dev_err(&motor->spi->dev, + "failed get motor backlash,use dafult value\n"); + } + ret = of_property_read_u32(node, + "focus-start-up-speed", + &motor->focus->start_up_speed); + if (ret != 0) { + motor->focus->start_up_speed = START_UP_HZ_DEF; + dev_err(&motor->spi->dev, + "failed get motor start up speed,use dafult value\n"); + } + ret = of_property_read_u32(node, + "focus-step-max", + &motor->focus->step_max); + if (ret != 0) { + motor->focus->step_max = FOCUS_MAX_STEP_DEF; + dev_err(&motor->spi->dev, + "failed get focus_pos_max,use dafult value\n"); + } + ret = of_property_read_u32(node, + "focus-ppw", + &motor->focus->run_data.ppw); + if (ret != 0 || (motor->focus->run_data.ppw > 0xff)) { + motor->focus->run_data.ppw = PPW_DEF; + dev_err(&motor->spi->dev, + "failed get focus ppw,use dafult value\n"); + } + ret = of_property_read_u32(node, + "focus-ppw-stop", + &motor->focus->run_data.ppw_stop); + if (ret != 0 || (motor->focus->run_data.ppw_stop > 0xff)) { + motor->focus->run_data.ppw_stop = PPW_STOP; + dev_err(&motor->spi->dev, + "failed get focus ppw_stop,use dafult value\n"); + } + ret = of_property_read_u32(node, + "focus-phmode", + &motor->focus->run_data.phmode); + if (ret != 0 || (motor->focus->run_data.phmode > 0x3f)) { + motor->focus->run_data.phmode = PHMODE_DEF; + dev_err(&motor->spi->dev, + "failed get focus phmode,use dafult value\n"); + } + ret = of_property_read_u32(node, + "focus-micro", + &motor->focus->run_data.micro); + if (ret != 0) { + motor->focus->run_data.micro = MICRO_DEF; + dev_err(&motor->spi->dev, + "failed get focus micro,use dafult value\n"); + } + if (step_motor_cnt == 1) + motor->dev0 = motor->focus; + else if (step_motor_cnt == 2) + motor->dev1 = motor->focus; + /* get focus pi gpio */ + motor->focus->pic_gpio = devm_gpiod_get(&motor->spi->dev, + "focus_pic", GPIOD_IN); + if (IS_ERR(motor->focus->pic_gpio)) + dev_err(&motor->spi->dev, "Failed to get focus-pi-c-gpios\n"); + motor->focus->pia_gpio = devm_gpiod_get(&motor->spi->dev, + "focus_pia", GPIOD_OUT_LOW); + if (IS_ERR(motor->focus->pia_gpio)) + dev_err(&motor->spi->dev, "Failed to get focus-pi-a-gpios\n"); + motor->focus->pie_gpio = devm_gpiod_get(&motor->spi->dev, + "focus_pie", GPIOD_OUT_LOW); + if (IS_ERR(motor->focus->pie_gpio)) + dev_err(&motor->spi->dev, "Failed to get focus-pi-e-gpios\n"); + ret = of_property_read_u32(node, + "focus-reback-distance", + &motor->focus->reback); + if (ret != 0) { + dev_err(&motor->spi->dev, + "failed get focus reback distance, return\n"); + return -EINVAL; + } + motor->focus->is_half_step_mode = + device_property_read_bool(&motor->spi->dev, "focus-1-2phase-excitation"); + motor->focus->is_dir_opp = + device_property_read_bool(&motor->spi->dev, "focus-dir-opposite"); + ret = of_property_read_s32(node, + "focus-min-pos", + &motor->focus->min_pos); + if (ret != 0) { + motor->focus->min_pos = 0; + dev_err(&motor->spi->dev, + "failed get focus min pos,use dafult value\n"); + } + ret = of_property_read_s32(node, + "focus-max-pos", + &motor->focus->max_pos); + if (ret != 0) { + motor->focus->max_pos = motor->focus->step_max; + dev_err(&motor->spi->dev, + "failed get focus max_pos pos,use dafult value\n"); + } + motor->focus->is_pihigh_positive_pos = + device_property_read_bool(&motor->spi->dev, "focus-pihigh-positive-pos"); + ret = of_property_read_u32(node, + "focus-findpi-step", + &motor->focus->findpi_step); + if (ret != 0) { + motor->focus->findpi_step = FOCUS_FINDPI_STEP; + dev_err(&motor->spi->dev, + "failed get focus find pi step\n"); + } + } + + if (motor->is_use_zoom) { + if (step_motor_cnt >= 2) { + dev_err(&motor->spi->dev, + "The driver support step-motor max num is 2\n"); + return -EINVAL; + } + step_motor_cnt++; + motor->zoom = devm_kzalloc(&motor->spi->dev, sizeof(*motor->zoom), GFP_KERNEL); + if (!motor->zoom) { + dev_err(&motor->spi->dev, + "__line__ %d, devm_kzalloc return fail!\n", __LINE__); + return -ENOMEM; + } + ret = of_property_read_string(node, "zoom-used-pin", + &str); + if (ret != 0) { + dev_err(&motor->spi->dev, + "get zoom-used-pin fail, please check it!\n"); + return -EINVAL; + } + if (strcmp(str, "ab") == 0) { + motor->zoom->reg_op = &motor->motor_op[0]; + if (motor->zoom->reg_op->is_used) { + dev_err(&motor->spi->dev, + "__line__ %d, pin already been used\n", __LINE__); + return -EINVAL; + } + motor->zoom->reg_op->is_used = true; + } else if (strcmp(str, "cd") == 0) { + motor->zoom->reg_op = &motor->motor_op[1]; + if (motor->zoom->reg_op->is_used) { + dev_err(&motor->spi->dev, + "__line__ %d, pin already been used\n", __LINE__); + return -EINVAL; + } + motor->zoom->reg_op->is_used = true; + } else { + dev_err(&motor->spi->dev, + "__line__ %d, pin require error\n", __LINE__); + return -EINVAL; + } + ret = of_property_read_u32(node, + "zoom-backlash", + &motor->zoom->backlash); + if (ret != 0) { + motor->zoom->backlash = 0; + dev_err(&motor->spi->dev, + "failed get motor backlash,use dafult value\n"); + } + ret = of_property_read_u32(node, + "zoom-step-max", + &motor->zoom->step_max); + if (ret != 0) { + motor->zoom->step_max = ZOOM_MAX_STEP_DEF; + dev_err(&motor->spi->dev, + "failed get iris zoom_pos_max,use dafult value\n"); + } + + ret = of_property_read_u32(node, + "zoom-start-up-speed", + &motor->zoom->start_up_speed); + if (ret != 0) { + motor->zoom->start_up_speed = START_UP_HZ_DEF; + dev_err(&motor->spi->dev, + "failed get motor start up speed,use dafult value\n"); + } + ret = of_property_read_u32(node, + "zoom-ppw", + &motor->zoom->run_data.ppw); + if (ret != 0 || (motor->zoom->run_data.ppw > 0xff)) { + motor->zoom->run_data.ppw = PPW_DEF; + dev_err(&motor->spi->dev, + "failed get zoom ppw,use dafult value\n"); + } + ret = of_property_read_u32(node, + "zoom-ppw-stop", + &motor->zoom->run_data.ppw_stop); + if (ret != 0 || (motor->zoom->run_data.ppw_stop > 0xff)) { + motor->zoom->run_data.ppw_stop = PPW_STOP; + dev_err(&motor->spi->dev, + "failed get zoom ppw_stop,use dafult value\n"); + } + ret = of_property_read_u32(node, + "zoom-phmode", + &motor->zoom->run_data.phmode); + if (ret != 0 || (motor->zoom->run_data.phmode > 0x3ff)) { + motor->zoom->run_data.phmode = PHMODE_DEF; + dev_err(&motor->spi->dev, + "failed get zoom phmode,use dafult value\n"); + } + ret = of_property_read_u32(node, + "zoom-micro", + &motor->zoom->run_data.micro); + if (ret != 0) { + motor->zoom->run_data.micro = MICRO_DEF; + dev_err(&motor->spi->dev, + "failed get zoom micro,use dafult value\n"); + } + motor->zoom->pic_gpio = devm_gpiod_get(&motor->spi->dev, + "zoom_pic", GPIOD_IN); + if (IS_ERR(motor->zoom->pic_gpio)) + dev_err(&motor->spi->dev, "Failed to get zoom-pi-c-gpios\n"); + motor->zoom->is_half_step_mode = + device_property_read_bool(&motor->spi->dev, "zoom-1-2phase-excitation"); + motor->zoom->is_dir_opp = + device_property_read_bool(&motor->spi->dev, "zoom-dir-opposite"); + if (step_motor_cnt == 1) + motor->dev0 = motor->zoom; + else if (step_motor_cnt == 2) + motor->dev1 = motor->zoom; + ret = of_property_read_s32(node, + "zoom-min-pos", + &motor->zoom->min_pos); + if (ret != 0) { + motor->zoom->min_pos = 0; + dev_err(&motor->spi->dev, + "failed get zoom min pos,use dafult value\n"); + } + ret = of_property_read_s32(node, + "zoom-max-pos", + &motor->zoom->max_pos); + if (ret != 0) { + motor->zoom->max_pos = motor->zoom->step_max; + dev_err(&motor->spi->dev, + "failed get zoom max_pos pos,use dafult value\n"); + } + ret = of_property_read_u32(node, + "zoom-reback-distance", + &motor->zoom->reback); + if (ret != 0) { + dev_err(&motor->spi->dev, + "failed get zoom reback distance, return\n"); + return -EINVAL; + } + motor->zoom->is_pihigh_positive_pos = + device_property_read_bool(&motor->spi->dev, "zoom-pihigh-positive-pos"); + ret = of_property_read_u32(node, + "zoom-findpi-step", + &motor->zoom->findpi_step); + if (ret != 0) { + motor->zoom->findpi_step = ZOOM_FINDPI_STEP; + dev_err(&motor->spi->dev, + "failed get zoom find pi step\n"); + } + } + + if (motor->is_use_zoom1) { + if (step_motor_cnt >= 2) { + dev_err(&motor->spi->dev, + "The driver support step-motor max num is 2\n"); + return -EINVAL; + } + step_motor_cnt++; + motor->zoom1 = devm_kzalloc(&motor->spi->dev, sizeof(*motor->zoom1), GFP_KERNEL); + if (!motor->zoom1) { + dev_err(&motor->spi->dev, + "__line__ %d, devm_kzalloc return fail!\n", __LINE__); + return -ENOMEM; + } + ret = of_property_read_string(node, "zoom1-used-pin", + &str); + if (ret != 0) { + dev_err(&motor->spi->dev, + "get zoom1-used-pin fail, please check it!\n"); + return -EINVAL; + } + if (strcmp(str, "ab") == 0) { + motor->zoom1->reg_op = &motor->motor_op[0]; + if (motor->zoom1->reg_op->is_used) { + dev_err(&motor->spi->dev, + "__line__ %d, pin already been used\n", __LINE__); + return -EINVAL; + } + motor->zoom1->reg_op->is_used = true; + } else if (strcmp(str, "cd") == 0) { + motor->zoom1->reg_op = &motor->motor_op[1]; + if (motor->zoom1->reg_op->is_used) { + dev_err(&motor->spi->dev, + "__line__ %d, pin already been used\n", __LINE__); + return -EINVAL; + } + motor->zoom1->reg_op->is_used = true; + } else { + dev_err(&motor->spi->dev, + "__line__ %d, pin require error\n", __LINE__); + return -EINVAL; + } + ret = of_property_read_u32(node, + "zoom1-backlash", + &motor->zoom1->backlash); + if (ret != 0) { + motor->zoom1->backlash = 0; + dev_err(&motor->spi->dev, + "failed get motor backlash,use dafult value\n"); + } + ret = of_property_read_u32(node, + "zoom1-step-max", + &motor->zoom1->step_max); + if (ret != 0) { + motor->zoom1->step_max = ZOOM_MAX_STEP_DEF; + dev_err(&motor->spi->dev, + "failed get zoom_pos_max,use dafult value\n"); + } + + ret = of_property_read_u32(node, + "zoom1-start-up-speed", + &motor->zoom1->start_up_speed); + if (ret != 0) { + motor->zoom1->start_up_speed = START_UP_HZ_DEF; + dev_err(&motor->spi->dev, + "failed get motor start up speed,use dafult value\n"); + } + ret = of_property_read_u32(node, + "zoom1-ppw", + &motor->zoom1->run_data.ppw); + if (ret != 0 || (motor->zoom1->run_data.ppw > 0xff)) { + motor->zoom1->run_data.ppw = PPW_DEF; + dev_err(&motor->spi->dev, + "failed get zoom1 ppw,use dafult value\n"); + } + ret = of_property_read_u32(node, + "zoom1-ppw-stop", + &motor->zoom1->run_data.ppw_stop); + if (ret != 0 || (motor->zoom1->run_data.ppw_stop > 0xff)) { + motor->zoom1->run_data.ppw_stop = PPW_STOP; + dev_err(&motor->spi->dev, + "failed get zoom1 ppw_stop,use dafult value\n"); + } + ret = of_property_read_u32(node, + "zoom1-phmode", + &motor->zoom1->run_data.phmode); + if (ret != 0 || (motor->zoom1->run_data.phmode > 0x3f)) { + motor->zoom1->run_data.phmode = PHMODE_DEF; + dev_err(&motor->spi->dev, + "failed get zoom1 phmode,use dafult value\n"); + } + ret = of_property_read_u32(node, + "zoom1-micro", + &motor->zoom1->run_data.micro); + if (ret != 0) { + motor->zoom1->run_data.micro = MICRO_DEF; + dev_err(&motor->spi->dev, + "failed get zoom1 micro,use dafult value\n"); + } + /* get zoom1 pi gpio */ + motor->zoom1->pic_gpio = devm_gpiod_get(&motor->spi->dev, + "zoom1_pic", GPIOD_IN); + if (IS_ERR(motor->zoom1->pic_gpio)) + dev_err(&motor->spi->dev, "Failed to get zoom1-pi-c-gpios\n"); + motor->zoom1->pia_gpio = devm_gpiod_get(&motor->spi->dev, + "zoom1_pia", GPIOD_OUT_LOW); + if (IS_ERR(motor->zoom1->pia_gpio)) + dev_err(&motor->spi->dev, "Failed to get zoom1-pi-a-gpios\n"); + motor->zoom1->pie_gpio = devm_gpiod_get(&motor->spi->dev, + "zoom1_pie", GPIOD_OUT_LOW); + if (IS_ERR(motor->zoom1->pie_gpio)) + dev_err(&motor->spi->dev, "Failed to get zoom1-pi-e-gpios\n"); + motor->zoom1->is_half_step_mode = + device_property_read_bool(&motor->spi->dev, "zoom1-1-2phase-excitation"); + motor->zoom1->is_dir_opp = + device_property_read_bool(&motor->spi->dev, "zoom1-dir-opposite"); + if (step_motor_cnt == 1) + motor->dev0 = motor->zoom1; + else if (step_motor_cnt == 2) + motor->dev1 = motor->zoom1; + ret = of_property_read_s32(node, + "zoom1-min-pos", + &motor->zoom1->min_pos); + if (ret != 0) { + motor->zoom1->min_pos = 0; + dev_err(&motor->spi->dev, + "failed get zoom1 min pos,use dafult value\n"); + } + ret = of_property_read_s32(node, + "zoom1-max-pos", + &motor->zoom1->max_pos); + if (ret != 0) { + motor->zoom1->max_pos = motor->zoom1->step_max; + dev_err(&motor->spi->dev, + "failed get zoom1 max_pos pos,use dafult value\n"); + } + ret = of_property_read_u32(node, + "zoom1-reback-distance", + &motor->zoom1->reback); + if (ret != 0) { + dev_err(&motor->spi->dev, + "failed get zoom1 reback distance, return\n"); + return -EINVAL; + } + motor->zoom1->is_pihigh_positive_pos = + device_property_read_bool(&motor->spi->dev, "zoom1-pihigh-positive-pos"); + ret = of_property_read_u32(node, + "zoom1-findpi-step", + &motor->zoom1->findpi_step); + if (ret != 0) { + motor->zoom1->findpi_step = ZOOM1_FINDPI_STEP; + dev_err(&motor->spi->dev, + "failed get zoom1 find pi step\n"); + } + } + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &motor->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &motor->module_facing); + if (ret) { + dev_err(&motor->spi->dev, + "could not get module information!\n"); + return -EINVAL; + } + return 0; +} + +static void motor_config_dev_next_status(struct motor_dev *motor, struct ext_dev *dev) +{ + u16 ppw = 0; + u16 psum = 0; + u16 micro = 0; + struct spi_device *spi = motor->spi; +#ifdef READREG_DEBUG + u16 intct = 0; + int i = 0; + u16 val = 0; + + if (dev->move_status != MOTOR_STATUS_STOPPED) { + dev_dbg(&spi->dev, + "__line__ %d dev type %d, cur_count %d !\n", __LINE__, + dev->type, + dev->run_data.cur_count); + dev_dbg(&spi->dev, + "__line__ %d, motor reg table: 0x%02x 0x%02x 0x%02x!\n", __LINE__, + dev->reg_op->reg.ppw, + dev->reg_op->reg.psum, + dev->reg_op->reg.intct); + for (i = 0; i < 11; i++) { + spi_read_reg(spi, 0x20 + i, &val); + dev_dbg(&spi->dev, + "========reg,val= 0x%02x, 0x%04x========\n", + 0x20 + i, + val); + } + } +#endif + +#ifdef READREG_DEBUG + spi_read_reg(spi, dev->reg_op->reg.ppw, &ppw); + spi_read_reg(spi, dev->reg_op->reg.psum, &psum); + spi_read_reg(spi, dev->reg_op->reg.intct, &intct); + dev_info(&spi->dev, + "__line__ %d dev type %d, cur_count %d , status %d! ppw 0x%x, psum 0x%x intct 0x%x\n", __LINE__, + dev->type, + dev->run_data.cur_count, + dev->move_status, ppw, psum, intct); +#endif + if (dev->run_data.cur_count != 0) { + if (dev->run_data.cur_count == dev->run_data.count && + dev->is_need_update_tim) { + dev->mv_tim.vcm_start_t = ns_to_kernel_old_timeval(ktime_get_ns()); + dev->is_need_update_tim = false; + } + dev->run_data.cur_count--; + ppw = (dev->run_data.ppw << 8) | dev->run_data.ppw; + switch (dev->run_data.micro) { + case 64: + micro = 0x03; + break; + case 128: + micro = 0x02; + break; + case 256: + micro = 0x00; + break; + default: + micro = 0x00; + break; + }; + switch (dev->run_data.cur_count) { + case 0: + psum = ((dev->move_status - 1) << 8) | + (micro << 12) | + (1 << 10); + ppw = (dev->run_data.ppw_stop << 8) | dev->run_data.ppw_stop; + break; + case 1: + psum = ((dev->move_status - 1) << 8) | + (1 << 10) | + (micro << 12) | + (dev->run_data.psum_last); + break; + default: + psum = ((dev->move_status - 1) << 8) | + (1 << 10) | + (micro << 12) | + (dev->run_data.psum); + break; + }; + spi_write_reg(motor->spi, 0x20, 0x1a01); + spi_write_reg(spi, dev->reg_op->reg.ppw, ppw); + spi_write_reg(spi, dev->reg_op->reg.psum, psum); + spi_write_reg(spi, dev->reg_op->reg.intct, + dev->run_data.intct); + dev->reg_op->tmp_psum = psum; + } else if (dev->move_status != MOTOR_STATUS_STOPPED) { + dev->mv_tim.vcm_end_t = ns_to_kernel_old_timeval(ktime_get_ns()); + dev->is_mv_tim_update = true; + dev->move_status = MOTOR_STATUS_STOPPED; + dev->reg_op->tmp_psum = 0; + dev->is_running = false; + complete(&dev->complete_out); + complete(&dev->complete); + } +} + +static void motor_op_work(struct work_struct *work) +{ + struct motor_work_s *wk = + container_of(work, struct motor_work_s, work); + struct motor_dev *motor = wk->dev; + static struct __kernel_old_timeval tv_last = {0}; + struct __kernel_old_timeval tv; + u64 time_dist = 0; + + tv = ns_to_kernel_old_timeval(ktime_get_ns()); + time_dist = tv.tv_sec * 1000000 + tv.tv_usec - (tv_last.tv_sec * 1000000 + tv_last.tv_usec); + tv_last = tv; + if (time_dist < motor->vd_fz_period_us && motor->is_timer_restart_bywq) + dev_info(&motor->spi->dev, + "Timer error, Current interrupt interval %llu\n", time_dist); + mutex_lock(&motor->mutex); + gpiod_set_value(motor->vd_fz_gpio, 1); + usleep_range(30, 60); + gpiod_set_value(motor->vd_fz_gpio, 0); + if (motor->dev0 && motor->dev0->run_data.cur_count == 0 && + motor->dev0->is_need_reback) { + if (motor->dev0->cur_back_delay < motor->dev0->max_back_delay) { + motor->dev0->cur_back_delay++; + motor->dev0->run_data.cur_count = 1; + } else { + motor->dev0->run_data = motor->dev0->reback_data; + motor->dev0->is_need_reback = false; + motor->dev0->move_status = motor->dev0->reback_status; + motor->dev0->last_dir = motor->dev1->reback_status; + motor->dev0->cur_back_delay = 0; + } + } + if (motor->dev1 && motor->dev1->run_data.cur_count == 0 && + motor->dev1->is_need_reback) { + if (motor->dev1->cur_back_delay < motor->dev1->max_back_delay) { + motor->dev1->cur_back_delay++; + motor->dev1->run_data.cur_count = 1; + } else { + motor->dev1->run_data = motor->dev1->reback_data; + motor->dev1->is_need_reback = false; + motor->dev1->move_status = motor->dev1->reback_status; + motor->dev1->last_dir = motor->dev1->reback_status; + motor->dev1->cur_back_delay = 0; + } + } + if ((motor->dev0 && motor->dev0->run_data.cur_count > 0) || + (motor->dev1 && motor->dev1->run_data.cur_count > 0)) { + motor->is_timer_restart = true; + motor->is_timer_restart_bywq = true; + hrtimer_start(&motor->timer, + motor->vd_fz_period_us * 1000, + HRTIMER_MODE_REL); + } else { + motor->is_timer_restart = false; + motor->is_timer_restart_bywq = false; + } + usleep_range(660, 700);//delay more than DT1 + + if (motor->dev0 && motor->dev0->move_status != MOTOR_STATUS_STOPPED) + motor_config_dev_next_status(motor, motor->dev0); + if (motor->dev1 && motor->dev1->move_status != MOTOR_STATUS_STOPPED) + motor_config_dev_next_status(motor, motor->dev1); + mutex_unlock(&motor->mutex); + motor->is_should_wait = false; +} + +static enum hrtimer_restart motor_timer_func(struct hrtimer *timer) +{ + struct motor_dev *motor = container_of(timer, struct motor_dev, timer); + + motor->is_should_wait = true; + schedule_work_on(smp_processor_id(), &motor->wk->work); + return HRTIMER_NORESTART; +} + +static int ms41908_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct motor_dev *motor = container_of(ctrl->handler, + struct motor_dev, ctrl_handler); + + switch (ctrl->id) { + case V4L2_CID_IRIS_ABSOLUTE: + if (motor->is_use_dc_iris) + ctrl->val = motor->dciris->last_log; + else if (motor->is_use_p_iris) + ctrl->val = motor->piris->last_pos; + return 0; + case V4L2_CID_FOCUS_ABSOLUTE: + ctrl->val = motor->focus->last_pos; + return 0; + case V4L2_CID_ZOOM_ABSOLUTE: + ctrl->val = motor->zoom->last_pos; + return 0; + case V4L2_CID_ZOOM_CONTINUOUS: + ctrl->val = motor->zoom1->last_pos; + return 0; + } + return 0; +} + +static void wait_for_motor_stop(struct motor_dev *motor, struct ext_dev *dev) +{ + unsigned long ret = 0; + + if (dev->is_running) { + ret = wait_for_completion_timeout(&dev->complete_out, 10 * HZ); + if (ret == 0) + dev_info(&motor->spi->dev, + "dev->type %d, wait for complete timeout\n", dev->type); + } +} + +static int ms41908_s_ctrl(struct v4l2_ctrl *ctrl) +{ +#ifdef READREG_DEBUG + int i = 0; + u16 val = 0; +#endif + int ret = 0; + struct motor_dev *motor = container_of(ctrl->handler, + struct motor_dev, ctrl_handler); + bool is_need_reback = false; + + switch (ctrl->id) { + case V4L2_CID_IRIS_ABSOLUTE: + if (motor->is_use_dc_iris) { + if (motor->dciris->is_reversed_polarity) + spi_write_reg(motor->spi, 0x00, + motor->dciris->max_log - ctrl->val); + else + spi_write_reg(motor->spi, 0x00, ctrl->val); + gpiod_set_value(motor->dciris->vd_iris_gpio, 1); + usleep_range(200, 400); + gpiod_set_value(motor->dciris->vd_iris_gpio, 0); + motor->dciris->last_log = ctrl->val; + dev_dbg(&motor->spi->dev, "set iris pos %d\n", ctrl->val); +#ifdef READREG_DEBUG + for (i = 0; i < 16; i++) { + spi_read_reg(motor->spi, i, &val); + dev_dbg(&motor->spi->dev, "reg,val=0x%02x,0x%04x\n", i, val); + } +#endif + } else if (motor->is_use_p_iris) { + ret = set_motor_running_status(motor, + motor->piris, + ctrl->val, + true, + false, + false); + wait_for_motor_stop(motor, motor->piris); + dev_dbg(&motor->spi->dev, "set piris pos %d\n", ctrl->val); + } + break; + case V4L2_CID_FOCUS_ABSOLUTE: + if (motor->focus->reback_ctrl) { + if (ctrl->val >= motor->focus->last_pos) + is_need_reback = false; + else + is_need_reback = true; + } + ret = set_motor_running_status(motor, + motor->focus, + ctrl->val, + true, + false, + is_need_reback); + wait_for_motor_stop(motor, motor->focus); + dev_dbg(&motor->spi->dev, "set focus pos %d\n", ctrl->val); + break; + case V4L2_CID_ZOOM_ABSOLUTE: + if (motor->zoom->reback_ctrl) { + if (ctrl->val >= motor->zoom->last_pos) + is_need_reback = false; + else + is_need_reback = true; + } + ret = set_motor_running_status(motor, + motor->zoom, + ctrl->val, + true, + false, + is_need_reback); + wait_for_motor_stop(motor, motor->zoom); + dev_dbg(&motor->spi->dev, "set zoom pos %d\n", ctrl->val); + break; + case V4L2_CID_ZOOM_CONTINUOUS: + if (motor->zoom1->reback_ctrl) { + if (ctrl->val >= motor->zoom1->last_pos) + is_need_reback = false; + else + is_need_reback = true; + } + ret = set_motor_running_status(motor, + motor->zoom1, + ctrl->val, + true, + false, + is_need_reback); + wait_for_motor_stop(motor, motor->zoom1); + dev_dbg(&motor->spi->dev, "set zoom1 pos %d\n", ctrl->val); + break; + default: + dev_err(&motor->spi->dev, "not support cmd %d\n", ctrl->id); + break; + } + return ret; +} + +static void ms41908_restore_speed(struct motor_dev *motor) +{ + struct ext_dev *zoom_dev = motor->zoom; + struct ext_dev *focus_dev = motor->focus; + + zoom_dev->run_data.psum = zoom_dev->default_psum; + zoom_dev->run_data.intct = zoom_dev->default_intct; + focus_dev->run_data.psum = focus_dev->default_psum; + focus_dev->run_data.intct = focus_dev->default_intct; + + dev_dbg(&motor->spi->dev, + "%s: zoom psum: %d, zoom intct: %d, focus psum: %d, focus intct: %d\n", + __func__, + zoom_dev->run_data.psum, + zoom_dev->run_data.intct, + focus_dev->run_data.psum, + focus_dev->run_data.intct); +} + +static void ms41908_sync_zoomfocus_speed(struct motor_dev *motor, s32 zoom_pos, s32 focus_pos, + bool is_zoom_need_reback, bool is_focus_need_reback) +{ + struct ext_dev *zoom_dev = motor->zoom; + struct ext_dev *focus_dev = motor->focus; + u32 zoom_mv_cnt = 0; + u32 focus_mv_cnt = 0; + u32 zoom_mv_step = 0; + u32 focus_mv_step = 0; + u32 zoom_vd_count = 0; + u32 focus_vd_count = 0; + u32 max_vd_count = 0; + u32 zoom_vd_psum = 0; + u32 focus_vd_psum = 0; + + zoom_mv_cnt = abs(zoom_pos - zoom_dev->last_pos); + if (is_zoom_need_reback) + zoom_mv_cnt += zoom_dev->reback; + + if (zoom_dev->is_half_step_mode) + zoom_mv_step = zoom_mv_cnt * 4; + else + zoom_mv_step = zoom_mv_cnt * 8; + + zoom_vd_count = (zoom_mv_step + zoom_dev->default_psum - 1) / zoom_dev->default_psum; + + focus_mv_cnt = abs(focus_pos - focus_dev->last_pos); + if (is_focus_need_reback) + focus_mv_cnt += focus_dev->reback; + + if (focus_dev->is_half_step_mode) + focus_mv_step = focus_mv_cnt * 4; + else + focus_mv_step = focus_mv_cnt * 8; + + focus_vd_count = (focus_mv_step + focus_dev->default_psum - 1) / focus_dev->default_psum; + max_vd_count = (zoom_vd_count > focus_vd_count) ? zoom_vd_count : focus_vd_count; + + if (zoom_mv_cnt > 0) { + zoom_vd_psum = (zoom_mv_step + max_vd_count - 1) / max_vd_count; + if (zoom_vd_psum > zoom_dev->default_psum) + zoom_vd_psum = zoom_dev->default_psum; + zoom_dev->run_data.psum = zoom_vd_psum; + zoom_dev->run_data.intct = motor->sys_clk * motor->vd_fz_period_us / + (zoom_dev->run_data.psum * 24); + } + + if (focus_mv_cnt > 0) { + focus_vd_psum = (focus_mv_step + max_vd_count - 1) / max_vd_count; + if (focus_vd_psum > focus_dev->default_psum) + focus_vd_psum = focus_dev->default_psum; + focus_dev->run_data.psum = focus_vd_psum; + focus_dev->run_data.intct = motor->sys_clk * motor->vd_fz_period_us / + (focus_dev->run_data.psum * 24); + } + + dev_dbg(&motor->spi->dev, + "%s: zoom_mv_cnt %d, focus_mv_cnt %d, zoom psum: %d, zoom intct: %d, focus psum: %d, focus intct: %d\n", + __func__, + zoom_mv_cnt, + focus_mv_cnt, + zoom_dev->run_data.psum, + zoom_dev->run_data.intct, + focus_dev->run_data.psum, + focus_dev->run_data.intct); +} + +static int ms41908_set_zoom_follow(struct motor_dev *motor, struct rk_cam_set_zoom *mv_param) +{ + int i = 0; + int ret = 0; + bool is_need_zoom_reback = mv_param->is_need_zoom_reback; + bool is_need_focus_reback = mv_param->is_need_focus_reback; + + for (i = 0; i < mv_param->setzoom_cnt; i++) { + dev_dbg(&motor->spi->dev, + "%s zoom %d, focus %d, i %d, setzoom_cnt %d, is_need_zoom_reback %d, is_need_focus_reback %d\n", + __func__, + mv_param->zoom_pos[i].zoom_pos, + mv_param->zoom_pos[i].focus_pos, + i, mv_param->setzoom_cnt, + is_need_zoom_reback, + is_need_focus_reback); + + ms41908_sync_zoomfocus_speed(motor, + mv_param->zoom_pos[i].zoom_pos, + mv_param->zoom_pos[i].focus_pos, + is_need_zoom_reback, + is_need_focus_reback); + if (i == (mv_param->setzoom_cnt - 1)) { + ret = set_motor_running_status(motor, + motor->focus, + mv_param->zoom_pos[i].focus_pos, + true, + true, + is_need_focus_reback); + ret |= set_motor_running_status(motor, + motor->zoom, + mv_param->zoom_pos[i].zoom_pos, + true, + false, + is_need_zoom_reback); + } else { + ret = set_motor_running_status(motor, + motor->focus, + mv_param->zoom_pos[i].focus_pos, + false, + true, + false); + ret |= set_motor_running_status(motor, + motor->zoom, + mv_param->zoom_pos[i].zoom_pos, + false, + false, + false); + } + wait_for_motor_stop(motor, motor->focus); + wait_for_motor_stop(motor, motor->zoom); + } + + ms41908_restore_speed(motor); + return ret; +} + +static int ms41908_find_pi_binarysearch(struct motor_dev *motor, + struct ext_dev *ext_dev, + int min, int max, bool *error) +{ + int gpio_val = 0; + int tmp_val = 0; + int mid = 0; + int last_pos = 0; + int new_min = 0; + int new_max = 0; + + dev_dbg(&motor->spi->dev, + "ext dev %d min %d, max %d\n", ext_dev->type, min, max); + *error = false; + if (min > max) { + *error = true; + return -EINVAL; + } + + tmp_val = gpiod_get_value(ext_dev->pic_gpio); + mid = (min + max) / 2; + if ((mid == min) || (mid == max)) { + dev_dbg(&motor->spi->dev, + "ext dev %d find pi %d\n", ext_dev->type, mid); + if (ext_dev->last_pos < mid) + set_motor_running_status(motor, + ext_dev, + mid, + false, + false, + false); + else + set_motor_running_status(motor, + ext_dev, + mid, + false, + false, + true); + wait_for_motor_stop(motor, ext_dev); + return mid; + } + last_pos = ext_dev->last_pos; + if (last_pos < mid) + set_motor_running_status(motor, + ext_dev, + mid, + false, + false, + false); + else + set_motor_running_status(motor, + ext_dev, + mid, + false, + false, + true); + wait_for_motor_stop(motor, ext_dev); + gpio_val = gpiod_get_value(ext_dev->pic_gpio); + if (tmp_val != gpio_val) { + usleep_range(10, 20); + gpio_val = gpiod_get_value(ext_dev->pic_gpio); + } + + dev_dbg(&motor->spi->dev, + "__line__ %d ext_dev type %d, get pi value %d, tmp_val %d, min %d, max %d\n", + __LINE__, ext_dev->type, gpio_val, tmp_val, min, max); + if (tmp_val != gpio_val) { + if (last_pos == min) { + new_min = min; + new_max = mid; + } else { + new_min = mid; + new_max = max; + } + } else { + if (last_pos == min) { + new_min = mid; + new_max = max; + } else { + new_min = min; + new_max = mid; + } + } + return ms41908_find_pi_binarysearch(motor, ext_dev, new_min, new_max, error); +} + +static int ms41908_find_pi(struct motor_dev *motor, + struct ext_dev *ext_dev, int step) +{ + int i = 0; + int idx_max = ext_dev->step_max + step - 1; + int tmp_val = 0; + int gpio_val = 0; + int min = 0; + int max = 0; + int pi_pos = 0; + bool is_find_pi = false; + bool if_find_error = false; + + tmp_val = gpiod_get_value(ext_dev->pic_gpio); + if ((!tmp_val && ext_dev->is_pihigh_positive_pos) || + (tmp_val && !ext_dev->is_pihigh_positive_pos)) { + for (i = ext_dev->last_pos + step; i < 2 * idx_max; i += step) { + set_motor_running_status(motor, + ext_dev, + i, + false, + false, + false); + wait_for_motor_stop(motor, ext_dev); + gpio_val = gpiod_get_value(ext_dev->pic_gpio); + if (tmp_val != gpio_val) { + usleep_range(10, 20); + gpio_val = gpiod_get_value(ext_dev->pic_gpio); + } + dev_dbg(&motor->spi->dev, + "__line__ %d ext_dev type %d, get pi value %d, i %d, tmp_val %d\n", + __LINE__, ext_dev->type, gpio_val, i, tmp_val); + if (tmp_val != gpio_val) { + min = i - step; + max = i; + is_find_pi = true; + break; + } + } + } else { + for (i = ext_dev->last_pos - step; i > -2 * idx_max; i -= step) { + set_motor_running_status(motor, + ext_dev, + i, + false, + false, + true); + wait_for_motor_stop(motor, ext_dev); + gpio_val = gpiod_get_value(ext_dev->pic_gpio); + if (tmp_val != gpio_val) { + usleep_range(10, 20); + gpio_val = gpiod_get_value(ext_dev->pic_gpio); + } + dev_dbg(&motor->spi->dev, + "__line__ %d ext_dev type %d, get pi value %d, i %d, tmp_val %d\n", + __LINE__, ext_dev->type, gpio_val, i, tmp_val); + if (tmp_val != gpio_val) { + min = i; + max = i + step; + is_find_pi = true; + break; + } + } + } + + if (is_find_pi) { + if (abs(step) != 1) { + pi_pos = ms41908_find_pi_binarysearch(motor, ext_dev, + min, + max, + &if_find_error); + dev_dbg(&motor->spi->dev, + "ext_dev type %d, pi_pos %d, if_find_error %d\n", + ext_dev->type, pi_pos, if_find_error); + if (if_find_error) + return -EINVAL; + } + return 0; + } else { + return -EINVAL; + } +} + +static int ms41908_reinit_piris(struct motor_dev *motor) +{ + int ret = 0; + + if (!IS_ERR(motor->piris->pic_gpio)) { + if (!IS_ERR(motor->piris->pia_gpio)) + gpiod_set_value(motor->piris->pia_gpio, 1); + if (!IS_ERR(motor->piris->pie_gpio)) + gpiod_set_value(motor->piris->pie_gpio, 0); + msleep(250); + #ifdef PI_TEST + motor->piris->last_pos = motor->piris->step_max; + ret = set_motor_running_status(motor, + motor->piris, + 0, + false, + false, + false); + wait_for_motor_stop(motor, motor->piris); + #else + motor->piris->last_pos = 0; + #endif + ret = ms41908_find_pi(motor, motor->piris, motor->piris->findpi_step); + if (ret < 0) { + dev_err(&motor->spi->dev, + "get piris pi fail, pls check it\n"); + return -EINVAL; + } + #ifdef PI_TEST + min = -ret; + max = motor->piris->step_max + min; + motor->piris->min_pos = min; + motor->piris->max_pos = max; + #endif + if (!IS_ERR(motor->piris->pia_gpio)) + gpiod_set_value(motor->piris->pia_gpio, 0); + if (!IS_ERR(motor->piris->pie_gpio)) + gpiod_set_value(motor->piris->pie_gpio, 0); + motor->piris->last_pos = 0; + } else { + motor->piris->last_pos = motor->piris->step_max; + ret = set_motor_running_status(motor, + motor->piris, + 0, + false, + false, + false); + wait_for_motor_stop(motor, motor->piris); + } + return ret; +} + +static void ms41908_reinit_piris_pos(struct motor_dev *motor) +{ + if (!motor->piris) { + dev_err(&motor->spi->dev, + "not support piris\n"); + return; + } + ms41908_reinit_piris(motor); + motor->piris->last_pos = 0; + __v4l2_ctrl_modify_range(motor->iris_ctrl, motor->piris->min_pos, + motor->piris->max_pos - motor->piris->reback, + 1, 0); +} + +static int ms41908_reinit_focus(struct motor_dev *motor) +{ + int ret = 0; + + if (!IS_ERR(motor->focus->pic_gpio)) { + mutex_lock(&motor->mutex); + if (motor->pi_gpio_usecnt == 0) { + if (!IS_ERR(motor->focus->pia_gpio)) + gpiod_set_value(motor->focus->pia_gpio, 1); + if (!IS_ERR(motor->focus->pie_gpio)) + gpiod_set_value(motor->focus->pie_gpio, 0); + msleep(250); + } + motor->pi_gpio_usecnt++; + mutex_unlock(&motor->mutex); + #ifdef PI_TEST + motor->focus->last_pos = motor->focus->step_max; + ret = set_motor_running_status(motor, + motor->focus, + 0, + false, + false, + false); + wait_for_motor_stop(motor, motor->focus); + #else + motor->focus->last_pos = 0; + #endif + ret = ms41908_find_pi(motor, motor->focus, motor->focus->findpi_step); + if (ret < 0) { + dev_info(&motor->spi->dev, + "get focus pi fail, pls check it\n"); + return -EINVAL; + } + #ifdef PI_TEST + min = -ret; + max = motor->focus->step_max + min; + motor->focus->min_pos = min; + motor->focus->max_pos = max; + #endif + + mutex_lock(&motor->mutex); + if (motor->pi_gpio_usecnt == 1) { + if (!IS_ERR(motor->focus->pia_gpio)) + gpiod_set_value(motor->focus->pia_gpio, 0); + if (!IS_ERR(motor->focus->pie_gpio)) + gpiod_set_value(motor->focus->pie_gpio, 0); + } + motor->pi_gpio_usecnt--; + mutex_unlock(&motor->mutex); + } else { + motor->focus->last_pos = motor->focus->step_max; + ret = set_motor_running_status(motor, + motor->focus, + 0, + false, + false, + true); + wait_for_motor_stop(motor, motor->focus); + } + return ret; +} + +static void ms41908_reinit_focus_pos(struct motor_dev *motor) +{ + if (!motor->focus) { + dev_err(&motor->spi->dev, + "not support focus\n"); + return; + } + ms41908_reinit_focus(motor); + motor->focus->last_pos = 0; + __v4l2_ctrl_modify_range(motor->focus_ctrl, motor->focus->min_pos, + motor->focus->max_pos - motor->focus->reback, + 1, 0); +} + +static int ms41908_reinit_zoom(struct motor_dev *motor) +{ + int ret = 0; + + if (!IS_ERR(motor->zoom->pic_gpio)) { + mutex_lock(&motor->mutex); + if (motor->pi_gpio_usecnt == 0) { + if (!IS_ERR(motor->focus->pia_gpio)) + gpiod_set_value(motor->focus->pia_gpio, 1); + if (!IS_ERR(motor->focus->pie_gpio)) + gpiod_set_value(motor->focus->pie_gpio, 0); + msleep(250); + } + motor->pi_gpio_usecnt++; + mutex_unlock(&motor->mutex); + + #ifdef PI_TEST + motor->zoom->last_pos = motor->zoom->step_max; + ret = set_motor_running_status(motor, + motor->zoom, + 0, + false, + false, + false); + wait_for_motor_stop(motor, motor->zoom); + #else + motor->zoom->last_pos = 0; + #endif + ret = ms41908_find_pi(motor, motor->zoom, motor->zoom->findpi_step); + if (ret < 0) { + dev_err(&motor->spi->dev, + "get zoom pi fail, pls check it\n"); + return -EINVAL; + } + #ifdef PI_TEST + min = -ret; + max = motor->zoom->step_max + min; + motor->zoom->min_pos = min; + motor->zoom->max_pos = max; + #endif + + mutex_lock(&motor->mutex); + if (motor->pi_gpio_usecnt == 1) { + if (!IS_ERR(motor->focus->pia_gpio)) + gpiod_set_value(motor->focus->pia_gpio, 0); + if (!IS_ERR(motor->focus->pie_gpio)) + gpiod_set_value(motor->focus->pie_gpio, 0); + } + motor->pi_gpio_usecnt--; + mutex_unlock(&motor->mutex); + } else { + motor->zoom->last_pos = motor->zoom->step_max; + ret = set_motor_running_status(motor, + motor->zoom, + 0, + false, + false, + true); + wait_for_motor_stop(motor, motor->zoom); + } + return ret; +} + +static void ms41908_reinit_zoom_pos(struct motor_dev *motor) +{ + if (!motor->zoom) { + dev_err(&motor->spi->dev, + "not support zoom\n"); + return; + } + ms41908_reinit_zoom(motor); + motor->zoom->last_pos = 0; + __v4l2_ctrl_modify_range(motor->zoom_ctrl, motor->zoom->min_pos, + motor->zoom->max_pos - motor->zoom->reback, + 1, 0); +} + +static int ms41908_reinit_zoom1(struct motor_dev *motor) +{ + int ret = 0; + + if (!IS_ERR(motor->zoom1->pic_gpio)) { + if (!IS_ERR(motor->zoom1->pia_gpio)) + gpiod_set_value(motor->zoom1->pia_gpio, 1); + if (!IS_ERR(motor->zoom1->pie_gpio)) + gpiod_set_value(motor->zoom1->pie_gpio, 0); + msleep(250); + #ifdef PI_TEST + motor->zoom1->last_pos = motor->zoom1->step_max; + ret = set_motor_running_status(motor, + motor->zoom1, + 0, + false, + false, + false); + wait_for_motor_stop(motor, motor->zoom1); + #else + motor->zoom1->last_pos = 0; + #endif + ret = ms41908_find_pi(motor, motor->zoom1, motor->zoom1->findpi_step); + if (ret < 0) { + dev_err(&motor->spi->dev, + "get zoom1 pi fail, pls check it\n"); + return -EINVAL; + } + #ifdef PI_TEST + min = -ret; + max = motor->zoom1->step_max + min; + motor->zoom1->min_pos = min; + motor->zoom1->max_pos = max; + #endif + if (!IS_ERR(motor->zoom1->pia_gpio)) + gpiod_set_value(motor->zoom1->pia_gpio, 0); + if (!IS_ERR(motor->zoom1->pie_gpio)) + gpiod_set_value(motor->zoom1->pie_gpio, 0); + } else { + motor->zoom1->last_pos = motor->zoom1->step_max; + ret = set_motor_running_status(motor, + motor->zoom1, + 0, + false, + false, + true); + wait_for_motor_stop(motor, motor->zoom1); + } + return ret; +} + +static void ms41908_reinit_zoom1_pos(struct motor_dev *motor) +{ + if (!motor->zoom1) { + dev_err(&motor->spi->dev, + "not support zoom1\n"); + return; + } + ms41908_reinit_zoom1(motor); + motor->zoom1->last_pos = 0; + __v4l2_ctrl_modify_range(motor->zoom1_ctrl, motor->zoom1->min_pos, + motor->zoom1->max_pos - motor->zoom1->reback, + 1, 0); +} + +//#define REBACK_CTRL_BY_DRV +static int ms41908_set_focus(struct motor_dev *motor, struct rk_cam_set_focus *mv_param) +{ + int ret = 0; + bool is_need_reback = mv_param->is_need_reback; + +#ifdef REBACK_CTRL_BY_DRV + if (mv_param->focus_pos > motor->focus->last_pos) + is_need_reback = false; + else + is_need_reback = true; +#endif + + dev_dbg(&motor->spi->dev, + "%s focus %d\n", __func__, mv_param->focus_pos); + + ret = set_motor_running_status(motor, + motor->focus, + mv_param->focus_pos, + true, + false, + is_need_reback); + wait_for_motor_stop(motor, motor->focus); + + return ret; +} + +static long ms41908_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct rk_cam_vcm_tim *mv_tim; + struct motor_dev *motor = to_motor_dev(sd); + u32 *pbacklash = 0; + struct rk_cam_set_zoom *mv_param; + struct rk_cam_set_focus *focus_param; + int ret = 0; + struct rk_cam_modify_pos *pos; + + switch (cmd) { + case RK_VIDIOC_IRIS_TIMEINFO: + mv_tim = (struct rk_cam_vcm_tim *)arg; + if (!motor->piris->is_mv_tim_update) + usleep_range(motor->piris->move_time_us, + motor->piris->move_time_us + 1000); + if (motor->piris->is_mv_tim_update) { + memcpy(mv_tim, &motor->piris->mv_tim, sizeof(*mv_tim)); + + dev_dbg(&motor->spi->dev, + "get_piris_move_tim 0x%lx, 0x%lx, 0x%lx, 0x%lx\n", + mv_tim->vcm_start_t.tv_sec, + mv_tim->vcm_start_t.tv_usec, + mv_tim->vcm_end_t.tv_sec, + mv_tim->vcm_end_t.tv_usec); + } else { + dev_err(&motor->spi->dev, "get_piris_move_tim failed\n"); + return -EINVAL; + } + break; + case RK_VIDIOC_VCM_TIMEINFO: + mv_tim = (struct rk_cam_vcm_tim *)arg; + if (!motor->focus->is_mv_tim_update) + usleep_range(motor->focus->move_time_us, + motor->focus->move_time_us + 1000); + if (motor->focus->is_mv_tim_update) { + memcpy(mv_tim, &motor->focus->mv_tim, sizeof(*mv_tim)); + + dev_dbg(&motor->spi->dev, + "get_focus_move_tim 0x%lx, 0x%lx, 0x%lx, 0x%lx\n", + mv_tim->vcm_start_t.tv_sec, + mv_tim->vcm_start_t.tv_usec, + mv_tim->vcm_end_t.tv_sec, + mv_tim->vcm_end_t.tv_usec); + } else { + dev_err(&motor->spi->dev, "get_focus_move_tim failed\n"); + return -EINVAL; + } + break; + case RK_VIDIOC_ZOOM_TIMEINFO: + mv_tim = (struct rk_cam_vcm_tim *)arg; + if (!motor->zoom->is_mv_tim_update) + usleep_range(motor->zoom->move_time_us, + motor->zoom->move_time_us + 1000); + if (motor->zoom->is_mv_tim_update) { + memcpy(mv_tim, &motor->zoom->mv_tim, sizeof(*mv_tim)); + + dev_dbg(&motor->spi->dev, + "get_zoom_move_tim 0x%lx, 0x%lx, 0x%lx, 0x%lx\n", + mv_tim->vcm_start_t.tv_sec, + mv_tim->vcm_start_t.tv_usec, + mv_tim->vcm_end_t.tv_sec, + mv_tim->vcm_end_t.tv_usec); + } else { + dev_err(&motor->spi->dev, "get_zoom_move_tim failed\n"); + return -EINVAL; + } + break; + case RK_VIDIOC_ZOOM1_TIMEINFO: + mv_tim = (struct rk_cam_vcm_tim *)arg; + if (!motor->zoom1->is_mv_tim_update) + usleep_range(motor->zoom1->move_time_us, + motor->zoom1->move_time_us + 1000); + if (motor->zoom1->is_mv_tim_update) { + memcpy(mv_tim, &motor->zoom1->mv_tim, sizeof(*mv_tim)); + + dev_dbg(&motor->spi->dev, + "get_zoom_move_tim 0x%lx, 0x%lx, 0x%lx, 0x%lx\n", + mv_tim->vcm_start_t.tv_sec, + mv_tim->vcm_start_t.tv_usec, + mv_tim->vcm_end_t.tv_sec, + mv_tim->vcm_end_t.tv_usec); + } else { + dev_err(&motor->spi->dev, "get_zoom_move_tim failed\n"); + return -EINVAL; + } + break; + case RK_VIDIOC_IRIS_SET_BACKLASH: + pbacklash = (u32 *)arg; + motor->piris->backlash = *pbacklash; + break; + case RK_VIDIOC_FOCUS_SET_BACKLASH: + pbacklash = (u32 *)arg; + motor->focus->backlash = *pbacklash; + break; + case RK_VIDIOC_ZOOM_SET_BACKLASH: + pbacklash = (u32 *)arg; + motor->zoom->backlash = *pbacklash; + break; + case RK_VIDIOC_ZOOM1_SET_BACKLASH: + pbacklash = (u32 *)arg; + motor->zoom1->backlash = *pbacklash; + break; + case RK_VIDIOC_IRIS_CORRECTION: + ms41908_reinit_piris_pos(motor); + break; + case RK_VIDIOC_FOCUS_CORRECTION: + ms41908_reinit_focus_pos(motor); + break; + case RK_VIDIOC_ZOOM_CORRECTION: + ms41908_reinit_zoom_pos(motor); + break; + case RK_VIDIOC_ZOOM1_CORRECTION: + ms41908_reinit_zoom1_pos(motor); + break; + case RK_VIDIOC_ZOOM_SET_POSITION: + mv_param = (struct rk_cam_set_zoom *)arg; + ret = ms41908_set_zoom_follow(motor, mv_param); + break; + case RK_VIDIOC_FOCUS_SET_POSITION: + focus_param = (struct rk_cam_set_focus *)arg; + ret = ms41908_set_focus(motor, focus_param); + break; + case RK_VIDIOC_MODIFY_POSITION: + pos = (struct rk_cam_modify_pos *)arg; + if (motor->focus) + motor->focus->last_pos = pos->focus_pos; + if (motor->zoom) + motor->zoom->last_pos = pos->zoom_pos; + if (motor->zoom1) + motor->zoom1->last_pos = pos->zoom1_pos; + break; + default: + break; + } + return ret; +} + +#ifdef CONFIG_COMPAT +static long ms41908_compat_ioctl32(struct v4l2_subdev *sd, unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rk_cam_compat_vcm_tim *compat_mv_tim; + struct rk_cam_set_zoom *mv_param; + struct rk_cam_set_focus *focus_param; + struct rk_cam_vcm_tim ioctl_mv_tim; + unsigned int ioctl_cmd; + int ret = 0; + u32 val = 0; + + switch (cmd) { + case RK_VIDIOC_COMPAT_VCM_TIMEINFO: + ioctl_cmd = RK_VIDIOC_VCM_TIMEINFO; + goto handle_mvtime; + case RK_VIDIOC_COMPAT_IRIS_TIMEINFO: + ioctl_cmd = RK_VIDIOC_IRIS_TIMEINFO; + goto handle_mvtime; + case RK_VIDIOC_COMPAT_ZOOM_TIMEINFO: + ioctl_cmd = RK_VIDIOC_ZOOM_TIMEINFO; + goto handle_mvtime; + case RK_VIDIOC_COMPAT_ZOOM1_TIMEINFO: + ioctl_cmd = RK_VIDIOC_ZOOM1_TIMEINFO; + +handle_mvtime: + compat_mv_tim = kzalloc(sizeof(*compat_mv_tim), GFP_KERNEL); + if (!compat_mv_tim) { + ret = -ENOMEM; + return ret; + } + ret = ms41908_ioctl(sd, ioctl_cmd, &ioctl_mv_tim); + if (!ret) { + compat_mv_tim->vcm_start_t.tv_sec = ioctl_mv_tim.vcm_start_t.tv_sec; + compat_mv_tim->vcm_start_t.tv_usec = ioctl_mv_tim.vcm_start_t.tv_usec; + compat_mv_tim->vcm_end_t.tv_sec = ioctl_mv_tim.vcm_end_t.tv_sec; + compat_mv_tim->vcm_end_t.tv_usec = ioctl_mv_tim.vcm_end_t.tv_usec; + if (copy_to_user(up, compat_mv_tim, sizeof(*compat_mv_tim))) { + kfree(compat_mv_tim); + return -EFAULT; + } + } + kfree(compat_mv_tim); + break; + case RK_VIDIOC_IRIS_SET_BACKLASH: + case RK_VIDIOC_FOCUS_SET_BACKLASH: + case RK_VIDIOC_ZOOM_SET_BACKLASH: + case RK_VIDIOC_ZOOM1_SET_BACKLASH: + if (copy_from_user(&val, up, sizeof(val))) + return -EFAULT; + ret = ms41908_ioctl(sd, cmd, &val); + break; + case RK_VIDIOC_IRIS_CORRECTION: + case RK_VIDIOC_FOCUS_CORRECTION: + case RK_VIDIOC_ZOOM_CORRECTION: + case RK_VIDIOC_ZOOM1_CORRECTION: + if (copy_from_user(&val, up, sizeof(val))) + return -EFAULT; + ret = ms41908_ioctl(sd, cmd, &val); + break; + case RK_VIDIOC_ZOOM_SET_POSITION: + mv_param = kzalloc(sizeof(*mv_param), GFP_KERNEL); + if (!mv_param) { + ret = -ENOMEM; + return ret; + } + if (copy_from_user(mv_param, up, sizeof(*mv_param))) { + kfree(mv_param); + return -EFAULT; + } + ret = ms41908_ioctl(sd, cmd, mv_param); + kfree(mv_param); + break; + case RK_VIDIOC_FOCUS_SET_POSITION: + focus_param = kzalloc(sizeof(*focus_param), GFP_KERNEL); + if (!focus_param) { + ret = -ENOMEM; + return ret; + } + if (copy_from_user(focus_param, up, sizeof(*focus_param))) { + kfree(focus_param); + return -EFAULT; + } + ret = ms41908_ioctl(sd, cmd, focus_param); + kfree(focus_param); + break; + default: + break; + } + return ret; +} +#endif + +#define USED_SYS_DEBUG +#ifdef USED_SYS_DEBUG +static ssize_t set_pid_dgain(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct motor_dev *motor = to_motor_dev(sd); + int val = 0; + int ret = 0; + u16 reg_val = 0; + + ret = kstrtoint(buf, 0, &val); + if (!ret) { + if (motor->is_use_dc_iris) { + spi_read_reg(motor->spi, 0x01, ®_val); + reg_val &= 0x01ff; + reg_val |= (val & 0x7f) << 9; + spi_write_reg(motor->spi, 0x01, reg_val); + gpiod_set_value(motor->dciris->vd_iris_gpio, 1); + usleep_range(200, 400); + gpiod_set_value(motor->dciris->vd_iris_gpio, 0); + dev_info(dev, "set pid dgain %d, reg val 0x%x\n", val, reg_val); + spi_read_reg(motor->spi, 0x01, ®_val); + dev_info(dev, "pid dgain reg val 0x%x, read from register\n", reg_val); + } else { + dev_err(dev, "not support dc-iris, do nothing\n"); + } + } + return count; +} + +static ssize_t set_pid_zero(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct motor_dev *motor = to_motor_dev(sd); + int val = 0; + int ret = 0; + u16 reg_val = 0; + + ret = kstrtoint(buf, 0, &val); + if (!ret) { + if (motor->is_use_dc_iris) { + spi_read_reg(motor->spi, 0x02, ®_val); + reg_val &= 0xf0ff; + reg_val |= (val & 0xf) << 8; + spi_write_reg(motor->spi, 0x02, reg_val); + gpiod_set_value(motor->dciris->vd_iris_gpio, 1); + usleep_range(200, 400); + gpiod_set_value(motor->dciris->vd_iris_gpio, 0); + dev_info(dev, "set pid zero %d, reg val 0x%x\n", val, reg_val); + spi_read_reg(motor->spi, 0x02, ®_val); + dev_info(dev, "pid zero reg val 0x%x, read from register\n", reg_val); + } else { + dev_err(dev, "not support dc-iris, do nothing\n"); + } + } + return count; +} + +static ssize_t set_pid_pole(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct motor_dev *motor = to_motor_dev(sd); + int val = 0; + int ret = 0; + u16 reg_val = 0; + + ret = kstrtoint(buf, 0, &val); + if (!ret) { + if (motor->is_use_dc_iris) { + spi_read_reg(motor->spi, 0x02, ®_val); + reg_val &= 0x0fff; + reg_val |= (val & 0xf) << 12; + spi_write_reg(motor->spi, 0x02, reg_val); + gpiod_set_value(motor->dciris->vd_iris_gpio, 1); + usleep_range(200, 400); + gpiod_set_value(motor->dciris->vd_iris_gpio, 0); + dev_info(dev, "set pid pole %d, reg val 0x%x\n", val, reg_val); + spi_read_reg(motor->spi, 0x02, ®_val); + dev_info(dev, "pid pole reg val 0x%x, read from register\n", reg_val); + } else { + dev_err(dev, "not support dc-iris, do nothing\n"); + } + } + return count; +} + +static ssize_t set_hall_bias(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct motor_dev *motor = to_motor_dev(sd); + int val = 0; + int ret = 0; + u16 reg_val = 0; + + ret = kstrtoint(buf, 0, &val); + if (!ret) { + if (motor->is_use_dc_iris) { + spi_read_reg(motor->spi, 0x04, ®_val); + reg_val &= 0xff00; + reg_val |= val & 0xff; + spi_write_reg(motor->spi, 0x04, reg_val); + gpiod_set_value(motor->dciris->vd_iris_gpio, 1); + usleep_range(200, 400); + gpiod_set_value(motor->dciris->vd_iris_gpio, 0); + dev_info(dev, "set hall_bias %d, reg val 0x%x\n", val, reg_val); + spi_read_reg(motor->spi, 0x04, ®_val); + dev_info(dev, "hall bias reg val 0x%x, read from register\n", reg_val); + } else { + dev_err(dev, "not support dc-iris, do nothing\n"); + } + } + return count; +} + +static ssize_t set_hall_offset(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct motor_dev *motor = to_motor_dev(sd); + int val = 0; + int ret = 0; + u16 reg_val = 0; + + ret = kstrtoint(buf, 0, &val); + if (!ret) { + if (motor->is_use_dc_iris) { + spi_read_reg(motor->spi, 0x04, ®_val); + reg_val &= 0x00ff; + reg_val |= (val & 0xff) << 8; + spi_write_reg(motor->spi, 0x04, reg_val); + gpiod_set_value(motor->dciris->vd_iris_gpio, 1); + usleep_range(200, 400); + gpiod_set_value(motor->dciris->vd_iris_gpio, 0); + dev_info(dev, "set hall_offset %d, reg val 0x%x\n", val, reg_val); + spi_read_reg(motor->spi, 0x04, ®_val); + dev_info(dev, "hall offset reg val 0x%x, read from register\n", reg_val); + } else { + dev_err(dev, "not support dc-iris, do nothing\n"); + } + } + return count; +} + +static ssize_t set_hall_gain(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct motor_dev *motor = to_motor_dev(sd); + int val = 0; + int ret = 0; + u16 reg_val = 0; + + ret = kstrtoint(buf, 0, &val); + if (!ret) { + if (motor->is_use_dc_iris) { + spi_read_reg(motor->spi, 0x05, ®_val); + reg_val &= 0xf0ff; + reg_val |= (val & 0xf) << 8; + spi_write_reg(motor->spi, 0x05, reg_val); + gpiod_set_value(motor->dciris->vd_iris_gpio, 1); + usleep_range(200, 400); + gpiod_set_value(motor->dciris->vd_iris_gpio, 0); + dev_info(dev, "set hall_offset %d, reg val 0x%04x\n", val, reg_val); + spi_read_reg(motor->spi, 0x05, ®_val); + dev_info(dev, "hall gain reg val 0x%04x, read from register\n", reg_val); + } else { + dev_err(dev, "not support dc-iris, do nothing\n"); + } + } + return count; +} + +static ssize_t reinit_piris_pos(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct motor_dev *motor = to_motor_dev(sd); + int val = 0; + int ret = 0; + + ret = kstrtoint(buf, 0, &val); + if (!ret) { + if (val == 1) + ms41908_reinit_piris_pos(motor); + } + return count; +} + +static ssize_t reinit_focus_pos(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct motor_dev *motor = to_motor_dev(sd); + int val = 0; + int ret = 0; + + ret = kstrtoint(buf, 0, &val); + if (!ret) { + if (val == 1) + ms41908_reinit_focus_pos(motor); + } + return count; +} + +static ssize_t reinit_zoom_pos(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct motor_dev *motor = to_motor_dev(sd); + int val = 0; + int ret = 0; + + ret = kstrtoint(buf, 0, &val); + if (!ret) { + if (val == 1) + ms41908_reinit_zoom_pos(motor); + } + return count; +} + +static ssize_t reinit_zoom1_pos(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct motor_dev *motor = to_motor_dev(sd); + int val = 0; + int ret = 0; + + ret = kstrtoint(buf, 0, &val); + if (!ret) { + if (val == 1) + ms41908_reinit_zoom1_pos(motor); + } + return count; +} + +static ssize_t set_focus_reback_ctrl(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct motor_dev *motor = to_motor_dev(sd); + int val = 0; + int ret = 0; + + ret = kstrtoint(buf, 0, &val); + if (!ret) { + if (val == 1) + motor->focus->reback_ctrl = true; + else + motor->focus->reback_ctrl = false; + } + return count; +} + +static ssize_t set_zoom_reback_ctrl(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct motor_dev *motor = to_motor_dev(sd); + int val = 0; + int ret = 0; + + ret = kstrtoint(buf, 0, &val); + if (!ret) { + if (val == 1) + motor->zoom->reback_ctrl = true; + else + motor->zoom->reback_ctrl = false; + } + return count; +} + +static struct device_attribute attributes[] = { + __ATTR(pid_dgain, S_IWUSR, NULL, set_pid_dgain), + __ATTR(pid_zero, S_IWUSR, NULL, set_pid_zero), + __ATTR(pid_pole, S_IWUSR, NULL, set_pid_pole), + __ATTR(hall_bias, S_IWUSR, NULL, set_hall_bias), + __ATTR(hall_offset, S_IWUSR, NULL, set_hall_offset), + __ATTR(hall_gain, S_IWUSR, NULL, set_hall_gain), + __ATTR(reinit_piris, S_IWUSR, NULL, reinit_piris_pos), + __ATTR(reinit_focus, S_IWUSR, NULL, reinit_focus_pos), + __ATTR(reinit_zoom, S_IWUSR, NULL, reinit_zoom_pos), + __ATTR(reinit_zoom1, S_IWUSR, NULL, reinit_zoom1_pos), + __ATTR(focus_reback_ctrl, S_IWUSR, NULL, set_focus_reback_ctrl), + __ATTR(zoom_reback_ctrl, S_IWUSR, NULL, set_zoom_reback_ctrl), +}; + +static int add_sysfs_interfaces(struct device *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(attributes); i++) + if (device_create_file(dev, attributes + i)) + goto undo; + return 0; +undo: + for (i--; i >= 0 ; i--) + device_remove_file(dev, attributes + i); + dev_err(dev, "%s: failed to create sysfs interface\n", __func__); + return -ENODEV; +} + +static int remove_sysfs_interfaces(struct device *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(attributes); i++) + device_remove_file(dev, attributes + i); + return 0; +} +#endif + +static const struct v4l2_subdev_core_ops motor_core_ops = { + .ioctl = ms41908_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = ms41908_compat_ioctl32 +#endif +}; + +static const struct v4l2_subdev_ops motor_subdev_ops = { + .core = &motor_core_ops, +}; + +static const struct v4l2_ctrl_ops motor_ctrl_ops = { + .g_volatile_ctrl = ms41908_g_volatile_ctrl, + .s_ctrl = ms41908_s_ctrl, +}; + +static int ms41908_initialize_controls(struct motor_dev *motor) +{ + struct v4l2_ctrl_handler *handler; + int ret = 0; + #ifdef PI_TEST + int min = 0; + int max = 0; + #endif + unsigned long flags = V4L2_CTRL_FLAG_EXECUTE_ON_WRITE | V4L2_CTRL_FLAG_VOLATILE; + + handler = &motor->ctrl_handler; + ret = v4l2_ctrl_handler_init(handler, 3); + if (ret) + return ret; + if (motor->is_use_dc_iris) { + motor->iris_ctrl = v4l2_ctrl_new_std(handler, &motor_ctrl_ops, + V4L2_CID_IRIS_ABSOLUTE, 0, motor->dciris->max_log, 1, 0); + if (motor->iris_ctrl) + motor->iris_ctrl->flags |= flags; + + } else if (motor->is_use_p_iris) { + #ifdef REINIT_BOOT + ret = ms41908_reinit_piris(motor); + if (ret < 0) + return -EINVAL; + #endif + motor->piris->last_pos = motor->piris->min_pos; + motor->iris_ctrl = v4l2_ctrl_new_std(handler, &motor_ctrl_ops, + V4L2_CID_IRIS_ABSOLUTE, + motor->piris->min_pos, + motor->piris->max_pos, + 1, motor->piris->min_pos); + if (motor->iris_ctrl) + motor->iris_ctrl->flags |= flags; + } + if (motor->is_use_focus) { + #ifdef REINIT_BOOT + ret = ms41908_reinit_focus(motor); + if (ret < 0) + return -EINVAL; + #endif + motor->focus->last_pos = motor->focus->min_pos; + motor->focus_ctrl = v4l2_ctrl_new_std(handler, &motor_ctrl_ops, + V4L2_CID_FOCUS_ABSOLUTE, motor->focus->min_pos, + motor->focus->max_pos - motor->focus->reback, + 1, motor->focus->min_pos); + if (motor->focus_ctrl) + motor->focus_ctrl->flags |= flags; + } + if (motor->is_use_zoom) { + #ifdef REINIT_BOOT + ret = ms41908_reinit_zoom(motor); + if (ret < 0) + return -EINVAL; + #endif + motor->zoom->last_pos = motor->zoom->min_pos; + motor->zoom_ctrl = v4l2_ctrl_new_std(handler, &motor_ctrl_ops, + V4L2_CID_ZOOM_ABSOLUTE, + motor->zoom->min_pos, + motor->zoom->max_pos - motor->zoom->reback, + 1, motor->zoom->min_pos); + if (motor->zoom_ctrl) + motor->zoom_ctrl->flags |= flags; + } + if (motor->is_use_zoom1) { + #ifdef REINIT_BOOT + ret = ms41908_reinit_zoom1(motor); + if (ret < 0) + return -EINVAL; + #endif + motor->zoom1->last_pos = motor->zoom1->min_pos; + motor->zoom1_ctrl = v4l2_ctrl_new_std(handler, &motor_ctrl_ops, + V4L2_CID_ZOOM_CONTINUOUS, + motor->zoom1->min_pos, + motor->zoom1->max_pos, + 1, motor->zoom1->min_pos); + if (motor->zoom1_ctrl) + motor->zoom1_ctrl->flags |= flags; + } + if (handler->error) { + ret = handler->error; + dev_err(&motor->spi->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + motor->subdev.ctrl_handler = handler; + return ret; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static void dev_param_init(struct motor_dev *motor) +{ + int step = 0; + u32 mv_cnt = 0; + u32 status = 0; + u32 reback_vd_cnt = 0; + + if (motor->is_use_dc_iris) + motor->dciris->last_log = 0; + if (motor->is_use_p_iris) { + motor->piris->is_mv_tim_update = false; + motor->piris->is_need_update_tim = false; + motor->piris->move_status = MOTOR_STATUS_STOPPED; + motor->piris->type = TYPE_IRIS; + motor->piris->mv_tim.vcm_start_t = ns_to_kernel_old_timeval(ktime_get_ns()); + motor->piris->mv_tim.vcm_end_t = ns_to_kernel_old_timeval(ktime_get_ns()); + init_completion(&motor->piris->complete); + init_completion(&motor->piris->complete_out); + motor->piris->run_data.psum = motor->vd_fz_period_us * + motor->piris->start_up_speed * 8 / 1000000; + if (motor->piris->is_half_step_mode) + motor->piris->run_data.psum /= 2; + motor->piris->run_data.intct = motor->sys_clk * motor->vd_fz_period_us / + (motor->piris->run_data.psum * 24); + motor->piris->is_running = false; + dev_info(&motor->spi->dev, + "piris vd_fz_period_us %u, psum %d, inict %d\n", + motor->vd_fz_period_us, + motor->piris->run_data.psum, + motor->piris->run_data.intct); + } + if (motor->is_use_focus) { + motor->focus->is_mv_tim_update = false; + motor->focus->is_need_update_tim = false; + motor->focus->move_status = MOTOR_STATUS_STOPPED; + motor->focus->type = TYPE_FOCUS; + motor->focus->mv_tim.vcm_start_t = ns_to_kernel_old_timeval(ktime_get_ns()); + motor->focus->mv_tim.vcm_end_t = ns_to_kernel_old_timeval(ktime_get_ns()); + init_completion(&motor->focus->complete); + init_completion(&motor->focus->complete_out); + motor->focus->run_data.psum = motor->vd_fz_period_us * + motor->focus->start_up_speed * 8 / 1000000; + if (motor->focus->is_half_step_mode) + motor->focus->run_data.psum /= 2; + motor->focus->run_data.intct = motor->sys_clk * motor->vd_fz_period_us / + (motor->focus->run_data.psum * 24); + motor->focus->is_running = false; + motor->focus->reback_ctrl = false; + motor->focus->default_intct = motor->focus->run_data.intct; + motor->focus->default_psum = motor->focus->run_data.psum; + dev_info(&motor->spi->dev, + "focus vd_fz_period_us %u, psum %d, inict %d, default_intct %d, default_psum %d\n", + motor->vd_fz_period_us, + motor->focus->run_data.psum, + motor->focus->run_data.intct, + motor->focus->default_intct, + motor->focus->default_psum); + if (motor->focus->reback != 0) { + motor->focus->cur_back_delay = 0; + motor->focus->max_back_delay = FOCUS_MAX_BACK_DELAY; + motor->focus->reback_data = motor->focus->run_data; + mv_cnt = motor->focus->reback; + if (motor->focus->is_dir_opp) { + mv_cnt += motor->focus->backlash; + status = MOTOR_STATUS_CCW; + } else { + mv_cnt += motor->focus->backlash; + status = MOTOR_STATUS_CW; + } + motor->focus->reback_status = status; + if (motor->focus->is_half_step_mode) + step = mv_cnt * 4; + else + step = mv_cnt * 8; + motor->focus->reback_data.count = (step + motor->focus->reback_data.psum - 1) / + motor->focus->reback_data.psum + 1; + motor->focus->reback_data.cur_count = motor->focus->reback_data.count; + motor->focus->reback_data.psum_last = step % motor->focus->reback_data.psum; + if (motor->focus->reback_data.psum_last == 0) + motor->focus->reback_data.psum_last = motor->focus->reback_data.psum; + reback_vd_cnt = motor->focus->reback_data.count + motor->focus->max_back_delay; + motor->focus->reback_move_time_us = reback_vd_cnt * (motor->vd_fz_period_us + 500); + } + } + if (motor->is_use_zoom) { + motor->zoom->is_mv_tim_update = false; + motor->zoom->is_need_update_tim = false; + motor->zoom->move_status = MOTOR_STATUS_STOPPED; + motor->zoom->type = TYPE_ZOOM; + motor->zoom->mv_tim.vcm_start_t = ns_to_kernel_old_timeval(ktime_get_ns()); + motor->zoom->mv_tim.vcm_end_t = ns_to_kernel_old_timeval(ktime_get_ns()); + init_completion(&motor->zoom->complete); + init_completion(&motor->zoom->complete_out); + motor->zoom->run_data.psum = motor->vd_fz_period_us * + motor->zoom->start_up_speed * 8 / 1000000; + if (motor->zoom->is_half_step_mode) + motor->zoom->run_data.psum /= 2; + motor->zoom->run_data.intct = motor->sys_clk * motor->vd_fz_period_us / + (motor->zoom->run_data.psum * 24); + motor->zoom->is_running = false; + motor->zoom->reback_ctrl = false; + motor->zoom->default_intct = motor->zoom->run_data.intct; + motor->zoom->default_psum = motor->zoom->run_data.psum; + if (motor->zoom->reback != 0) { + motor->zoom->cur_back_delay = 0; + motor->zoom->max_back_delay = ZOOM_MAX_BACK_DELAY; + motor->zoom->reback_data = motor->zoom->run_data; + mv_cnt = motor->zoom->reback; + if (motor->zoom->is_dir_opp) { + mv_cnt += motor->zoom->backlash; + status = MOTOR_STATUS_CCW; + } else { + mv_cnt += motor->zoom->backlash; + status = MOTOR_STATUS_CW; + } + motor->zoom->reback_status = status; + if (motor->zoom->is_half_step_mode) + step = mv_cnt * 4; + else + step = mv_cnt * 8; + motor->zoom->reback_data.count = (step + motor->zoom->reback_data.psum - 1) / + motor->zoom->reback_data.psum + 1; + motor->zoom->reback_data.cur_count = motor->zoom->reback_data.count; + motor->zoom->reback_data.psum_last = step % motor->zoom->reback_data.psum; + if (motor->zoom->reback_data.psum_last == 0) + motor->zoom->reback_data.psum_last = motor->zoom->reback_data.psum; + reback_vd_cnt = motor->zoom->reback_data.count + motor->zoom->max_back_delay; + motor->zoom->reback_move_time_us = reback_vd_cnt * (motor->vd_fz_period_us + 500); + } + dev_info(&motor->spi->dev, + "zoom vd_fz_period_us %u, psum %d, inict %d, default_intct %d, default_psum %d\n", + motor->vd_fz_period_us, + motor->zoom->run_data.psum, + motor->zoom->run_data.intct, + motor->zoom->default_intct, + motor->zoom->default_psum); + } + if (motor->is_use_zoom1) { + motor->zoom1->is_mv_tim_update = false; + motor->zoom1->is_need_update_tim = false; + motor->zoom1->move_status = MOTOR_STATUS_STOPPED; + motor->zoom1->type = TYPE_ZOOM1; + motor->zoom1->mv_tim.vcm_start_t = ns_to_kernel_old_timeval(ktime_get_ns()); + motor->zoom1->mv_tim.vcm_end_t = ns_to_kernel_old_timeval(ktime_get_ns()); + init_completion(&motor->zoom1->complete); + init_completion(&motor->zoom1->complete_out); + motor->zoom1->run_data.psum = motor->vd_fz_period_us * + motor->zoom1->start_up_speed * 8 / 1000000; + if (motor->zoom1->is_half_step_mode) + motor->zoom1->run_data.psum /= 2; + motor->zoom1->run_data.intct = motor->sys_clk * motor->vd_fz_period_us / + (motor->zoom1->run_data.psum * 24); + motor->zoom1->is_running = false; + motor->zoom1->reback_ctrl = false; + motor->zoom1->default_intct = motor->zoom1->run_data.intct; + motor->zoom1->default_psum = motor->zoom1->run_data.psum; + dev_info(&motor->spi->dev, + "zoom1 vd_fz_period_us %u, psum %d, inict %d, default_intct %d, default_psum %d\n", + motor->vd_fz_period_us, + motor->zoom1->run_data.psum, + motor->zoom1->run_data.intct, + motor->zoom1->default_intct, + motor->zoom1->default_psum); + } + + motor->is_should_wait = false; + motor->is_timer_restart = false; + motor->is_timer_restart_bywq = false; + motor->wait_cnt = 0; + motor->pi_gpio_usecnt = 0; +} + +static void dev_reg_init(struct motor_dev *motor) +{ + spi_write_reg(motor->spi, 0x20, 0x1a01);//27M/(30*2^3*2^0) + spi_write_reg(motor->spi, 0x21, 0x0085); + spi_write_reg(motor->spi, 0x23, PPW_STOP); + spi_write_reg(motor->spi, 0x28, PPW_STOP); + if (motor->dev0) + spi_write_reg(motor->spi, + motor->dev0->reg_op->reg.dt2_phmod, + (motor->dev0->run_data.phmode << 8) | 0x0001); + if (motor->dev1) + spi_write_reg(motor->spi, + motor->dev1->reg_op->reg.dt2_phmod, + (motor->dev1->run_data.phmode << 8) | 0x0001); + + spi_write_reg(motor->spi, 0x0b, 0x0480); + if (motor->is_use_dc_iris) { + //DC-IRIS reg init + if (motor->dciris->is_reversed_polarity) + spi_write_reg(motor->spi, 0x00, + motor->dciris->max_log - motor->dciris->last_log); + else + spi_write_reg(motor->spi, 0x00, motor->dciris->last_log); + spi_write_reg(motor->spi, 0x01, 0x6000); + spi_write_reg(motor->spi, 0x02, 0x66f0); + spi_write_reg(motor->spi, 0x03, 0x0e10); + spi_write_reg(motor->spi, 0x04, 0xd640); + spi_write_reg(motor->spi, 0x05, 0x0004); + spi_write_reg(motor->spi, 0x0b, 0x0480); + spi_write_reg(motor->spi, 0x0a, 0x0000); + spi_write_reg(motor->spi, 0x0e, 0x0300); + + } + if (!IS_ERR(motor->vd_fz_gpio)) + gpiod_set_value(motor->vd_fz_gpio, 1); + if (motor->is_use_dc_iris && (!IS_ERR(motor->dciris->vd_iris_gpio))) + gpiod_set_value(motor->dciris->vd_iris_gpio, 1); + usleep_range(100, 200); + if (!IS_ERR(motor->vd_fz_gpio)) + gpiod_set_value(motor->vd_fz_gpio, 0); + if (motor->is_use_dc_iris && (!IS_ERR(motor->dciris->vd_iris_gpio))) + gpiod_set_value(motor->dciris->vd_iris_gpio, 0); +} + + +static int ms41908_check_id(struct motor_dev *motor) +{ + u16 val = 0xffff; + int i = 0; + + for (i = 0; i < 0x20; i++) + spi_read_reg(motor->spi, i, &val); + spi_read_reg(motor->spi, 0x20, &val); + if (val == 0xffff) { + dev_err(&motor->spi->dev, + "check id fail, spi transfer err or driver not connect, val 0x%x\n", + val); + return -EINVAL; + } + return 0; +} + +static int ms41908_dev_init(struct motor_dev *motor) +{ + int ret = 0; + + if (!IS_ERR(motor->reset_gpio)) { + gpiod_set_value_cansleep(motor->reset_gpio, 0); + usleep_range(100, 200); + gpiod_set_value_cansleep(motor->reset_gpio, 1); + } + ret = ms41908_check_id(motor); + if (ret < 0) + return -EINVAL; + dev_param_init(motor); + dev_reg_init(motor); + + motor->wk = devm_kzalloc(&motor->spi->dev, sizeof(*motor->wk), GFP_KERNEL); + if (!motor->wk) { + dev_err(&motor->spi->dev, "failed to alloc work struct\n"); + return -ENOMEM; + } + motor->wk->dev = motor; + INIT_WORK(&motor->wk->work, motor_op_work); + + return 0; +} + +static int ms41908_dev_probe(struct spi_device *spi) +{ + int ret = 0; + struct device *dev = &spi->dev; + struct motor_dev *motor; + struct v4l2_subdev *sd; + char facing[2]; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + motor = devm_kzalloc(dev, sizeof(*motor), GFP_KERNEL); + if (!motor) + return -ENOMEM; + spi->mode = SPI_MODE_3 | SPI_LSB_FIRST | SPI_CS_HIGH; + spi->irq = -1; + spi->max_speed_hz = 5000000; + spi->bits_per_word = 8; + ret = spi_setup(spi); + if (ret < 0) { + dev_err(dev, "could not setup spi!\n"); + return -EINVAL; + } + motor->spi = spi; + motor->motor_op[0] = g_motor_op[0]; + motor->motor_op[1] = g_motor_op[1]; + + if (ms41908_dev_parse_dt(motor)) { + dev_err(&motor->spi->dev, "parse dt error\n"); + return -EINVAL; + } + ret = ms41908_dev_init(motor); + if (ret) + goto err_free; + + mutex_init(&motor->mutex); + hrtimer_init(&motor->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + motor->timer.function = motor_timer_func; + + sd = &motor->subdev; + v4l2_spi_subdev_init(sd, spi, &motor_subdev_ops); + sd->entity.function = MEDIA_ENT_F_LENS; + sd->entity.flags = 0; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + ms41908_initialize_controls(motor); + ret = media_entity_pads_init(&motor->subdev.entity, 0, NULL); + if (ret < 0) + goto err_free; + memset(facing, 0, sizeof(facing)); + if (strcmp(motor->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s_%d", + motor->module_index, facing, + DRIVER_NAME, + motor->id); + ret = v4l2_async_register_subdev(sd); + if (ret) + dev_err(&spi->dev, "v4l2 async register subdev failed\n"); +#ifdef USED_SYS_DEBUG + add_sysfs_interfaces(dev); +#endif + dev_info(&motor->spi->dev, "gpio motor driver probe success\n"); + return 0; +err_free: + v4l2_ctrl_handler_free(&motor->ctrl_handler); + v4l2_device_unregister_subdev(&motor->subdev); + media_entity_cleanup(&motor->subdev.entity); + return ret; +} + +static void ms41908_dev_remove(struct spi_device *spi) +{ + struct v4l2_subdev *sd = spi_get_drvdata(spi); + struct motor_dev *motor = to_motor_dev(sd); + + hrtimer_cancel(&motor->timer); + if (sd) + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&motor->ctrl_handler); + media_entity_cleanup(&motor->subdev.entity); +#ifdef USED_SYS_DEBUG + remove_sysfs_interfaces(&spi->dev); +#endif +} + +static const struct spi_device_id ms41908_match_id[] = { + {"relmon,ms41908", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ms41908_match_id); + +#if defined(CONFIG_OF) +static const struct of_device_id ms41908_dev_of_match[] = { + {.compatible = "relmon,ms41908", }, + {}, +}; +#endif + +static struct spi_driver ms41908_dev_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(ms41908_dev_of_match), + }, + .probe = ms41908_dev_probe, + .remove = ms41908_dev_remove, + .id_table = ms41908_match_id, +}; + +static int __init ms41908_mod_init(void) +{ + return spi_register_driver(&ms41908_dev_driver); +} + +static void __exit ms41908_mod_exit(void) +{ + spi_unregister_driver(&ms41908_dev_driver); +} + +device_initcall_sync(ms41908_mod_init); +module_exit(ms41908_mod_exit); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:motor"); +MODULE_AUTHOR("zefa.chen@rock-chips.com"); diff --git a/drivers/media/spi/ms41968.c b/drivers/media/spi/ms41968.c new file mode 100644 index 000000000000..1a531b1361db --- /dev/null +++ b/drivers/media/spi/ms41968.c @@ -0,0 +1,3503 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * motor driver + * + * Copyright (C) 2024 Rockchip Electronics Co., Ltd. + * + */ +//#define DEBUG +//#define READREG_DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "linux/rk_vcm_head.h" +#include +#include +#include +#include +#include + +#define DRIVER_VERSION KERNEL_VERSION(0, 0x01, 0x00) + +#define DRIVER_NAME "ms41968" + +#define START_UP_HZ_DEF (800) +#define PIRIS_MAX_STEP_DEF (80) +#define FOCUS_MAX_STEP_DEF (3060) +#define ZOOM_MAX_STEP_DEF (1520) + +#define DCIRIS_MAX_LOG 1023 + +#define VD_FZ_US 10000 + +#define PPW_DEF 0xff +#define MICRO_DEF 64 +#define PHMODE_DEF 0 +#define PPW_STOP 0x00 + +#define FOCUS_MAX_BACK_DELAY 4 +#define ZOOM_MAX_BACK_DELAY 4 +#define ZOOM1_MAX_BACK_DELAY 4 + +#define SYS_CLK_DEF_27MHZ 27 + +#define PIRIS_FINDPI_STEP 10 +#define FOCUS_FINDPI_STEP 200 +#define ZOOM_FINDPI_STEP 200 +#define ZOOM1_FINDPI_STEP 200 + +#define to_motor_dev(sd) container_of(sd, struct motor_dev, subdev) + +enum { + MOTOR_STATUS_STOPPED = 0, + MOTOR_STATUS_CW = 1, + MOTOR_STATUS_CCW = 2, +}; + +enum ext_dev_type { + TYPE_IRIS = 0, + TYPE_FOCUS = 1, + TYPE_ZOOM = 2, + TYPE_ZOOM1 = 3, +}; + +struct motor_reg_s { + u16 dt2_phmod_micro; // 起始点激励等待时间 / 相位矫正 / 细分选择 + u16 ppw; // 脉冲宽度 + u16 psum; // 步进数 / 转动方向 / 刹车状态 / Enable/Disable + u16 intct; // 每一步周期 + u16 pwm; // 微步进输出 PWM频率 / PWM 分辨率 +}; + +struct reg_op_s { + struct motor_reg_s reg; + u16 tmp_psum; + bool is_used; +}; + +struct run_data_s { + u32 count; + u32 cur_count; + u32 psum; + u32 psum_last; + u32 intct; + u32 ppw; + u32 ppw_stop; + u32 phmode; + u32 micro; +}; + +struct ext_dev { + u8 type; + u32 step_max; + int last_pos; + u32 start_up_speed; + u32 move_status; + u32 reback_status; + u32 move_time_us; + u32 reback_move_time_us; + u32 backlash; + int reback; + u32 last_dir; + int min_pos; + int max_pos; + bool is_half_step_mode; + bool is_mv_tim_update; + bool is_need_update_tim; + bool is_dir_opp; + bool is_need_reback; + bool reback_ctrl; + bool is_pihigh_positive_pos; + u32 findpi_step; + struct rk_cam_vcm_tim mv_tim; + struct run_data_s run_data; + struct run_data_s reback_data; + struct completion complete; + struct reg_op_s *reg_op; + struct gpio_desc *pic_gpio; + struct gpio_desc *pia_gpio; + struct gpio_desc *pie_gpio; + struct gpio_desc *vd_gpio; + struct iio_channel *channel; + int cur_back_delay; + int max_back_delay; + struct completion complete_out; + bool is_running; + + u32 default_psum; + u32 default_intct; +}; + +struct dciris_dev { + u32 last_log; + u32 max_log; + bool is_reversed_polarity; + struct gpio_desc *vd_iris_gpio; +}; + +struct motor_dev { + struct spi_device *spi; + struct v4l2_subdev subdev; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *iris_ctrl; + struct v4l2_ctrl *focus_ctrl; + struct v4l2_ctrl *zoom_ctrl; + struct v4l2_ctrl *zoom1_ctrl; + struct hrtimer timer; + struct mutex mutex; + u32 module_index; + const char *module_facing; + struct ext_dev *piris; + struct ext_dev *focus; + struct ext_dev *zoom; + struct ext_dev *zoom1; + struct ext_dev *dev0; + struct ext_dev *dev1; + struct ext_dev *dev2; + struct ext_dev *dev3; + bool is_use_dc_iris; + bool is_use_p_iris; + bool is_use_focus; + bool is_use_zoom; + bool is_use_zoom1; + struct gpio_desc *reset_gpio; + bool is_timer_restart; + bool is_timer_restart_bywq; + bool is_should_wait; + struct motor_work_s *wk; + u32 vd_fz_period_us; + struct reg_op_s motor_op[4]; + struct dciris_dev *dciris; + int id; + int wait_cnt; + int pi_gpio_usecnt; + u32 sys_clk; +}; + +struct motor_work_s { + struct work_struct work; + struct motor_dev *dev; +}; + +static const struct reg_op_s g_motor_op[4] = {{{0x22, 0x23, 0x24, 0x25, 0x26}, 0, 0}, + {{0x27, 0x28, 0x29, 0x2a, 0x2b}, 0, 0}, + {{0x2c, 0x2d, 0x2e, 0x2f, 0x30}, 0, 0}, + {{0x31, 0x32, 0x33, 0x34, 0x35}, 0, 0}}; + +static int spi_write_reg(struct spi_device *spi, u8 reg, u16 val) +{ + int ret = 0; + u8 buf_reg = reg; + u16 buf_val = val; + + struct spi_message msg; + struct spi_transfer tx[] = { + { + .tx_buf = &buf_reg, + .len = 1, + .delay.value = 1, + .delay.unit = SPI_DELAY_UNIT_USECS, + }, { + .tx_buf = &buf_val, + .len = 2, + .delay.value = 1, + .delay.unit = SPI_DELAY_UNIT_USECS, + }, + }; + spi_message_init(&msg); + spi_message_add_tail(&tx[0], &msg); + spi_message_add_tail(&tx[1], &msg); + ret = spi_sync(spi, &msg); + return ret; +} + +static __maybe_unused int spi_read_reg(struct spi_device *spi, u8 reg, u16 *val) +{ + int ret = 0; + u8 buf_reg = reg | 0x40; + u16 buf_val = 0; + + struct spi_message msg; + struct spi_transfer tx[] = { + { + .tx_buf = &buf_reg, + .len = 1, + .delay.value = 1, + .delay.unit = SPI_DELAY_UNIT_USECS, + }, { + .rx_buf = &buf_val, + .len = 2, + .delay.value = 1, + .delay.unit = SPI_DELAY_UNIT_USECS, + }, + }; + spi_message_init(&msg); + spi_message_add_tail(&tx[0], &msg); + spi_message_add_tail(&tx[1], &msg); + ret = spi_sync(spi, &msg); + *val = buf_val; + + return ret; +} + +static int ms41968_get_pic_val(struct ext_dev *ext_dev) +{ + int value = 0; + + if (ext_dev->channel) { + iio_read_channel_raw(ext_dev->channel, &value); + + if (value > 2047) + return 1; + else + return 0; + } else { + return gpiod_get_value(ext_dev->pic_gpio); + } +} + +static int set_motor_running_status(struct motor_dev *motor, + struct ext_dev *ext_dev, + s32 pos, + bool is_need_update_tim, + bool is_should_wait, + bool is_need_reback) +{ + int ret = 0; + u32 step = 0; + u16 psum = 0; + struct run_data_s run_data = ext_dev->run_data; + u32 micro = 0; + u32 mv_cnt = 0; + int status = 0; + + if (ext_dev->move_status != MOTOR_STATUS_STOPPED) + wait_for_completion(&ext_dev->complete); + ext_dev->is_mv_tim_update = false; + + ext_dev->move_time_us = 0; + mv_cnt = abs(pos - ext_dev->last_pos); + if (is_need_reback) + mv_cnt += ext_dev->reback; + dev_dbg(&motor->spi->dev, + "dev type %d pos %d, last_pos %d, mv_cnt %d, status %d is_need_reback %d\n", + ext_dev->type, pos, ext_dev->last_pos, mv_cnt, status, is_need_reback); + if (mv_cnt == 0) { + mutex_lock(&motor->mutex); + if (is_need_update_tim) { + ext_dev->mv_tim.vcm_start_t = ns_to_kernel_old_timeval(ktime_get_ns()); + ext_dev->mv_tim.vcm_end_t = ext_dev->mv_tim.vcm_start_t; + ext_dev->is_mv_tim_update = true; + if (motor->wait_cnt == 0) { + mutex_unlock(&motor->mutex); + return 0; + } + } + if (is_should_wait) { + motor->wait_cnt++; + } else if (motor->is_timer_restart == false && motor->wait_cnt) { + motor->is_timer_restart = true; + motor->is_timer_restart_bywq = false; + hrtimer_start(&motor->timer, ktime_set(0, 0), HRTIMER_MODE_REL); + motor->wait_cnt = 0; + } else { + motor->wait_cnt = 0; + } + mutex_unlock(&motor->mutex); + return 0; + } + ext_dev->is_running = true; + reinit_completion(&ext_dev->complete); + reinit_completion(&ext_dev->complete_out); + + if (ext_dev->is_dir_opp) { + if (pos > ext_dev->last_pos) { + if (ext_dev->last_dir == MOTOR_STATUS_CW) + mv_cnt += ext_dev->backlash; + status = MOTOR_STATUS_CCW; + } else { + if (ext_dev->last_dir == MOTOR_STATUS_CCW) + mv_cnt += ext_dev->backlash; + status = MOTOR_STATUS_CW; + } + } else { + if (pos > ext_dev->last_pos) { + if (ext_dev->last_dir == MOTOR_STATUS_CCW) + mv_cnt += ext_dev->backlash; + status = MOTOR_STATUS_CW; + } else { + if (ext_dev->last_dir == MOTOR_STATUS_CW) + mv_cnt += ext_dev->backlash; + status = MOTOR_STATUS_CCW; + } + } + + if (ext_dev->is_half_step_mode) + step = mv_cnt * 4; + else + step = mv_cnt * 8; + + run_data.count = (step + run_data.psum - 1) / run_data.psum; + run_data.cur_count = run_data.count; + run_data.psum_last = step % run_data.psum; + if (run_data.psum_last == 0) + run_data.psum_last = run_data.psum; + + switch (run_data.micro) { + case 64: + micro = 0x03; + break; + case 128: + micro = 0x02; + break; + case 256: + micro = 0x01; + break; + default: + micro = 0x00; + break; + }; + if (run_data.count == 1) + psum = ((status - 1) << 12) | + (1 << 14) | + (run_data.psum_last); + else + psum = ((status - 1) << 12) | + (1 << 14) | + (run_data.psum); + mutex_lock(&motor->mutex); + ext_dev->is_need_update_tim = is_need_update_tim; + ext_dev->is_need_reback = is_need_reback; + ext_dev->move_time_us = (run_data.count + 1) * (motor->vd_fz_period_us + 500); + if (is_need_reback) + ext_dev->move_time_us += ext_dev->reback_move_time_us; + + ext_dev->last_pos = pos; + ext_dev->run_data = run_data; + ext_dev->move_status = status; + spi_write_reg(motor->spi, 0x20, 0x01); + spi_write_reg(motor->spi, + ext_dev->reg_op->reg.dt2_phmod_micro, + (ext_dev->run_data.phmode << 8) | 0x0001 | (micro << 14)); + spi_write_reg(motor->spi, ext_dev->reg_op->reg.ppw, run_data.ppw | (run_data.ppw << 8)); + spi_write_reg(motor->spi, ext_dev->reg_op->reg.psum, psum); + spi_write_reg(motor->spi, ext_dev->reg_op->reg.intct, ext_dev->run_data.intct); + spi_write_reg(motor->spi, ext_dev->reg_op->reg.pwm, 0x541a); + ext_dev->reg_op->tmp_psum = psum; + + ext_dev->last_dir = status; + if (is_should_wait) { + motor->wait_cnt++; + } else if (motor->is_timer_restart == false) { + motor->is_timer_restart = true; + motor->is_timer_restart_bywq = false; + hrtimer_start(&motor->timer, ktime_set(0, 0), HRTIMER_MODE_REL); + motor->wait_cnt = 0; + } else { + motor->wait_cnt = 0; + } + mutex_unlock(&motor->mutex); + dev_dbg(&motor->spi->dev, + "ext_dev type %d move count %d, psum %d, psum_last %d, move_time_us %u!\n", + ext_dev->type, + ext_dev->run_data.count, + ext_dev->run_data.psum, + ext_dev->run_data.psum_last, + ext_dev->move_time_us); + + return ret; +} + +static int ms41968_dev_parse_dt(struct motor_dev *motor) +{ + struct device_node *node = motor->spi->dev.of_node; + int ret = 0; + const char *str; + int step_motor_cnt = 0; + + motor->is_use_dc_iris = + device_property_read_bool(&motor->spi->dev, "use-dc-iris"); + motor->is_use_p_iris = + device_property_read_bool(&motor->spi->dev, "use-p-iris"); + motor->is_use_focus = + device_property_read_bool(&motor->spi->dev, "use-focus"); + motor->is_use_zoom = + device_property_read_bool(&motor->spi->dev, "use-zoom"); + motor->is_use_zoom1 = + device_property_read_bool(&motor->spi->dev, "use-zoom1"); + + ret = of_property_read_u32(node, + "sys-clk", + &motor->sys_clk); + if (ret != 0) { + motor->sys_clk = SYS_CLK_DEF_27MHZ; + dev_err(&motor->spi->dev, + "failed get sys clk,use dafult value %d MHz\n", SYS_CLK_DEF_27MHZ); + } + /* get reset gpio */ + motor->reset_gpio = devm_gpiod_get(&motor->spi->dev, + "reset", GPIOD_OUT_LOW); + if (IS_ERR(motor->reset_gpio)) + dev_err(&motor->spi->dev, "Failed to get reset-gpios\n"); + + ret = of_property_read_u32(node, + "vd_fz-period-us", + &motor->vd_fz_period_us); + if (ret != 0) { + motor->vd_fz_period_us = VD_FZ_US; + dev_err(&motor->spi->dev, + "failed get vd_fz-period-us,use dafult value\n"); + } + + ret = of_property_read_u32(node, + "id", + &motor->id); + if (ret != 0) { + motor->id = 0; + dev_err(&motor->spi->dev, + "failed get driver id,use dafult value\n"); + } + + if (motor->is_use_dc_iris) { + motor->dciris = devm_kzalloc(&motor->spi->dev, sizeof(*motor->dciris), GFP_KERNEL); + if (!motor->dciris) + return -ENOMEM; + + /* get vd_iris gpio */ + motor->dciris->vd_iris_gpio = devm_gpiod_get(&motor->spi->dev, + "vd_iris", GPIOD_OUT_LOW); + if (IS_ERR(motor->dciris->vd_iris_gpio)) + dev_info(&motor->spi->dev, "Failed to get vd_iris-gpios\n"); + motor->dciris->is_reversed_polarity = + device_property_read_bool(&motor->spi->dev, "dc-iris-reserved-polarity"); + ret = of_property_read_u32(node, + "dc-iris-max-log", + &motor->dciris->max_log); + if (ret != 0) { + motor->dciris->max_log = DCIRIS_MAX_LOG; + dev_err(&motor->spi->dev, + "failed get dc-iris max log,use dafult value\n"); + } + } + + if (motor->is_use_p_iris) { + if (motor->is_use_dc_iris) { + dev_err(&motor->spi->dev, + "Does not support p-iris and dc-iris on the same module\n"); + return -EINVAL; + } + step_motor_cnt++; + motor->piris = devm_kzalloc(&motor->spi->dev, sizeof(*motor->piris), GFP_KERNEL); + if (!motor->piris) + return -ENOMEM; + + ret = of_property_read_string(node, "piris-used-pin", + &str); + if (ret != 0) { + dev_err(&motor->spi->dev, + "get piris-used-pin fail, please check it!\n"); + return -EINVAL; + } + if (strcmp(str, "ab") == 0) { + motor->piris->reg_op = &motor->motor_op[0]; + if (motor->piris->reg_op->is_used) { + dev_err(&motor->spi->dev, + "__line__ %d, pin already been used\n", __LINE__); + return -EINVAL; + } + motor->piris->reg_op->is_used = true; + } else if (strcmp(str, "cd") == 0) { + motor->piris->reg_op = &motor->motor_op[1]; + if (motor->piris->reg_op->is_used) { + dev_err(&motor->spi->dev, + "__line__ %d, pin already been used\n", __LINE__); + return -EINVAL; + } + motor->piris->reg_op->is_used = true; + } else if (strcmp(str, "ef") == 0) { + motor->piris->reg_op = &motor->motor_op[2]; + if (motor->piris->reg_op->is_used) { + dev_err(&motor->spi->dev, + "__line__ %d, pin already been used\n", __LINE__); + return -EINVAL; + } + motor->piris->reg_op->is_used = true; + } else if (strcmp(str, "gh") == 0) { + motor->piris->reg_op = &motor->motor_op[3]; + if (motor->piris->reg_op->is_used) { + dev_err(&motor->spi->dev, + "__line__ %d, pin already been used\n", __LINE__); + return -EINVAL; + } + motor->piris->reg_op->is_used = true; + } else { + dev_err(&motor->spi->dev, + "__line__ %d, pin require error\n", __LINE__); + return -EINVAL; + } + ret = of_property_read_u32(node, + "piris-backlash", + &motor->piris->backlash); + if (ret != 0) { + motor->piris->backlash = 0; + dev_err(&motor->spi->dev, + "failed get motor backlash,use dafult value\n"); + } + ret = of_property_read_u32(node, + "piris-start-up-speed", + &motor->piris->start_up_speed); + if (ret != 0) { + motor->piris->start_up_speed = START_UP_HZ_DEF; + dev_err(&motor->spi->dev, + "failed get motor start up speed,use dafult value\n"); + } + ret = of_property_read_u32(node, + "piris-step-max", + &motor->piris->step_max); + if (ret != 0) { + motor->piris->step_max = PIRIS_MAX_STEP_DEF; + dev_err(&motor->spi->dev, + "failed get piris pos_max,use dafult value\n"); + } + ret = of_property_read_u32(node, + "piris-ppw", + &motor->piris->run_data.ppw); + if (ret != 0 || (motor->piris->run_data.ppw > 0xff)) { + motor->piris->run_data.ppw = PPW_DEF; + dev_err(&motor->spi->dev, + "failed get piris ppw,use dafult value\n"); + } + ret = of_property_read_u32(node, + "piris-ppw-stop", + &motor->piris->run_data.ppw_stop); + if (ret != 0 || (motor->piris->run_data.ppw_stop > 0xff)) { + motor->piris->run_data.ppw_stop = PPW_STOP; + dev_err(&motor->spi->dev, + "failed get piris ppw_stop,use dafult value\n"); + } + ret = of_property_read_u32(node, + "piris-phmode", + &motor->piris->run_data.phmode); + if (ret != 0 || (motor->piris->run_data.phmode > 0x3f)) { + motor->piris->run_data.phmode = PHMODE_DEF; + dev_err(&motor->spi->dev, + "failed get piris phmode,use dafult value\n"); + } + ret = of_property_read_u32(node, + "piris-micro", + &motor->piris->run_data.micro); + if (ret != 0) { + motor->piris->run_data.micro = MICRO_DEF; + dev_err(&motor->spi->dev, + "failed get piris micro,use dafult value\n"); + } + /* get piris pi gpio */ + motor->piris->pic_gpio = devm_gpiod_get(&motor->spi->dev, + "piris_pic", GPIOD_IN); + if (IS_ERR(motor->piris->pic_gpio)) + dev_err(&motor->spi->dev, "Failed to get piris-pi-c-gpios\n"); + motor->piris->pia_gpio = devm_gpiod_get(&motor->spi->dev, + "piris_pia", GPIOD_OUT_LOW); + if (IS_ERR(motor->piris->pia_gpio)) + dev_err(&motor->spi->dev, "Failed to get piris-pi-a-gpios\n"); + motor->piris->pie_gpio = devm_gpiod_get(&motor->spi->dev, + "piris_pie", GPIOD_OUT_LOW); + if (IS_ERR(motor->piris->pie_gpio)) + dev_err(&motor->spi->dev, "Failed to get piris-pi-e-gpios\n"); + motor->piris->is_half_step_mode = + device_property_read_bool(&motor->spi->dev, "piris-1-2phase-excitation"); + motor->piris->is_dir_opp = + device_property_read_bool(&motor->spi->dev, "piris-dir-opposite"); + ret = of_property_read_s32(node, + "piris-min-pos", + &motor->piris->min_pos); + if (ret != 0) { + motor->piris->min_pos = 0; + dev_err(&motor->spi->dev, + "failed get piris min pos,use dafult value\n"); + } + ret = of_property_read_s32(node, + "piris-max-pos", + &motor->piris->max_pos); + if (ret != 0) { + motor->piris->max_pos = motor->piris->step_max; + dev_err(&motor->spi->dev, + "failed get piris max_pos pos,use dafult value\n"); + } + if (step_motor_cnt == 1) + motor->dev0 = motor->piris; + else if (step_motor_cnt == 2) + motor->dev1 = motor->piris; + ret = of_property_read_u32(node, + "piris-reback-distance", + &motor->piris->reback); + if (ret != 0) { + dev_err(&motor->spi->dev, + "failed get piris reback distance, return\n"); + return -EINVAL; + } + motor->piris->is_pihigh_positive_pos = + device_property_read_bool(&motor->spi->dev, "piris-pihigh-positive-pos"); + ret = of_property_read_u32(node, + "piris-findpi-step", + &motor->piris->findpi_step); + if (ret != 0) { + motor->piris->findpi_step = PIRIS_FINDPI_STEP; + dev_err(&motor->spi->dev, + "failed get piris find pi step\n"); + } + /* get vd_fz gpio */ + motor->piris->vd_gpio = devm_gpiod_get(&motor->spi->dev, + "vd-piris", GPIOD_OUT_LOW); + if (IS_ERR(motor->piris->vd_gpio)) + dev_info(&motor->spi->dev, "Failed to get piris vd-piris-gpios\n"); + + motor->piris->channel = devm_iio_channel_get(&motor->spi->dev, "piris_pic"); + if (IS_ERR(motor->piris->channel)) + return PTR_ERR(motor->piris->channel); + if (!motor->piris->channel->indio_dev) + return -ENXIO; + } + + if (motor->is_use_focus) { + step_motor_cnt++; + motor->focus = devm_kzalloc(&motor->spi->dev, sizeof(*motor->focus), GFP_KERNEL); + if (!motor->focus) + return -ENOMEM; + + ret = of_property_read_string(node, "focus-used-pin", + &str); + if (ret != 0) { + dev_err(&motor->spi->dev, + "get focus-used-pin fail, please check it!\n"); + return -EINVAL; + } + if (strcmp(str, "ab") == 0) { + motor->focus->reg_op = &motor->motor_op[0]; + if (motor->focus->reg_op->is_used) { + dev_err(&motor->spi->dev, + "__line__ %d, pin already been used\n", __LINE__); + return -EINVAL; + } + motor->focus->reg_op->is_used = true; + } else if (strcmp(str, "cd") == 0) { + motor->focus->reg_op = &motor->motor_op[1]; + if (motor->focus->reg_op->is_used) { + dev_err(&motor->spi->dev, + "__line__ %d, pin already been used\n", __LINE__); + return -EINVAL; + } + motor->focus->reg_op->is_used = true; + } else if (strcmp(str, "ef") == 0) { + motor->focus->reg_op = &motor->motor_op[2]; + if (motor->focus->reg_op->is_used) { + dev_err(&motor->spi->dev, + "__line__ %d, pin already been used\n", __LINE__); + return -EINVAL; + } + motor->focus->reg_op->is_used = true; + } else if (strcmp(str, "gh") == 0) { + motor->focus->reg_op = &motor->motor_op[3]; + if (motor->focus->reg_op->is_used) { + dev_err(&motor->spi->dev, + "__line__ %d, pin already been used\n", __LINE__); + return -EINVAL; + } + motor->focus->reg_op->is_used = true; + } else { + dev_err(&motor->spi->dev, + "__line__ %d, pin require error\n", __LINE__); + return -EINVAL; + } + ret = of_property_read_u32(node, + "focus-backlash", + &motor->focus->backlash); + if (ret != 0) { + motor->focus->backlash = 0; + dev_err(&motor->spi->dev, + "failed get motor backlash,use dafult value\n"); + } + ret = of_property_read_u32(node, + "focus-start-up-speed", + &motor->focus->start_up_speed); + if (ret != 0) { + motor->focus->start_up_speed = START_UP_HZ_DEF; + dev_err(&motor->spi->dev, + "failed get motor start up speed,use dafult value\n"); + } + ret = of_property_read_u32(node, + "focus-step-max", + &motor->focus->step_max); + if (ret != 0) { + motor->focus->step_max = FOCUS_MAX_STEP_DEF; + dev_err(&motor->spi->dev, + "failed get focus_pos_max,use dafult value\n"); + } + ret = of_property_read_u32(node, + "focus-ppw", + &motor->focus->run_data.ppw); + if (ret != 0 || (motor->focus->run_data.ppw > 0xff)) { + motor->focus->run_data.ppw = PPW_DEF; + dev_err(&motor->spi->dev, + "failed get focus ppw,use dafult value\n"); + } + ret = of_property_read_u32(node, + "focus-ppw-stop", + &motor->focus->run_data.ppw_stop); + if (ret != 0 || (motor->focus->run_data.ppw_stop > 0xff)) { + motor->focus->run_data.ppw_stop = PPW_STOP; + dev_err(&motor->spi->dev, + "failed get focus ppw_stop,use dafult value\n"); + } + ret = of_property_read_u32(node, + "focus-phmode", + &motor->focus->run_data.phmode); + if (ret != 0 || (motor->focus->run_data.phmode > 0x3f)) { + motor->focus->run_data.phmode = PHMODE_DEF; + dev_err(&motor->spi->dev, + "failed get focus phmode,use dafult value\n"); + } + ret = of_property_read_u32(node, + "focus-micro", + &motor->focus->run_data.micro); + if (ret != 0) { + motor->focus->run_data.micro = MICRO_DEF; + dev_err(&motor->spi->dev, + "failed get focus micro,use dafult value\n"); + } + if (step_motor_cnt == 1) + motor->dev0 = motor->focus; + else if (step_motor_cnt == 2) + motor->dev1 = motor->focus; + else if (step_motor_cnt == 3) + motor->dev2 = motor->focus; + /* get focus pi gpio */ + motor->focus->pic_gpio = devm_gpiod_get(&motor->spi->dev, + "focus_pic", GPIOD_IN); + if (IS_ERR(motor->focus->pic_gpio)) + dev_err(&motor->spi->dev, "Failed to get focus-pi-c-gpios\n"); + motor->focus->pia_gpio = devm_gpiod_get(&motor->spi->dev, + "focus_pia", GPIOD_OUT_LOW); + if (IS_ERR(motor->focus->pia_gpio)) + dev_err(&motor->spi->dev, "Failed to get focus-pi-a-gpios\n"); + motor->focus->pie_gpio = devm_gpiod_get(&motor->spi->dev, + "focus_pie", GPIOD_OUT_LOW); + if (IS_ERR(motor->focus->pie_gpio)) + dev_err(&motor->spi->dev, "Failed to get focus-pi-e-gpios\n"); + ret = of_property_read_u32(node, + "focus-reback-distance", + &motor->focus->reback); + if (ret != 0) { + dev_err(&motor->spi->dev, + "failed get focus reback distance, return\n"); + return -EINVAL; + } + motor->focus->is_half_step_mode = + device_property_read_bool(&motor->spi->dev, "focus-1-2phase-excitation"); + motor->focus->is_dir_opp = + device_property_read_bool(&motor->spi->dev, "focus-dir-opposite"); + ret = of_property_read_s32(node, + "focus-min-pos", + &motor->focus->min_pos); + if (ret != 0) { + motor->focus->min_pos = 0; + dev_err(&motor->spi->dev, + "failed get focus min pos,use dafult value\n"); + } + ret = of_property_read_s32(node, + "focus-max-pos", + &motor->focus->max_pos); + if (ret != 0) { + motor->focus->max_pos = motor->focus->step_max; + dev_err(&motor->spi->dev, + "failed get focus max_pos pos,use dafult value\n"); + } + motor->focus->is_pihigh_positive_pos = + device_property_read_bool(&motor->spi->dev, "focus-pihigh-positive-pos"); + ret = of_property_read_u32(node, + "focus-findpi-step", + &motor->focus->findpi_step); + if (ret != 0) { + motor->focus->findpi_step = FOCUS_FINDPI_STEP; + dev_err(&motor->spi->dev, + "failed get focus find pi step\n"); + } + /* get vd_gpio */ + motor->focus->vd_gpio = devm_gpiod_get(&motor->spi->dev, + "vd_focus", GPIOD_OUT_LOW); + if (IS_ERR(motor->focus->vd_gpio)) + dev_info(&motor->spi->dev, "Failed to get vd_focus\n"); + + motor->focus->channel = devm_iio_channel_get(&motor->spi->dev, "focus_pic"); + if (IS_ERR(motor->focus->channel)) + return PTR_ERR(motor->focus->channel); + if (!motor->focus->channel->indio_dev) + return -ENXIO; + } + + if (motor->is_use_zoom) { + if (step_motor_cnt >= 4) { + dev_err(&motor->spi->dev, + "The driver support step-motor max num is 2\n"); + return -EINVAL; + } + step_motor_cnt++; + motor->zoom = devm_kzalloc(&motor->spi->dev, sizeof(*motor->zoom), GFP_KERNEL); + if (!motor->zoom) + return -ENOMEM; + + ret = of_property_read_string(node, "zoom-used-pin", + &str); + if (ret != 0) { + dev_err(&motor->spi->dev, + "get zoom-used-pin fail, please check it!\n"); + return -EINVAL; + } + if (strcmp(str, "ab") == 0) { + motor->zoom->reg_op = &motor->motor_op[0]; + if (motor->zoom->reg_op->is_used) { + dev_err(&motor->spi->dev, + "__line__ %d, pin already been used\n", __LINE__); + return -EINVAL; + } + motor->zoom->reg_op->is_used = true; + } else if (strcmp(str, "cd") == 0) { + motor->zoom->reg_op = &motor->motor_op[1]; + if (motor->zoom->reg_op->is_used) { + dev_err(&motor->spi->dev, + "__line__ %d, pin already been used\n", __LINE__); + return -EINVAL; + } + motor->zoom->reg_op->is_used = true; + } else if (strcmp(str, "ef") == 0) { + motor->zoom->reg_op = &motor->motor_op[2]; + if (motor->zoom->reg_op->is_used) { + dev_err(&motor->spi->dev, + "__line__ %d, pin already been used\n", __LINE__); + return -EINVAL; + } + motor->zoom->reg_op->is_used = true; + } else if (strcmp(str, "gh") == 0) { + motor->zoom->reg_op = &motor->motor_op[3]; + if (motor->zoom->reg_op->is_used) { + dev_err(&motor->spi->dev, + "__line__ %d, pin already been used\n", __LINE__); + return -EINVAL; + } + motor->zoom->reg_op->is_used = true; + } else { + dev_err(&motor->spi->dev, + "__line__ %d, pin require error\n", __LINE__); + return -EINVAL; + } + ret = of_property_read_u32(node, + "zoom-backlash", + &motor->zoom->backlash); + if (ret != 0) { + motor->zoom->backlash = 0; + dev_err(&motor->spi->dev, + "failed get motor backlash,use dafult value\n"); + } + ret = of_property_read_u32(node, + "zoom-step-max", + &motor->zoom->step_max); + if (ret != 0) { + motor->zoom->step_max = ZOOM_MAX_STEP_DEF; + dev_err(&motor->spi->dev, + "failed get iris zoom_pos_max,use dafult value\n"); + } + + ret = of_property_read_u32(node, + "zoom-start-up-speed", + &motor->zoom->start_up_speed); + if (ret != 0) { + motor->zoom->start_up_speed = START_UP_HZ_DEF; + dev_err(&motor->spi->dev, + "failed get motor start up speed,use dafult value\n"); + } + ret = of_property_read_u32(node, + "zoom-ppw", + &motor->zoom->run_data.ppw); + if (ret != 0 || (motor->zoom->run_data.ppw > 0xff)) { + motor->zoom->run_data.ppw = PPW_DEF; + dev_err(&motor->spi->dev, + "failed get zoom ppw,use dafult value\n"); + } + ret = of_property_read_u32(node, + "zoom-ppw-stop", + &motor->zoom->run_data.ppw_stop); + if (ret != 0 || (motor->zoom->run_data.ppw_stop > 0xff)) { + motor->zoom->run_data.ppw_stop = PPW_STOP; + dev_err(&motor->spi->dev, + "failed get zoom ppw_stop,use dafult value\n"); + } + ret = of_property_read_u32(node, + "zoom-phmode", + &motor->zoom->run_data.phmode); + if (ret != 0 || (motor->zoom->run_data.phmode > 0x3ff)) { + motor->zoom->run_data.phmode = PHMODE_DEF; + dev_err(&motor->spi->dev, + "failed get zoom phmode,use dafult value\n"); + } + ret = of_property_read_u32(node, + "zoom-micro", + &motor->zoom->run_data.micro); + if (ret != 0) { + motor->zoom->run_data.micro = MICRO_DEF; + dev_err(&motor->spi->dev, + "failed get zoom micro,use dafult value\n"); + } + motor->zoom->pic_gpio = devm_gpiod_get(&motor->spi->dev, + "zoom_pic", GPIOD_IN); + if (IS_ERR(motor->zoom->pic_gpio)) + dev_err(&motor->spi->dev, "Failed to get zoom-pi-c-gpios\n"); + motor->zoom->is_half_step_mode = + device_property_read_bool(&motor->spi->dev, "zoom-1-2phase-excitation"); + motor->zoom->is_dir_opp = + device_property_read_bool(&motor->spi->dev, "zoom-dir-opposite"); + if (step_motor_cnt == 1) + motor->dev0 = motor->zoom; + else if (step_motor_cnt == 2) + motor->dev1 = motor->zoom; + else if (step_motor_cnt == 3) + motor->dev2 = motor->zoom; + else if (step_motor_cnt == 4) + motor->dev3 = motor->zoom; + ret = of_property_read_s32(node, + "zoom-min-pos", + &motor->zoom->min_pos); + if (ret != 0) { + motor->zoom->min_pos = 0; + dev_err(&motor->spi->dev, + "failed get zoom min pos,use dafult value\n"); + } + ret = of_property_read_s32(node, + "zoom-max-pos", + &motor->zoom->max_pos); + if (ret != 0) { + motor->zoom->max_pos = motor->zoom->step_max; + dev_err(&motor->spi->dev, + "failed get zoom max_pos pos,use dafult value\n"); + } + ret = of_property_read_u32(node, + "zoom-reback-distance", + &motor->zoom->reback); + if (ret != 0) { + dev_err(&motor->spi->dev, + "failed get zoom reback distance, return\n"); + return -EINVAL; + } + motor->zoom->is_pihigh_positive_pos = + device_property_read_bool(&motor->spi->dev, "zoom-pihigh-positive-pos"); + ret = of_property_read_u32(node, + "zoom-findpi-step", + &motor->zoom->findpi_step); + if (ret != 0) { + motor->zoom->findpi_step = ZOOM_FINDPI_STEP; + dev_err(&motor->spi->dev, + "failed get zoom find pi step\n"); + } + /* get vd_fz gpio */ + motor->zoom->vd_gpio = devm_gpiod_get(&motor->spi->dev, + "vd_zoom", GPIOD_OUT_LOW); + if (IS_ERR(motor->zoom->vd_gpio)) + dev_info(&motor->spi->dev, "Failed to get vd_zoom\n"); + + motor->zoom->channel = devm_iio_channel_get(&motor->spi->dev, "zoom_pic"); + if (IS_ERR(motor->zoom->channel)) + return PTR_ERR(motor->zoom->channel); + if (!motor->zoom->channel->indio_dev) + return -ENXIO; + } + + if (motor->is_use_zoom1) { + if (step_motor_cnt >= 4) { + dev_err(&motor->spi->dev, + "The driver support step-motor max num is 2\n"); + return -EINVAL; + } + step_motor_cnt++; + motor->zoom1 = devm_kzalloc(&motor->spi->dev, sizeof(*motor->zoom1), GFP_KERNEL); + if (!motor->zoom1) + return -ENOMEM; + + ret = of_property_read_string(node, "zoom1-used-pin", + &str); + if (ret != 0) { + dev_err(&motor->spi->dev, + "get zoom1-used-pin fail, please check it!\n"); + return -EINVAL; + } + if (strcmp(str, "ab") == 0) { + motor->zoom1->reg_op = &motor->motor_op[0]; + if (motor->zoom1->reg_op->is_used) { + dev_err(&motor->spi->dev, + "__line__ %d, pin already been used\n", __LINE__); + return -EINVAL; + } + motor->zoom1->reg_op->is_used = true; + } else if (strcmp(str, "cd") == 0) { + motor->zoom1->reg_op = &motor->motor_op[1]; + if (motor->zoom1->reg_op->is_used) { + dev_err(&motor->spi->dev, + "__line__ %d, pin already been used\n", __LINE__); + return -EINVAL; + } + motor->zoom1->reg_op->is_used = true; + } else if (strcmp(str, "ef") == 0) { + motor->zoom1->reg_op = &motor->motor_op[2]; + if (motor->zoom1->reg_op->is_used) { + dev_err(&motor->spi->dev, + "__line__ %d, pin already been used\n", __LINE__); + return -EINVAL; + } + motor->zoom1->reg_op->is_used = true; + } else if (strcmp(str, "gh") == 0) { + motor->zoom1->reg_op = &motor->motor_op[3]; + if (motor->zoom1->reg_op->is_used) { + dev_err(&motor->spi->dev, + "__line__ %d, pin already been used\n", __LINE__); + return -EINVAL; + } + motor->zoom1->reg_op->is_used = true; + } else { + dev_err(&motor->spi->dev, + "__line__ %d, pin require error\n", __LINE__); + return -EINVAL; + } + ret = of_property_read_u32(node, + "zoom1-backlash", + &motor->zoom1->backlash); + if (ret != 0) { + motor->zoom1->backlash = 0; + dev_err(&motor->spi->dev, + "failed get motor backlash,use dafult value\n"); + } + ret = of_property_read_u32(node, + "zoom1-step-max", + &motor->zoom1->step_max); + if (ret != 0) { + motor->zoom1->step_max = ZOOM_MAX_STEP_DEF; + dev_err(&motor->spi->dev, + "failed get zoom_pos_max,use dafult value\n"); + } + + ret = of_property_read_u32(node, + "zoom1-start-up-speed", + &motor->zoom1->start_up_speed); + if (ret != 0) { + motor->zoom1->start_up_speed = START_UP_HZ_DEF; + dev_err(&motor->spi->dev, + "failed get motor start up speed,use dafult value\n"); + } + ret = of_property_read_u32(node, + "zoom1-ppw", + &motor->zoom1->run_data.ppw); + if (ret != 0 || (motor->zoom1->run_data.ppw > 0xff)) { + motor->zoom1->run_data.ppw = PPW_DEF; + dev_err(&motor->spi->dev, + "failed get zoom1 ppw,use dafult value\n"); + } + ret = of_property_read_u32(node, + "zoom1-ppw-stop", + &motor->zoom1->run_data.ppw_stop); + if (ret != 0 || (motor->zoom1->run_data.ppw_stop > 0xff)) { + motor->zoom1->run_data.ppw_stop = PPW_STOP; + dev_err(&motor->spi->dev, + "failed get zoom1 ppw_stop,use dafult value\n"); + } + ret = of_property_read_u32(node, + "zoom1-phmode", + &motor->zoom1->run_data.phmode); + if (ret != 0 || (motor->zoom1->run_data.phmode > 0x3f)) { + motor->zoom1->run_data.phmode = PHMODE_DEF; + dev_err(&motor->spi->dev, + "failed get zoom1 phmode,use dafult value\n"); + } + ret = of_property_read_u32(node, + "zoom1-micro", + &motor->zoom1->run_data.micro); + if (ret != 0) { + motor->zoom1->run_data.micro = MICRO_DEF; + dev_err(&motor->spi->dev, + "failed get zoom1 micro,use dafult value\n"); + } + /* get zoom1 pi gpio */ + motor->zoom1->pic_gpio = devm_gpiod_get(&motor->spi->dev, + "zoom1_pic", GPIOD_IN); + if (IS_ERR(motor->zoom1->pic_gpio)) + dev_err(&motor->spi->dev, "Failed to get zoom1-pi-c-gpios\n"); + motor->zoom1->pia_gpio = devm_gpiod_get(&motor->spi->dev, + "zoom1_pia", GPIOD_OUT_LOW); + if (IS_ERR(motor->zoom1->pia_gpio)) + dev_err(&motor->spi->dev, "Failed to get zoom1-pi-a-gpios\n"); + motor->zoom1->pie_gpio = devm_gpiod_get(&motor->spi->dev, + "zoom1_pie", GPIOD_OUT_LOW); + if (IS_ERR(motor->zoom1->pie_gpio)) + dev_err(&motor->spi->dev, "Failed to get zoom1-pi-e-gpios\n"); + motor->zoom1->is_half_step_mode = + device_property_read_bool(&motor->spi->dev, "zoom1-1-2phase-excitation"); + motor->zoom1->is_dir_opp = + device_property_read_bool(&motor->spi->dev, "zoom1-dir-opposite"); + if (step_motor_cnt == 1) + motor->dev0 = motor->zoom1; + else if (step_motor_cnt == 2) + motor->dev1 = motor->zoom1; + else if (step_motor_cnt == 3) + motor->dev2 = motor->zoom1; + else if (step_motor_cnt == 4) + motor->dev3 = motor->zoom1; + ret = of_property_read_s32(node, + "zoom1-min-pos", + &motor->zoom1->min_pos); + if (ret != 0) { + motor->zoom1->min_pos = 0; + dev_err(&motor->spi->dev, + "failed get zoom1 min pos,use dafult value\n"); + } + ret = of_property_read_s32(node, + "zoom1-max-pos", + &motor->zoom1->max_pos); + if (ret != 0) { + motor->zoom1->max_pos = motor->zoom1->step_max; + dev_err(&motor->spi->dev, + "failed get zoom1 max_pos pos,use dafult value\n"); + } + ret = of_property_read_u32(node, + "zoom1-reback-distance", + &motor->zoom1->reback); + if (ret != 0) { + dev_err(&motor->spi->dev, + "failed get zoom1 reback distance, return\n"); + return -EINVAL; + } + motor->zoom1->is_pihigh_positive_pos = + device_property_read_bool(&motor->spi->dev, "zoom1-pihigh-positive-pos"); + ret = of_property_read_u32(node, + "zoom1-findpi-step", + &motor->zoom1->findpi_step); + if (ret != 0) { + motor->zoom1->findpi_step = ZOOM1_FINDPI_STEP; + dev_err(&motor->spi->dev, + "failed get zoom1 find pi step\n"); + } + /* get vd_fz gpio */ + motor->zoom1->vd_gpio = devm_gpiod_get(&motor->spi->dev, + "vd_zoom1", GPIOD_OUT_LOW); + if (IS_ERR(motor->zoom1->vd_gpio)) + dev_info(&motor->spi->dev, "Failed to get vd_zoom1\n"); + + motor->zoom1->channel = devm_iio_channel_get(&motor->spi->dev, "zoom1_pic"); + if (IS_ERR(motor->zoom1->channel)) + return PTR_ERR(motor->zoom1->channel); + if (!motor->zoom1->channel->indio_dev) + return -ENXIO; + } + + ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, + &motor->module_index); + ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, + &motor->module_facing); + if (ret) { + dev_err(&motor->spi->dev, + "could not get module information!\n"); + return -EINVAL; + } + return 0; +} + +static void motor_config_dev_next_status(struct motor_dev *motor, struct ext_dev *dev) +{ + u16 ppw = 0; + u16 psum = 0; + u16 micro = 0; + struct spi_device *spi = motor->spi; +#ifdef READREG_DEBUG + u16 intct = 0; + u16 pwmmode = 0; + int i = 0; + u16 val = 0; + u16 dt2_phmod = 0; + + if (dev->move_status != MOTOR_STATUS_STOPPED) { + dev_dbg(&spi->dev, + "__line__ %d dev type %d, cur_count %d !\n", __LINE__, + dev->type, + dev->run_data.cur_count); + dev_dbg(&spi->dev, + "__line__ %d, motor reg table: 0x%02x 0x%02x 0x%02x!\n", __LINE__, + dev->reg_op->reg.ppw, + dev->reg_op->reg.psum, + dev->reg_op->reg.intct); + for (i = 0; i < 31; i++) { + spi_read_reg(spi, 0x20 + i, &val); + dev_dbg(&spi->dev, + "========reg,val= 0x%02x, 0x%04x========\n", + 0x20 + i, + val); + } + } +#endif + + if (dev->run_data.cur_count != 0) { + if (dev->run_data.cur_count == dev->run_data.count && + dev->is_need_update_tim) { + dev->mv_tim.vcm_start_t = ns_to_kernel_old_timeval(ktime_get_ns()); + dev->is_need_update_tim = false; + } + dev->run_data.cur_count--; + ppw = (dev->run_data.ppw << 8) | dev->run_data.ppw; + switch (dev->run_data.micro) { + case 64: + micro = 0x03; + break; + case 128: + micro = 0x02; + break; + case 256: + micro = 0x01; + break; + default: + micro = 0x00; + break; + }; + switch (dev->run_data.cur_count) { + case 0: + psum = ((dev->move_status - 1) << 12) | + (1 << 14); + ppw = (dev->run_data.ppw_stop << 8) | dev->run_data.ppw_stop; + break; + case 1: + psum = ((dev->move_status - 1) << 12) | + (1 << 14) | + (dev->run_data.psum_last); + break; + default: + psum = ((dev->move_status - 1) << 12) | + (1 << 14) | + (dev->run_data.psum); + break; + }; + + spi_write_reg(motor->spi, 0x20, 0x01); + spi_write_reg(motor->spi, + dev->reg_op->reg.dt2_phmod_micro, + (dev->run_data.phmode << 8) | 0x0001 | (micro << 14)); + spi_write_reg(spi, dev->reg_op->reg.ppw, ppw); + spi_write_reg(spi, dev->reg_op->reg.psum, psum); + spi_write_reg(spi, dev->reg_op->reg.intct, + dev->run_data.intct); + spi_write_reg(motor->spi, dev->reg_op->reg.pwm, 0x541a); + dev->reg_op->tmp_psum = psum; + } else if (dev->move_status != MOTOR_STATUS_STOPPED) { + dev->mv_tim.vcm_end_t = ns_to_kernel_old_timeval(ktime_get_ns()); + dev->is_mv_tim_update = true; + dev->move_status = MOTOR_STATUS_STOPPED; + dev->reg_op->tmp_psum = 0; + dev->is_running = false; + complete(&dev->complete_out); + complete(&dev->complete); + } +#ifdef READREG_DEBUG + spi_read_reg(spi, dev->reg_op->reg.dt2_phmod_micro, &dt2_phmod); + spi_read_reg(spi, dev->reg_op->reg.ppw, &ppw); + spi_read_reg(spi, dev->reg_op->reg.psum, &psum); + spi_read_reg(spi, dev->reg_op->reg.intct, &intct); + spi_read_reg(spi, dev->reg_op->reg.pwm, &pwmmode); + dev_info(&spi->dev, + "__line__ %d dev type %d, cur_count %d , status %d phmod 0x%x:0x%x, ppw 0x%x:0x%x, psum 0x%x:0x%x intct 0x%x:0x%x pwmmode 0x%x:0x%x\n", __LINE__, + (u32)dev->type, + (u32)dev->run_data.cur_count, + (u32)dev->move_status, + (u32)dev->reg_op->reg.dt2_phmod_micro, (u32)dt2_phmod, + (u32)dev->reg_op->reg.ppw, (u32)ppw, + (u32)dev->reg_op->reg.psum, (u32)psum, + (u32)dev->reg_op->reg.intct, (u32)intct, + (u32)dev->reg_op->reg.pwm, (u32)pwmmode); +#endif +} + +static void motor_op_work(struct work_struct *work) +{ + struct motor_work_s *wk = + container_of(work, struct motor_work_s, work); + struct motor_dev *motor = wk->dev; + static struct __kernel_old_timeval tv_last = {0}; + struct __kernel_old_timeval tv; + u64 time_dist = 0; + + tv = ns_to_kernel_old_timeval(ktime_get_ns()); + time_dist = tv.tv_sec * 1000000 + tv.tv_usec - (tv_last.tv_sec * 1000000 + tv_last.tv_usec); + tv_last = tv; + if (time_dist < motor->vd_fz_period_us && motor->is_timer_restart_bywq) + dev_info(&motor->spi->dev, + "Timer error, Current interrupt interval %llu\n", time_dist); + mutex_lock(&motor->mutex); + if (motor->dev0) + gpiod_set_value(motor->dev0->vd_gpio, 1); + if (motor->dev1) + gpiod_set_value(motor->dev1->vd_gpio, 1); + if (motor->dev2) + gpiod_set_value(motor->dev2->vd_gpio, 1); + if (motor->dev3) + gpiod_set_value(motor->dev3->vd_gpio, 1); + usleep_range(80, 120); + + if (motor->dev0) + gpiod_set_value(motor->dev0->vd_gpio, 0); + if (motor->dev1) + gpiod_set_value(motor->dev1->vd_gpio, 0); + if (motor->dev2) + gpiod_set_value(motor->dev2->vd_gpio, 0); + if (motor->dev3) + gpiod_set_value(motor->dev3->vd_gpio, 0); + if (motor->dev0 && motor->dev0->run_data.cur_count == 0 && + motor->dev0->is_need_reback) { + if (motor->dev0->cur_back_delay < motor->dev0->max_back_delay) { + motor->dev0->cur_back_delay++; + motor->dev0->run_data.cur_count = 1; + } else { + motor->dev0->run_data = motor->dev0->reback_data; + motor->dev0->is_need_reback = false; + motor->dev0->move_status = motor->dev0->reback_status; + motor->dev0->last_dir = motor->dev0->reback_status; + motor->dev0->cur_back_delay = 0; + } + } + if (motor->dev1 && motor->dev1->run_data.cur_count == 0 && + motor->dev1->is_need_reback) { + if (motor->dev1->cur_back_delay < motor->dev1->max_back_delay) { + motor->dev1->cur_back_delay++; + motor->dev1->run_data.cur_count = 1; + } else { + motor->dev1->run_data = motor->dev1->reback_data; + motor->dev1->is_need_reback = false; + motor->dev1->move_status = motor->dev1->reback_status; + motor->dev1->last_dir = motor->dev1->reback_status; + motor->dev1->cur_back_delay = 0; + } + } + if (motor->dev2 && motor->dev2->run_data.cur_count == 0 && + motor->dev2->is_need_reback) { + if (motor->dev2->cur_back_delay < motor->dev2->max_back_delay) { + motor->dev2->cur_back_delay++; + motor->dev2->run_data.cur_count = 1; + } else { + motor->dev2->run_data = motor->dev2->reback_data; + motor->dev2->is_need_reback = false; + motor->dev2->move_status = motor->dev2->reback_status; + motor->dev2->last_dir = motor->dev2->reback_status; + motor->dev2->cur_back_delay = 0; + } + } + if (motor->dev3 && motor->dev3->run_data.cur_count == 0 && + motor->dev3->is_need_reback) { + if (motor->dev3->cur_back_delay < motor->dev3->max_back_delay) { + motor->dev3->cur_back_delay++; + motor->dev3->run_data.cur_count = 1; + } else { + motor->dev3->run_data = motor->dev3->reback_data; + motor->dev3->is_need_reback = false; + motor->dev3->move_status = motor->dev3->reback_status; + motor->dev3->last_dir = motor->dev3->reback_status; + motor->dev3->cur_back_delay = 0; + } + } + if ((motor->dev0 && motor->dev0->run_data.cur_count > 0) || + (motor->dev1 && motor->dev1->run_data.cur_count > 0) || + (motor->dev2 && motor->dev2->run_data.cur_count > 0) || + (motor->dev3 && motor->dev3->run_data.cur_count > 0)) { + motor->is_timer_restart = true; + motor->is_timer_restart_bywq = true; + hrtimer_start(&motor->timer, + motor->vd_fz_period_us * 1000, + HRTIMER_MODE_REL); + } else { + motor->is_timer_restart = false; + motor->is_timer_restart_bywq = false; + } + usleep_range(1000, 2000);//delay more than DT1 + + if (motor->dev0 && motor->dev0->move_status != MOTOR_STATUS_STOPPED) + motor_config_dev_next_status(motor, motor->dev0); + if (motor->dev1 && motor->dev1->move_status != MOTOR_STATUS_STOPPED) + motor_config_dev_next_status(motor, motor->dev1); + if (motor->dev2 && motor->dev2->move_status != MOTOR_STATUS_STOPPED) + motor_config_dev_next_status(motor, motor->dev2); + if (motor->dev3 && motor->dev3->move_status != MOTOR_STATUS_STOPPED) + motor_config_dev_next_status(motor, motor->dev3); + mutex_unlock(&motor->mutex); + motor->is_should_wait = false; +} + +static enum hrtimer_restart motor_timer_func(struct hrtimer *timer) +{ + struct motor_dev *motor = container_of(timer, struct motor_dev, timer); + + motor->is_should_wait = true; + schedule_work_on(smp_processor_id(), &motor->wk->work); + return HRTIMER_NORESTART; +} + +static int ms41968_g_volatile_ctrl(struct v4l2_ctrl *ctrl) +{ + struct motor_dev *motor = container_of(ctrl->handler, + struct motor_dev, ctrl_handler); + + switch (ctrl->id) { + case V4L2_CID_IRIS_ABSOLUTE: + if (motor->is_use_dc_iris) + ctrl->val = motor->dciris->last_log; + else if (motor->is_use_p_iris) + ctrl->val = motor->piris->last_pos; + return 0; + case V4L2_CID_FOCUS_ABSOLUTE: + ctrl->val = motor->focus->last_pos; + return 0; + case V4L2_CID_ZOOM_ABSOLUTE: + ctrl->val = motor->zoom->last_pos; + return 0; + case V4L2_CID_ZOOM_CONTINUOUS: + ctrl->val = motor->zoom1->last_pos; + return 0; + } + return 0; +} + +static void wait_for_motor_stop(struct motor_dev *motor, struct ext_dev *dev) +{ + unsigned long ret = 0; + + if (dev->is_running) { + ret = wait_for_completion_timeout(&dev->complete_out, 10 * HZ); + if (ret == 0) + dev_info(&motor->spi->dev, + "dev->type %d, wait for complete timeout\n", dev->type); + } +} + +static int ms41968_s_ctrl(struct v4l2_ctrl *ctrl) +{ +#ifdef READREG_DEBUG + int i = 0; + u16 val = 0; +#endif + int ret = 0; + struct motor_dev *motor = container_of(ctrl->handler, + struct motor_dev, ctrl_handler); + bool is_need_reback = false; + + switch (ctrl->id) { + case V4L2_CID_IRIS_ABSOLUTE: + if (motor->is_use_dc_iris) { + if (motor->dciris->is_reversed_polarity) + spi_write_reg(motor->spi, 0x00, + motor->dciris->max_log - ctrl->val); + else + spi_write_reg(motor->spi, 0x00, ctrl->val); + gpiod_set_value(motor->dciris->vd_iris_gpio, 1); + usleep_range(200, 400); + gpiod_set_value(motor->dciris->vd_iris_gpio, 0); + motor->dciris->last_log = ctrl->val; + dev_dbg(&motor->spi->dev, "set iris pos %d\n", ctrl->val); +#ifdef READREG_DEBUG + for (i = 0; i < 16; i++) { + spi_read_reg(motor->spi, i, &val); + dev_dbg(&motor->spi->dev, "reg,val=0x%02x,0x%04x\n", i, val); + } +#endif + } else if (motor->is_use_p_iris) { + ret = set_motor_running_status(motor, + motor->piris, + ctrl->val, + true, + false, + false); + wait_for_motor_stop(motor, motor->piris); + dev_dbg(&motor->spi->dev, "set piris pos %d\n", ctrl->val); + } + break; + case V4L2_CID_FOCUS_ABSOLUTE: + if (motor->focus->reback_ctrl) { + if (ctrl->val >= motor->focus->last_pos) + is_need_reback = false; + else + is_need_reback = true; + } + ret = set_motor_running_status(motor, + motor->focus, + ctrl->val, + true, + false, + is_need_reback); + wait_for_motor_stop(motor, motor->focus); + dev_dbg(&motor->spi->dev, "set focus pos %d\n", ctrl->val); + break; + case V4L2_CID_ZOOM_ABSOLUTE: + if (motor->zoom->reback_ctrl) { + if (ctrl->val >= motor->zoom->last_pos) + is_need_reback = false; + else + is_need_reback = true; + } + ret = set_motor_running_status(motor, + motor->zoom, + ctrl->val, + true, + false, + is_need_reback); + wait_for_motor_stop(motor, motor->zoom); + dev_dbg(&motor->spi->dev, "set zoom pos %d\n", ctrl->val); + break; + case V4L2_CID_ZOOM_CONTINUOUS: + if (motor->zoom1->reback_ctrl) { + if (ctrl->val >= motor->zoom1->last_pos) + is_need_reback = false; + else + is_need_reback = true; + } + ret = set_motor_running_status(motor, + motor->zoom1, + ctrl->val, + true, + false, + is_need_reback); + wait_for_motor_stop(motor, motor->zoom1); + dev_dbg(&motor->spi->dev, "set zoom1 pos %d\n", ctrl->val); + break; + default: + dev_err(&motor->spi->dev, "not support cmd %d\n", ctrl->id); + break; + } + return ret; +} + +static void ms41968_restore_speed(struct motor_dev *motor) +{ + struct ext_dev *zoom_dev = motor->zoom; + struct ext_dev *zoom1_dev = motor->zoom1; + struct ext_dev *focus_dev = motor->focus; + + zoom_dev->run_data.psum = zoom_dev->default_psum; + zoom_dev->run_data.intct = zoom_dev->default_intct; + if (motor->zoom1) { + zoom1_dev->run_data.psum = zoom1_dev->default_psum; + zoom1_dev->run_data.intct = zoom1_dev->default_intct; + } + focus_dev->run_data.psum = focus_dev->default_psum; + focus_dev->run_data.intct = focus_dev->default_intct; + + if (motor->zoom1) { + dev_dbg(&motor->spi->dev, + "%s: zoom psum: %d, zoom intct: %d, zoom1 psum: %d, zoom1 intct: %d, focus psum: %d, focus intct: %d\n", + __func__, + zoom_dev->run_data.psum, + zoom_dev->run_data.intct, + zoom1_dev->run_data.psum, + zoom1_dev->run_data.intct, + focus_dev->run_data.psum, + focus_dev->run_data.intct); + } else { + dev_dbg(&motor->spi->dev, + "%s: zoom psum: %d, zoom intct: %d, focus psum: %d, focus intct: %d\n", + __func__, + zoom_dev->run_data.psum, + zoom_dev->run_data.intct, + focus_dev->run_data.psum, + focus_dev->run_data.intct); + } +} + +static void ms41968_sync_zoomfocus_speed(struct motor_dev *motor, s32 zoom_pos, s32 zoom1_pos, + s32 focus_pos, bool is_zoom_need_reback, + bool is_zoom1_need_reback, bool is_focus_need_reback) +{ + struct ext_dev *zoom_dev = motor->zoom; + struct ext_dev *zoom1_dev = motor->zoom1; + struct ext_dev *focus_dev = motor->focus; + u32 zoom_mv_cnt = 0; + u32 zoom1_mv_cnt = 0; + u32 focus_mv_cnt = 0; + u32 zoom_mv_step = 0; + u32 zoom1_mv_step = 0; + u32 focus_mv_step = 0; + u32 zoom_vd_count = 0; + u32 zoom1_vd_count = 0; + u32 focus_vd_count = 0; + u32 max_vd_count = 0; + u32 zoom_vd_psum = 0; + u32 zoom1_vd_psum = 0; + u32 focus_vd_psum = 0; + + zoom_mv_cnt = abs(zoom_pos - zoom_dev->last_pos); + if (is_zoom_need_reback) + zoom_mv_cnt += zoom_dev->reback; + + if (zoom_dev->is_half_step_mode) + zoom_mv_step = zoom_mv_cnt * 4; + else + zoom_mv_step = zoom_mv_cnt * 8; + + zoom_vd_count = (zoom_mv_step + zoom_dev->default_psum - 1) / zoom_dev->default_psum; + + if (motor->zoom1) { + zoom1_mv_cnt = abs(zoom1_pos - zoom1_dev->last_pos); + if (is_zoom1_need_reback) + zoom1_mv_cnt += zoom1_dev->reback; + + if (zoom1_dev->is_half_step_mode) + zoom1_mv_step = zoom1_mv_cnt * 4; + else + zoom1_mv_step = zoom1_mv_cnt * 8; + + zoom1_vd_count = (zoom1_mv_step + zoom1_dev->default_psum - 1) / zoom1_dev->default_psum; + } + + focus_mv_cnt = abs(focus_pos - focus_dev->last_pos); + if (is_focus_need_reback) + focus_mv_cnt += focus_dev->reback; + + if (focus_dev->is_half_step_mode) + focus_mv_step = focus_mv_cnt * 4; + else + focus_mv_step = focus_mv_cnt * 8; + + focus_vd_count = (focus_mv_step + focus_dev->default_psum - 1) / focus_dev->default_psum; + max_vd_count = (zoom_vd_count > zoom1_vd_count) ? zoom_vd_count : zoom1_vd_count; + max_vd_count = (max_vd_count > focus_vd_count) ? max_vd_count : focus_vd_count; + + if (zoom_mv_cnt > 0) { + zoom_vd_psum = (zoom_mv_step + max_vd_count - 1) / max_vd_count; + if (zoom_vd_psum > zoom_dev->default_psum) + zoom_vd_psum = zoom_dev->default_psum; + zoom_dev->run_data.psum = zoom_vd_psum; + zoom_dev->run_data.intct = motor->sys_clk * motor->vd_fz_period_us / + (zoom_dev->run_data.psum * 24); + } + + if (zoom1_mv_cnt > 0) { + zoom1_vd_psum = (zoom1_mv_step + max_vd_count - 1) / max_vd_count; + if (zoom1_vd_psum > zoom1_dev->default_psum) + zoom1_vd_psum = zoom1_dev->default_psum; + zoom1_dev->run_data.psum = zoom1_vd_psum; + zoom1_dev->run_data.intct = motor->sys_clk * motor->vd_fz_period_us / + (zoom1_dev->run_data.psum * 24); + } + + if (focus_mv_cnt > 0) { + focus_vd_psum = (focus_mv_step + max_vd_count - 1) / max_vd_count; + if (focus_vd_psum > focus_dev->default_psum) + focus_vd_psum = focus_dev->default_psum; + focus_dev->run_data.psum = focus_vd_psum; + focus_dev->run_data.intct = motor->sys_clk * motor->vd_fz_period_us / + (focus_dev->run_data.psum * 24); + } + + if (motor->zoom1) { + dev_dbg(&motor->spi->dev, + "%s: zoom_mv_cnt %d, zoom1_mv_cnt %d, focus_mv_cnt %d, zoom psum: %d, zoom intct: %d, zoom1 psum: %d, zoom1 intct: %d, focus psum: %d, focus intct: %d\n", + __func__, + zoom_mv_cnt, + zoom1_mv_cnt, + focus_mv_cnt, + zoom_dev->run_data.psum, + zoom_dev->run_data.intct, + zoom1_dev->run_data.psum, + zoom1_dev->run_data.intct, + focus_dev->run_data.psum, + focus_dev->run_data.intct); + } else { + dev_dbg(&motor->spi->dev, + "%s: zoom_mv_cnt %d, focus_mv_cnt %d, zoom psum: %d, zoom intct: %d, focus psum: %d, focus intct: %d\n", + __func__, + zoom_mv_cnt, + focus_mv_cnt, + zoom_dev->run_data.psum, + zoom_dev->run_data.intct, + focus_dev->run_data.psum, + focus_dev->run_data.intct); + } +} + +static int ms41968_set_zoom_follow(struct motor_dev *motor, struct rk_cam_set_zoom *mv_param) +{ + int i = 0; + int ret = 0; + bool is_need_zoom_reback = mv_param->is_need_zoom_reback; + bool is_need_zoom1_reback = mv_param->is_need_zoom1_reback; + bool is_need_focus_reback = mv_param->is_need_focus_reback; + + for (i = 0; i < mv_param->setzoom_cnt; i++) { + dev_dbg(&motor->spi->dev, + "%s zoom %d, zoom1 %d, focus %d, i %d, setzoom_cnt %d, is_need_zoom_reback %d, is_need_zoom1_reback %d, is_need_focus_reback %d\n", + __func__, + mv_param->zoom_pos[i].zoom_pos, + mv_param->zoom_pos[i].zoom1_pos, + mv_param->zoom_pos[i].focus_pos, + i, mv_param->setzoom_cnt, + is_need_zoom_reback, + is_need_zoom1_reback, + is_need_focus_reback); + + ms41968_sync_zoomfocus_speed(motor, + mv_param->zoom_pos[i].zoom_pos, + mv_param->zoom_pos[i].zoom1_pos, + mv_param->zoom_pos[i].focus_pos, + is_need_zoom_reback, + is_need_zoom1_reback, + is_need_focus_reback); + if (i == (mv_param->setzoom_cnt - 1)) { + ret = set_motor_running_status(motor, + motor->focus, + mv_param->zoom_pos[i].focus_pos, + true, + true, + is_need_focus_reback); + if (motor->zoom1) { + ret |= set_motor_running_status(motor, + motor->zoom1, + mv_param->zoom_pos[i].zoom1_pos, + true, + true, + is_need_zoom1_reback); + } + ret |= set_motor_running_status(motor, + motor->zoom, + mv_param->zoom_pos[i].zoom_pos, + true, + false, + is_need_zoom_reback); + } else { + ret = set_motor_running_status(motor, + motor->focus, + mv_param->zoom_pos[i].focus_pos, + false, + true, + false); + if (motor->zoom1) { + ret |= set_motor_running_status(motor, + motor->zoom1, + mv_param->zoom_pos[i].zoom1_pos, + false, + true, + false); + } + ret |= set_motor_running_status(motor, + motor->zoom, + mv_param->zoom_pos[i].zoom_pos, + false, + false, + false); + } + wait_for_motor_stop(motor, motor->focus); + if (motor->zoom1) + wait_for_motor_stop(motor, motor->zoom1); + wait_for_motor_stop(motor, motor->zoom); + } + + ms41968_restore_speed(motor); + return ret; +} + +static int ms41968_find_pi_binarysearch(struct motor_dev *motor, + struct ext_dev *ext_dev, + int min, int max, bool *error) +{ + int gpio_val = 0; + int tmp_val = 0; + int mid = 0; + int last_pos = 0; + int new_min = 0; + int new_max = 0; + + dev_dbg(&motor->spi->dev, + "ext dev %d min %d, max %d\n", ext_dev->type, min, max); + *error = false; + if (min > max) { + *error = true; + return -EINVAL; + } + + tmp_val = ms41968_get_pic_val(ext_dev); + mid = (min + max) / 2; + if ((mid == min) || (mid == max)) { + dev_dbg(&motor->spi->dev, + "ext dev %d find pi %d\n", ext_dev->type, mid); + if (ext_dev->last_pos < mid) + set_motor_running_status(motor, + ext_dev, + mid, + false, + false, + false); + else + set_motor_running_status(motor, + ext_dev, + mid, + false, + false, + true); + wait_for_motor_stop(motor, ext_dev); + return mid; + } + last_pos = ext_dev->last_pos; + if (last_pos < mid) + set_motor_running_status(motor, + ext_dev, + mid, + false, + false, + false); + else + set_motor_running_status(motor, + ext_dev, + mid, + false, + false, + true); + wait_for_motor_stop(motor, ext_dev); + gpio_val = ms41968_get_pic_val(ext_dev); + if (tmp_val != gpio_val) { + usleep_range(10, 20); + gpio_val = ms41968_get_pic_val(ext_dev); + } + + dev_dbg(&motor->spi->dev, + "__line__ %d ext_dev type %d, get pi value %d, tmp_val %d, min %d, max %d\n", + __LINE__, ext_dev->type, gpio_val, tmp_val, min, max); + if (tmp_val != gpio_val) { + if (last_pos == min) { + new_min = min; + new_max = mid; + } else { + new_min = mid; + new_max = max; + } + } else { + if (last_pos == min) { + new_min = mid; + new_max = max; + } else { + new_min = min; + new_max = mid; + } + } + return ms41968_find_pi_binarysearch(motor, ext_dev, new_min, new_max, error); +} + +static int ms41968_find_pi(struct motor_dev *motor, + struct ext_dev *ext_dev, int step) +{ + int i = 0; + int idx_max = ext_dev->step_max + step - 1; + int tmp_val = 0; + int gpio_val = 0; + int min = 0; + int max = 0; + int pi_pos = 0; + bool is_find_pi = false; + bool if_find_error = false; + + tmp_val = ms41968_get_pic_val(ext_dev); + if ((!tmp_val && ext_dev->is_pihigh_positive_pos) || + (tmp_val && !ext_dev->is_pihigh_positive_pos)) { + for (i = ext_dev->last_pos + step; i < 2 * idx_max; i += step) { + set_motor_running_status(motor, + ext_dev, + i, + false, + false, + false); + wait_for_motor_stop(motor, ext_dev); + gpio_val = ms41968_get_pic_val(ext_dev); + if (tmp_val != gpio_val) { + usleep_range(10, 20); + gpio_val = ms41968_get_pic_val(ext_dev); + } + dev_dbg(&motor->spi->dev, + "__line__ %d ext_dev type %d, get pi value %d, i %d, tmp_val %d\n", + __LINE__, ext_dev->type, gpio_val, i, tmp_val); + if (tmp_val != gpio_val) { + min = i - step; + max = i; + is_find_pi = true; + break; + } + } + } else { + for (i = ext_dev->last_pos - step; i > -2 * idx_max; i -= step) { + set_motor_running_status(motor, + ext_dev, + i, + false, + false, + true); + wait_for_motor_stop(motor, ext_dev); + gpio_val = ms41968_get_pic_val(ext_dev); + if (tmp_val != gpio_val) { + usleep_range(10, 20); + gpio_val = ms41968_get_pic_val(ext_dev); + } + dev_dbg(&motor->spi->dev, + "__line__ %d ext_dev type %d, get pi value %d, i %d, tmp_val %d\n", + __LINE__, ext_dev->type, gpio_val, i, tmp_val); + if (tmp_val != gpio_val) { + min = i; + max = i + step; + is_find_pi = true; + break; + } + } + } + + if (is_find_pi) { + if (abs(step) != 1) { + pi_pos = ms41968_find_pi_binarysearch(motor, ext_dev, + min, + max, + &if_find_error); + dev_dbg(&motor->spi->dev, + "ext_dev type %d, pi_pos %d, if_find_error %d\n", + ext_dev->type, pi_pos, if_find_error); + if (if_find_error) + return -EINVAL; + } + return 0; + } else { + return -EINVAL; + } +} + +static int ms41968_reinit_piris(struct motor_dev *motor) +{ + int ret = 0; + + if (!IS_ERR(motor->piris->pic_gpio) || !IS_ERR(motor->piris->channel)) { + if (!IS_ERR(motor->piris->pia_gpio)) + gpiod_set_value(motor->piris->pia_gpio, 1); + if (!IS_ERR(motor->piris->pie_gpio)) + gpiod_set_value(motor->piris->pie_gpio, 0); + msleep(250); + #ifdef PI_TEST + motor->piris->last_pos = motor->piris->step_max; + ret = set_motor_running_status(motor, + motor->piris, + 0, + false, + false, + false); + wait_for_motor_stop(motor, motor->piris); + #else + motor->piris->last_pos = 0; + #endif + ret = ms41968_find_pi(motor, motor->piris, motor->piris->findpi_step); + if (ret < 0) { + dev_err(&motor->spi->dev, + "get piris pi fail, pls check it\n"); + return -EINVAL; + } + #ifdef PI_TEST + min = -ret; + max = motor->piris->step_max + min; + motor->piris->min_pos = min; + motor->piris->max_pos = max; + #endif + if (!IS_ERR(motor->piris->pia_gpio)) + gpiod_set_value(motor->piris->pia_gpio, 0); + if (!IS_ERR(motor->piris->pie_gpio)) + gpiod_set_value(motor->piris->pie_gpio, 0); + motor->piris->last_pos = 0; + } else { + motor->piris->last_pos = motor->piris->step_max; + ret = set_motor_running_status(motor, + motor->piris, + 0, + false, + false, + false); + wait_for_motor_stop(motor, motor->piris); + } + return ret; +} + +static void ms41968_reinit_piris_pos(struct motor_dev *motor) +{ + if (!motor->piris) { + dev_err(&motor->spi->dev, + "not support piris\n"); + return; + } + ms41968_reinit_piris(motor); + motor->piris->last_pos = 0; + __v4l2_ctrl_modify_range(motor->iris_ctrl, motor->piris->min_pos, + motor->piris->max_pos - motor->piris->reback, + 1, 0); +} + +static int ms41968_reinit_focus(struct motor_dev *motor) +{ + int ret = 0; + + if (!IS_ERR(motor->focus->pic_gpio) || !IS_ERR(motor->focus->channel)) { + mutex_lock(&motor->mutex); + if (motor->pi_gpio_usecnt == 0) { + if (!IS_ERR(motor->focus->pia_gpio)) + gpiod_set_value(motor->focus->pia_gpio, 1); + if (!IS_ERR(motor->focus->pie_gpio)) + gpiod_set_value(motor->focus->pie_gpio, 0); + msleep(250); + } + motor->pi_gpio_usecnt++; + mutex_unlock(&motor->mutex); + #ifdef PI_TEST + motor->focus->last_pos = motor->focus->step_max; + ret = set_motor_running_status(motor, + motor->focus, + 0, + false, + false, + false); + wait_for_motor_stop(motor, motor->focus); + #else + motor->focus->last_pos = 0; + #endif + ret = ms41968_find_pi(motor, motor->focus, motor->focus->findpi_step); + if (ret < 0) { + dev_info(&motor->spi->dev, + "get focus pi fail, pls check it\n"); + return -EINVAL; + } + #ifdef PI_TEST + min = -ret; + max = motor->focus->step_max + min; + motor->focus->min_pos = min; + motor->focus->max_pos = max; + #endif + + mutex_lock(&motor->mutex); + if (motor->pi_gpio_usecnt == 1) { + if (!IS_ERR(motor->focus->pia_gpio)) + gpiod_set_value(motor->focus->pia_gpio, 0); + if (!IS_ERR(motor->focus->pie_gpio)) + gpiod_set_value(motor->focus->pie_gpio, 0); + } + motor->pi_gpio_usecnt--; + mutex_unlock(&motor->mutex); + } else { + motor->focus->last_pos = motor->focus->step_max; + ret = set_motor_running_status(motor, + motor->focus, + 0, + false, + false, + true); + wait_for_motor_stop(motor, motor->focus); + } + return ret; +} + +static int ms41968_reinit_focus_pos(struct motor_dev *motor) +{ + int ret = 0; + + if (!motor->focus) { + dev_err(&motor->spi->dev, + "not support focus\n"); + return -1; + } + ret = ms41968_reinit_focus(motor); + motor->focus->last_pos = 0; + __v4l2_ctrl_modify_range(motor->focus_ctrl, motor->focus->min_pos, + motor->focus->max_pos - motor->focus->reback, + 1, 0); + return ret; +} + +static int ms41968_reinit_zoom(struct motor_dev *motor) +{ + int ret = 0; + + if (!IS_ERR(motor->zoom->pic_gpio) || !IS_ERR(motor->zoom->channel)) { + mutex_lock(&motor->mutex); + if (motor->pi_gpio_usecnt == 0) { + if (!IS_ERR(motor->focus->pia_gpio)) + gpiod_set_value(motor->focus->pia_gpio, 1); + if (!IS_ERR(motor->focus->pie_gpio)) + gpiod_set_value(motor->focus->pie_gpio, 0); + msleep(250); + } + motor->pi_gpio_usecnt++; + mutex_unlock(&motor->mutex); + + #ifdef PI_TEST + motor->zoom->last_pos = motor->zoom->step_max; + ret = set_motor_running_status(motor, + motor->zoom, + 0, + false, + false, + false); + wait_for_motor_stop(motor, motor->zoom); + #else + motor->zoom->last_pos = 0; + #endif + ret = ms41968_find_pi(motor, motor->zoom, motor->zoom->findpi_step); + if (ret < 0) { + dev_err(&motor->spi->dev, + "get zoom pi fail, pls check it\n"); + return -EINVAL; + } + #ifdef PI_TEST + min = -ret; + max = motor->zoom->step_max + min; + motor->zoom->min_pos = min; + motor->zoom->max_pos = max; + #endif + + mutex_lock(&motor->mutex); + if (motor->pi_gpio_usecnt == 1) { + if (!IS_ERR(motor->focus->pia_gpio)) + gpiod_set_value(motor->focus->pia_gpio, 0); + if (!IS_ERR(motor->focus->pie_gpio)) + gpiod_set_value(motor->focus->pie_gpio, 0); + } + motor->pi_gpio_usecnt--; + mutex_unlock(&motor->mutex); + } else { + motor->zoom->last_pos = motor->zoom->step_max; + ret = set_motor_running_status(motor, + motor->zoom, + 0, + false, + false, + true); + wait_for_motor_stop(motor, motor->zoom); + } + return ret; +} + +static int ms41968_reinit_zoom_pos(struct motor_dev *motor) +{ + int ret = 0; + + if (!motor->zoom) { + dev_err(&motor->spi->dev, + "not support zoom\n"); + return -1; + } + ret = ms41968_reinit_zoom(motor); + motor->zoom->last_pos = 0; + __v4l2_ctrl_modify_range(motor->zoom_ctrl, motor->zoom->min_pos, + motor->zoom->max_pos - motor->zoom->reback, + 1, 0); + return ret; +} + +static int ms41968_reinit_zoom1(struct motor_dev *motor) +{ + int ret = 0; + + if (!IS_ERR(motor->zoom1->pic_gpio) || !IS_ERR(motor->zoom1->channel)) { + if (!IS_ERR(motor->zoom1->pia_gpio)) + gpiod_set_value(motor->zoom1->pia_gpio, 1); + if (!IS_ERR(motor->zoom1->pie_gpio)) + gpiod_set_value(motor->zoom1->pie_gpio, 0); + msleep(250); + #ifdef PI_TEST + motor->zoom1->last_pos = motor->zoom1->step_max; + ret = set_motor_running_status(motor, + motor->zoom1, + 0, + false, + false, + false); + wait_for_motor_stop(motor, motor->zoom1); + #else + motor->zoom1->last_pos = 0; + #endif + ret = ms41968_find_pi(motor, motor->zoom1, motor->zoom1->findpi_step); + if (ret < 0) { + dev_err(&motor->spi->dev, + "get zoom1 pi fail, pls check it\n"); + return -EINVAL; + } + #ifdef PI_TEST + min = -ret; + max = motor->zoom1->step_max + min; + motor->zoom1->min_pos = min; + motor->zoom1->max_pos = max; + #endif + if (!IS_ERR(motor->zoom1->pia_gpio)) + gpiod_set_value(motor->zoom1->pia_gpio, 0); + if (!IS_ERR(motor->zoom1->pie_gpio)) + gpiod_set_value(motor->zoom1->pie_gpio, 0); + } else { + motor->zoom1->last_pos = motor->zoom1->step_max; + ret = set_motor_running_status(motor, + motor->zoom1, + 0, + false, + false, + true); + wait_for_motor_stop(motor, motor->zoom1); + } + return ret; +} + +static int ms41968_reinit_zoom1_pos(struct motor_dev *motor) +{ + int ret = 0; + + if (!motor->zoom1) { + dev_err(&motor->spi->dev, + "not support zoom1\n"); + return -1; + } + ret = ms41968_reinit_zoom1(motor); + motor->zoom1->last_pos = 0; + __v4l2_ctrl_modify_range(motor->zoom1_ctrl, motor->zoom1->min_pos, + motor->zoom1->max_pos - motor->zoom1->reback, + 1, 0); + return ret; +} + +//#define REBACK_CTRL_BY_DRV +static int ms41968_set_focus(struct motor_dev *motor, struct rk_cam_set_focus *mv_param) +{ + int ret = 0; + bool is_need_reback = mv_param->is_need_reback; + +#ifdef REBACK_CTRL_BY_DRV + if (mv_param->focus_pos > motor->focus->last_pos) + is_need_reback = false; + else + is_need_reback = true; +#endif + + dev_dbg(&motor->spi->dev, + "%s focus %d\n", __func__, mv_param->focus_pos); + + ret = set_motor_running_status(motor, + motor->focus, + mv_param->focus_pos, + true, + false, + is_need_reback); + wait_for_motor_stop(motor, motor->focus); + + return ret; +} + +static long ms41968_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct rk_cam_vcm_tim *mv_tim; + struct motor_dev *motor = to_motor_dev(sd); + u32 *pbacklash = 0; + struct rk_cam_set_zoom *mv_param; + struct rk_cam_set_focus *focus_param; + int ret = 0; + struct rk_cam_modify_pos *pos; + + switch (cmd) { + case RK_VIDIOC_IRIS_TIMEINFO: + mv_tim = (struct rk_cam_vcm_tim *)arg; + if (!motor->piris->is_mv_tim_update) + usleep_range(motor->piris->move_time_us, + motor->piris->move_time_us + 1000); + if (motor->piris->is_mv_tim_update) { + memcpy(mv_tim, &motor->piris->mv_tim, sizeof(*mv_tim)); + + dev_dbg(&motor->spi->dev, + "get_piris_move_tim 0x%lx, 0x%lx, 0x%lx, 0x%lx\n", + mv_tim->vcm_start_t.tv_sec, + mv_tim->vcm_start_t.tv_usec, + mv_tim->vcm_end_t.tv_sec, + mv_tim->vcm_end_t.tv_usec); + } else { + dev_err(&motor->spi->dev, "get_piris_move_tim failed\n"); + return -EINVAL; + } + break; + case RK_VIDIOC_VCM_TIMEINFO: + mv_tim = (struct rk_cam_vcm_tim *)arg; + if (!motor->focus->is_mv_tim_update) + usleep_range(motor->focus->move_time_us, + motor->focus->move_time_us + 1000); + if (motor->focus->is_mv_tim_update) { + memcpy(mv_tim, &motor->focus->mv_tim, sizeof(*mv_tim)); + + dev_dbg(&motor->spi->dev, + "get_focus_move_tim 0x%lx, 0x%lx, 0x%lx, 0x%lx\n", + mv_tim->vcm_start_t.tv_sec, + mv_tim->vcm_start_t.tv_usec, + mv_tim->vcm_end_t.tv_sec, + mv_tim->vcm_end_t.tv_usec); + } else { + dev_err(&motor->spi->dev, "get_focus_move_tim failed\n"); + return -EINVAL; + } + break; + case RK_VIDIOC_ZOOM_TIMEINFO: + mv_tim = (struct rk_cam_vcm_tim *)arg; + if (!motor->zoom->is_mv_tim_update) + usleep_range(motor->zoom->move_time_us, + motor->zoom->move_time_us + 1000); + if (motor->zoom->is_mv_tim_update) { + memcpy(mv_tim, &motor->zoom->mv_tim, sizeof(*mv_tim)); + + dev_dbg(&motor->spi->dev, + "get_zoom_move_tim 0x%lx, 0x%lx, 0x%lx, 0x%lx\n", + mv_tim->vcm_start_t.tv_sec, + mv_tim->vcm_start_t.tv_usec, + mv_tim->vcm_end_t.tv_sec, + mv_tim->vcm_end_t.tv_usec); + } else { + dev_err(&motor->spi->dev, "get_zoom_move_tim failed\n"); + return -EINVAL; + } + break; + case RK_VIDIOC_ZOOM1_TIMEINFO: + mv_tim = (struct rk_cam_vcm_tim *)arg; + if (!motor->zoom1->is_mv_tim_update) + usleep_range(motor->zoom1->move_time_us, + motor->zoom1->move_time_us + 1000); + if (motor->zoom1->is_mv_tim_update) { + memcpy(mv_tim, &motor->zoom1->mv_tim, sizeof(*mv_tim)); + + dev_dbg(&motor->spi->dev, + "get_zoom1_move_tim 0x%lx, 0x%lx, 0x%lx, 0x%lx\n", + mv_tim->vcm_start_t.tv_sec, + mv_tim->vcm_start_t.tv_usec, + mv_tim->vcm_end_t.tv_sec, + mv_tim->vcm_end_t.tv_usec); + } else { + dev_err(&motor->spi->dev, "get_zoom1_move_tim failed\n"); + return -EINVAL; + } + break; + case RK_VIDIOC_IRIS_SET_BACKLASH: + pbacklash = (u32 *)arg; + motor->piris->backlash = *pbacklash; + break; + case RK_VIDIOC_FOCUS_SET_BACKLASH: + pbacklash = (u32 *)arg; + motor->focus->backlash = *pbacklash; + break; + case RK_VIDIOC_ZOOM_SET_BACKLASH: + pbacklash = (u32 *)arg; + motor->zoom->backlash = *pbacklash; + break; + case RK_VIDIOC_ZOOM1_SET_BACKLASH: + pbacklash = (u32 *)arg; + motor->zoom1->backlash = *pbacklash; + break; + case RK_VIDIOC_IRIS_CORRECTION: + ms41968_reinit_piris_pos(motor); + break; + case RK_VIDIOC_FOCUS_CORRECTION: + ret = ms41968_reinit_focus_pos(motor); + break; + case RK_VIDIOC_ZOOM_CORRECTION: + ret = ms41968_reinit_zoom_pos(motor); + break; + case RK_VIDIOC_ZOOM1_CORRECTION: + ret = ms41968_reinit_zoom1_pos(motor); + break; + case RK_VIDIOC_ZOOM_SET_POSITION: + mv_param = (struct rk_cam_set_zoom *)arg; + ret = ms41968_set_zoom_follow(motor, mv_param); + break; + case RK_VIDIOC_FOCUS_SET_POSITION: + focus_param = (struct rk_cam_set_focus *)arg; + ret = ms41968_set_focus(motor, focus_param); + break; + case RK_VIDIOC_MODIFY_POSITION: + pos = (struct rk_cam_modify_pos *)arg; + if (motor->focus) + motor->focus->last_pos = pos->focus_pos; + if (motor->zoom) + motor->zoom->last_pos = pos->zoom_pos; + if (motor->zoom1) + motor->zoom1->last_pos = pos->zoom1_pos; + break; + default: + break; + } + return ret; +} + +#ifdef CONFIG_COMPAT +static long ms41968_compat_ioctl32(struct v4l2_subdev *sd, unsigned int cmd, unsigned long arg) +{ + void __user *up = compat_ptr(arg); + struct rk_cam_compat_vcm_tim *compat_mv_tim; + struct rk_cam_set_zoom *mv_param; + struct rk_cam_set_focus *focus_param; + struct rk_cam_vcm_tim ioctl_mv_tim; + unsigned int ioctl_cmd; + int ret = 0; + u32 val = 0; + + switch (cmd) { + case RK_VIDIOC_COMPAT_VCM_TIMEINFO: + ioctl_cmd = RK_VIDIOC_VCM_TIMEINFO; + goto handle_mvtime; + case RK_VIDIOC_COMPAT_IRIS_TIMEINFO: + ioctl_cmd = RK_VIDIOC_IRIS_TIMEINFO; + goto handle_mvtime; + case RK_VIDIOC_COMPAT_ZOOM_TIMEINFO: + ioctl_cmd = RK_VIDIOC_ZOOM_TIMEINFO; + goto handle_mvtime; + case RK_VIDIOC_COMPAT_ZOOM1_TIMEINFO: + ioctl_cmd = RK_VIDIOC_ZOOM1_TIMEINFO; + +handle_mvtime: + compat_mv_tim = kzalloc(sizeof(*compat_mv_tim), GFP_KERNEL); + if (!compat_mv_tim) { + ret = -ENOMEM; + return ret; + } + ret = ms41968_ioctl(sd, ioctl_cmd, &ioctl_mv_tim); + if (!ret) { + compat_mv_tim->vcm_start_t.tv_sec = ioctl_mv_tim.vcm_start_t.tv_sec; + compat_mv_tim->vcm_start_t.tv_usec = ioctl_mv_tim.vcm_start_t.tv_usec; + compat_mv_tim->vcm_end_t.tv_sec = ioctl_mv_tim.vcm_end_t.tv_sec; + compat_mv_tim->vcm_end_t.tv_usec = ioctl_mv_tim.vcm_end_t.tv_usec; + if (copy_to_user(up, compat_mv_tim, sizeof(*compat_mv_tim))) { + kfree(compat_mv_tim); + return -EFAULT; + } + } + kfree(compat_mv_tim); + break; + case RK_VIDIOC_IRIS_SET_BACKLASH: + case RK_VIDIOC_FOCUS_SET_BACKLASH: + case RK_VIDIOC_ZOOM_SET_BACKLASH: + case RK_VIDIOC_ZOOM1_SET_BACKLASH: + if (copy_from_user(&val, up, sizeof(val))) + return -EFAULT; + ret = ms41968_ioctl(sd, cmd, &val); + break; + case RK_VIDIOC_IRIS_CORRECTION: + case RK_VIDIOC_FOCUS_CORRECTION: + case RK_VIDIOC_ZOOM_CORRECTION: + case RK_VIDIOC_ZOOM1_CORRECTION: + if (copy_from_user(&val, up, sizeof(val))) + return -EFAULT; + ret = ms41968_ioctl(sd, cmd, &val); + break; + case RK_VIDIOC_ZOOM_SET_POSITION: + mv_param = kzalloc(sizeof(*mv_param), GFP_KERNEL); + if (!mv_param) { + ret = -ENOMEM; + return ret; + } + if (copy_from_user(mv_param, up, sizeof(*mv_param))) { + kfree(mv_param); + return -EFAULT; + } + ret = ms41968_ioctl(sd, cmd, mv_param); + kfree(mv_param); + break; + case RK_VIDIOC_FOCUS_SET_POSITION: + focus_param = kzalloc(sizeof(*focus_param), GFP_KERNEL); + if (!focus_param) { + ret = -ENOMEM; + return ret; + } + if (copy_from_user(focus_param, up, sizeof(*focus_param))) { + kfree(focus_param); + return -EFAULT; + } + ret = ms41968_ioctl(sd, cmd, focus_param); + kfree(focus_param); + break; + default: + break; + } + return ret; +} +#endif + +#define USED_SYS_DEBUG +#ifdef USED_SYS_DEBUG +static ssize_t set_pid_dgain(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct motor_dev *motor = to_motor_dev(sd); + int val = 0; + int ret = 0; + u16 reg_val = 0; + + ret = kstrtoint(buf, 0, &val); + if (!ret) { + if (motor->is_use_dc_iris) { + spi_read_reg(motor->spi, 0x01, ®_val); + reg_val &= 0x01ff; + reg_val |= (val & 0x7f) << 9; + spi_write_reg(motor->spi, 0x01, reg_val); + gpiod_set_value(motor->dciris->vd_iris_gpio, 1); + usleep_range(200, 400); + gpiod_set_value(motor->dciris->vd_iris_gpio, 0); + dev_info(dev, "set pid dgain %d, reg val 0x%x\n", val, reg_val); + spi_read_reg(motor->spi, 0x01, ®_val); + dev_info(dev, "pid dgain reg val 0x%x, read from register\n", reg_val); + } else { + dev_err(dev, "not support dc-iris, do nothing\n"); + } + } + return count; +} + +static ssize_t set_pid_zero(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct motor_dev *motor = to_motor_dev(sd); + int val = 0; + int ret = 0; + u16 reg_val = 0; + + ret = kstrtoint(buf, 0, &val); + if (!ret) { + if (motor->is_use_dc_iris) { + spi_read_reg(motor->spi, 0x02, ®_val); + reg_val &= 0xf0ff; + reg_val |= (val & 0xf) << 8; + spi_write_reg(motor->spi, 0x02, reg_val); + gpiod_set_value(motor->dciris->vd_iris_gpio, 1); + usleep_range(200, 400); + gpiod_set_value(motor->dciris->vd_iris_gpio, 0); + dev_info(dev, "set pid zero %d, reg val 0x%x\n", val, reg_val); + spi_read_reg(motor->spi, 0x02, ®_val); + dev_info(dev, "pid zero reg val 0x%x, read from register\n", reg_val); + } else { + dev_err(dev, "not support dc-iris, do nothing\n"); + } + } + return count; +} + +static ssize_t set_pid_pole(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct motor_dev *motor = to_motor_dev(sd); + int val = 0; + int ret = 0; + u16 reg_val = 0; + + ret = kstrtoint(buf, 0, &val); + if (!ret) { + if (motor->is_use_dc_iris) { + spi_read_reg(motor->spi, 0x02, ®_val); + reg_val &= 0x0fff; + reg_val |= (val & 0xf) << 12; + spi_write_reg(motor->spi, 0x02, reg_val); + gpiod_set_value(motor->dciris->vd_iris_gpio, 1); + usleep_range(200, 400); + gpiod_set_value(motor->dciris->vd_iris_gpio, 0); + dev_info(dev, "set pid pole %d, reg val 0x%x\n", val, reg_val); + spi_read_reg(motor->spi, 0x02, ®_val); + dev_info(dev, "pid pole reg val 0x%x, read from register\n", reg_val); + } else { + dev_err(dev, "not support dc-iris, do nothing\n"); + } + } + return count; +} + +static ssize_t set_hall_bias(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct motor_dev *motor = to_motor_dev(sd); + int val = 0; + int ret = 0; + u16 reg_val = 0; + + ret = kstrtoint(buf, 0, &val); + if (!ret) { + if (motor->is_use_dc_iris) { + spi_read_reg(motor->spi, 0x04, ®_val); + reg_val &= 0xff00; + reg_val |= val & 0xff; + spi_write_reg(motor->spi, 0x04, reg_val); + gpiod_set_value(motor->dciris->vd_iris_gpio, 1); + usleep_range(200, 400); + gpiod_set_value(motor->dciris->vd_iris_gpio, 0); + dev_info(dev, "set hall_bias %d, reg val 0x%x\n", val, reg_val); + spi_read_reg(motor->spi, 0x04, ®_val); + dev_info(dev, "hall bias reg val 0x%x, read from register\n", reg_val); + } else { + dev_err(dev, "not support dc-iris, do nothing\n"); + } + } + return count; +} + +static ssize_t set_hall_offset(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct motor_dev *motor = to_motor_dev(sd); + int val = 0; + int ret = 0; + u16 reg_val = 0; + + ret = kstrtoint(buf, 0, &val); + if (!ret) { + if (motor->is_use_dc_iris) { + spi_read_reg(motor->spi, 0x04, ®_val); + reg_val &= 0x00ff; + reg_val |= (val & 0xff) << 8; + spi_write_reg(motor->spi, 0x04, reg_val); + gpiod_set_value(motor->dciris->vd_iris_gpio, 1); + usleep_range(200, 400); + gpiod_set_value(motor->dciris->vd_iris_gpio, 0); + dev_info(dev, "set hall_offset %d, reg val 0x%x\n", val, reg_val); + spi_read_reg(motor->spi, 0x04, ®_val); + dev_info(dev, "hall offset reg val 0x%x, read from register\n", reg_val); + } else { + dev_err(dev, "not support dc-iris, do nothing\n"); + } + } + return count; +} + +static ssize_t set_hall_gain(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct motor_dev *motor = to_motor_dev(sd); + int val = 0; + int ret = 0; + u16 reg_val = 0; + + ret = kstrtoint(buf, 0, &val); + if (!ret) { + if (motor->is_use_dc_iris) { + spi_read_reg(motor->spi, 0x05, ®_val); + reg_val &= 0xf0ff; + reg_val |= (val & 0xf) << 8; + spi_write_reg(motor->spi, 0x05, reg_val); + gpiod_set_value(motor->dciris->vd_iris_gpio, 1); + usleep_range(200, 400); + gpiod_set_value(motor->dciris->vd_iris_gpio, 0); + dev_info(dev, "set hall_offset %d, reg val 0x%04x\n", val, reg_val); + spi_read_reg(motor->spi, 0x05, ®_val); + dev_info(dev, "hall gain reg val 0x%04x, read from register\n", reg_val); + } else { + dev_err(dev, "not support dc-iris, do nothing\n"); + } + } + return count; +} + +static ssize_t reinit_piris_pos(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct motor_dev *motor = to_motor_dev(sd); + int val = 0; + int ret = 0; + + ret = kstrtoint(buf, 0, &val); + if (!ret) { + if (val == 1) + ms41968_reinit_piris_pos(motor); + } + return count; +} + +static ssize_t reinit_focus_pos(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct motor_dev *motor = to_motor_dev(sd); + int val = 0; + int ret = 0; + + ret = kstrtoint(buf, 0, &val); + if (!ret) { + if (val == 1) + ms41968_reinit_focus_pos(motor); + } + return count; +} + +static ssize_t reinit_zoom_pos(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct motor_dev *motor = to_motor_dev(sd); + int val = 0; + int ret = 0; + + ret = kstrtoint(buf, 0, &val); + if (!ret) { + if (val == 1) + ms41968_reinit_zoom_pos(motor); + } + return count; +} + +static ssize_t reinit_zoom1_pos(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct motor_dev *motor = to_motor_dev(sd); + int val = 0; + int ret = 0; + + ret = kstrtoint(buf, 0, &val); + if (!ret) { + if (val == 1) + ms41968_reinit_zoom1_pos(motor); + } + return count; +} + +static ssize_t set_focus_reback_ctrl(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct motor_dev *motor = to_motor_dev(sd); + int val = 0; + int ret = 0; + + ret = kstrtoint(buf, 0, &val); + if (!ret) { + if (val == 1) + motor->focus->reback_ctrl = true; + else + motor->focus->reback_ctrl = false; + } + return count; +} + +static ssize_t set_zoom_reback_ctrl(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct motor_dev *motor = to_motor_dev(sd); + int val = 0; + int ret = 0; + + ret = kstrtoint(buf, 0, &val); + if (!ret) { + if (val == 1) + motor->zoom->reback_ctrl = true; + else + motor->zoom->reback_ctrl = false; + } + return count; +} + +static ssize_t set_zoom1_reback_ctrl(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct motor_dev *motor = to_motor_dev(sd); + int val = 0; + int ret = 0; + + ret = kstrtoint(buf, 0, &val); + if (!ret) { + if (val == 1) + motor->zoom1->reback_ctrl = true; + else + motor->zoom1->reback_ctrl = false; + } + return count; +} + +static ssize_t get_focus_pic(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct motor_dev *motor = to_motor_dev(sd); + struct ext_dev *ext_dev = motor->focus; + int val = 0; + int ret = 0; + + ret = kstrtoint(buf, 0, &val); + if (!ret) { + if (val == 1) { + val = ms41968_get_pic_val(ext_dev); + dev_info(&motor->spi->dev, + "ext dev %d get pic %d\n", ext_dev->type, val); + } + } + return count; +} + +static ssize_t get_zoom_pic(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct motor_dev *motor = to_motor_dev(sd); + struct ext_dev *ext_dev = motor->zoom; + int val = 0; + int ret = 0; + + ret = kstrtoint(buf, 0, &val); + if (!ret) { + if (val == 1) { + val = ms41968_get_pic_val(ext_dev); + dev_info(&motor->spi->dev, + "ext dev %d get pic %d\n", ext_dev->type, val); + } + } + return count; +} + +static ssize_t get_zoom1_pic(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct v4l2_subdev *sd = dev_get_drvdata(dev); + struct motor_dev *motor = to_motor_dev(sd); + struct ext_dev *ext_dev = motor->zoom1; + int val = 0; + int ret = 0; + + ret = kstrtoint(buf, 0, &val); + if (!ret) { + if (val == 1) { + val = ms41968_get_pic_val(ext_dev); + dev_info(&motor->spi->dev, + "ext dev %d get pic %d\n", ext_dev->type, val); + } + } + return count; +} + +static struct device_attribute attributes[] = { + __ATTR(pid_dgain, 0200, NULL, set_pid_dgain), + __ATTR(pid_zero, 0200, NULL, set_pid_zero), + __ATTR(pid_pole, 0200, NULL, set_pid_pole), + __ATTR(hall_bias, 0200, NULL, set_hall_bias), + __ATTR(hall_offset, 0200, NULL, set_hall_offset), + __ATTR(hall_gain, 0200, NULL, set_hall_gain), + __ATTR(reinit_piris, 0200, NULL, reinit_piris_pos), + __ATTR(reinit_focus, 0200, NULL, reinit_focus_pos), + __ATTR(reinit_zoom, 0200, NULL, reinit_zoom_pos), + __ATTR(reinit_zoom1, 0200, NULL, reinit_zoom1_pos), + __ATTR(focus_reback_ctrl, 0200, NULL, set_focus_reback_ctrl), + __ATTR(zoom_reback_ctrl, 0200, NULL, set_zoom_reback_ctrl), + __ATTR(zoom1_reback_ctrl, 0200, NULL, set_zoom1_reback_ctrl), + __ATTR(focus_pic, 0200, NULL, get_focus_pic), + __ATTR(zoom_pic, 0200, NULL, get_zoom_pic), + __ATTR(zoom1_pic, 0200, NULL, get_zoom1_pic), +}; + +static int add_sysfs_interfaces(struct device *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(attributes); i++) + if (device_create_file(dev, attributes + i)) + goto undo; + return 0; +undo: + for (i--; i >= 0 ; i--) + device_remove_file(dev, attributes + i); + dev_err(dev, "%s: failed to create sysfs interface\n", __func__); + return -ENODEV; +} + +static int remove_sysfs_interfaces(struct device *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(attributes); i++) + device_remove_file(dev, attributes + i); + return 0; +} +#endif + +static const struct v4l2_subdev_core_ops motor_core_ops = { + .ioctl = ms41968_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl32 = ms41968_compat_ioctl32 +#endif +}; + +static const struct v4l2_subdev_ops motor_subdev_ops = { + .core = &motor_core_ops, +}; + +static const struct v4l2_ctrl_ops motor_ctrl_ops = { + .g_volatile_ctrl = ms41968_g_volatile_ctrl, + .s_ctrl = ms41968_s_ctrl, +}; + +static int ms41968_initialize_controls(struct motor_dev *motor) +{ + struct v4l2_ctrl_handler *handler; + int ret = 0; + #ifdef PI_TEST + int min = 0; + int max = 0; + #endif + unsigned long flags = V4L2_CTRL_FLAG_EXECUTE_ON_WRITE | V4L2_CTRL_FLAG_VOLATILE; + + handler = &motor->ctrl_handler; + ret = v4l2_ctrl_handler_init(handler, 3); + if (ret) + return ret; + if (motor->is_use_dc_iris) { + motor->iris_ctrl = v4l2_ctrl_new_std(handler, &motor_ctrl_ops, + V4L2_CID_IRIS_ABSOLUTE, 0, motor->dciris->max_log, 1, 0); + if (motor->iris_ctrl) + motor->iris_ctrl->flags |= flags; + + } else if (motor->is_use_p_iris) { + #ifdef REINIT_BOOT + ret = ms41968_reinit_piris(motor); + if (ret < 0) + return -EINVAL; + #endif + motor->piris->last_pos = motor->piris->min_pos; + motor->iris_ctrl = v4l2_ctrl_new_std(handler, &motor_ctrl_ops, + V4L2_CID_IRIS_ABSOLUTE, + motor->piris->min_pos, + motor->piris->max_pos, + 1, motor->piris->min_pos); + if (motor->iris_ctrl) + motor->iris_ctrl->flags |= flags; + } + if (motor->is_use_focus) { + #ifdef REINIT_BOOT + ret = ms41968_reinit_focus(motor); + if (ret < 0) + return -EINVAL; + #endif + motor->focus->last_pos = motor->focus->min_pos; + motor->focus_ctrl = v4l2_ctrl_new_std(handler, &motor_ctrl_ops, + V4L2_CID_FOCUS_ABSOLUTE, motor->focus->min_pos, + motor->focus->max_pos - motor->focus->reback, + 1, motor->focus->min_pos); + if (motor->focus_ctrl) + motor->focus_ctrl->flags |= flags; + } + if (motor->is_use_zoom) { + #ifdef REINIT_BOOT + ret = ms41968_reinit_zoom(motor); + if (ret < 0) + return -EINVAL; + #endif + motor->zoom->last_pos = motor->zoom->min_pos; + motor->zoom_ctrl = v4l2_ctrl_new_std(handler, &motor_ctrl_ops, + V4L2_CID_ZOOM_ABSOLUTE, + motor->zoom->min_pos, + motor->zoom->max_pos - motor->zoom->reback, + 1, motor->zoom->min_pos); + if (motor->zoom_ctrl) + motor->zoom_ctrl->flags |= flags; + } + if (motor->is_use_zoom1) { + #ifdef REINIT_BOOT + ret = ms41968_reinit_zoom1(motor); + if (ret < 0) + return -EINVAL; + #endif + motor->zoom1->last_pos = motor->zoom1->min_pos; + motor->zoom1_ctrl = v4l2_ctrl_new_std(handler, &motor_ctrl_ops, + V4L2_CID_ZOOM_CONTINUOUS, + motor->zoom1->min_pos, + motor->zoom1->max_pos, + 1, motor->zoom1->min_pos); + if (motor->zoom1_ctrl) + motor->zoom1_ctrl->flags |= flags; + } + if (handler->error) { + ret = handler->error; + dev_err(&motor->spi->dev, + "Failed to init controls(%d)\n", ret); + goto err_free_handler; + } + + motor->subdev.ctrl_handler = handler; + return ret; + +err_free_handler: + v4l2_ctrl_handler_free(handler); + + return ret; +} + +static void dev_param_init(struct motor_dev *motor) +{ + int step = 0; + u32 mv_cnt = 0; + u32 status = 0; + u32 reback_vd_cnt = 0; + + if (motor->is_use_dc_iris) + motor->dciris->last_log = 0; + if (motor->is_use_p_iris) { + motor->piris->is_mv_tim_update = false; + motor->piris->is_need_update_tim = false; + motor->piris->move_status = MOTOR_STATUS_STOPPED; + motor->piris->type = TYPE_IRIS; + motor->piris->mv_tim.vcm_start_t = ns_to_kernel_old_timeval(ktime_get_ns()); + motor->piris->mv_tim.vcm_end_t = ns_to_kernel_old_timeval(ktime_get_ns()); + init_completion(&motor->piris->complete); + init_completion(&motor->piris->complete_out); + motor->piris->run_data.psum = motor->vd_fz_period_us * + motor->piris->start_up_speed * 8 / 1000000; + if (motor->piris->is_half_step_mode) + motor->piris->run_data.psum /= 2; + motor->piris->run_data.intct = motor->sys_clk * motor->vd_fz_period_us / + (motor->piris->run_data.psum * 24); + motor->piris->is_running = false; + dev_info(&motor->spi->dev, + "piris vd_fz_period_us %u, psum %d, inict %d\n", + motor->vd_fz_period_us, + motor->piris->run_data.psum, + motor->piris->run_data.intct); + } + if (motor->is_use_focus) { + motor->focus->is_mv_tim_update = false; + motor->focus->is_need_update_tim = false; + motor->focus->move_status = MOTOR_STATUS_STOPPED; + motor->focus->type = TYPE_FOCUS; + motor->focus->mv_tim.vcm_start_t = ns_to_kernel_old_timeval(ktime_get_ns()); + motor->focus->mv_tim.vcm_end_t = ns_to_kernel_old_timeval(ktime_get_ns()); + init_completion(&motor->focus->complete); + init_completion(&motor->focus->complete_out); + motor->focus->run_data.psum = motor->vd_fz_period_us * + motor->focus->start_up_speed * 8 / 1000000; + if (motor->focus->is_half_step_mode) + motor->focus->run_data.psum /= 2; + motor->focus->run_data.intct = motor->sys_clk * motor->vd_fz_period_us / + (motor->focus->run_data.psum * 24); + motor->focus->is_running = false; + motor->focus->reback_ctrl = false; + motor->focus->default_intct = motor->focus->run_data.intct; + motor->focus->default_psum = motor->focus->run_data.psum; + dev_info(&motor->spi->dev, + "focus vd_fz_period_us %u, psum %d, inict %d, default_intct %d, default_psum %d\n", + motor->vd_fz_period_us, + motor->focus->run_data.psum, + motor->focus->run_data.intct, + motor->focus->default_intct, + motor->focus->default_psum); + if (motor->focus->reback != 0) { + motor->focus->cur_back_delay = 0; + motor->focus->max_back_delay = FOCUS_MAX_BACK_DELAY; + motor->focus->reback_data = motor->focus->run_data; + mv_cnt = motor->focus->reback; + if (motor->focus->is_dir_opp) { + mv_cnt += motor->focus->backlash; + status = MOTOR_STATUS_CCW; + } else { + mv_cnt += motor->focus->backlash; + status = MOTOR_STATUS_CW; + } + motor->focus->reback_status = status; + if (motor->focus->is_half_step_mode) + step = mv_cnt * 4; + else + step = mv_cnt * 8; + motor->focus->reback_data.count = (step + motor->focus->reback_data.psum - 1) / + motor->focus->reback_data.psum + 1; + motor->focus->reback_data.cur_count = motor->focus->reback_data.count; + motor->focus->reback_data.psum_last = step % motor->focus->reback_data.psum; + if (motor->focus->reback_data.psum_last == 0) + motor->focus->reback_data.psum_last = motor->focus->reback_data.psum; + reback_vd_cnt = motor->focus->reback_data.count + motor->focus->max_back_delay; + motor->focus->reback_move_time_us = reback_vd_cnt * (motor->vd_fz_period_us + 500); + } + } + if (motor->is_use_zoom) { + motor->zoom->is_mv_tim_update = false; + motor->zoom->is_need_update_tim = false; + motor->zoom->move_status = MOTOR_STATUS_STOPPED; + motor->zoom->type = TYPE_ZOOM; + motor->zoom->mv_tim.vcm_start_t = ns_to_kernel_old_timeval(ktime_get_ns()); + motor->zoom->mv_tim.vcm_end_t = ns_to_kernel_old_timeval(ktime_get_ns()); + init_completion(&motor->zoom->complete); + init_completion(&motor->zoom->complete_out); + motor->zoom->run_data.psum = motor->vd_fz_period_us * + motor->zoom->start_up_speed * 8 / 1000000; + if (motor->zoom->is_half_step_mode) + motor->zoom->run_data.psum /= 2; + motor->zoom->run_data.intct = motor->sys_clk * motor->vd_fz_period_us / + (motor->zoom->run_data.psum * 24); + motor->zoom->is_running = false; + motor->zoom->reback_ctrl = false; + motor->zoom->default_intct = motor->zoom->run_data.intct; + motor->zoom->default_psum = motor->zoom->run_data.psum; + if (motor->zoom->reback != 0) { + motor->zoom->cur_back_delay = 0; + motor->zoom->max_back_delay = ZOOM_MAX_BACK_DELAY; + motor->zoom->reback_data = motor->zoom->run_data; + mv_cnt = motor->zoom->reback; + if (motor->zoom->is_dir_opp) { + mv_cnt += motor->zoom->backlash; + status = MOTOR_STATUS_CCW; + } else { + mv_cnt += motor->zoom->backlash; + status = MOTOR_STATUS_CW; + } + motor->zoom->reback_status = status; + if (motor->zoom->is_half_step_mode) + step = mv_cnt * 4; + else + step = mv_cnt * 8; + motor->zoom->reback_data.count = (step + motor->zoom->reback_data.psum - 1) / + motor->zoom->reback_data.psum + 1; + motor->zoom->reback_data.cur_count = motor->zoom->reback_data.count; + motor->zoom->reback_data.psum_last = step % motor->zoom->reback_data.psum; + if (motor->zoom->reback_data.psum_last == 0) + motor->zoom->reback_data.psum_last = motor->zoom->reback_data.psum; + reback_vd_cnt = motor->zoom->reback_data.count + motor->zoom->max_back_delay; + motor->zoom->reback_move_time_us = reback_vd_cnt * (motor->vd_fz_period_us + 500); + } + dev_info(&motor->spi->dev, + "zoom vd_fz_period_us %u, psum %d, inict %d, default_intct %d, default_psum %d\n", + motor->vd_fz_period_us, + motor->zoom->run_data.psum, + motor->zoom->run_data.intct, + motor->zoom->default_intct, + motor->zoom->default_psum); + } + if (motor->is_use_zoom1) { + motor->zoom1->is_mv_tim_update = false; + motor->zoom1->is_need_update_tim = false; + motor->zoom1->move_status = MOTOR_STATUS_STOPPED; + motor->zoom1->type = TYPE_ZOOM1; + motor->zoom1->mv_tim.vcm_start_t = ns_to_kernel_old_timeval(ktime_get_ns()); + motor->zoom1->mv_tim.vcm_end_t = ns_to_kernel_old_timeval(ktime_get_ns()); + init_completion(&motor->zoom1->complete); + init_completion(&motor->zoom1->complete_out); + motor->zoom1->run_data.psum = motor->vd_fz_period_us * + motor->zoom1->start_up_speed * 8 / 1000000; + if (motor->zoom1->is_half_step_mode) + motor->zoom1->run_data.psum /= 2; + motor->zoom1->run_data.intct = motor->sys_clk * motor->vd_fz_period_us / + (motor->zoom1->run_data.psum * 24); + motor->zoom1->is_running = false; + motor->zoom1->reback_ctrl = false; + motor->zoom1->default_intct = motor->zoom1->run_data.intct; + motor->zoom1->default_psum = motor->zoom1->run_data.psum; + if (motor->zoom1->reback != 0) { + motor->zoom1->cur_back_delay = 0; + motor->zoom1->max_back_delay = ZOOM_MAX_BACK_DELAY; + motor->zoom1->reback_data = motor->zoom1->run_data; + mv_cnt = motor->zoom1->reback; + if (motor->zoom1->is_dir_opp) { + mv_cnt += motor->zoom1->backlash; + status = MOTOR_STATUS_CCW; + } else { + mv_cnt += motor->zoom1->backlash; + status = MOTOR_STATUS_CW; + } + motor->zoom1->reback_status = status; + if (motor->zoom1->is_half_step_mode) + step = mv_cnt * 4; + else + step = mv_cnt * 8; + motor->zoom1->reback_data.count = (step + motor->zoom1->reback_data.psum - 1) / + motor->zoom1->reback_data.psum + 1; + motor->zoom1->reback_data.cur_count = motor->zoom1->reback_data.count; + motor->zoom1->reback_data.psum_last = step % motor->zoom1->reback_data.psum; + if (motor->zoom1->reback_data.psum_last == 0) + motor->zoom1->reback_data.psum_last = motor->zoom1->reback_data.psum; + reback_vd_cnt = motor->zoom1->reback_data.count + motor->zoom1->max_back_delay; + motor->zoom1->reback_move_time_us = reback_vd_cnt * (motor->vd_fz_period_us + 500); + } + dev_info(&motor->spi->dev, + "zoom1 vd_fz_period_us %u, psum %d, inict %d, default_intct %d, default_psum %d\n", + motor->vd_fz_period_us, + motor->zoom1->run_data.psum, + motor->zoom1->run_data.intct, + motor->zoom1->default_intct, + motor->zoom1->default_psum); + } + + motor->is_should_wait = false; + motor->is_timer_restart = false; + motor->is_timer_restart_bywq = false; + motor->wait_cnt = 0; + motor->pi_gpio_usecnt = 0; +} + +static void dev_reg_init(struct motor_dev *motor) +{ + spi_write_reg(motor->spi, 0x20, 0x01);//27M/(30*2^3*2^0) + spi_write_reg(motor->spi, 0x26, 0x541a); + spi_write_reg(motor->spi, 0x2b, 0x541a); + spi_write_reg(motor->spi, 0x30, 0x541a); + spi_write_reg(motor->spi, 0x35, 0x541a); + spi_write_reg(motor->spi, 0x23, PPW_STOP); + spi_write_reg(motor->spi, 0x28, PPW_STOP); + spi_write_reg(motor->spi, 0x2d, PPW_STOP); + spi_write_reg(motor->spi, 0x32, PPW_STOP); + + spi_write_reg(motor->spi, 0x0b, 0x0480); + if (motor->is_use_dc_iris) { + //DC-IRIS reg init + if (motor->dciris->is_reversed_polarity) + spi_write_reg(motor->spi, 0x00, + motor->dciris->max_log - motor->dciris->last_log); + else + spi_write_reg(motor->spi, 0x00, motor->dciris->last_log); + spi_write_reg(motor->spi, 0x01, 0x6000); + spi_write_reg(motor->spi, 0x02, 0x66f0); + spi_write_reg(motor->spi, 0x03, 0x0e10); + spi_write_reg(motor->spi, 0x04, 0xd640); + spi_write_reg(motor->spi, 0x05, 0x0004); + spi_write_reg(motor->spi, 0x0b, 0x0480); + spi_write_reg(motor->spi, 0x0a, 0x0000); + spi_write_reg(motor->spi, 0x0e, 0x0300); + } + if (motor->dev0) + gpiod_set_value(motor->dev0->vd_gpio, 1); + if (motor->dev1) + gpiod_set_value(motor->dev1->vd_gpio, 1); + if (motor->dev2) + gpiod_set_value(motor->dev2->vd_gpio, 1); + if (motor->dev3) + gpiod_set_value(motor->dev3->vd_gpio, 1); + if (motor->is_use_dc_iris && (!IS_ERR(motor->dciris->vd_iris_gpio))) + gpiod_set_value(motor->dciris->vd_iris_gpio, 1); + usleep_range(100, 200); + if (motor->dev0) + gpiod_set_value(motor->dev0->vd_gpio, 0); + if (motor->dev1) + gpiod_set_value(motor->dev1->vd_gpio, 0); + if (motor->dev2) + gpiod_set_value(motor->dev2->vd_gpio, 0); + if (motor->dev3) + gpiod_set_value(motor->dev3->vd_gpio, 0); + if (motor->is_use_dc_iris && (!IS_ERR(motor->dciris->vd_iris_gpio))) + gpiod_set_value(motor->dciris->vd_iris_gpio, 0); +} + + +static int ms41968_check_id(struct motor_dev *motor) +{ + u16 val = 0xffff; + int i = 0; + + for (i = 0; i < 0x20; i++) + spi_read_reg(motor->spi, i, &val); + spi_read_reg(motor->spi, 0x20, &val); + if (val == 0xffff) { + dev_err(&motor->spi->dev, + "check id fail, spi transfer err or driver not connect, val 0x%x\n", + val); + return -EINVAL; + } + return 0; +} + +static int ms41968_dev_init(struct motor_dev *motor) +{ + int ret = 0; + + if (!IS_ERR(motor->reset_gpio)) { + gpiod_set_value_cansleep(motor->reset_gpio, 0); + usleep_range(100, 200); + gpiod_set_value_cansleep(motor->reset_gpio, 1); + } + ret = ms41968_check_id(motor); + if (ret < 0) + return -EINVAL; + dev_param_init(motor); + dev_reg_init(motor); + + motor->wk = devm_kzalloc(&motor->spi->dev, sizeof(*motor->wk), GFP_KERNEL); + if (!motor->wk) + return -ENOMEM; + + motor->wk->dev = motor; + INIT_WORK(&motor->wk->work, motor_op_work); + + return 0; +} + +static int ms41968_dev_probe(struct spi_device *spi) +{ + int ret = 0; + struct device *dev = &spi->dev; + struct motor_dev *motor; + struct v4l2_subdev *sd; + char facing[2]; + + dev_info(dev, "driver version: %02x.%02x.%02x", + DRIVER_VERSION >> 16, + (DRIVER_VERSION & 0xff00) >> 8, + DRIVER_VERSION & 0x00ff); + motor = devm_kzalloc(dev, sizeof(*motor), GFP_KERNEL); + if (!motor) + return -ENOMEM; + spi->mode = SPI_MODE_3 | SPI_LSB_FIRST | SPI_CS_HIGH; + spi->irq = -1; + spi->max_speed_hz = 5000000; + spi->bits_per_word = 8; + ret = spi_setup(spi); + if (ret < 0) { + dev_err(dev, "could not setup spi!\n"); + return -EINVAL; + } + motor->spi = spi; + motor->motor_op[0] = g_motor_op[0]; + motor->motor_op[1] = g_motor_op[1]; + motor->motor_op[2] = g_motor_op[2]; + motor->motor_op[3] = g_motor_op[3]; + + if (ms41968_dev_parse_dt(motor)) { + dev_err(&motor->spi->dev, "parse dt error\n"); + return -EINVAL; + } + ret = ms41968_dev_init(motor); + if (ret) + goto err_free; + + mutex_init(&motor->mutex); + hrtimer_init(&motor->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + motor->timer.function = motor_timer_func; + + sd = &motor->subdev; + v4l2_spi_subdev_init(sd, spi, &motor_subdev_ops); + sd->entity.function = MEDIA_ENT_F_LENS; + sd->entity.flags = 0; + sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + ms41968_initialize_controls(motor); + ret = media_entity_pads_init(&motor->subdev.entity, 0, NULL); + if (ret < 0) + goto err_free; + memset(facing, 0, sizeof(facing)); + if (strcmp(motor->module_facing, "back") == 0) + facing[0] = 'b'; + else + facing[0] = 'f'; + snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s_%d", + motor->module_index, facing, + DRIVER_NAME, + motor->id); + ret = v4l2_async_register_subdev(sd); + if (ret) + dev_err(&spi->dev, "v4l2 async register subdev failed\n"); +#ifdef USED_SYS_DEBUG + add_sysfs_interfaces(dev); +#endif + dev_info(&motor->spi->dev, "gpio motor driver probe success\n"); + return 0; +err_free: + v4l2_ctrl_handler_free(&motor->ctrl_handler); + v4l2_device_unregister_subdev(&motor->subdev); + media_entity_cleanup(&motor->subdev.entity); + return ret; +} + +static void ms41968_dev_remove(struct spi_device *spi) +{ + struct v4l2_subdev *sd = spi_get_drvdata(spi); + struct motor_dev *motor = to_motor_dev(sd); + + hrtimer_cancel(&motor->timer); + if (sd) + v4l2_device_unregister_subdev(sd); + v4l2_ctrl_handler_free(&motor->ctrl_handler); + media_entity_cleanup(&motor->subdev.entity); +#ifdef USED_SYS_DEBUG + remove_sysfs_interfaces(&spi->dev); +#endif +} + +static const struct spi_device_id ms41968_match_id[] = { + {"relmon,ms41968", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, ms41968_match_id); + +#if defined(CONFIG_OF) +static const struct of_device_id ms41968_dev_of_match[] = { + {.compatible = "relmon,ms41968", }, + {}, +}; +#endif + +static struct spi_driver ms41968_dev_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(ms41968_dev_of_match), + }, + .probe = ms41968_dev_probe, + .remove = ms41968_dev_remove, + .id_table = ms41968_match_id, +}; + +static int __init ms41968_mod_init(void) +{ + return spi_register_driver(&ms41968_dev_driver); +} + +static void __exit ms41968_mod_exit(void) +{ + spi_unregister_driver(&ms41968_dev_driver); +} + +device_initcall_sync(ms41968_mod_init); +module_exit(ms41968_mod_exit); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:motor"); +MODULE_AUTHOR("zefa.chen@rock-chips.com"); From 663db123edf8ac37f1a86a3eb6ec25bfee04eada Mon Sep 17 00:00:00 2001 From: Cai YiWei Date: Mon, 28 Jul 2025 15:34:28 +0800 Subject: [PATCH 18/28] media: rockchip: isp: fix unite div calculation Change-Id: Iea94ff13b0cc1c3553996e22051266f4af2c1a76 Signed-off-by: Cai YiWei --- drivers/media/platform/rockchip/isp/rkisp.c | 25 ++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/drivers/media/platform/rockchip/isp/rkisp.c b/drivers/media/platform/rockchip/isp/rkisp.c index 27ca6de91a6b..180c43eb4da1 100644 --- a/drivers/media/platform/rockchip/isp/rkisp.c +++ b/drivers/media/platform/rockchip/isp/rkisp.c @@ -3003,22 +3003,34 @@ static int rkisp_unite_div(struct rkisp_device *dev, u32 w, u32 h) case ISP_V30: max_size = CIF_ISP_INPUT_W_MAX_V30 * CIF_ISP_INPUT_H_MAX_V30; max_w = CIF_ISP_INPUT_W_MAX_V30; - max_h = max_size / w; + if (w > max_w) + max_h = max_size * 2 / w; + else + max_h = max_size / w; break; case ISP_V32: max_size = CIF_ISP_INPUT_W_MAX_V32 * CIF_ISP_INPUT_H_MAX_V32; max_w = CIF_ISP_INPUT_W_MAX_V32; - max_h = max_size / w; + if (w > max_w) + max_h = max_size * 2 / w; + else + max_h = max_size / w; break; case ISP_V32_L: max_size = CIF_ISP_INPUT_W_MAX_V32_L * CIF_ISP_INPUT_H_MAX_V32_L; max_w = CIF_ISP_INPUT_W_MAX_V32_L; - max_h = max_size / w; + if (w > max_w) + max_h = max_size * 2 / w; + else + max_h = max_size / w; break; case ISP_V33: max_size = CIF_ISP_INPUT_W_MAX_V33 * CIF_ISP_INPUT_H_MAX_V33; max_w = CIF_ISP_INPUT_W_MAX_V33; - max_h = max_size / w; + if (w > max_w) + max_h = max_size * 2 / w; + else + max_h = max_size / w; break; case ISP_V35: max_size = CIF_ISP_INPUT_W_MAX_V35 * CIF_ISP_INPUT_H_MAX_V35; @@ -3028,7 +3040,10 @@ static int rkisp_unite_div(struct rkisp_device *dev, u32 w, u32 h) case ISP_V39: max_size = CIF_ISP_INPUT_W_MAX_V39_UNITE / 2 * CIF_ISP_INPUT_H_MAX_V39_UNITE; max_w = CIF_ISP_INPUT_W_MAX_V39; - max_h = max_size / w; + if (w > max_w) + max_h = max_size * 2 / w; + else + max_h = max_size / w; break; default: return -EINVAL; From e09a9363f41f2e14adfb1365fbe77535929733f1 Mon Sep 17 00:00:00 2001 From: Xing Zheng Date: Tue, 29 Jul 2025 20:46:24 +0800 Subject: [PATCH 19/28] soc: rockchip: aoa_middleware: Add support AOA middleware modules The goal of this driver is to implement the minimum configuration of AOA-related drivers and interact with AOA user-mode applications through middleware. Signed-off-by: Xing Zheng Change-Id: I60937d8b6a3b6465a3251b3b3db3d6ad676f2d6f --- drivers/soc/rockchip/Kconfig | 1 + drivers/soc/rockchip/Makefile | 1 + drivers/soc/rockchip/aoa_middleware/Kconfig | 7 + drivers/soc/rockchip/aoa_middleware/Makefile | 7 + drivers/soc/rockchip/aoa_middleware/aoa_drv.c | 81 +++++ drivers/soc/rockchip/aoa_middleware/aoa_drv.h | 15 + .../rockchip/aoa_middleware/aoa_middleware.c | 279 ++++++++++++++++++ .../rockchip/aoa_middleware/aoa_middleware.h | 15 + .../soc/rockchip/aoa_middleware/aoa_mmap.c | 155 ++++++++++ .../soc/rockchip/aoa_middleware/aoa_mmap.h | 15 + .../soc/rockchip/aoa_middleware/lp_rkdma.c | 228 ++++++++++++++ .../soc/rockchip/aoa_middleware/lp_rkdma.h | 15 + 12 files changed, 819 insertions(+) create mode 100644 drivers/soc/rockchip/aoa_middleware/Kconfig create mode 100644 drivers/soc/rockchip/aoa_middleware/Makefile create mode 100644 drivers/soc/rockchip/aoa_middleware/aoa_drv.c create mode 100644 drivers/soc/rockchip/aoa_middleware/aoa_drv.h create mode 100644 drivers/soc/rockchip/aoa_middleware/aoa_middleware.c create mode 100644 drivers/soc/rockchip/aoa_middleware/aoa_middleware.h create mode 100644 drivers/soc/rockchip/aoa_middleware/aoa_mmap.c create mode 100644 drivers/soc/rockchip/aoa_middleware/aoa_mmap.h create mode 100644 drivers/soc/rockchip/aoa_middleware/lp_rkdma.c create mode 100644 drivers/soc/rockchip/aoa_middleware/lp_rkdma.h diff --git a/drivers/soc/rockchip/Kconfig b/drivers/soc/rockchip/Kconfig index 97af2f1d9528..98f1660a23fb 100644 --- a/drivers/soc/rockchip/Kconfig +++ b/drivers/soc/rockchip/Kconfig @@ -372,6 +372,7 @@ config RK_ZONEINFO_PROCFS help Dump all zone information. +source "drivers/soc/rockchip/aoa_middleware/Kconfig" source "drivers/soc/rockchip/minidump/Kconfig" endmenu diff --git a/drivers/soc/rockchip/Makefile b/drivers/soc/rockchip/Makefile index efa1809dd949..24a9975f7c82 100644 --- a/drivers/soc/rockchip/Makefile +++ b/drivers/soc/rockchip/Makefile @@ -2,6 +2,7 @@ # # Rockchip Soc drivers # +obj-$(CONFIG_ROCKCHIP_AOA_MIDDLEWARE) += aoa_middleware/ obj-$(CONFIG_ROCKCHIP_AMP) += rockchip_amp.o obj-$(CONFIG_ROCKCHIP_CPUINFO) += rockchip-cpuinfo.o obj-$(CONFIG_ROCKCHIP_CSU) += rockchip_csu.o diff --git a/drivers/soc/rockchip/aoa_middleware/Kconfig b/drivers/soc/rockchip/aoa_middleware/Kconfig new file mode 100644 index 000000000000..767e78d4e488 --- /dev/null +++ b/drivers/soc/rockchip/aoa_middleware/Kconfig @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +config ROCKCHIP_AOA_MIDDLEWARE + tristate "Rockchip AOA Middleware Driver" + help + Enable this configuration option to enable notify (AOA/DMA) events + on driver for user applications. diff --git a/drivers/soc/rockchip/aoa_middleware/Makefile b/drivers/soc/rockchip/aoa_middleware/Makefile new file mode 100644 index 000000000000..a40462432cad --- /dev/null +++ b/drivers/soc/rockchip/aoa_middleware/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Rockchip AOA Middleware Modules +# +obj-$(CONFIG_ROCKCHIP_AOA_MIDDLEWARE) += rk_aoa.o + +rk_aoa-objs := aoa_drv.o aoa_mmap.o aoa_middleware.o lp_rkdma.o diff --git a/drivers/soc/rockchip/aoa_middleware/aoa_drv.c b/drivers/soc/rockchip/aoa_middleware/aoa_drv.c new file mode 100644 index 000000000000..084775879078 --- /dev/null +++ b/drivers/soc/rockchip/aoa_middleware/aoa_drv.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Rockchip AOA Controller Driver + * + * Copyright (c) 2025 Rockchip Electronics Co., Ltd. + * Author: Xing Zheng + */ + +#include +#include + +#include "aoa_drv.h" +#include "aoa_middleware.h" + +#define DRV_NAME "rockchip-aoa" +#define AOA_AAD_IRQ_ST 0x01a8 + +struct rk_aoa_dev { + struct device *dev; + struct reset_control *rst; + void __iomem *base; +}; + +static const struct of_device_id rockchip_aoa_match[] __maybe_unused = { + { .compatible = "rockchip,aoa", }, + {}, +}; + +static irqreturn_t rockchip_aoa_isr(int irq, void *devid) +{ + struct rk_aoa_dev *aoa = (struct rk_aoa_dev *)devid; + u32 st, s; + + st = readl(aoa->base + AOA_AAD_IRQ_ST); + writel(st, aoa->base + AOA_AAD_IRQ_ST); + for (s = 1; s < 8; s++) { + if (st & (1 << s)) + aoa_middleware_aoa_notifier(s); + } + + return IRQ_HANDLED; +} + +int rockchip_aoa_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct rk_aoa_dev *aoa; + int ret, irq; + + aoa = devm_kzalloc(&pdev->dev, sizeof(*aoa), GFP_KERNEL); + if (!aoa) + return -ENOMEM; + + aoa->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, aoa); + + aoa->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(aoa->base)) + return PTR_ERR(aoa->base); + + aoa->rst = devm_reset_control_array_get_optional_exclusive(aoa->dev); + if (IS_ERR(aoa->rst)) + return PTR_ERR(aoa->rst); + + irq = platform_get_irq_optional(pdev, 0); + if (irq > 0) { + ret = devm_request_irq(&pdev->dev, irq, rockchip_aoa_isr, + IRQF_SHARED, node->name, aoa); + if (ret) { + dev_err(&pdev->dev, "Failed to request irq %d\n", irq); + return ret; + } + } + + return 0; +} + +int rockchip_aoa_remove(struct platform_device *pdev) +{ + return 0; +} diff --git a/drivers/soc/rockchip/aoa_middleware/aoa_drv.h b/drivers/soc/rockchip/aoa_middleware/aoa_drv.h new file mode 100644 index 000000000000..697ad36f28c8 --- /dev/null +++ b/drivers/soc/rockchip/aoa_middleware/aoa_drv.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Rockchip AOA Controller Driver + * + * Copyright (c) 2025 Rockchip Electronics Co., Ltd. + * Author: Xing Zheng + */ + +#ifndef __AOA_DRV_H__ +#define __AOA_DRV_H__ + +int rockchip_aoa_probe(struct platform_device *pdev); +int rockchip_aoa_remove(struct platform_device *pdev); + +#endif /* __AOA_DRV_H__ */ diff --git a/drivers/soc/rockchip/aoa_middleware/aoa_middleware.c b/drivers/soc/rockchip/aoa_middleware/aoa_middleware.c new file mode 100644 index 000000000000..a05be63789ff --- /dev/null +++ b/drivers/soc/rockchip/aoa_middleware/aoa_middleware.c @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Rockchip AOA Middleware Driver + * + * Copyright (c) 2025 Rockchip Electronics Co., Ltd. + * Author: Xing Zheng + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "aoa_middleware.h" +#include "aoa_mmap.h" +#include "aoa_drv.h" +#include "lp_rkdma.h" + +#define NOTIFY_RKDMA_SET_PERIODS _IOR('N', 1, u32) +#define NOTIFY_RKDMA_GET_TIMESTAMP_NS _IOR('N', 2, s64) + +struct notify_ns { + s32 ns_id; + s64 ns; +}; + +struct notify_rkdma { + s64 *ns_tbl; + s32 last_ns_id; + u32 periods; +}; + +struct aoa_middleware_devs { + struct device *dev; + struct platform_device *pdev_aoa; + struct platform_device *pdev_dma; +}; + +static struct fasync_struct *rk_aoa_fasync_queue; +static struct fasync_struct *rk_dma_fasync_queue; + +static struct notify_rkdma g_notify; +static struct notify_ns g_dma_ns; + +int aoa_middleware_aoa_notifier(s32 status) +{ + /* AOA Notify starting from SIGRTMIN + 1 */ + kill_fasync(&rk_aoa_fasync_queue, SIGRTMIN + status, POLL_IN); + return 0; +} +EXPORT_SYMBOL(aoa_middleware_aoa_notifier); + +int aoa_middleware_dma_notifier(s32 dma_count) +{ + struct notify_rkdma *n_rkdma = &g_notify; + struct notify_ns *n_ns = &g_dma_ns; + struct timespec64 ts; + s32 delta_id; + + ktime_get_boottime_ts64(&ts); + n_ns->ns = timespec64_to_ns(&ts); + kill_fasync(&rk_dma_fasync_queue, SIGRTMIN + 0, POLL_IN); + + /* ns_id: start from 1, range: 0 ~ (periods-1) */ + if (dma_count < 0 || dma_count >= n_rkdma->periods) { + pr_err("Invalid dma_count: %d\n", dma_count); + return -EINVAL; + } + dma_count = array_index_nospec(dma_count, n_rkdma->periods); + n_ns->ns_id = dma_count; + n_rkdma->ns_tbl[n_ns->ns_id] = n_ns->ns; + if (n_ns->ns_id < n_rkdma->last_ns_id) + delta_id = n_ns->ns_id + n_rkdma->periods - n_rkdma->last_ns_id; + else + delta_id = n_ns->ns_id - n_rkdma->last_ns_id; + if (delta_id > 1) { + s32 missed_id = 0; + + while (missed_id <= delta_id) { + /** + * During sleep, the CPU does not respond to DMA interruptsand + * requires manual calibration of PTS. + */ + s32 id = (n_ns->ns_id - missed_id); + + if (id < 0) + id += n_rkdma->periods; + n_rkdma->ns_tbl[id] = n_ns->ns - missed_id * 16000000; + missed_id++; + } + } + n_rkdma->last_ns_id = n_ns->ns_id; + return 0; +} +EXPORT_SYMBOL(aoa_middleware_dma_notifier); + +static int rk_aoa_notifier_fasync(int fd, struct file *file, int mode) +{ + return fasync_helper(fd, file, mode, &rk_aoa_fasync_queue); +} + +static const struct file_operations rk_aoa_notifier_fops = { + .owner = THIS_MODULE, + .fasync = rk_aoa_notifier_fasync, +}; + +static struct miscdevice rk_aoa_notifier_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "rk-aoa-notifier", + .fops = &rk_aoa_notifier_fops, +}; + +static int rk_dma_notifier_fasync(int fd, struct file *file, int mode) +{ + return fasync_helper(fd, file, mode, &rk_dma_fasync_queue); +} + +static long rk_dma_notifier_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct notify_rkdma *n_rkdma = &g_notify; + struct notify_ns n_ns_user; + + switch (cmd) { + case NOTIFY_RKDMA_SET_PERIODS: + n_rkdma->periods = arg; + kfree(n_rkdma->ns_tbl); + n_rkdma->last_ns_id = 0; /* last_ns_id start from 0, current ns_id start from 1 */ + n_rkdma->ns_tbl = kcalloc(n_rkdma->periods, sizeof(s64), GFP_KERNEL); + pr_debug("rk_dma_notifier: set and alloc ns table periods: %d\n", n_rkdma->periods); + break; + case NOTIFY_RKDMA_GET_TIMESTAMP_NS: + /* return last timestamp from ns */ + if (copy_from_user(&n_ns_user, (struct notify_ns __user *)arg, sizeof(n_ns_user))) + return -EFAULT; + if (n_ns_user.ns_id < 0 || n_ns_user.ns_id >= n_rkdma->periods) { + pr_err("Invalid ns_id: %d\n", n_ns_user.ns_id); + return -EINVAL; + } + n_ns_user.ns_id = array_index_nospec(n_ns_user.ns_id, n_rkdma->periods); + n_ns_user.ns = n_rkdma->ns_tbl[n_ns_user.ns_id]; + if (copy_to_user((struct notify_ns __user *)arg, &n_ns_user, sizeof(n_ns_user))) + return -EFAULT; + break; + default: + return -ENOTTY; + } + + return 0; +} + +static const struct file_operations rk_dma_notifier_fops = { + .owner = THIS_MODULE, + .fasync = rk_dma_notifier_fasync, + .compat_ioctl = rk_dma_notifier_ioctl, + .unlocked_ioctl = rk_dma_notifier_ioctl, +}; + +static struct miscdevice rk_dma_notifier_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "rk-dma-notifier", + .fops = &rk_dma_notifier_fops, +}; + +static int aoa_middleware_probe(struct platform_device *pdev) +{ + struct aoa_middleware_devs *amw_d; + struct platform_device *pdev_slave; + struct device_node *np; + int ret; + + amw_d = devm_kzalloc(&pdev->dev, sizeof(*amw_d), GFP_KERNEL); + if (!amw_d) + return -ENOMEM; + amw_d->dev = &pdev->dev; + + /* prepare rockchip aoa control driver */ + np = of_parse_phandle(pdev->dev.of_node, "rockchip,aoa", 0); + if (!np || !of_device_is_available(np)) { + dev_err(&pdev->dev, "can't find 'rockchip,aoa' node\n"); + return -ENODEV; + } + pdev_slave = of_find_device_by_node(np); + of_node_put(np); + if (!pdev_slave) { + dev_err(&pdev->dev, "get aoa node failed\n"); + return -ENODEV; + } + ret = rockchip_aoa_probe(pdev_slave); + if (ret) { + dev_err(&pdev->dev, "probe rockchip aoa failed: %d\n", ret); + return ret; + } + amw_d->pdev_aoa = pdev_slave; + + /* prepare rockchip low-power dma driver */ + np = of_parse_phandle(pdev->dev.of_node, "rockchip,dma", 0); + if (!np || !of_device_is_available(np)) { + dev_err(&pdev->dev, "can't find 'rockchip,dma' node\n"); + return -ENODEV; + } + pdev_slave = of_find_device_by_node(np); + of_node_put(np); + if (!pdev_slave) { + dev_err(&pdev->dev, "get dma node failed\n"); + return -ENODEV; + } + ret = lp_rkdma_probe(pdev_slave); + if (ret) { + dev_err(&pdev->dev, "probe rockchip dma failed: %d\n", ret); + return ret; + } + amw_d->pdev_dma = pdev_slave; + + /* prepare aoa memory mapping */ + ret = aoa_mmap_probe(pdev); + if (ret) { + dev_err(&pdev->dev, "%s: aoa mmap probe failed (%d)\n", __func__, ret); + return ret; + } + + /* prepare aoa/dma notifiers */ + ret = misc_register(&rk_aoa_notifier_misc); + if (ret) { + dev_err(&pdev->dev, "%s: aoa notifier misc register failed (%d)\n", __func__, ret); + return ret; + } + + ret = misc_register(&rk_dma_notifier_misc); + if (ret) { + dev_err(&pdev->dev, "%s: dma notifier misc register failed (%d)\n", __func__, ret); + return ret; + } + + platform_set_drvdata(pdev, amw_d); + dev_info(&pdev->dev, "%s: all aoa middlewares are registered\n", __func__); + return 0; +} + +static int aoa_middleware_remove(struct platform_device *pdev) +{ + struct aoa_middleware_devs *amw_d = platform_get_drvdata(pdev); + + if (IS_ERR(amw_d)) + return -EINVAL; + + rockchip_aoa_remove(amw_d->pdev_aoa); + lp_rkdma_remove(amw_d->pdev_dma); + aoa_mmap_remove(pdev); + + misc_deregister(&rk_dma_notifier_misc); + misc_deregister(&rk_aoa_notifier_misc); + + dev_info(&pdev->dev, "%s: all aoa middlewares are unregistered\n", __func__); + return 0; +} + +static const struct of_device_id aoa_middleware_of_match[] = { + { .compatible = "rockchip,aoa-middleware", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, aoa_middleware_of_match); + +static struct platform_driver aoa_middleware_driver = { + .probe = aoa_middleware_probe, + .remove = aoa_middleware_remove, + .driver = { + .name = "aoa-middleware", + .of_match_table = aoa_middleware_of_match, + }, +}; + +module_platform_driver(aoa_middleware_driver); + +MODULE_DESCRIPTION("Rockchip AOA Middleware Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("xing.zheng@rock-chips.com"); diff --git a/drivers/soc/rockchip/aoa_middleware/aoa_middleware.h b/drivers/soc/rockchip/aoa_middleware/aoa_middleware.h new file mode 100644 index 000000000000..5c51d6028899 --- /dev/null +++ b/drivers/soc/rockchip/aoa_middleware/aoa_middleware.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Rockchip AOA Middleware Driver + * + * Copyright (c) 2025 Rockchip Electronics Co., Ltd. + * Author: Xing Zheng + */ + +#ifndef __AOA_MIDDLEWARE_H__ +#define __AOA_MIDDLEWARE_H__ + +int aoa_middleware_aoa_notifier(s32 status); +int aoa_middleware_dma_notifier(s32 dma_count); + +#endif /* __AOA_MIDDLEWARE_H__ */ diff --git a/drivers/soc/rockchip/aoa_middleware/aoa_mmap.c b/drivers/soc/rockchip/aoa_middleware/aoa_mmap.c new file mode 100644 index 000000000000..55bce67ea3f6 --- /dev/null +++ b/drivers/soc/rockchip/aoa_middleware/aoa_mmap.c @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Rockchip AOA Memory Mapping Driver + * + * Copyright (c) 2025 Rockchip Electronics Co., Ltd. + * Author: Xing Zheng + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aoa_mmap.h" + +#define DEVICE_NAME "aoa-mmap" +#define AOA_MMAP_IOC_MAGIC 'a' +#define AOA_MMAP_IOC_GET_INFO _IOR(AOA_MMAP_IOC_MAGIC, 1, struct aoa_mmap_info) + +struct aoa_mmap_info { + __u32 phys_addr; + __u32 size; +}; + +struct aoa_mmap_dev { + struct device *dev; /* dev structure of platform device */ + void __iomem *kvirt; /* kernel virtual address, obtained by memremap */ + phys_addr_t phys; /* fixed physical start (0x3ff20000) */ + u32 size; /* size 64KB */ + struct miscdevice misc; +}; + +static int aoa_mmap_open(struct inode *inode, struct file *file) +{ + struct miscdevice *misc = file->private_data; + struct aoa_mmap_dev *am_d = container_of(misc, struct aoa_mmap_dev, misc); + + file->private_data = am_d; + return 0; +} + +static int aoa_mmap_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static int aoa_mmap_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct aoa_mmap_dev *am_d = file->private_data; + unsigned long length = vma->vm_end - vma->vm_start; + + /* The length cannot exceed the reserved size */ + if (length > am_d->size) + return -EINVAL; + + /* Force Non-Cacheable */ + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + /* Mapping to user space by physical address */ + return remap_pfn_range(vma, vma->vm_start, am_d->phys >> PAGE_SHIFT, + length, vma->vm_page_prot); +} + +static long aoa_mmap_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct aoa_mmap_dev *am_d = file->private_data; + struct aoa_mmap_info info; + + if (unlikely(!am_d)) + return -ENODEV; + if (IS_ERR(am_d)) + return PTR_ERR(am_d); + + switch (cmd) { + case AOA_MMAP_IOC_GET_INFO: + info.phys_addr = am_d->phys; + info.size = am_d->size; + if (copy_to_user((struct aoa_mmap_info __user *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + default: + return -EINVAL; + } +} + +static const struct file_operations aoa_mmap_fops = { + .owner = THIS_MODULE, + .open = aoa_mmap_open, + .release = aoa_mmap_release, + .mmap = aoa_mmap_mmap, + .unlocked_ioctl = aoa_mmap_ioctl, +}; + +int aoa_mmap_probe(struct platform_device *pdev) +{ + struct aoa_mmap_dev *am_d; + struct resource res; + struct device_node *res_node; + int ret; + + am_d = devm_kzalloc(&pdev->dev, sizeof(*am_d), GFP_KERNEL); + if (!am_d) + return -ENOMEM; + am_d->dev = &pdev->dev; + + res_node = of_parse_phandle(pdev->dev.of_node, "memory-region", 0); + if (!res_node) { + dev_err(&pdev->dev, "failed to get memory region node\n"); + return -ENODEV; + } + + ret = of_address_to_resource(res_node, 0, &res); + of_node_put(res_node); + if (ret) { + dev_err(&pdev->dev, "failed to get reserved region address\n"); + return -ENODEV; + } + + am_d->phys = res.start; + am_d->size = resource_size(&res); + + /* ioremap the SRAM area (Non-Cacheable Device memory) */ + am_d->kvirt = devm_ioremap(am_d->dev, am_d->phys, am_d->size); + if (!am_d->kvirt) { + dev_err(am_d->dev, "ioremap failed\n"); + return -ENOMEM; + } + + am_d->misc.minor = MISC_DYNAMIC_MINOR; + am_d->misc.name = DEVICE_NAME; + am_d->misc.fops = &aoa_mmap_fops; + + ret = misc_register(&am_d->misc); + if (ret) { + dev_err(am_d->dev, "misc_register failed: %d\n", ret); + return ret; + } + + platform_set_drvdata(pdev, am_d); + dev_info(am_d->dev, "aoa_mmap_mem: mapped phys=%pa size=%u\n", + &am_d->phys, am_d->size); + return 0; +} + +int aoa_mmap_remove(struct platform_device *pdev) +{ + struct aoa_mmap_dev *am_d = platform_get_drvdata(pdev); + + misc_deregister(&am_d->misc); + return 0; +} diff --git a/drivers/soc/rockchip/aoa_middleware/aoa_mmap.h b/drivers/soc/rockchip/aoa_middleware/aoa_mmap.h new file mode 100644 index 000000000000..5e0e7438dda7 --- /dev/null +++ b/drivers/soc/rockchip/aoa_middleware/aoa_mmap.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Rockchip AOA Memory Mapping Driver + * + * Copyright (c) 2025 Rockchip Electronics Co., Ltd. + * Author: Xing Zheng + */ + +#ifndef __AOA_MMAP_H__ +#define __AOA_MMAP_H__ + +int aoa_mmap_probe(struct platform_device *pdev); +int aoa_mmap_remove(struct platform_device *pdev); + +#endif /* __AOA_MMAP_H__ */ diff --git a/drivers/soc/rockchip/aoa_middleware/lp_rkdma.c b/drivers/soc/rockchip/aoa_middleware/lp_rkdma.c new file mode 100644 index 000000000000..4f0e938539ca --- /dev/null +++ b/drivers/soc/rockchip/aoa_middleware/lp_rkdma.c @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Rockchip Low Power DMA Driver + + * Copyright (c) 2025 Rockchip Electronics Co., Ltd. + * Author: Xing Zheng + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "lp_rkdma.h" +#include "aoa_middleware.h" + +#define DRIVER_NAME "lp-rkdma" +#define DMA_MAX_SIZE (0x1000000) +#define LLI_BLOCK_SIZE (SZ_4K) +#define RK_DMA_VER(major, minor) (((major) << 16) | ((minor) << 0)) + +#define HIWORD_UPDATE(v, h, l) (((v) << (l)) | (GENMASK((h), (l)) << 16)) +#define GENMASK_VAL(v, h, l) (((v) & GENMASK(h, l)) >> l) + +#define RK_DMA_CMN_GROUP_SIZE 0x100 +#define RK_DMA_LCH_GROUP_SIZE 0x40 + +#define RK_DMA_CMN_REG(x) (d->base + (x)) +#define RK_DMA_LCH_REG(x) (l->base + (x)) +#define RK_DMA_LCHn_REG(n, x) (d->base + RK_DMA_CMN_GROUP_SIZE + \ + (RK_DMA_LCH_GROUP_SIZE * (n)) + (x)) + +/* RK_DMA Common Register Define */ +#define RK_DMA_CMN_VER RK_DMA_CMN_REG(0x0000) /* Address Offset: 0x0000 */ +#define RK_DMA_CMN_CFG RK_DMA_CMN_REG(0x0004) /* Address Offset: 0x0004 */ +#define RK_DMA_CMN_CTL0 RK_DMA_CMN_REG(0x0008) /* Address Offset: 0x0008 */ +#define RK_DMA_CMN_CTL1 RK_DMA_CMN_REG(0x000c) /* Address Offset: 0x000C */ +#define RK_DMA_CMN_AXICTL RK_DMA_CMN_REG(0x0010) /* Address Offset: 0x0010 */ +#define RK_DMA_CMN_DYNCTL RK_DMA_CMN_REG(0x0014) /* Address Offset: 0x0014 */ +#define RK_DMA_CMN_IS0 RK_DMA_CMN_REG(0x0018) /* Address Offset: 0x0018 */ +#define RK_DMA_CMN_IS1 RK_DMA_CMN_REG(0x001c) /* Address Offset: 0x001C */ +#define RK_DMA_CMN_CAP0 RK_DMA_CMN_REG(0x0030) /* Address Offset: 0x0030 */ +#define RK_DMA_CMN_CAP1 RK_DMA_CMN_REG(0x0034) /* Address Offset: 0x0034 */ +#define RK_DMA_CMN_PCH_EN RK_DMA_CMN_REG(0x0040) /* Address Offset: 0x0040 */ +#define RK_DMA_CMN_PCH_SEN RK_DMA_CMN_REG(0x0044) /* Address Offset: 0x0044 */ + +/* RK_DMA_Logic Channel Register Define */ +#define RK_DMA_LCH_CTL0 RK_DMA_LCH_REG(0x0000) /* Address Offset: 0x0000 */ +#define RK_DMA_LCH_CTL1 RK_DMA_LCH_REG(0x0004) /* Address Offset: 0x0004 */ +#define RK_DMA_LCH_CMDBA RK_DMA_LCH_REG(0x0008) /* Address Offset: 0x0008 */ +#define RK_DMA_LCH_TRF_CMD RK_DMA_LCH_REG(0x000c) /* Address Offset: 0x000C */ +#define RK_DMA_LCH_CMDBA_HIGH RK_DMA_LCH_REG(0x0010) /* Address Offset: 0x0010 */ +#define RK_DMA_LCH_IS RK_DMA_LCH_REG(0x0014) /* Address Offset: 0x0014 */ +#define RK_DMA_LCH_IE RK_DMA_LCH_REG(0x0018) /* Address Offset: 0x0018 */ +#define RK_DMA_LCH_DBGS0 RK_DMA_LCH_REG(0x001c) /* Address Offset: 0x001C */ +#define RK_DMA_LCH_DBGC0 RK_DMA_LCH_REG(0x0020) /* Address Offset: 0x0020 */ +#define RK_DMA_LCH_LLI_CNT RK_DMA_LCH_REG(0x0030) /* Address Offset: 0x0030 */ + +/* CMN_VER */ +#define CMN_VER_MAJOR(v) GENMASK_VAL(v, 31, 16) +#define CMN_VER_MINOR(v) GENMASK_VAL(v, 15, 0) + +/* CMN_CFG */ +#define CMN_CFG_EN HIWORD_UPDATE(1, 0, 0) +#define CMN_CFG_DIS HIWORD_UPDATE(0, 0, 0) +#define CMN_CFG_SRST HIWORD_UPDATE(1, 1, 1) +#define CMN_CFG_IE_EN HIWORD_UPDATE(1, 2, 2) +#define CMN_CFG_IE_DIS HIWORD_UPDATE(0, 2, 2) + +/* CMN_CAP0 */ +#define CMN_LCH_NUM(v) (GENMASK_VAL(v, 5, 0) + 1) +#define CMN_PCH_NUM(v) (GENMASK_VAL(v, 11, 6) + 1) +#define CMN_BUF_DEPTH(v) (GENMASK_VAL(v, 31, 21) + 1) + +/* CMN_CAP1 */ +#define CMN_AXI_SIZE(v) (1 << GENMASK_VAL(v, 2, 0)) +#define CMN_AXI_LEN(v) (GENMASK_VAL(v, 10, 3) + 1) +#define CMN_AXADDR_WIDTH(v) (32 + GENMASK_VAL(v, 18, 14) - 3) +#define CMN_AXOSR_SUP(v) (GENMASK_VAL(v, 23, 19) + 1) + +/* CMN_PCH_EN */ +#define CMN_PCH_EN(n) HIWORD_UPDATE(1, (n), (n)) +#define CMN_PCH_DIS(n) HIWORD_UPDATE(0, (n), (n)) + +struct lp_rkdma_lch { + void __iomem *base; + u32 id; +}; + +struct lp_rkdma_dev { + struct device *dev; + struct lp_rkdma_lch *lch; + struct clk_bulk_data *clks; + void __iomem *base; + int irq; + int num_clks; + u32 bus_width; + u32 buf_dep; + u32 dma_channels; + u32 dma_requests; + u32 version; +}; + +static int lp_rkdma_init(struct lp_rkdma_dev *d) +{ + int i, lch, pch, buswidth, maxburst, dep, addrwidth; + u32 cap0, cap1, ver; + + writel(CMN_CFG_EN | CMN_CFG_IE_EN, RK_DMA_CMN_CFG); + + ver = readl(RK_DMA_CMN_VER); + cap0 = readl(RK_DMA_CMN_CAP0); + cap1 = readl(RK_DMA_CMN_CAP1); + + lch = CMN_LCH_NUM(cap0); + pch = CMN_PCH_NUM(cap0); + dep = CMN_BUF_DEPTH(cap0); + + addrwidth = CMN_AXADDR_WIDTH(cap1); + buswidth = CMN_AXI_SIZE(cap1); + maxburst = CMN_AXI_LEN(cap1); + + d->version = ver; + d->bus_width = buswidth; + d->buf_dep = dep; + d->dma_channels = CMN_LCH_NUM(cap0); + d->dma_requests = CMN_LCH_NUM(cap0); + + writel(0xffffffff, RK_DMA_CMN_DYNCTL); + writel(0xffffffff, RK_DMA_CMN_IS0); + writel(0xffffffff, RK_DMA_CMN_IS1); + + for (i = 0; i < pch; i++) + writel(CMN_PCH_EN(i), RK_DMA_CMN_PCH_EN); + + dev_info(d->dev, "Lowpower RKDMA: NR_LCH-%d NR_PCH-%d PCH_BUF-%dx%dBytes AXI_LEN-%d ADDR-%dBits V%lu.%lu\n", + lch, pch, dep, buswidth, maxburst, addrwidth, + CMN_VER_MAJOR(ver), CMN_VER_MINOR(ver)); + + return 0; +} + +static irqreturn_t lp_rkdma_irq_handler(int irq, void *dev_id) +{ + struct lp_rkdma_dev *d = (struct lp_rkdma_dev *)dev_id; + struct lp_rkdma_lch *l = &d->lch[0]; + u64 is = 0, is_raw = 0; + u32 i = 0; + + aoa_middleware_dma_notifier(readl(RK_DMA_LCH_LLI_CNT)); + + is = readq(RK_DMA_CMN_IS0); + is_raw = is; + + while (is) { + i = __ffs64(is); + is &= ~BIT_ULL(i); + l = &d->lch[i]; + writel(readl(RK_DMA_LCH_IS), RK_DMA_LCH_IS); + } + + writeq(is_raw, RK_DMA_CMN_IS0); + return IRQ_HANDLED; +} + +int lp_rkdma_probe(struct platform_device *pdev) +{ + struct lp_rkdma_dev *d; + int i, ret; + + d = devm_kzalloc(&pdev->dev, sizeof(*d), GFP_KERNEL); + if (!d) + return -ENOMEM; + + d->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(d->base)) + return PTR_ERR(d->base); + + d->num_clks = devm_clk_bulk_get_all(&pdev->dev, &d->clks); + if (d->num_clks < 1) { + dev_err(&pdev->dev, "Failed to get clk\n"); + return -ENODEV; + } + + d->irq = platform_get_irq(pdev, 0); + + platform_set_drvdata(pdev, d); + + /* Enable clock before access registers */ + ret = clk_bulk_prepare_enable(d->num_clks, d->clks); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to enable clk: %d\n", ret); + return ret; + } + + lp_rkdma_init(d); + + /* init lch channel */ + d->lch = devm_kcalloc(&pdev->dev, d->dma_channels, sizeof(struct lp_rkdma_lch), GFP_KERNEL); + if (!d->lch) { + ret = -ENOMEM; + goto err_disable_clk; + } + + for (i = 0; i < d->dma_channels; i++) { + struct lp_rkdma_lch *l = &d->lch[i]; + + l->id = i; + l->base = RK_DMA_LCHn_REG(i, 0); + } + + return devm_request_irq(&pdev->dev, d->irq, lp_rkdma_irq_handler, 0, dev_name(&pdev->dev), d); + +err_disable_clk: + clk_bulk_disable_unprepare(d->num_clks, d->clks); + return ret; +} + +int lp_rkdma_remove(struct platform_device *pdev) +{ + struct lp_rkdma_dev *d = platform_get_drvdata(pdev); + + clk_bulk_disable_unprepare(d->num_clks, d->clks); + return 0; +} diff --git a/drivers/soc/rockchip/aoa_middleware/lp_rkdma.h b/drivers/soc/rockchip/aoa_middleware/lp_rkdma.h new file mode 100644 index 000000000000..e79bcf1d4db5 --- /dev/null +++ b/drivers/soc/rockchip/aoa_middleware/lp_rkdma.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Rockchip Low Power DMA Driver + + * Copyright (c) 2025 Rockchip Electronics Co., Ltd. + * Author: Xing Zheng + */ + +#ifndef __LP_RKDMA_H__ +#define __LP_RKDMA_H__ + +int lp_rkdma_probe(struct platform_device *pdev); +int lp_rkdma_remove(struct platform_device *pdev); + +#endif /* __LP_RKDMA_H__ */ From ba3cbacee706ae81b7287905f16e6910154cddf2 Mon Sep 17 00:00:00 2001 From: Xing Zheng Date: Fri, 25 Jul 2025 18:21:46 +0800 Subject: [PATCH 20/28] arm64: dts: rockchip: rv1126b: Add aoa/dma-lp/aoa_mmap/aoa_sram for AOA feature Signed-off-by: Xing Zheng Change-Id: Ie5dfb9b8add09ffa4bb9a79a455c969c51811b4a --- arch/arm64/boot/dts/rockchip/rv1126b.dtsi | 29 +++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rv1126b.dtsi b/arch/arm64/boot/dts/rockchip/rv1126b.dtsi index fa046cdc5f6f..3c89c7feeab9 100644 --- a/arch/arm64/boot/dts/rockchip/rv1126b.dtsi +++ b/arch/arm64/boot/dts/rockchip/rv1126b.dtsi @@ -1629,6 +1629,16 @@ }; }; + dmac_lp: dma-controller@20880000 { + compatible = "rockchip,rv1126b-dma", "rockchip,dma"; + reg = <0x20880000 0x2000>; + interrupts = ; + clocks = <&xin24m>; + clock-names = "aclk"; + #dma-cells = <1>; + status = "disabled"; + }; + audio_codec_pmu: audio-codec@20898000 { compatible = "rockchip,rv1126b-codec", "rockchip,rk3506-codec"; reg = <0x20898000 0x1000>; @@ -1642,6 +1652,16 @@ status = "disabled"; }; + aoa: aoa@208b0000 { + compatible = "rockchip,rv1126b-aoa", "rockchip,aoa"; + reg = <0x208b0000 0x1000>; + interrupts = ; + resets = <&cru SRST_PRESETN_AOA_TDD>, <&cru SRST_PRESETN_AOA_FE>, + <&cru SRST_PRESETN_AOA_AAD>, <&cru SRST_PRESETN_AOA_APB>; + reset-names = "atd", "afe", "aad", "apb"; + status = "disabled"; + }; + fspi1: spi@208c0000 { compatible = "rockchip,rv1126b-fspi", "rockchip,fspi"; reg = <0x208c0000 0x4000>; @@ -3556,6 +3576,15 @@ <0x22710000 0x1000>; }; + aoa_sram: sram@3ff20000 { + compatible = "mmio-sram"; + reg = <0x3ff20000 0x10000>; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0x3ff20000 0x10000>; + status = "disabled"; + }; + system_sram: sram@3ffb0000 { compatible = "mmio-sram"; reg = <0x3ffb0000 0x10000>; From baa721578bef8af99e450813863cb9bf3f96ada2 Mon Sep 17 00:00:00 2001 From: Xing Zheng Date: Wed, 30 Jul 2025 11:30:46 +0800 Subject: [PATCH 21/28] arm64: dts: rockchip: rv1126b-aoa: Add aoa_middleware node and enable related modules for AOA feature Signed-off-by: Xing Zheng Change-Id: Iedf6208dc65461caf60b5598490fd6d1427ad378 --- arch/arm64/boot/dts/rockchip/rv1126b-aoa.dtsi | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 arch/arm64/boot/dts/rockchip/rv1126b-aoa.dtsi diff --git a/arch/arm64/boot/dts/rockchip/rv1126b-aoa.dtsi b/arch/arm64/boot/dts/rockchip/rv1126b-aoa.dtsi new file mode 100644 index 000000000000..f0a70eaf5177 --- /dev/null +++ b/arch/arm64/boot/dts/rockchip/rv1126b-aoa.dtsi @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2025 Rockchip Electronics Co., Ltd. + */ + +/ { + aoa_middleware { + compatible = "rockchip,aoa-middleware"; + memory-region = <&aoa_sram>; + rockchip,aoa = <&aoa>; + rockchip,dma = <&dmac_lp>; + }; +}; + +&dmac_lp { + /* Avoid being mapped by standard DMA drivers */ + compatible = "rockchip,rv1126b-dma-lp"; + status = "okay"; +}; + +&aoa { + status = "okay"; +}; + +&aoa_sram { + status = "okay"; +}; From 3c09428e5088a5ce1e0da1cf55cfc1b7d2937566 Mon Sep 17 00:00:00 2001 From: Xing Zheng Date: Mon, 28 Jul 2025 16:22:18 +0800 Subject: [PATCH 22/28] arm64: dts: rockchip: rv1126b-evb: enable AOA features Signed-off-by: Xing Zheng Change-Id: I082a440003acf701cd5b797028e073f5c87a4fe9 --- arch/arm64/boot/dts/rockchip/rv1126b-evb.dtsi | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/boot/dts/rockchip/rv1126b-evb.dtsi b/arch/arm64/boot/dts/rockchip/rv1126b-evb.dtsi index 9acb6da89aba..eb3879ca4c5f 100644 --- a/arch/arm64/boot/dts/rockchip/rv1126b-evb.dtsi +++ b/arch/arm64/boot/dts/rockchip/rv1126b-evb.dtsi @@ -6,6 +6,8 @@ #include #include +#include "rv1126b-aoa.dtsi" + / { chosen { bootargs = "earlycon=uart8250,mmio32,0x20810000 console=ttyFIQ0 rw root=PARTUUID=614e0000-0000 rootfstype=ext4 rootwait snd_soc_core.prealloc_buffer_size_kbytes=16 coherent_pool=32K"; From f9b09b3737c1b708553ce0d9cd7ffe6dc3da8bf6 Mon Sep 17 00:00:00 2001 From: Xing Zheng Date: Fri, 25 Jul 2025 18:19:06 +0800 Subject: [PATCH 23/28] ARM: configs: rv1126b-aoa.config: Add config and enable ROCKCHIP_AOA_MIDDLEWARE with module Signed-off-by: Xing Zheng Change-Id: I6b61313865fd7785ac28a7a6030755f1e245b897 --- arch/arm/configs/rv1126b-aoa.config | 1 + 1 file changed, 1 insertion(+) create mode 100644 arch/arm/configs/rv1126b-aoa.config diff --git a/arch/arm/configs/rv1126b-aoa.config b/arch/arm/configs/rv1126b-aoa.config new file mode 100644 index 000000000000..a39a9473371c --- /dev/null +++ b/arch/arm/configs/rv1126b-aoa.config @@ -0,0 +1 @@ +CONFIG_ROCKCHIP_AOA_MIDDLEWARE=m From 905a8088c7d5acc87a1a7aa07b68fe6cf058f810 Mon Sep 17 00:00:00 2001 From: Shengfei Xu Date: Thu, 31 Jul 2025 09:37:23 +0800 Subject: [PATCH 24/28] regulator: rk806: Modify the implementation of RK806 shutdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Configure the shutdown function and level by directly setting PWRCTRL1 register‌, bypassing the pinctrl subsystem. Implement PMIC reset by toggling the RESET pin‌ of PMIC through CPU GPIO control. Change-Id: I370fd20638ef0fa08bcb67a3304509dc058b99d6 Signed-off-by: Shengfei Xu --- drivers/regulator/rk806-regulator.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/regulator/rk806-regulator.c b/drivers/regulator/rk806-regulator.c index e42c234b802b..2672bf91dd77 100644 --- a/drivers/regulator/rk806-regulator.c +++ b/drivers/regulator/rk806-regulator.c @@ -1250,12 +1250,10 @@ static void rk806_regulator_shutdown(struct platform_device *pdev) if (system_state == SYSTEM_POWER_OFF) { rk806_shutdown_requence_config(rk806); - if ((rk806->pins->p) && (rk806->pins->power_off)) - pinctrl_select_state(rk806->pins->p, rk806->pins->power_off); + rk806_field_write(rk806, PWRCTRL1_FUN, PWRCTRL_NULL_FUN); + rk806_field_write(rk806, PWRCTRL1_POL, POL_HIGH); + rk806_field_write(rk806, PWRCTRL1_FUN, PWRCTRL_POWOFF_FUN); } - if (system_state == SYSTEM_RESTART) - if ((rk806->pins->p) && (rk806->pins->reset)) - pinctrl_select_state(rk806->pins->p, rk806->pins->reset); } static const struct platform_device_id rk806_regulator_id_table[] = { From 87bf9e13ce98ca439a2500fb2fb568f8726ecca0 Mon Sep 17 00:00:00 2001 From: Shengfei Xu Date: Thu, 31 Jul 2025 09:19:09 +0800 Subject: [PATCH 25/28] arm64: dts: rockchip: rk3588-rk806: Changing the implementation of pin functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The functional switching of pins is implemented by drivers directly manipulating registers‌. Change-Id: Ib703c24c9fdc17e0217c9b9243f0c190261d7739 Signed-off-by: Shengfei Xu --- arch/arm64/boot/dts/rockchip/rk3588-rk806-dual.dtsi | 8 ++------ arch/arm64/boot/dts/rockchip/rk3588-rk806-single.dtsi | 5 ++--- arch/arm64/boot/dts/rockchip/rk3588s-rk806-dual.dtsi | 9 ++------- 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3588-rk806-dual.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-rk806-dual.dtsi index 49ba5baf1a76..8ef88735348f 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-rk806-dual.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-rk806-dual.dtsi @@ -21,9 +21,8 @@ interrupt-parent = <&gpio0>; interrupts = <7 IRQ_TYPE_LEVEL_LOW>; - pinctrl-names = "default", "pmic-power-off"; - pinctrl-0 = <&pmic_pins>, <&rk806_dvs1_null>, <&rk806_dvs2_null>, <&rk806_dvs3_null>; - pinctrl-1 = <&rk806_dvs1_pwrdn>; + pinctrl-names = "default"; + pinctrl-0 = <&pmic_pins>; /* 2800mv-3500mv */ low_voltage_threshold = <3000>; @@ -411,9 +410,6 @@ interrupt-parent = <&gpio0>; interrupts = <7 IRQ_TYPE_LEVEL_LOW>; - pinctrl-names = "default"; - pinctrl-0 = <&rk806_slave_dvs1_null>, <&rk806_slave_dvs2_null>, <&rk806_slave_dvs3_null>; - /* 0: restart PMU; * 1: reset all the power off reset registers, * forcing the state to switch to ACTIVE mode; diff --git a/arch/arm64/boot/dts/rockchip/rk3588-rk806-single.dtsi b/arch/arm64/boot/dts/rockchip/rk3588-rk806-single.dtsi index a56f572c61e5..f62c001500f8 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588-rk806-single.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588-rk806-single.dtsi @@ -23,9 +23,8 @@ interrupt-parent = <&gpio0>; interrupts = <7 IRQ_TYPE_LEVEL_LOW>; - pinctrl-names = "default", "pmic-power-off"; - pinctrl-0 = <&pmic_pins>, <&rk806_dvs1_null>, <&rk806_dvs2_null>, <&rk806_dvs3_null>; - pinctrl-1 = <&rk806_dvs1_pwrdn>; + pinctrl-names = "default"; + pinctrl-0 = <&pmic_pins>; /* 2800mv-3500mv */ low_voltage_threshold = <3000>; diff --git a/arch/arm64/boot/dts/rockchip/rk3588s-rk806-dual.dtsi b/arch/arm64/boot/dts/rockchip/rk3588s-rk806-dual.dtsi index 9fe6af59ddc7..21b42ac6b8d2 100644 --- a/arch/arm64/boot/dts/rockchip/rk3588s-rk806-dual.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3588s-rk806-dual.dtsi @@ -21,9 +21,8 @@ interrupt-parent = <&gpio0>; interrupts = <7 IRQ_TYPE_LEVEL_LOW>; - pinctrl-names = "default", "pmic-power-off"; - pinctrl-0 = <&pmic_pins>, <&rk806_dvs1_null>, <&rk806_dvs2_null>, <&rk806_dvs3_null>; - pinctrl-1 = <&rk806_dvs1_pwrdn>; + pinctrl-names = "default"; + pinctrl-0 = <&pmic_pins>; /* 2800mv-3500mv */ low_voltage_threshold = <3000>; @@ -409,10 +408,6 @@ interrupt-parent = <&gpio0>; interrupts = <7 IRQ_TYPE_LEVEL_LOW>; - pinctrl-names = "default", "pmic-sleep"; - pinctrl-0 = <&rk806_slave_dvs1_null>, <&rk806_slave_dvs2_null>, <&rk806_slave_dvs3_null>; - pinctrl-1 = <&rk806_slave_dvs1_slp>, <&rk806_slave_dvs2_null>, <&rk806_slave_dvs3_null>; - /* 0: restart PMU; * 1: reset all the power off reset registers, * forcing the state to switch to ACTIVE mode; From 3e3408b05a7c7cc9be7fbda58270456469073b05 Mon Sep 17 00:00:00 2001 From: Shengfei Xu Date: Thu, 31 Jul 2025 09:56:22 +0800 Subject: [PATCH 26/28] arm64: dts: rockchip: rk3576-rk806: Changing the implementation of pin functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The functional switching of pins is implemented by drivers directly manipulating registers‌. Change-Id: Idfed4063feac123cb94b12680fe2c744c7dd692e Signed-off-by: Shengfei Xu --- arch/arm64/boot/dts/rockchip/rk3576-rk806.dtsi | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rk3576-rk806.dtsi b/arch/arm64/boot/dts/rockchip/rk3576-rk806.dtsi index 705d77527536..84746bcc56ef 100644 --- a/arch/arm64/boot/dts/rockchip/rk3576-rk806.dtsi +++ b/arch/arm64/boot/dts/rockchip/rk3576-rk806.dtsi @@ -17,9 +17,8 @@ interrupt-parent = <&gpio0>; interrupts = <6 IRQ_TYPE_LEVEL_LOW>; - pinctrl-names = "default", "pmic-power-off"; - pinctrl-0 = <&pmic_pins>, <&rk806_dvs1_null>, <&rk806_dvs2_null>, <&rk806_dvs3_null>; - pinctrl-1 = <&rk806_dvs1_pwrdn>; + pinctrl-names = "default"; + pinctrl-0 = <&pmic_pins>; /* 2800mv-3500mv */ low_voltage_threshold = <3000>; From 2c7f131ecb4b70642b4f8c74329aa55677f73c1a Mon Sep 17 00:00:00 2001 From: Andy Yan Date: Thu, 31 Jul 2025 16:48:35 +0800 Subject: [PATCH 27/28] drm/rockchip: vop2: Fix error handling for get dclk_src Fixes: f2b2283cd004 ("drm/rockchip: vop2: Support set dclk parent") Signed-off-by: Andy Yan Change-Id: If0bee52a63e45584c2f65f53411ff4667bc70f6d --- drivers/gpu/drm/rockchip/rockchip_drm_vop2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 735f86630ac4..caa054ae2569 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -15202,9 +15202,9 @@ static int vop2_create_crtc(struct vop2 *vop2, uint8_t enabled_vp_mask) snprintf(clk_name, sizeof(clk_name), "dclk_src_vp%d", vp->id); vp->dclk_parent = devm_clk_get_optional(vop2->dev, clk_name); - if (IS_ERR(vp->dclk)) { + if (IS_ERR(vp->dclk_parent)) { DRM_DEV_ERROR(vop2->dev, "failed to get %s\n", clk_name); - return PTR_ERR(vp->dclk); + return PTR_ERR(vp->dclk_parent); } crtc = &vp->rockchip_crtc.crtc; From 3d712791cdfaf3011a49ac70dca4fd25febec256 Mon Sep 17 00:00:00 2001 From: Cai YiWei Date: Tue, 29 Jul 2025 10:03:31 +0800 Subject: [PATCH 28/28] media: rockchip: isp: add iommu fault handler Change-Id: Ic0cf002567e00dbdf2170cfcb3f6ac9bc6924d31 Signed-off-by: Cai YiWei --- drivers/media/platform/rockchip/isp/hw.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/media/platform/rockchip/isp/hw.c b/drivers/media/platform/rockchip/isp/hw.c index d1a8f6ffa8d2..501821baa926 100644 --- a/drivers/media/platform/rockchip/isp/hw.c +++ b/drivers/media/platform/rockchip/isp/hw.c @@ -1014,9 +1014,25 @@ static const struct of_device_id rkisp_hw_of_match[] = { {}, }; -static inline bool is_iommu_enable(struct device *dev) +static int rkisp_iommu_fault_handle(struct iommu_domain *iommu, struct device *iommu_dev, + unsigned long iova, int status, void *arg) { + struct rkisp_hw_dev *hw_dev = arg; + + dev_err(iommu_dev, "fault addr:0x%08lx status:%x arg:%p\n", iova, status, arg); + if (!hw_dev) { + dev_err(iommu_dev, "pagefault without device to handle\n"); + return 0; + } + rockchip_iommu_mask_irq(hw_dev->dev); + return 0; +} + +static inline bool is_iommu_enable(struct rkisp_hw_dev *hw_dev) +{ + struct device *dev = hw_dev->dev; struct device_node *iommu; + struct iommu_domain *domain; iommu = of_parse_phandle(dev->of_node, "iommus", 0); if (!iommu) { @@ -1029,6 +1045,9 @@ static inline bool is_iommu_enable(struct device *dev) } of_node_put(iommu); + domain = iommu_get_domain_for_dev(dev); + if (domain) + iommu_set_fault_handler(domain, rkisp_iommu_fault_handle, hw_dev); return true; } @@ -1469,7 +1488,7 @@ static int rkisp_hw_probe(struct platform_device *pdev) hw_dev->is_dma_sg_ops = true; hw_dev->is_buf_init = false; hw_dev->is_shutdown = false; - hw_dev->is_mmu = is_iommu_enable(dev); + hw_dev->is_mmu = is_iommu_enable(hw_dev); ret = of_reserved_mem_device_init(dev); if (ret) { is_mem_reserved = false;