mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-23 15:32:33 +00:00
267 lines
7.3 KiB
Diff
267 lines
7.3 KiB
Diff
|
From c16cebe5ebfb309074ff7cc9ddeaf37724d31512 Mon Sep 17 00:00:00 2001
|
||
|
From: Daniel Scally <djrscally@gmail.com>
|
||
|
Date: Tue, 15 Feb 2022 23:07:32 +0000
|
||
|
Subject: [PATCH] media: i2c: Add ov7251_pll_configure()
|
||
|
|
||
|
Rather than having the pll settings hidden inside mode blobs, define
|
||
|
them in structs and use a dedicated function to set them. This makes
|
||
|
it simpler to extend the driver to support other external clock
|
||
|
frequencies.
|
||
|
|
||
|
Signed-off-by: Daniel Scally <djrscally@gmail.com>
|
||
|
---
|
||
|
drivers/media/i2c/ov7251.c | 165 ++++++++++++++++++++++++++++++-------
|
||
|
1 file changed, 135 insertions(+), 30 deletions(-)
|
||
|
|
||
|
--- a/drivers/media/i2c/ov7251.c
|
||
|
+++ b/drivers/media/i2c/ov7251.c
|
||
|
@@ -42,6 +42,16 @@
|
||
|
#define OV7251_TIMING_FORMAT2_MIRROR BIT(2)
|
||
|
#define OV7251_PRE_ISP_00 0x5e00
|
||
|
#define OV7251_PRE_ISP_00_TEST_PATTERN BIT(7)
|
||
|
+#define OV7251_PLL1_PRE_DIV_REG 0x30b4
|
||
|
+#define OV7251_PLL1_MULT_REG 0x30b3
|
||
|
+#define OV7251_PLL1_DIVIDER_REG 0x30b1
|
||
|
+#define OV7251_PLL1_PIX_DIV_REG 0x30b0
|
||
|
+#define OV7251_PLL1_MIPI_DIV_REG 0x30b5
|
||
|
+#define OV7251_PLL2_PRE_DIV_REG 0x3098
|
||
|
+#define OV7251_PLL2_MULT_REG 0x3099
|
||
|
+#define OV7251_PLL2_DIVIDER_REG 0x309d
|
||
|
+#define OV7251_PLL2_SYS_DIV_REG 0x309a
|
||
|
+#define OV7251_PLL2_ADC_DIV_REG 0x309b
|
||
|
|
||
|
struct reg_value {
|
||
|
u16 reg;
|
||
|
@@ -60,6 +70,27 @@ struct ov7251_mode_info {
|
||
|
struct v4l2_fract timeperframe;
|
||
|
};
|
||
|
|
||
|
+struct ov7251_pll1_config {
|
||
|
+ unsigned int pre_div;
|
||
|
+ unsigned int mult;
|
||
|
+ unsigned int div;
|
||
|
+ unsigned int pix_div;
|
||
|
+ unsigned int mipi_div;
|
||
|
+};
|
||
|
+
|
||
|
+struct ov7251_pll2_config {
|
||
|
+ unsigned int pre_div;
|
||
|
+ unsigned int mult;
|
||
|
+ unsigned int div;
|
||
|
+ unsigned int sys_div;
|
||
|
+ unsigned int adc_div;
|
||
|
+};
|
||
|
+
|
||
|
+struct ov7251_pll_configs {
|
||
|
+ const struct ov7251_pll1_config *pll1;
|
||
|
+ const struct ov7251_pll2_config *pll2;
|
||
|
+};
|
||
|
+
|
||
|
struct ov7251 {
|
||
|
struct i2c_client *i2c_client;
|
||
|
struct device *dev;
|
||
|
@@ -71,6 +102,8 @@ struct ov7251 {
|
||
|
struct clk *xclk;
|
||
|
u32 xclk_freq;
|
||
|
|
||
|
+ const struct ov7251_pll_configs *pll_configs;
|
||
|
+
|
||
|
struct regulator *io_regulator;
|
||
|
struct regulator *core_regulator;
|
||
|
struct regulator *analog_regulator;
|
||
|
@@ -100,6 +133,36 @@ static inline struct ov7251 *to_ov7251(s
|
||
|
return container_of(sd, struct ov7251, sd);
|
||
|
}
|
||
|
|
||
|
+enum xclk_rate {
|
||
|
+ OV7251_24_MHZ,
|
||
|
+ OV7251_NUM_SUPPORTED_RATES
|
||
|
+};
|
||
|
+
|
||
|
+static const struct ov7251_pll1_config ov7251_pll1_config_24_mhz = {
|
||
|
+ .pre_div = 0x03,
|
||
|
+ .mult = 0x64,
|
||
|
+ .div = 0x01,
|
||
|
+ .pix_div = 0x0a,
|
||
|
+ .mipi_div = 0x05
|
||
|
+};
|
||
|
+
|
||
|
+static const struct ov7251_pll2_config ov7251_pll2_config_24_mhz = {
|
||
|
+ .pre_div = 0x04,
|
||
|
+ .mult = 0x28,
|
||
|
+ .div = 0x00,
|
||
|
+ .sys_div = 0x05,
|
||
|
+ .adc_div = 0x04
|
||
|
+};
|
||
|
+
|
||
|
+static const struct ov7251_pll_configs ov7251_pll_configs_24_mhz = {
|
||
|
+ .pll1 = &ov7251_pll1_config_24_mhz,
|
||
|
+ .pll2 = &ov7251_pll2_config_24_mhz
|
||
|
+};
|
||
|
+
|
||
|
+static const struct ov7251_pll_configs *ov7251_pll_configs[] = {
|
||
|
+ [OV7251_24_MHZ] = &ov7251_pll_configs_24_mhz
|
||
|
+};
|
||
|
+
|
||
|
static const struct reg_value ov7251_global_init_setting[] = {
|
||
|
{ 0x0103, 0x01 },
|
||
|
{ 0x303b, 0x02 },
|
||
|
@@ -118,16 +181,6 @@ static const struct reg_value ov7251_set
|
||
|
{ 0x301c, 0xf0 },
|
||
|
{ 0x3023, 0x05 },
|
||
|
{ 0x3037, 0xf0 },
|
||
|
- { 0x3098, 0x04 }, /* pll2 pre divider */
|
||
|
- { 0x3099, 0x28 }, /* pll2 multiplier */
|
||
|
- { 0x309a, 0x05 }, /* pll2 sys divider */
|
||
|
- { 0x309b, 0x04 }, /* pll2 adc divider */
|
||
|
- { 0x309d, 0x00 }, /* pll2 divider */
|
||
|
- { 0x30b0, 0x0a }, /* pll1 pix divider */
|
||
|
- { 0x30b1, 0x01 }, /* pll1 divider */
|
||
|
- { 0x30b3, 0x64 }, /* pll1 multiplier */
|
||
|
- { 0x30b4, 0x03 }, /* pll1 pre divider */
|
||
|
- { 0x30b5, 0x05 }, /* pll1 mipi divider */
|
||
|
{ 0x3106, 0xda },
|
||
|
{ 0x3503, 0x07 },
|
||
|
{ 0x3509, 0x10 },
|
||
|
@@ -256,16 +309,6 @@ static const struct reg_value ov7251_set
|
||
|
{ 0x301c, 0x00 },
|
||
|
{ 0x3023, 0x05 },
|
||
|
{ 0x3037, 0xf0 },
|
||
|
- { 0x3098, 0x04 }, /* pll2 pre divider */
|
||
|
- { 0x3099, 0x28 }, /* pll2 multiplier */
|
||
|
- { 0x309a, 0x05 }, /* pll2 sys divider */
|
||
|
- { 0x309b, 0x04 }, /* pll2 adc divider */
|
||
|
- { 0x309d, 0x00 }, /* pll2 divider */
|
||
|
- { 0x30b0, 0x0a }, /* pll1 pix divider */
|
||
|
- { 0x30b1, 0x01 }, /* pll1 divider */
|
||
|
- { 0x30b3, 0x64 }, /* pll1 multiplier */
|
||
|
- { 0x30b4, 0x03 }, /* pll1 pre divider */
|
||
|
- { 0x30b5, 0x05 }, /* pll1 mipi divider */
|
||
|
{ 0x3106, 0xda },
|
||
|
{ 0x3503, 0x07 },
|
||
|
{ 0x3509, 0x10 },
|
||
|
@@ -394,16 +437,6 @@ static const struct reg_value ov7251_set
|
||
|
{ 0x301c, 0x00 },
|
||
|
{ 0x3023, 0x05 },
|
||
|
{ 0x3037, 0xf0 },
|
||
|
- { 0x3098, 0x04 }, /* pll2 pre divider */
|
||
|
- { 0x3099, 0x28 }, /* pll2 multiplier */
|
||
|
- { 0x309a, 0x05 }, /* pll2 sys divider */
|
||
|
- { 0x309b, 0x04 }, /* pll2 adc divider */
|
||
|
- { 0x309d, 0x00 }, /* pll2 divider */
|
||
|
- { 0x30b0, 0x0a }, /* pll1 pix divider */
|
||
|
- { 0x30b1, 0x01 }, /* pll1 divider */
|
||
|
- { 0x30b3, 0x64 }, /* pll1 multiplier */
|
||
|
- { 0x30b4, 0x03 }, /* pll1 pre divider */
|
||
|
- { 0x30b5, 0x05 }, /* pll1 mipi divider */
|
||
|
{ 0x3106, 0xda },
|
||
|
{ 0x3503, 0x07 },
|
||
|
{ 0x3509, 0x10 },
|
||
|
@@ -519,6 +552,10 @@ static const struct reg_value ov7251_set
|
||
|
{ 0x5001, 0x80 },
|
||
|
};
|
||
|
|
||
|
+static const unsigned long supported_xclk_rates[] = {
|
||
|
+ [OV7251_24_MHZ] = 24000000,
|
||
|
+};
|
||
|
+
|
||
|
static const s64 link_freq[] = {
|
||
|
240000000,
|
||
|
};
|
||
|
@@ -692,6 +729,63 @@ static int ov7251_read_reg(struct ov7251
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
+static int ov7251_pll_configure(struct ov7251 *ov7251)
|
||
|
+{
|
||
|
+ const struct ov7251_pll_configs *configs;
|
||
|
+ int ret;
|
||
|
+
|
||
|
+ configs = ov7251->pll_configs;
|
||
|
+
|
||
|
+ ret = ov7251_write_reg(ov7251, OV7251_PLL1_PRE_DIV_REG,
|
||
|
+ configs->pll1->pre_div);
|
||
|
+ if (ret < 0)
|
||
|
+ return ret;
|
||
|
+
|
||
|
+ ret = ov7251_write_reg(ov7251, OV7251_PLL1_MULT_REG,
|
||
|
+ configs->pll1->mult);
|
||
|
+ if (ret < 0)
|
||
|
+ return ret;
|
||
|
+ ret = ov7251_write_reg(ov7251, OV7251_PLL1_DIVIDER_REG,
|
||
|
+ configs->pll1->div);
|
||
|
+ if (ret < 0)
|
||
|
+ return ret;
|
||
|
+
|
||
|
+ ret = ov7251_write_reg(ov7251, OV7251_PLL1_PIX_DIV_REG,
|
||
|
+ configs->pll1->pix_div);
|
||
|
+ if (ret < 0)
|
||
|
+ return ret;
|
||
|
+
|
||
|
+ ret = ov7251_write_reg(ov7251, OV7251_PLL1_MIPI_DIV_REG,
|
||
|
+ configs->pll1->mipi_div);
|
||
|
+ if (ret < 0)
|
||
|
+ return ret;
|
||
|
+
|
||
|
+ ret = ov7251_write_reg(ov7251, OV7251_PLL2_PRE_DIV_REG,
|
||
|
+ configs->pll2->pre_div);
|
||
|
+ if (ret < 0)
|
||
|
+ return ret;
|
||
|
+
|
||
|
+ ret = ov7251_write_reg(ov7251, OV7251_PLL2_MULT_REG,
|
||
|
+ configs->pll2->mult);
|
||
|
+ if (ret < 0)
|
||
|
+ return ret;
|
||
|
+
|
||
|
+ ret = ov7251_write_reg(ov7251, OV7251_PLL2_DIVIDER_REG,
|
||
|
+ configs->pll2->div);
|
||
|
+ if (ret < 0)
|
||
|
+ return ret;
|
||
|
+
|
||
|
+ ret = ov7251_write_reg(ov7251, OV7251_PLL2_SYS_DIV_REG,
|
||
|
+ configs->pll2->sys_div);
|
||
|
+ if (ret < 0)
|
||
|
+ return ret;
|
||
|
+
|
||
|
+ ret = ov7251_write_reg(ov7251, OV7251_PLL2_ADC_DIV_REG,
|
||
|
+ configs->pll2->adc_div);
|
||
|
+
|
||
|
+ return ret;
|
||
|
+}
|
||
|
+
|
||
|
static int ov7251_set_exposure(struct ov7251 *ov7251, s32 exposure)
|
||
|
{
|
||
|
u16 reg;
|
||
|
@@ -1143,6 +1237,11 @@ static int ov7251_s_stream(struct v4l2_s
|
||
|
mutex_lock(&ov7251->lock);
|
||
|
|
||
|
if (enable) {
|
||
|
+ ret = ov7251_pll_configure(ov7251);
|
||
|
+ if (ret)
|
||
|
+ return dev_err_probe(ov7251->dev, ret,
|
||
|
+ "error configuring PLLs\n");
|
||
|
+
|
||
|
ret = ov7251_set_register_array(ov7251,
|
||
|
ov7251->current_mode->data,
|
||
|
ov7251->current_mode->data_size);
|
||
|
@@ -1326,6 +1425,7 @@ static int ov7251_probe(struct i2c_clien
|
||
|
struct ov7251 *ov7251;
|
||
|
u8 chip_id_high, chip_id_low, chip_rev;
|
||
|
int ret;
|
||
|
+ int i;
|
||
|
|
||
|
ov7251 = devm_kzalloc(dev, sizeof(struct ov7251), GFP_KERNEL);
|
||
|
if (!ov7251)
|
||
|
@@ -1364,6 +1464,11 @@ static int ov7251_probe(struct i2c_clien
|
||
|
dev_err(dev, "could not set xclk frequency\n");
|
||
|
return ret;
|
||
|
}
|
||
|
+ for (i = 0; i < ARRAY_SIZE(supported_xclk_rates); i++)
|
||
|
+ if (ov7251->xclk_freq == supported_xclk_rates[i])
|
||
|
+ break;
|
||
|
+
|
||
|
+ ov7251->pll_configs = ov7251_pll_configs[i];
|
||
|
|
||
|
ov7251->io_regulator = devm_regulator_get(dev, "vdddo");
|
||
|
if (IS_ERR(ov7251->io_regulator)) {
|