[PATCH 1/2] drivers: media: i2c: s5kjn1: Add support for 19.2MHz MCLK
Danila Tikhonov <[email protected]> Sat, 1 Aug 2026 17:31:24 +0300
| Newsgroups | org.kernel.vger.linux-media,org.kernel.vger.linux-arm-msm,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
The S5KJN1 sensor supports a range of external clock frequencies (MCLK), while the driver currently only supports a 24MHz MCLK. The 19.2MHz clock is used by the S5KJN1 camera sensor on the Nothing Phone (1) aka sm7325-nothing-spacewar. Add support for this frequency. While splitting the clock settings into separate register sequences, add definitions for the known clock-related registers and values instead of using raw register addresses and magic values. Signed-off-by: Danila Tikhonov <[email protected]> --- drivers/media/i2c/s5kjn1.c | 183 ++++++++++++++++++++++++++++++------- 1 file changed, 151 insertions(+), 32 deletions(-) diff --git a/drivers/media/i2c/s5kjn1.c b/drivers/media/i2c/s5kjn1.c index a707cb740556..1036e629758f 100644 --- a/drivers/media/i2c/s5kjn1.c +++ b/drivers/media/i2c/s5kjn1.c @@ -15,6 +15,7 @@ #include <media/v4l2-fwnode.h> #define S5KJN1_LINK_FREQ_700MHZ (700ULL * HZ_PER_MHZ) +#define S5KJN1_MCLK_FREQ_19P2MHZ (19200 * HZ_PER_KHZ) #define S5KJN1_MCLK_FREQ_24MHZ (24 * HZ_PER_MHZ) #define S5KJN1_DATA_LANES 4 @@ -25,6 +26,24 @@ #define S5KJN1_REG_CTRL_MODE CCI_REG8(0x0100) #define S5KJN1_MODE_STREAMING BIT(0) +#define S5KJN1_REG_EXTCLK_INTEGER CCI_REG16(0x0136) +#define S5KJN1_REG_EXTCLK_FRACTION CCI_REG16(0x013e) +#define S5KJN1_REG_VT_PIX_CLK_DIV CCI_REG16(0x0300) +#define S5KJN1_REG_VT_SYS_CLK_DIV CCI_REG16(0x0302) +#define S5KJN1_REG_VT_PRE_PLL_CLK_DIV CCI_REG16(0x0304) +#define S5KJN1_REG_VT_PLL_MULTIPLIER CCI_REG16(0x0306) + +#define S5KJN1_EXTCLK_19P2MHZ_INTEGER 0x1300 +#define S5KJN1_EXTCLK_19P2MHZ_FRACTION 0x00c8 +#define S5KJN1_EXTCLK_24MHZ_INTEGER 0x1800 +#define S5KJN1_EXTCLK_24MHZ_FRACTION 0x0000 +#define S5KJN1_VT_PIX_CLK_DIV 0x0006 +#define S5KJN1_VT_SYS_CLK_DIV 0x0001 +#define S5KJN1_VT_PRE_PLL_DIV_19P2MHZ 0x0003 +#define S5KJN1_VT_PRE_PLL_DIV_24MHZ 0x0004 +#define S5KJN1_VT_PLL_MUL_19P2MHZ 0x0083 +#define S5KJN1_VT_PLL_MUL_24MHZ 0x008c + #define S5KJN1_REG_ORIENTATION CCI_REG8(0x0101) #define S5KJN1_VFLIP BIT(1) #define S5KJN1_HFLIP BIT(0) @@ -70,6 +89,17 @@ struct s5kjn1_reg_list { unsigned int num_regs; }; +enum s5kjn1_mclk { + S5KJN1_MCLK_19P2MHZ, + S5KJN1_MCLK_24MHZ, + S5KJN1_NUM_MCLKS, +}; + +static const unsigned long s5kjn1_mclk_freqs[S5KJN1_NUM_MCLKS] = { + [S5KJN1_MCLK_19P2MHZ] = S5KJN1_MCLK_FREQ_19P2MHZ, + [S5KJN1_MCLK_24MHZ] = S5KJN1_MCLK_FREQ_24MHZ, +}; + struct s5kjn1_mode { u32 width; /* Frame width in pixels */ u32 height; /* Frame height in pixels */ @@ -78,7 +108,10 @@ struct s5kjn1_mode { u32 exposure; /* Default exposure value */ u32 exposure_margin; /* Exposure margin */ - const struct s5kjn1_reg_list reg_list; /* Sensor register setting */ + /* Sensor register setting */ + const struct s5kjn1_reg_list mode_setup_reg_list; + const struct s5kjn1_reg_list clk_reg_list[S5KJN1_NUM_MCLKS]; + const struct s5kjn1_reg_list frame_timing_reg_list; }; static const char * const s5kjn1_test_pattern_menu[] = { @@ -93,6 +126,7 @@ struct s5kjn1 { struct device *dev; struct regmap *regmap; struct clk *mclk; + unsigned int mclk_index; struct gpio_desc *reset_gpio; struct regulator *afvdd; /* Autofocus actuator power */ struct regulator *vdda; /* Analog power */ @@ -147,7 +181,7 @@ static const struct cci_reg_sequence init_array_setting[] = { { CCI_REG16(0x011a), 0x0001 }, }; -static const struct cci_reg_sequence s5kjn1_4080x3072_30fps_mode[] = { +static const struct cci_reg_sequence s5kjn1_4080x3072_30fps_mode_setup[] = { { CCI_REG16(0x6028), 0x2400 }, { CCI_REG16(0x602a), 0x1a28 }, { CCI_REG16(0x6f12), 0x4c00 }, @@ -403,21 +437,39 @@ static const struct cci_reg_sequence s5kjn1_4080x3072_30fps_mode[] = { { CCI_REG16(0x0110), 0x1002 }, { CCI_REG16(0x0114), 0x0301 }, { CCI_REG16(0x0116), 0x3000 }, +}; - /* Clock settings */ - { CCI_REG16(0x0136), 0x1800 }, - { CCI_REG16(0x013e), 0x0000 }, - { CCI_REG16(0x0300), 0x0006 }, - { CCI_REG16(0x0302), 0x0001 }, - { CCI_REG16(0x0304), 0x0004 }, - { CCI_REG16(0x0306), 0x008c }, +static const struct cci_reg_sequence s5kjn1_4080x3072_30fps_19p2mhz_clk[] = { + { S5KJN1_REG_EXTCLK_INTEGER, S5KJN1_EXTCLK_19P2MHZ_INTEGER }, + { S5KJN1_REG_EXTCLK_FRACTION, S5KJN1_EXTCLK_19P2MHZ_FRACTION }, + { S5KJN1_REG_VT_PIX_CLK_DIV, S5KJN1_VT_PIX_CLK_DIV }, + { S5KJN1_REG_VT_SYS_CLK_DIV, S5KJN1_VT_SYS_CLK_DIV }, + { S5KJN1_REG_VT_PRE_PLL_CLK_DIV, S5KJN1_VT_PRE_PLL_DIV_19P2MHZ }, + { S5KJN1_REG_VT_PLL_MULTIPLIER, S5KJN1_VT_PLL_MUL_19P2MHZ }, + { CCI_REG16(0x0308), 0x0008 }, + { CCI_REG16(0x030a), 0x0001 }, + { CCI_REG16(0x030c), 0x0000 }, + { CCI_REG16(0x030e), 0x0003 }, + { CCI_REG16(0x0310), 0x0089 }, + { CCI_REG16(0x0312), 0x0000 }, +}; + +static const struct cci_reg_sequence s5kjn1_4080x3072_30fps_24mhz_clk[] = { + { S5KJN1_REG_EXTCLK_INTEGER, S5KJN1_EXTCLK_24MHZ_INTEGER }, + { S5KJN1_REG_EXTCLK_FRACTION, S5KJN1_EXTCLK_24MHZ_FRACTION }, + { S5KJN1_REG_VT_PIX_CLK_DIV, S5KJN1_VT_PIX_CLK_DIV }, + { S5KJN1_REG_VT_SYS_CLK_DIV, S5KJN1_VT_SYS_CLK_DIV }, + { S5KJN1_REG_VT_PRE_PLL_CLK_DIV, S5KJN1_VT_PRE_PLL_DIV_24MHZ }, + { S5KJN1_REG_VT_PLL_MULTIPLIER, S5KJN1_VT_PLL_MUL_24MHZ }, { CCI_REG16(0x0308), 0x0008 }, { CCI_REG16(0x030a), 0x0001 }, { CCI_REG16(0x030c), 0x0000 }, { CCI_REG16(0x030e), 0x0004 }, { CCI_REG16(0x0310), 0x0092 }, { CCI_REG16(0x0312), 0x0000 }, +}; +static const struct cci_reg_sequence s5kjn1_4080x3072_30fps_frame_timing[] = { { CCI_REG16(0x080e), 0x0000 }, { S5KJN1_REG_VTS, 0x10c0 }, { S5KJN1_REG_HTS, 0x1100 }, @@ -431,7 +483,7 @@ static const struct cci_reg_sequence s5kjn1_4080x3072_30fps_mode[] = { { CCI_REG16(0x0816), 0x1c00 }, }; -static const struct cci_reg_sequence s5kjn1_8160x6144_10fps_mode[] = { +static const struct cci_reg_sequence s5kjn1_8160x6144_10fps_mode_setup[] = { { CCI_REG16(0x6028), 0x2400 }, { CCI_REG16(0x602a), 0x1a28 }, { CCI_REG16(0x6f12), 0x4c00 }, @@ -687,21 +739,39 @@ static const struct cci_reg_sequence s5kjn1_8160x6144_10fps_mode[] = { { CCI_REG16(0x0110), 0x1002 }, { CCI_REG16(0x0114), 0x0300 }, { CCI_REG16(0x0116), 0x3000 }, +}; + +static const struct cci_reg_sequence s5kjn1_8160x6144_10fps_19p2mhz_clk[] = { + { S5KJN1_REG_EXTCLK_INTEGER, S5KJN1_EXTCLK_19P2MHZ_INTEGER }, + { S5KJN1_REG_EXTCLK_FRACTION, S5KJN1_EXTCLK_19P2MHZ_FRACTION }, + { S5KJN1_REG_VT_PIX_CLK_DIV, S5KJN1_VT_PIX_CLK_DIV }, + { S5KJN1_REG_VT_SYS_CLK_DIV, S5KJN1_VT_SYS_CLK_DIV }, + { S5KJN1_REG_VT_PRE_PLL_CLK_DIV, S5KJN1_VT_PRE_PLL_DIV_19P2MHZ }, + { S5KJN1_REG_VT_PLL_MULTIPLIER, S5KJN1_VT_PLL_MUL_19P2MHZ }, + { CCI_REG16(0x0308), 0x0008 }, + { CCI_REG16(0x030a), 0x0001 }, + { CCI_REG16(0x030c), 0x0000 }, + { CCI_REG16(0x030e), 0x0003 }, + { CCI_REG16(0x0310), 0x006d }, + { CCI_REG16(0x0312), 0x0000 }, +}; - /* Clock settings */ - { CCI_REG16(0x0136), 0x1800 }, - { CCI_REG16(0x013e), 0x0000 }, - { CCI_REG16(0x0300), 0x0006 }, - { CCI_REG16(0x0302), 0x0001 }, - { CCI_REG16(0x0304), 0x0004 }, - { CCI_REG16(0x0306), 0x008c }, +static const struct cci_reg_sequence s5kjn1_8160x6144_10fps_24mhz_clk[] = { + { S5KJN1_REG_EXTCLK_INTEGER, S5KJN1_EXTCLK_24MHZ_INTEGER }, + { S5KJN1_REG_EXTCLK_FRACTION, S5KJN1_EXTCLK_24MHZ_FRACTION }, + { S5KJN1_REG_VT_PIX_CLK_DIV, S5KJN1_VT_PIX_CLK_DIV }, + { S5KJN1_REG_VT_SYS_CLK_DIV, S5KJN1_VT_SYS_CLK_DIV }, + { S5KJN1_REG_VT_PRE_PLL_CLK_DIV, S5KJN1_VT_PRE_PLL_DIV_24MHZ }, + { S5KJN1_REG_VT_PLL_MULTIPLIER, S5KJN1_VT_PLL_MUL_24MHZ }, { CCI_REG16(0x0308), 0x0008 }, { CCI_REG16(0x030a), 0x0001 }, { CCI_REG16(0x030c), 0x0000 }, { CCI_REG16(0x030e), 0x0004 }, { CCI_REG16(0x0310), 0x0074 }, { CCI_REG16(0x0312), 0x0000 }, +}; +static const struct cci_reg_sequence s5kjn1_8160x6144_10fps_frame_timing[] = { { CCI_REG16(0x080e), 0x0000 }, { S5KJN1_REG_VTS, 0x1900 }, { S5KJN1_REG_HTS, 0x21f0 }, @@ -722,9 +792,23 @@ static const struct s5kjn1_mode s5kjn1_supported_modes[] = { .vts = 4288, .exposure = 3840, .exposure_margin = 22, - .reg_list = { - .regs = s5kjn1_4080x3072_30fps_mode, - .num_regs = ARRAY_SIZE(s5kjn1_4080x3072_30fps_mode), + .mode_setup_reg_list = { + .regs = s5kjn1_4080x3072_30fps_mode_setup, + .num_regs = ARRAY_SIZE(s5kjn1_4080x3072_30fps_mode_setup), + }, + .clk_reg_list = { + [S5KJN1_MCLK_19P2MHZ] = { + .regs = s5kjn1_4080x3072_30fps_19p2mhz_clk, + .num_regs = ARRAY_SIZE(s5kjn1_4080x3072_30fps_19p2mhz_clk), + }, + [S5KJN1_MCLK_24MHZ] = { + .regs = s5kjn1_4080x3072_30fps_24mhz_clk, + .num_regs = ARRAY_SIZE(s5kjn1_4080x3072_30fps_24mhz_clk), + }, + }, + .frame_timing_reg_list = { + .regs = s5kjn1_4080x3072_30fps_frame_timing, + .num_regs = ARRAY_SIZE(s5kjn1_4080x3072_30fps_frame_timing), }, }, { @@ -734,9 +818,23 @@ static const struct s5kjn1_mode s5kjn1_supported_modes[] = { .vts = 6400, .exposure = 6144, .exposure_margin = 44, - .reg_list = { - .regs = s5kjn1_8160x6144_10fps_mode, - .num_regs = ARRAY_SIZE(s5kjn1_8160x6144_10fps_mode), + .mode_setup_reg_list = { + .regs = s5kjn1_8160x6144_10fps_mode_setup, + .num_regs = ARRAY_SIZE(s5kjn1_8160x6144_10fps_mode_setup), + }, + .clk_reg_list = { + [S5KJN1_MCLK_19P2MHZ] = { + .regs = s5kjn1_8160x6144_10fps_19p2mhz_clk, + .num_regs = ARRAY_SIZE(s5kjn1_8160x6144_10fps_19p2mhz_clk), + }, + [S5KJN1_MCLK_24MHZ] = { + .regs = s5kjn1_8160x6144_10fps_24mhz_clk, + .num_regs = ARRAY_SIZE(s5kjn1_8160x6144_10fps_24mhz_clk), + }, + }, + .frame_timing_reg_list = { + .regs = s5kjn1_8160x6144_10fps_frame_timing, + .num_regs = ARRAY_SIZE(s5kjn1_8160x6144_10fps_frame_timing), }, }, }; @@ -894,9 +992,12 @@ static int s5kjn1_enable_streams(struct v4l2_subdev *sd, u64 streams_mask) { struct s5kjn1 *s5kjn1 = to_s5kjn1(sd); - const struct s5kjn1_reg_list *reg_list = &s5kjn1->mode->reg_list; + const struct s5kjn1_mode *mode = s5kjn1->mode; + const struct s5kjn1_reg_list *clk_reg_list; int ret; + clk_reg_list = &mode->clk_reg_list[s5kjn1->mclk_index]; + ret = pm_runtime_resume_and_get(s5kjn1->dev); if (ret) return ret; @@ -924,8 +1025,12 @@ static int s5kjn1_enable_streams(struct v4l2_subdev *sd, /* Sensor init settings */ cci_multi_reg_write(s5kjn1->regmap, init_array_setting, ARRAY_SIZE(init_array_setting), &ret); - cci_multi_reg_write(s5kjn1->regmap, reg_list->regs, - reg_list->num_regs, &ret); + cci_multi_reg_write(s5kjn1->regmap, mode->mode_setup_reg_list.regs, + mode->mode_setup_reg_list.num_regs, &ret); + cci_multi_reg_write(s5kjn1->regmap, clk_reg_list->regs, + clk_reg_list->num_regs, &ret); + cci_multi_reg_write(s5kjn1->regmap, mode->frame_timing_reg_list.regs, + mode->frame_timing_reg_list.num_regs, &ret); if (ret) goto error; @@ -1199,6 +1304,23 @@ static int s5kjn1_check_hwcfg(struct s5kjn1 *s5kjn1) return ret; } +static int s5kjn1_get_mclk_index(struct s5kjn1 *s5kjn1) +{ + unsigned long freq = clk_get_rate(s5kjn1->mclk); + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(s5kjn1_mclk_freqs); i++) { + if (freq == s5kjn1_mclk_freqs[i]) { + s5kjn1->mclk_index = i; + return 0; + } + } + + return dev_err_probe(s5kjn1->dev, -EINVAL, + "MCLK clock frequency %lu is not supported\n", + freq); +} + static int s5kjn1_power_on(struct device *dev) { struct v4l2_subdev *sd = dev_get_drvdata(dev); @@ -1290,7 +1412,6 @@ static int s5kjn1_power_off(struct device *dev) static int s5kjn1_probe(struct i2c_client *client) { struct s5kjn1 *s5kjn1; - unsigned long freq; int ret; s5kjn1 = devm_kzalloc(&client->dev, sizeof(*s5kjn1), GFP_KERNEL); @@ -1310,11 +1431,9 @@ static int s5kjn1_probe(struct i2c_client *client) return dev_err_probe(s5kjn1->dev, PTR_ERR(s5kjn1->mclk), "failed to get MCLK clock\n"); - freq = clk_get_rate(s5kjn1->mclk); - if (freq != S5KJN1_MCLK_FREQ_24MHZ) - return dev_err_probe(s5kjn1->dev, -EINVAL, - "MCLK clock frequency %lu is not supported\n", - freq); + ret = s5kjn1_get_mclk_index(s5kjn1); + if (ret) + return ret; ret = s5kjn1_check_hwcfg(s5kjn1); if (ret) -- 2.55.0