* [PATCH 1/2] drivers: media: i2c: s5kjn1: Add support for 19.2MHz MCLK
2026-08-01 14:31 [PATCH 0/2] Add Ultra Wide Camera Support for Nothing Phone (1) Danila Tikhonov
@ 2026-08-01 14:31 ` Danila Tikhonov
2026-08-01 14:39 ` Neko
2026-08-01 14:31 ` [PATCH 2/2] arm64: dts: qcom: sm7325-nothing-spacewar: Add ultra wide camera support Danila Tikhonov
1 sibling, 1 reply; 5+ messages in thread
From: Danila Tikhonov @ 2026-08-01 14:31 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Sakari Ailus, Vladimir Zapolskiy,
Mauro Carvalho Chehab, Eugene Lepshy, Vasiliy Doylov,
Achill Gilgenast, Luca Weiss
Cc: linux-arm-msm, devicetree, linux-kernel, linux-media,
~postmarketos/upstreaming, Danila Tikhonov
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 <danila@mainlining.org>
---
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
^ permalink raw reply related [flat|nested] 5+ messages in thread