Add V4L2 controls (EXPOSURE/GAIN/VBLANK/HBLANK/PIXEL_RATE/LINK_FREQ) and libcamera support
- Add mandatory V4L2 controls required by libcamera: PIXEL_RATE, LINK_FREQ, EXPOSURE, ANALOGUE_GAIN, VBLANK, HBLANK - Add V4L2_SUBDEV_FL_HAS_DEVNODE flag to create /dev/v4l-subdev0 - Rename driver .name to 'ov5647' to use existing IPA cam_helper - Add sc235hai_set_exposure/gain/vts() register write functions - Add sc235hai_init_controls() and sc235hai_s_ctrl() ctrl ops - Register exposure (3e00/01/02), gain (3e08/09), VTS (320e/0f) Result: libcamera v0.6.0 recognizes camera, mediamtx RTSP stream working at rtsp://[board]:8554/cam with ISP processing
This commit is contained in:
parent
55dfae8c2c
commit
c32fd1b233
161
sc235hai.c
161
sc235hai.c
|
|
@ -31,11 +31,34 @@
|
|||
/* Chip ID */
|
||||
#define SC235HAI_REG_LOW_POWER 0x302c
|
||||
|
||||
/* Exposure registers: {3e00[3:0], 3e01[7:0], 3e02[7:4]} in half-row units */
|
||||
#define SC235HAI_REG_EXP_H 0x3e00
|
||||
#define SC235HAI_REG_EXP_M 0x3e01
|
||||
#define SC235HAI_REG_EXP_L 0x3e02
|
||||
/* Gain registers */
|
||||
#define SC235HAI_REG_GAIN_H 0x3e08 /* coarse analog gain */
|
||||
#define SC235HAI_REG_GAIN_L 0x3e09 /* fine analog gain */
|
||||
|
||||
/* Vertical timing size register */
|
||||
#define SC235HAI_REG_VTS_H 0x320e
|
||||
#define SC235HAI_REG_VTS_L 0x320f
|
||||
|
||||
/* Pixel rate: HTS * VTS * FPS = 2200 * 1125 * 30 = 74,250,000 Hz */
|
||||
#define SC235HAI_PIXEL_RATE 74250000UL
|
||||
/* MIPI link frequency: pixel_rate * bpp / (2 * lanes) = 74.25M * 10 / 4 */
|
||||
#define SC235HAI_LINK_FREQ 185625000LL
|
||||
|
||||
/* Default initial values */
|
||||
#define SC235HAI_DEFAULT_WIDTH 1920
|
||||
#define SC235HAI_DEFAULT_HEIGHT 1080
|
||||
#define SC235HAI_DEFAULT_FPS 30
|
||||
|
||||
/* Gain limits */
|
||||
#define SC235HAI_GAIN_MIN 0x80
|
||||
#define SC235HAI_GAIN_MAX 0x7fc0
|
||||
#define SC235HAI_GAIN_DEF 0x80
|
||||
#define SC235HAI_GAIN_STEP 1
|
||||
|
||||
struct sc235hai_mode {
|
||||
u32 width;
|
||||
u32 height;
|
||||
|
|
@ -63,11 +86,16 @@ struct sc235hai {
|
|||
const struct sc235hai_mode *cur_mode;
|
||||
struct v4l2_ctrl *exposure;
|
||||
struct v4l2_ctrl *gain;
|
||||
struct v4l2_ctrl *vblank;
|
||||
|
||||
struct mutex lock;
|
||||
bool streaming;
|
||||
};
|
||||
|
||||
static const s64 sc235hai_link_freq_menu[] = {
|
||||
SC235HAI_LINK_FREQ,
|
||||
};
|
||||
|
||||
static inline struct sc235hai *to_sc235hai(struct v4l2_subdev *sd)
|
||||
{
|
||||
return container_of(sd, struct sc235hai, sd);
|
||||
|
|
@ -468,6 +496,125 @@ static const struct v4l2_subdev_ops sc235hai_subdev_ops = {
|
|||
.pad = &sc235hai_pad_ops,
|
||||
};
|
||||
|
||||
static int sc235hai_set_exposure(struct sc235hai *sc235hai, u32 exp_lines)
|
||||
{
|
||||
/* Exposure in half-row units: val = exp_lines * 2 */
|
||||
u32 val = exp_lines * 2;
|
||||
|
||||
sc235hai_write_reg(sc235hai, SC235HAI_REG_EXP_H, (val >> 12) & 0x0f);
|
||||
sc235hai_write_reg(sc235hai, SC235HAI_REG_EXP_M, (val >> 4) & 0xff);
|
||||
return sc235hai_write_reg(sc235hai, SC235HAI_REG_EXP_L,
|
||||
(val & 0x0f) << 4);
|
||||
}
|
||||
|
||||
static int sc235hai_set_gain(struct sc235hai *sc235hai, u32 gain)
|
||||
{
|
||||
/* gain: upper 7 bits → 0x3e08 coarse; lower 8 bits → 0x3e09 fine */
|
||||
u8 coarse = (gain >> 8) & 0x7f;
|
||||
u8 fine = gain & 0xff;
|
||||
|
||||
sc235hai_write_reg(sc235hai, SC235HAI_REG_GAIN_H, coarse);
|
||||
return sc235hai_write_reg(sc235hai, SC235HAI_REG_GAIN_L, fine);
|
||||
}
|
||||
|
||||
static int sc235hai_set_vts(struct sc235hai *sc235hai, u32 vblank)
|
||||
{
|
||||
u32 vts = vblank + sc235hai->cur_mode->height;
|
||||
|
||||
sc235hai_write_reg(sc235hai, SC235HAI_REG_VTS_H, (vts >> 8) & 0xff);
|
||||
return sc235hai_write_reg(sc235hai, SC235HAI_REG_VTS_L, vts & 0xff);
|
||||
}
|
||||
|
||||
static int sc235hai_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct sc235hai *sc235hai =
|
||||
container_of(ctrl->handler, struct sc235hai, ctrl_handler);
|
||||
int ret = 0;
|
||||
|
||||
if (pm_runtime_get_if_in_use(sc235hai->sd.dev) == 0)
|
||||
return 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_EXPOSURE:
|
||||
ret = sc235hai_set_exposure(sc235hai, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_ANALOGUE_GAIN:
|
||||
ret = sc235hai_set_gain(sc235hai, ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_VBLANK:
|
||||
ret = sc235hai_set_vts(sc235hai, ctrl->val);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
pm_runtime_put(sc235hai->sd.dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct v4l2_ctrl_ops sc235hai_ctrl_ops = {
|
||||
.s_ctrl = sc235hai_s_ctrl,
|
||||
};
|
||||
|
||||
static int sc235hai_init_controls(struct sc235hai *sc235hai)
|
||||
{
|
||||
struct v4l2_ctrl_handler *hdl = &sc235hai->ctrl_handler;
|
||||
const struct sc235hai_mode *mode = sc235hai->cur_mode;
|
||||
s64 vblank_def = mode->vts - mode->height;
|
||||
s64 vblank_max = 0x7fff - mode->height;
|
||||
int ret;
|
||||
|
||||
v4l2_ctrl_handler_init(hdl, 7);
|
||||
|
||||
/* Pixel rate — read-only */
|
||||
v4l2_ctrl_new_std(hdl, &sc235hai_ctrl_ops,
|
||||
V4L2_CID_PIXEL_RATE,
|
||||
SC235HAI_PIXEL_RATE, SC235HAI_PIXEL_RATE, 1,
|
||||
SC235HAI_PIXEL_RATE);
|
||||
|
||||
/* Link frequency — read-only */
|
||||
v4l2_ctrl_new_int_menu(hdl, &sc235hai_ctrl_ops,
|
||||
V4L2_CID_LINK_FREQ,
|
||||
ARRAY_SIZE(sc235hai_link_freq_menu) - 1, 0,
|
||||
sc235hai_link_freq_menu);
|
||||
|
||||
/* Exposure — in whole lines */
|
||||
sc235hai->exposure = v4l2_ctrl_new_std(hdl, &sc235hai_ctrl_ops,
|
||||
V4L2_CID_EXPOSURE,
|
||||
4, mode->vts - 4, 1,
|
||||
mode->vts / 2);
|
||||
|
||||
/* Analogue gain — raw register composite value */
|
||||
sc235hai->gain = v4l2_ctrl_new_std(hdl, &sc235hai_ctrl_ops,
|
||||
V4L2_CID_ANALOGUE_GAIN,
|
||||
SC235HAI_GAIN_MIN,
|
||||
SC235HAI_GAIN_MAX,
|
||||
SC235HAI_GAIN_STEP,
|
||||
SC235HAI_GAIN_DEF);
|
||||
|
||||
/* Vertical blanking */
|
||||
sc235hai->vblank = v4l2_ctrl_new_std(hdl, &sc235hai_ctrl_ops,
|
||||
V4L2_CID_VBLANK,
|
||||
vblank_def, vblank_max, 1,
|
||||
vblank_def);
|
||||
|
||||
/* Horizontal blanking — read-only */
|
||||
v4l2_ctrl_new_std(hdl, &sc235hai_ctrl_ops,
|
||||
V4L2_CID_HBLANK,
|
||||
mode->hts - mode->width,
|
||||
mode->hts - mode->width, 1,
|
||||
mode->hts - mode->width);
|
||||
|
||||
if (hdl->error) {
|
||||
ret = hdl->error;
|
||||
v4l2_ctrl_handler_free(hdl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
sc235hai->sd.ctrl_handler = hdl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sc235hai_power_on(struct device *dev)
|
||||
{
|
||||
struct v4l2_subdev *sd = dev_get_drvdata(dev);
|
||||
|
|
@ -521,6 +668,7 @@ static int sc235hai_probe(struct i2c_client *client)
|
|||
return -ENOMEM;
|
||||
|
||||
v4l2_i2c_subdev_init(&sc235hai->sd, client, &sc235hai_subdev_ops);
|
||||
sc235hai->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
|
||||
|
||||
ret = sc235hai_check_hwcfg(dev);
|
||||
if (ret) {
|
||||
|
|
@ -589,13 +737,20 @@ static int sc235hai_probe(struct i2c_client *client)
|
|||
/* Set default mode */
|
||||
sc235hai->cur_mode = &sc235hai_modes[0];
|
||||
|
||||
/* Initialize V4L2 controls */
|
||||
ret = sc235hai_init_controls(sc235hai);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to init controls: %d\n", ret);
|
||||
goto error_power_off;
|
||||
}
|
||||
|
||||
/* Initialize media entity */
|
||||
sc235hai->pad.flags = MEDIA_PAD_FL_SOURCE;
|
||||
sc235hai->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
|
||||
ret = media_entity_pads_init(&sc235hai->sd.entity, 1, &sc235hai->pad);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to init entity pads: %d\n", ret);
|
||||
goto error_power_off;
|
||||
goto error_ctrl;
|
||||
}
|
||||
|
||||
ret = v4l2_async_register_subdev(&sc235hai->sd);
|
||||
|
|
@ -612,6 +767,8 @@ static int sc235hai_probe(struct i2c_client *client)
|
|||
|
||||
error_media:
|
||||
media_entity_cleanup(&sc235hai->sd.entity);
|
||||
error_ctrl:
|
||||
v4l2_ctrl_handler_free(&sc235hai->ctrl_handler);
|
||||
error_power_off:
|
||||
sc235hai_power_off(dev);
|
||||
error_mutex:
|
||||
|
|
@ -648,7 +805,7 @@ static const struct dev_pm_ops sc235hai_pm_ops = {
|
|||
|
||||
static struct i2c_driver sc235hai_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "sc235hai",
|
||||
.name = "ov5647",
|
||||
.of_match_table = sc235hai_of_match,
|
||||
.pm = &sc235hai_pm_ops,
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue