Skip to content

Commit

Permalink
media: ov2740: Add support for 180 MHz link frequency
Browse files Browse the repository at this point in the history
On various Lenovo Thinkpad models with an ov2740 sensor the 360 MHz
link frequency is not supported.

Add support for 180 MHz link frequency, even though this has half the
pixel clock, this supports the same framerate by using half the VTS value
(significantly reducing the amount of empty lines send during vblank).

Normally if there are multiple link-frequencies then the sensor driver
choses the lowest link-frequency still usable for the chosen resolution.

In this case the board supports only 1 link-frequency. Which frequency
is supported is checked in ov2740_check_hwcfg() and then a different
set of supported_modes (using only the supported link-freq) is selected.

The register settings for this were taken from the ov2740 sensor driver
in the out of tree IPU6 driver:

https://github.com/intel/ipu6-drivers/

Signed-off-by: Hans de Goede <[email protected]>
Signed-off-by: Sakari Ailus <[email protected]>
Signed-off-by: Hans Verkuil <[email protected]>
  • Loading branch information
jwrdegoede authored and Hans Verkuil committed Dec 13, 2023
1 parent 4024107 commit 0677a2d
Showing 1 changed file with 239 additions and 24 deletions.
263 changes: 239 additions & 24 deletions drivers/media/i2c/ov2740.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <media/v4l2-fwnode.h>

#define OV2740_LINK_FREQ_360MHZ 360000000ULL
#define OV2740_LINK_FREQ_180MHZ 180000000ULL
#define OV2740_SCLK 72000000LL
#define OV2740_MCLK 19200000
#define OV2740_DATA_LANES 2
Expand All @@ -30,9 +31,6 @@

/* vertical-timings from sensor */
#define OV2740_REG_VTS 0x380e
#define OV2740_VTS_DEF 0x088a
#define OV2740_VTS_MIN 0x0460
#define OV2740_VTS_MAX 0x7fff

/* horizontal-timings from sensor */
#define OV2740_REG_HTS 0x380c
Expand Down Expand Up @@ -86,6 +84,7 @@ struct nvm_data {

enum {
OV2740_LINK_FREQ_360MHZ_INDEX,
OV2740_LINK_FREQ_180MHZ_INDEX,
};

struct ov2740_reg {
Expand Down Expand Up @@ -118,6 +117,9 @@ struct ov2740_mode {
/* Min vertical timining size */
u32 vts_min;

/* Max vertical timining size */
u32 vts_max;

/* Link frequency needed for this resolution */
u32 link_freq_index;

Expand All @@ -134,7 +136,18 @@ static const struct ov2740_reg mipi_data_rate_720mbps[] = {
{0x0312, 0x11},
};

static const struct ov2740_reg mode_1932x1092_regs[] = {
static const struct ov2740_reg mipi_data_rate_360mbps[] = {
{0x0103, 0x01},
{0x0302, 0x4b},
{0x0303, 0x01},
{0x030d, 0x4b},
{0x030e, 0x02},
{0x030a, 0x01},
{0x0312, 0x11},
{0x4837, 0x2c},
};

static const struct ov2740_reg mode_1932x1092_regs_360mhz[] = {
{0x3000, 0x00},
{0x3018, 0x32},
{0x3031, 0x0a},
Expand Down Expand Up @@ -287,6 +300,159 @@ static const struct ov2740_reg mode_1932x1092_regs[] = {
{0x3813, 0x01},
};

static const struct ov2740_reg mode_1932x1092_regs_180mhz[] = {
{0x3000, 0x00},
{0x3018, 0x32}, /* 0x32 for 2 lanes, 0x12 for 1 lane */
{0x3031, 0x0a},
{0x3080, 0x08},
{0x3083, 0xB4},
{0x3103, 0x00},
{0x3104, 0x01},
{0x3106, 0x01},
{0x3500, 0x00},
{0x3501, 0x44},
{0x3502, 0x40},
{0x3503, 0x88},
{0x3507, 0x00},
{0x3508, 0x00},
{0x3509, 0x80},
{0x350c, 0x00},
{0x350d, 0x80},
{0x3510, 0x00},
{0x3511, 0x00},
{0x3512, 0x20},
{0x3632, 0x00},
{0x3633, 0x10},
{0x3634, 0x10},
{0x3635, 0x10},
{0x3645, 0x13},
{0x3646, 0x81},
{0x3636, 0x10},
{0x3651, 0x0a},
{0x3656, 0x02},
{0x3659, 0x04},
{0x365a, 0xda},
{0x365b, 0xa2},
{0x365c, 0x04},
{0x365d, 0x1d},
{0x365e, 0x1a},
{0x3662, 0xd7},
{0x3667, 0x78},
{0x3669, 0x0a},
{0x366a, 0x92},
{0x3700, 0x54},
{0x3702, 0x10},
{0x3706, 0x42},
{0x3709, 0x30},
{0x370b, 0xc2},
{0x3714, 0x63},
{0x3715, 0x01},
{0x3716, 0x00},
{0x371a, 0x3e},
{0x3732, 0x0e},
{0x3733, 0x10},
{0x375f, 0x0e},
{0x3768, 0x30},
{0x3769, 0x44},
{0x376a, 0x22},
{0x377b, 0x20},
{0x377c, 0x00},
{0x377d, 0x0c},
{0x3798, 0x00},
{0x37a1, 0x55},
{0x37a8, 0x6d},
{0x37c2, 0x04},
{0x37c5, 0x00},
{0x37c8, 0x00},
{0x3800, 0x00},
{0x3801, 0x00},
{0x3802, 0x00},
{0x3803, 0x00},
{0x3804, 0x07},
{0x3805, 0x8f},
{0x3806, 0x04},
{0x3807, 0x47},
{0x3808, 0x07},
{0x3809, 0x88},
{0x380a, 0x04},
{0x380b, 0x40},
{0x380c, 0x08},
{0x380d, 0x70},
{0x380e, 0x04},
{0x380f, 0x56},
{0x3810, 0x00},
{0x3811, 0x04},
{0x3812, 0x00},
{0x3813, 0x04},
{0x3814, 0x01},
{0x3815, 0x01},
{0x3820, 0x80},
{0x3821, 0x46},
{0x3822, 0x84},
{0x3829, 0x00},
{0x382a, 0x01},
{0x382b, 0x01},
{0x3830, 0x04},
{0x3836, 0x01},
{0x3837, 0x08},
{0x3839, 0x01},
{0x383a, 0x00},
{0x383b, 0x08},
{0x383c, 0x00},
{0x3f0b, 0x00},
{0x4001, 0x20},
{0x4009, 0x07},
{0x4003, 0x10},
{0x4010, 0xe0},
{0x4016, 0x00},
{0x4017, 0x10},
{0x4044, 0x02},
{0x4304, 0x08},
{0x4307, 0x30},
{0x4320, 0x80},
{0x4322, 0x00},
{0x4323, 0x00},
{0x4324, 0x00},
{0x4325, 0x00},
{0x4326, 0x00},
{0x4327, 0x00},
{0x4328, 0x00},
{0x4329, 0x00},
{0x432c, 0x03},
{0x432d, 0x81},
{0x4501, 0x84},
{0x4502, 0x40},
{0x4503, 0x18},
{0x4504, 0x04},
{0x4508, 0x02},
{0x4601, 0x10},
{0x4800, 0x00},
{0x4816, 0x52},
{0x5000, 0x73}, /* 0x7f enable DPC */
{0x5001, 0x00},
{0x5005, 0x38},
{0x501e, 0x0d},
{0x5040, 0x00},
{0x5901, 0x00},
{0x3800, 0x00},
{0x3801, 0x00},
{0x3802, 0x00},
{0x3803, 0x00},
{0x3804, 0x07},
{0x3805, 0x8f},
{0x3806, 0x04},
{0x3807, 0x47},
{0x3808, 0x07},
{0x3809, 0x8c},
{0x380a, 0x04},
{0x380b, 0x44},
{0x3810, 0x00},
{0x3811, 0x00},
{0x3812, 0x00},
{0x3813, 0x01},
{0x4003, 0x40}, /* set Black level to 0x40 */
};

static const char * const ov2740_test_pattern_menu[] = {
"Disabled",
"Color Bar",
Expand All @@ -297,6 +463,7 @@ static const char * const ov2740_test_pattern_menu[] = {

static const s64 link_freq_menu_items[] = {
OV2740_LINK_FREQ_360MHZ,
OV2740_LINK_FREQ_180MHZ,
};

static const struct ov2740_link_freq_config link_freq_configs[] = {
Expand All @@ -306,23 +473,46 @@ static const struct ov2740_link_freq_config link_freq_configs[] = {
.regs = mipi_data_rate_720mbps,
}
},
[OV2740_LINK_FREQ_180MHZ_INDEX] = {
.reg_list = {
.num_of_regs = ARRAY_SIZE(mipi_data_rate_360mbps),
.regs = mipi_data_rate_360mbps,
}
},
};

static const struct ov2740_mode supported_modes[] = {
static const struct ov2740_mode supported_modes_360mhz[] = {
{
.width = 1932,
.height = 1092,
.hts = 2160,
.vts_def = OV2740_VTS_DEF,
.vts_min = OV2740_VTS_MIN,
.vts_min = 1120,
.vts_def = 2186,
.vts_max = 32767,
.reg_list = {
.num_of_regs = ARRAY_SIZE(mode_1932x1092_regs),
.regs = mode_1932x1092_regs,
.num_of_regs = ARRAY_SIZE(mode_1932x1092_regs_360mhz),
.regs = mode_1932x1092_regs_360mhz,
},
.link_freq_index = OV2740_LINK_FREQ_360MHZ_INDEX,
},
};

static const struct ov2740_mode supported_modes_180mhz[] = {
{
.width = 1932,
.height = 1092,
.hts = 2160,
.vts_min = 1110,
.vts_def = 1110,
.vts_max = 2047,
.reg_list = {
.num_of_regs = ARRAY_SIZE(mode_1932x1092_regs_180mhz),
.regs = mode_1932x1092_regs_180mhz,
},
.link_freq_index = OV2740_LINK_FREQ_180MHZ_INDEX,
},
};

struct ov2740 {
struct v4l2_subdev sd;
struct media_pad pad;
Expand All @@ -345,6 +535,10 @@ struct ov2740 {
/* NVM data inforamtion */
struct nvm_data *nvm;

/* Supported modes */
const struct ov2740_mode *supported_modes;
int supported_modes_count;

/* True if the device has been identified */
bool identified;
};
Expand Down Expand Up @@ -589,7 +783,7 @@ static int ov2740_init_controls(struct ov2740 *ov2740)
pixel_rate, 1, pixel_rate);

vblank_min = cur_mode->vts_min - cur_mode->height;
vblank_max = OV2740_VTS_MAX - cur_mode->height;
vblank_max = cur_mode->vts_max - cur_mode->height;
vblank_default = cur_mode->vts_def - cur_mode->height;
ov2740->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &ov2740_ctrl_ops,
V4L2_CID_VBLANK, vblank_min,
Expand Down Expand Up @@ -816,10 +1010,10 @@ static int ov2740_set_format(struct v4l2_subdev *sd,
const struct ov2740_mode *mode;
s32 vblank_def, h_blank;

mode = v4l2_find_nearest_size(supported_modes,
ARRAY_SIZE(supported_modes), width,
height, fmt->format.width,
fmt->format.height);
mode = v4l2_find_nearest_size(ov2740->supported_modes,
ov2740->supported_modes_count,
width, height,
fmt->format.width, fmt->format.height);

ov2740_update_pad_format(mode, &fmt->format);
*v4l2_subdev_state_get_format(sd_state, fmt->pad) = fmt->format;
Expand All @@ -836,7 +1030,7 @@ static int ov2740_set_format(struct v4l2_subdev *sd,
vblank_def = mode->vts_def - mode->height;
__v4l2_ctrl_modify_range(ov2740->vblank,
mode->vts_min - mode->height,
OV2740_VTS_MAX - mode->height, 1, vblank_def);
mode->vts_max - mode->height, 1, vblank_def);
__v4l2_ctrl_s_ctrl(ov2740->vblank, vblank_def);
h_blank = mode->hts - mode->width;
__v4l2_ctrl_modify_range(ov2740->hblank, h_blank, h_blank, 1, h_blank);
Expand All @@ -860,7 +1054,10 @@ static int ov2740_enum_frame_size(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_frame_size_enum *fse)
{
if (fse->index >= ARRAY_SIZE(supported_modes))
struct ov2740 *ov2740 = to_ov2740(sd);
const struct ov2740_mode *supported_modes = ov2740->supported_modes;

if (fse->index >= ov2740->supported_modes_count)
return -EINVAL;

if (fse->code != MEDIA_BUS_FMT_SGRBG10_1X10)
Expand All @@ -877,9 +1074,10 @@ static int ov2740_enum_frame_size(struct v4l2_subdev *sd,
static int ov2740_init_state(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state)
{
ov2740_update_pad_format(&supported_modes[0],
v4l2_subdev_state_get_format(sd_state, 0));
struct ov2740 *ov2740 = to_ov2740(sd);

ov2740_update_pad_format(&ov2740->supported_modes[0],
v4l2_subdev_state_get_format(sd_state, 0));
return 0;
}

Expand Down Expand Up @@ -909,6 +1107,8 @@ static const struct media_entity_operations ov2740_subdev_entity_ops = {

static int ov2740_check_hwcfg(struct device *dev)
{
struct v4l2_subdev *sd = dev_get_drvdata(dev);
struct ov2740 *ov2740 = to_ov2740(sd);
struct fwnode_handle *ep;
struct fwnode_handle *fwnode = dev_fwnode(dev);
struct v4l2_fwnode_endpoint bus_cfg = {
Expand Down Expand Up @@ -964,14 +1164,29 @@ static int ov2740_check_hwcfg(struct device *dev)
break;
}

if (j == bus_cfg.nr_of_link_frequencies) {
ret = dev_err_probe(dev, -EINVAL,
"no link frequency %lld supported\n",
link_freq_menu_items[i]);
goto check_hwcfg_error;
if (j == bus_cfg.nr_of_link_frequencies)
continue;

switch (i) {
case OV2740_LINK_FREQ_360MHZ_INDEX:
ov2740->supported_modes = supported_modes_360mhz;
ov2740->supported_modes_count =
ARRAY_SIZE(supported_modes_360mhz);
break;
case OV2740_LINK_FREQ_180MHZ_INDEX:
ov2740->supported_modes = supported_modes_180mhz;
ov2740->supported_modes_count =
ARRAY_SIZE(supported_modes_180mhz);
break;
}

break; /* Prefer modes from first available link-freq */
}

if (!ov2740->supported_modes)
ret = dev_err_probe(dev, -EINVAL,
"no supported link frequencies\n");

check_hwcfg_error:
v4l2_fwnode_endpoint_free(&bus_cfg);

Expand Down Expand Up @@ -1133,7 +1348,7 @@ static int ov2740_probe(struct i2c_client *client)
}
}

ov2740->cur_mode = &supported_modes[0];
ov2740->cur_mode = &ov2740->supported_modes[0];
ret = ov2740_init_controls(ov2740);
if (ret) {
dev_err_probe(dev, ret, "failed to init controls\n");
Expand Down

0 comments on commit 0677a2d

Please sign in to comment.