Hello,
I previously posted a similar message, and although I am still a beginner when it comes to developing camera device drivers, I am working hard to make progress. I have disabled the media controller as you suggested and set the clock mode to continuous to match the MIPI output. Since the last time, I have made some advancements.
First of all, the camera module I am using has an ISP built into the image sensor. Therefore, instead of Bayer format, the output is in YUV422 format. Please refer to the attached file.
Active Resolution : 1280 x 960, YUV422, 30fps
Blank : 1195(Horizontal), 40(Vertical)
Pixel rate : 74.25MHz
MIPI PHY clock : 192MHz
I have attached my DTS file and driver source code for reference.
Device tree fileDriver source codesThe issue I am currently facing is that the video buffer is not being received correctly. As shown in the diagram below, only about 5000 bytes of data are being received initially.dmesgdmesgdmesgdmesgI have also attached the image data received via MIPI. I would greatly appreciate it if you could help me resolve this problem.
Thank you.
I previously posted a similar message, and although I am still a beginner when it comes to developing camera device drivers, I am working hard to make progress. I have disabled the media controller as you suggested and set the clock mode to continuous to match the MIPI output. Since the last time, I have made some advancements.
First of all, the camera module I am using has an ISP built into the image sensor. Therefore, instead of Bayer format, the output is in YUV422 format. Please refer to the attached file.
Active Resolution : 1280 x 960, YUV422, 30fps
Blank : 1195(Horizontal), 40(Vertical)
Pixel rate : 74.25MHz
MIPI PHY clock : 192MHz
I have attached my DTS file and driver source code for reference.
Device tree file
Code:
// SPDX-License-Identifier: GPL-2.0-only// Definitions for Analog Devices ADV7282-M video to CSI2 bridge on VC I2C bus/dts-v1/;/plugin/;/{compatible = "brcm,bcm2835";fragment@0 {target = <&i2c_csi_dsi>;__overlay__ {#address-cells = <1>;#size-cells = <0>;status = "okay";px6130: px6130@36 {compatible = "pixelplus,px6130";reg = <0x36>;status = "okay";//clock-frequency = <24000000>;port {px6130_out: endpoint {remote-endpoint = <&csi1_ep>;clock-lanes = <0>;data-lanes = <1 2>;link-frequencies =/bits/ 64 <384000000>;};};};};};fragment@1 {target = <&csi1>;__overlay__ {status = "okay";port {csi1_ep: endpoint {remote-endpoint = <&px6130_out>;data-lanes = <1 2>;};};};};fragment@2 {target = <&i2c0if>;__overlay__ {status = "okay";};};fragment@3 {target = <&i2c0mux>;__overlay__ {status = "okay";};};fragment@4 {target = <&csi1>;__dormant__ {brcm,media-controller;};};__overrides__ {addr =<&px6130>,"reg:0";media-controller = <0>,"=4";};};
Code:
// SPDX-License-Identifier: GPL-2.0/* * A V4L2 driver for Pixelplus PX6130 cameras. * * Based on Samsung S5K6AAFX SXGA 1/6" 1.3M CMOS Image Sensor driver * Copyright (C) 2011 Sylwester Nawrocki <s.nawrocki@samsung.com> * * Based on Pixelplus OV7670 Camera Driver * Copyright (C) 2006-7 Jonathan Corbet <corbet@lwn.net> * * Copyright (C) 2016, Synopsys, Inc. */#include <linux/clk.h>#include <linux/delay.h>#include <linux/gpio/consumer.h>#include <linux/i2c.h>#include <linux/init.h>#include <linux/io.h>#include <linux/module.h>#include <linux/of_graph.h>#include <linux/pm_runtime.h>#include <linux/regulator/consumer.h>#include <linux/slab.h>#include <linux/videodev2.h>#include <media/v4l2-ctrls.h>#include <media/v4l2-device.h>#include <media/v4l2-event.h>#include <media/v4l2-fwnode.h>#include <media/v4l2-image-sizes.h>#include <media/v4l2-mediabus.h>#define MIPI_CTRL00_CLOCK_LANE_GATEBIT(5)#define MIPI_CTRL00_LINE_SYNC_ENABLEBIT(4)#define MIPI_CTRL00_BUS_IDLEBIT(2)#define MIPI_CTRL00_CLOCK_LANE_DISABLEBIT(0)#define PX6130_NATIVE_WIDTH1280U#define PX6130_NATIVE_HEIGHT960U#define PX6130_PIXEL_ARRAY_LEFT0U#define PX6130_PIXEL_ARRAY_TOP0U#define PX6130_PIXEL_ARRAY_WIDTH1280U#define PX6130_PIXEL_ARRAY_HEIGHT960U#define PX6130_HORIZONTAL_TOTAL_SIZE2475U// pixel domain#define PX6130_VERTICAL_TOTAL_SIZE1000U// pixel domain#define PX6130_VBLANK_MIN0#define PX6130_VTS_MAX32767#define PX6130_HTS_MAX0x1fff/* regulator supplies */static const char * const PX6130_supply_names[] = {"avdd",/* Analog power */"dovdd",/* Digital I/O power */"dvdd",/* Digital core power */};#define PX6130_NUM_SUPPLIES ARRAY_SIZE(PX6130_supply_names)struct regval_list {u16 addr;u8 data;};struct PX6130_mode {struct v4l2_mbus_framefmtformat;struct v4l2_rectcrop;u64pixel_rate;inthts;intvts;const struct regval_list*reg_list;unsigned intnum_regs;};struct PX6130 {struct v4l2_subdevsd;struct media_padpad;struct mutexlock;struct clk*xclk;struct gpio_desc*pwdn;struct regulator_bulk_data supplies[PX6130_NUM_SUPPLIES];boolclock_ncont;struct v4l2_ctrl_handlerctrls;const struct PX6130_mode*mode;struct v4l2_ctrl*pixel_rate;struct v4l2_ctrl*hblank;struct v4l2_ctrl*vblank;struct v4l2_ctrl*exposure;struct v4l2_ctrl*hflip;struct v4l2_ctrl*vflip;boolstreaming;};static inline struct PX6130 *to_sensor(struct v4l2_subdev *sd){return container_of(sd, struct PX6130, sd);}static struct regval_list PX6130_1280x960_YUV422[] = {{ 0x0000, 0x00 },{ 0x0001, 0x01 },};static const struct PX6130_mode PX6130_modes[] = {{.format = { .code = MEDIA_BUS_FMT_YUYV8_2X8,.colorspace= V4L2_COLORSPACE_SMPTE170M,.field= V4L2_FIELD_NONE,.width= PX6130_PIXEL_ARRAY_WIDTH,.height= PX6130_PIXEL_ARRAY_HEIGHT},.crop = {.left= 0,.top= 0,.width= PX6130_PIXEL_ARRAY_WIDTH,.height= PX6130_PIXEL_ARRAY_HEIGHT},.pixel_rate= 74250000,.hts= PX6130_HORIZONTAL_TOTAL_SIZE,.vts= PX6130_VERTICAL_TOTAL_SIZE,.reg_list= PX6130_1280x960_YUV422,.num_regs= ARRAY_SIZE(PX6130_1280x960_YUV422)},};#define PX6130_DEFAULT_MODE (&PX6130_modes[0])#define PX6130_DEFAULT_FORMAT (PX6130_modes[0].format)static int PX6130_write16(struct v4l2_subdev *sd, u16 reg, u16 val){unsigned char data[4] = { reg >> 8, reg & 0xff, val >> 8, val & 0xff};struct i2c_client *client = v4l2_get_subdevdata(sd);int ret;ret = i2c_master_send(client, data, 4);/* * Writing the wrong number of bytes also needs to be flagged as an * error. Success needs to produce a 0 return code. */if (ret == 4) {ret = 0;} else {dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n",__func__, reg);return ret;}return 0;}static int PX6130_write(struct v4l2_subdev *sd, u16 reg, u8 val){unsigned char data[3] = { reg >> 8, reg & 0xff, val};struct i2c_client *client = v4l2_get_subdevdata(sd);int ret;ret = i2c_master_send(client, data, 3);/* * Writing the wrong number of bytes also needs to be flagged as an * error. Success needs to produce a 0 return code. */if (ret == 3) {ret = 0;} else {dev_dbg(&client->dev, "%s: i2c write error, reg: %x\n",__func__, reg);if (ret >= 0)ret = -EINVAL;}return 0;}static int PX6130_read(struct v4l2_subdev *sd, u16 reg, u8 *val){struct i2c_client *client = v4l2_get_subdevdata(sd);u8 buf[2] = { reg >> 8, reg & 0xff };struct i2c_msg msg[2];int ret;msg[0].addr = client->addr;msg[0].flags = client->flags;msg[0].buf = buf;msg[0].len = sizeof(buf);msg[1].addr = client->addr;msg[1].flags = client->flags | I2C_M_RD;msg[1].buf = buf;msg[1].len = 1;ret = i2c_transfer(client->adapter, msg, 2);if (ret != 2) {dev_err(&client->dev, "%s: i2c read error, reg: %x = %d\n",__func__, reg, ret);return ret >= 0 ? -EINVAL : ret;}*val = buf[0];return 0;}static int PX6130_write_array(struct v4l2_subdev *sd, const struct regval_list *regs, int array_size){int i, ret;for (i = 0; i < array_size; i++) {ret = PX6130_write(sd, regs[i].addr, regs[i].data);if (ret < 0)return ret;}return 0;}static int PX6130_set_virtual_channel(struct v4l2_subdev *sd, int channel){u8 channel_id = 0;int ret = 0;channel_id &= ~(3 << 6);return ret;}static int PX6130_set_mode(struct v4l2_subdev *sd){struct i2c_client *client = v4l2_get_subdevdata(sd);struct PX6130 *sensor = to_sensor(sd);u8 resetval, rdval;int ret;ret = PX6130_set_virtual_channel(sd, 0);dev_info(&client->dev, "%s(%d) ret: %d\n", __func__, __LINE__, ret);if (ret < 0)return ret;return 0;}static int PX6130_stream_on(struct v4l2_subdev *sd){struct i2c_client *client = v4l2_get_subdevdata(sd);struct PX6130 *sensor = to_sensor(sd);u8 val = MIPI_CTRL00_BUS_IDLE;int ret;ret = PX6130_set_mode(sd);dev_info(&client->dev, "%s(%d) ret: %d\n", __func__, __LINE__, ret);if (ret) {dev_err(&client->dev, "Failed to program sensor mode: %d\n", ret);return ret;}/* Apply customized values from user when stream starts. */ret = __v4l2_ctrl_handler_setup(sd->ctrl_handler);dev_info(&client->dev, "%s(%d): ret %d\n", __func__, __LINE__, ret);if (ret)return ret;return 0;}static int PX6130_stream_off(struct v4l2_subdev *sd){struct i2c_client *client = v4l2_get_subdevdata(sd);int ret;dev_info(&client->dev, "%s(%d)\n", __func__, __LINE__);return 0;}static int PX6130_power_on(struct device *dev){struct PX6130 *sensor = dev_get_drvdata(dev);int ret;dev_info(dev, "PX6130 power on\n");return 0;error_clk_disable:clk_disable_unprepare(sensor->xclk);error_pwdn:gpiod_set_value_cansleep(sensor->pwdn, 1);regulator_bulk_disable(PX6130_NUM_SUPPLIES, sensor->supplies);return ret;}static int PX6130_power_off(struct device *dev){struct PX6130 *sensor = dev_get_drvdata(dev);u8 rdval;int ret;dev_info(dev, "PX6130 power off\n");return 0;}#ifdef CONFIG_VIDEO_ADV_DEBUGstatic int PX6130_sensor_get_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg){pr_info("%s(%d): %d\n", __func__, __LINE__, which);int ret;u8 val;ret = PX6130_read(sd, reg->reg & 0xff, &val);if (ret < 0)return ret;reg->val = val;reg->size = 1;return 0;}static int PX6130_sensor_set_register(struct v4l2_subdev *sd, const struct v4l2_dbg_register *reg){pr_info("%s(%d): %d\n", __func__, __LINE__, which);return PX6130_write(sd, reg->reg & 0xff, reg->val & 0xff);}#endifstatic const struct v4l2_rect *__PX6130_get_pad_crop(struct PX6130 *PX6130, struct v4l2_subdev_state *sd_state, unsigned int pad, enum v4l2_subdev_format_whence which){pr_info("%s(%d): %d\n", __func__, __LINE__, which);switch (which) {case V4L2_SUBDEV_FORMAT_TRY:return v4l2_subdev_get_try_crop(&PX6130->sd, sd_state, pad);case V4L2_SUBDEV_FORMAT_ACTIVE:return &PX6130->mode->crop;}return NULL;}static int PX6130_s_stream(struct v4l2_subdev *sd, int enable){struct i2c_client *client = v4l2_get_subdevdata(sd);struct PX6130 *sensor = to_sensor(sd);int ret = 0;dev_info(&client->dev, "dbg_info: %s, line: %d, enable: %d(%d)\n", __func__, __LINE__, enable, sensor->streaming);mutex_lock(&sensor->lock);if (sensor->streaming == enable) {mutex_unlock(&sensor->lock);dev_info(&client->dev, "dbg_info: %s, line: %d\n", __func__, __LINE__);return 0;}if (enable) {//ret = pm_runtime_resume_and_get(&client->dev);dev_info(&client->dev, "%s(%d) ret: %d\n", __func__, __LINE__, ret);if (ret < 0) {dev_info(&client->dev, "%s(%d) ret: %d\n", __func__, __LINE__, ret);goto error_unlock;}ret = PX6130_stream_on(sd);dev_info(&client->dev, "%s(%d) ret: %d\n", __func__, __LINE__, ret);if (ret < 0) {dev_info(&client->dev, "stream start failed: %d\n", ret);goto error_pm;}} else {dev_info(&client->dev, "dbg_info: %s, line: %d\n", __func__, __LINE__);ret = PX6130_stream_off(sd);if (ret < 0) {dev_info(&client->dev, "stream stop failed: %d\n", ret);goto error_pm;}//pm_runtime_put(&client->dev);}sensor->streaming = enable;mutex_unlock(&sensor->lock);dev_info(&client->dev, "dbg_info: %s, line: %d\n", __func__, __LINE__);return 0;error_pm:dev_info(&client->dev, "dbg_info: %s, line: %d\n", __func__, __LINE__);pm_runtime_put(&client->dev);error_unlock:dev_info(&client->dev, "dbg_info: %s, line: %d\n", __func__, __LINE__);mutex_unlock(&sensor->lock);return ret;}static int PX6130_g_frame_interval(struct v4l2_subdev *sd, struct v4l2_subdev_frame_interval *interval){struct PX6130 *sensor = to_sensor(sd);struct i2c_client *client = v4l2_get_subdevdata(sd);dev_info(&client->dev, "%s, %d\n", __func__, __LINE__);// 고정된 프레임 속도를 반환interval->interval.numerator = 1;interval->interval.denominator = 30; // 고정된 30fpsreturn 0;}/* This function returns the mbus code for the current settings of the HFLIP and VFLIP controls. */static u32 PX6130_get_mbus_code(struct v4l2_subdev *sd){struct i2c_client *client = v4l2_get_subdevdata(sd);dev_info(&client->dev, "%s, %d\n", __func__, __LINE__);struct PX6130 *sensor = to_sensor(sd);/* The control values are only 0 or 1. *///int index = sensor->hflip->val | (sensor->vflip->val << 1);int index = 0;static const u32 codes[4] = {MEDIA_BUS_FMT_YUYV8_2X8,MEDIA_BUS_FMT_UYVY8_2X8,MEDIA_BUS_FMT_YVYU8_2X8,MEDIA_BUS_FMT_VYUY8_2X8};return codes[index];}static int PX6130_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code){if (code->index > 0)return -EINVAL;code->code = PX6130_get_mbus_code(sd);return 0;}static int PX6130_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse){const struct v4l2_mbus_framefmt *fmt;struct i2c_client *client = v4l2_get_subdevdata(sd);dev_info(&client->dev, "dbg_info: %s(%d), state:%x, fse:%x\n",__func__, __LINE__, sd_state, fse);if (fse->code != PX6130_get_mbus_code(sd) || fse->index >= ARRAY_SIZE(PX6130_modes)){dev_info(&client->dev, "Err!! code: %d, index: %d, mbus: %d, size : %d\n",fse->code, fse->index, PX6130_get_mbus_code(sd), ARRAY_SIZE(PX6130_modes));return -EINVAL;}fmt = &PX6130_modes[fse->index].format;dev_info(&client->dev, "dbg_info: %s(%d) (%d, %d)\n",__func__, __LINE__, fmt->width, fmt->height);fse->min_width = fmt->width;fse->max_width = fmt->width;fse->min_height = fmt->height;fse->max_height = fmt->height;return 0;}static int PX6130_get_pad_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format){struct i2c_client *client = v4l2_get_subdevdata(sd);dev_info(&client->dev, "%s(%d), fmt: %d\n", __func__, __LINE__, format->which);struct PX6130 *sensor = to_sensor(sd);if (!sensor) {dev_info(&client->dev, "sensor is NULL\n");}if (!sensor->mode) {dev_info(&client->dev, "sensor->mode is NULL\n");}struct v4l2_mbus_framefmt *fmt = &format->format;const struct v4l2_mbus_framefmt *sensor_format;mutex_lock(&sensor->lock);switch (format->which) {case V4L2_SUBDEV_FORMAT_TRY:sensor_format = v4l2_subdev_get_try_format(sd, sd_state, format->pad);break;default:sensor_format = &sensor->mode->format;break;}*fmt = *sensor_format;dev_info(&client->dev, "size(%d, %d), code: %d, fld: %d, color: %d, yuv_enc: %d, hsv_enc: %d\n",fmt->width, fmt->height, fmt->code, fmt->field, fmt->colorspace, fmt->ycbcr_enc, fmt->hsv_enc);/* The code we pass back must reflect the current h/vflips. */fmt->code = PX6130_get_mbus_code(sd);mutex_unlock(&sensor->lock);return 0;}static int PX6130_set_pad_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format){struct v4l2_mbus_framefmt *fmt = &format->format;struct PX6130 *sensor = to_sensor(sd);struct i2c_client *client = v4l2_get_subdevdata(sd);const struct PX6130_mode *mode;dev_info(&client->dev, "%s(%d)\n", __func__, __LINE__);mode = v4l2_find_nearest_size(PX6130_modes, ARRAY_SIZE(PX6130_modes), format.width, format.height, fmt->width, fmt->height);/* Update the sensor mode and apply at it at streamon time. */mutex_lock(&sensor->lock);if (format->which == V4L2_SUBDEV_FORMAT_TRY) {*v4l2_subdev_get_try_format(sd, sd_state, format->pad) = mode->format;} else {// we don't need to control for exposure nowint exposure_max, exposure_def;int hblank, vblank;sensor->mode = mode;__v4l2_ctrl_modify_range(sensor->pixel_rate, mode->pixel_rate, mode->pixel_rate, 1, mode->pixel_rate);hblank = mode->hts - mode->format.width;__v4l2_ctrl_modify_range(sensor->hblank, hblank, PX6130_HTS_MAX - mode->format.width, 1, hblank);vblank = mode->vts - mode->format.height;__v4l2_ctrl_modify_range(sensor->vblank, PX6130_VBLANK_MIN, PX6130_VTS_MAX - mode->format.height, 1, vblank);__v4l2_ctrl_s_ctrl(sensor->vblank, vblank);/*exposure_max = mode->vts - 4;exposure_def = min(exposure_max, PX6130_EXPOSURE_DEFAULT);__v4l2_ctrl_modify_range(sensor->exposure, sensor->exposure->minimum, exposure_max, sensor->exposure->step, exposure_def); */}*fmt = mode->format;/* The code we pass back must reflect the current h/vflips. */fmt->code = PX6130_get_mbus_code(sd);mutex_unlock(&sensor->lock);return 0;}static int PX6130_get_selection(struct v4l2_subdev *sd,struct v4l2_subdev_state *sd_state,struct v4l2_subdev_selection *sel){struct i2c_client *client = v4l2_get_subdevdata(sd);dev_info(&client->dev, "%s(%d), target: %d\n", __func__, __LINE__, sel->target);switch (sel->target) {case V4L2_SEL_TGT_CROP: {struct PX6130 *sensor = to_sensor(sd);mutex_lock(&sensor->lock);sel->r = *__PX6130_get_pad_crop(sensor, sd_state, sel->pad,sel->which);mutex_unlock(&sensor->lock);return 0;}case V4L2_SEL_TGT_NATIVE_SIZE:sel->r.top = 0;sel->r.left = 0;sel->r.width = PX6130_NATIVE_WIDTH;sel->r.height = PX6130_NATIVE_HEIGHT;return 0;case V4L2_SEL_TGT_CROP_DEFAULT:case V4L2_SEL_TGT_CROP_BOUNDS:sel->r.top = PX6130_PIXEL_ARRAY_TOP;sel->r.left = PX6130_PIXEL_ARRAY_LEFT;sel->r.width = PX6130_PIXEL_ARRAY_WIDTH;sel->r.height = PX6130_PIXEL_ARRAY_HEIGHT;return 0;}return -EINVAL;}/* Subdev core operations registration */static const struct v4l2_subdev_core_ops PX6130_subdev_core_ops = {.subscribe_event= v4l2_ctrl_subdev_subscribe_event,.unsubscribe_event= v4l2_event_subdev_unsubscribe,#ifdef CONFIG_VIDEO_ADV_DEBUG.g_register= PX6130_sensor_get_register,.s_register= PX6130_sensor_set_register,#endif//.subscribe_event = v4l2_ctrl_subdev_subscribe_event,//.unsubscribe_event = v4l2_event_subdev_unsubscribe,};static const struct v4l2_subdev_video_ops PX6130_subdev_video_ops = {.s_stream =PX6130_s_stream,.g_frame_interval = PX6130_g_frame_interval,};static const struct v4l2_subdev_pad_ops PX6130_subdev_pad_ops = {.enum_mbus_code= PX6130_enum_mbus_code,.enum_frame_size= PX6130_enum_frame_size,.set_fmt= PX6130_set_pad_fmt,.get_fmt= PX6130_get_pad_fmt,.get_selection= PX6130_get_selection,};static const struct v4l2_subdev_ops PX6130_subdev_ops = {.core= &PX6130_subdev_core_ops,.video= &PX6130_subdev_video_ops,.pad= &PX6130_subdev_pad_ops,};static int PX6130_detect(struct v4l2_subdev *sd){struct i2c_client *client = v4l2_get_subdevdata(sd);u8 read;int ret = 0;return ret;}static int PX6130_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh){struct i2c_client *client = v4l2_get_subdevdata(sd);dev_info(&client->dev, "%s, %d\n", __func__, __LINE__);struct v4l2_mbus_framefmt *format =v4l2_subdev_get_try_format(sd, fh->state, 0);struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, fh->state, 0);crop->left = PX6130_PIXEL_ARRAY_LEFT;crop->top = PX6130_PIXEL_ARRAY_TOP;crop->width = PX6130_PIXEL_ARRAY_WIDTH;crop->height = PX6130_PIXEL_ARRAY_HEIGHT;*format = PX6130_DEFAULT_FORMAT;return 0;}static const struct v4l2_subdev_internal_ops PX6130_subdev_internal_ops = {.open = PX6130_open,};static int PX6130_s_ctrl(struct v4l2_ctrl *ctrl){struct PX6130 *sensor = container_of(ctrl->handler, struct PX6130, ctrls);struct v4l2_subdev *sd = &sensor->sd;struct i2c_client *client = v4l2_get_subdevdata(sd);int ret = 0;dev_info(&client->dev, "dbg_info: %s, line: %d, id: %d\n", __func__, __LINE__, ctrl->id);switch (ctrl->id) {case V4L2_CID_PIXEL_RATE:dev_info(&client->dev, "V4L2_CID_PIXEL_RATE val:%d\n", ctrl->val);/* Read-only, but we adjust it based on mode. */break;case V4L2_CID_HFLIP:/* There's an in-built hflip in the sensor, so account for that here. *///PX6130_s_flip(sd, PX6130_REG_HFLIP, !ctrl->val);break;case V4L2_CID_VFLIP:dev_info(&client->dev, "V4L2_CID_VFLIP val:%d\n", ctrl->val);//PX6130_s_flip(sd, PX6130_REG_VFLIP, ctrl->val);break;case V4L2_CID_HBLANK:dev_info(&client->dev, "V4L2_CID_HBLANK val:%d\n", ctrl->val);break;case V4L2_CID_VBLANK:dev_info(&client->dev, "V4L2_CID_VBLANK val:%d\n", ctrl->val);break;case V4L2_CID_LINK_FREQ:dev_info(&client->dev, "V4L2_CID_LINK_FREQ val:%d\n", ctrl->val);break;default:dev_info(&client->dev, "Control (id:0x%x, val:0x%x) not supported\n", ctrl->id, ctrl->val);return -EINVAL;};return ret;}static const struct v4l2_ctrl_ops PX6130_ctrl_ops = {.s_ctrl = PX6130_s_ctrl,};static int PX6130_init_controls(struct PX6130 *sensor, struct device *dev){struct i2c_client *client = v4l2_get_subdevdata(&sensor->sd);int hblank, vblank, exposure_max, exposure_def;struct v4l2_fwnode_device_properties props;v4l2_ctrl_handler_init(&sensor->ctrls, 5);/*v4l2_ctrl_new_std(&sensor->ctrls, &PX6130_ctrl_ops, V4L2_CID_AUTOGAIN, 0, 1, 1, 0);v4l2_ctrl_new_std(&sensor->ctrls, &PX6130_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE, 0, 1, 1, 0);v4l2_ctrl_new_std_menu(&sensor->ctrls, &PX6130_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL, 0, V4L2_EXPOSURE_MANUAL);exposure_max = sensor->mode->vts - 4;exposure_def = min(exposure_max, PX6130_EXPOSURE_DEFAULT);sensor->exposure = v4l2_ctrl_new_std(&sensor->ctrls, &PX6130_ctrl_ops, V4L2_CID_EXPOSURE, PX6130_EXPOSURE_MIN, exposure_max, PX6130_EXPOSURE_STEP, exposure_def);// min: 16 = 1.0x; max (10 bits); default: 32 = 2.0x.v4l2_ctrl_new_std(&sensor->ctrls, &PX6130_ctrl_ops, V4L2_CID_ANALOGUE_GAIN, 16, 1023, 1, 32);*/// By default, PIXEL_RATE is read only, but it does change per modesensor->pixel_rate = v4l2_ctrl_new_std(&sensor->ctrls, &PX6130_ctrl_ops, V4L2_CID_PIXEL_RATE, sensor->mode->pixel_rate, sensor->mode->pixel_rate, 1, sensor->mode->pixel_rate);hblank = sensor->mode->hts - sensor->mode->format.width;sensor->hblank = v4l2_ctrl_new_std(&sensor->ctrls, &PX6130_ctrl_ops, V4L2_CID_HBLANK, hblank, PX6130_HTS_MAX - sensor->mode->format.width, 1, hblank);vblank = sensor->mode->vts - sensor->mode->format.height;sensor->vblank = v4l2_ctrl_new_std(&sensor->ctrls, &PX6130_ctrl_ops, V4L2_CID_VBLANK, PX6130_VBLANK_MIN, PX6130_VTS_MAX - sensor->mode->format.height, 1, vblank);dev_info(dev, "dbg_info: %s(%d), blank(%d, %d), sensor_blank(%d, %d)\n",__func__, __LINE__, hblank, vblank, sensor->hblank, sensor->vblank); /*v4l2_ctrl_new_std_menu_items(&sensor->ctrls, &PX6130_ctrl_ops, V4L2_CID_TEST_PATTERN, ARRAY_SIZE(PX6130_test_pattern_menu) - 1, 0, 0, PX6130_test_pattern_menu); */sensor->hflip = v4l2_ctrl_new_std(&sensor->ctrls, &PX6130_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);if (sensor->hflip)sensor->hflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;sensor->vflip = v4l2_ctrl_new_std(&sensor->ctrls, &PX6130_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);if (sensor->vflip)sensor->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;v4l2_fwnode_device_parse(dev, &props);v4l2_ctrl_new_fwnode_properties(&sensor->ctrls, &PX6130_ctrl_ops,&props);if (sensor->ctrls.error) {dev_info(dev, "dbg_info: %s(%d), err: %d\n", __func__, __LINE__, sensor->ctrls.error);goto handler_free;}dev_info(dev, "dbg_info: %s, line: %d\n", __func__, __LINE__);sensor->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;sensor->sd.ctrl_handler = &sensor->ctrls;dev_info(dev, "dbg_info: %s, line: %d\n", __func__, __LINE__);return 0;handler_free:dev_info(&client->dev, "%s Controls initialization failed (%d)\n",__func__, sensor->ctrls.error);v4l2_ctrl_handler_free(&sensor->ctrls);return sensor->ctrls.error;}static int PX6130_parse_dt(struct PX6130 *sensor, struct device_node *np){struct v4l2_fwnode_endpoint bus_cfg = {.bus_type = V4L2_MBUS_CSI2_DPHY,};struct device_node *ep;int ret;ep = of_graph_get_next_endpoint(np, NULL);if (!ep)return -EINVAL;ret = v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep), &bus_cfg);if (ret)goto out;sensor->clock_ncont = bus_cfg.bus.mipi_csi2.flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK;out:of_node_put(ep);return ret;}static int PX6130_probe(struct i2c_client *client){struct device_node *np = client->dev.of_node;struct device *dev = &client->dev;struct PX6130 *sensor;struct v4l2_subdev *sd;u32 xclk_freq;int ret;sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);if (!sensor)return -ENOMEM;if (IS_ENABLED(CONFIG_OF) && np) {ret = PX6130_parse_dt(sensor, np);if (ret) {dev_err(dev, "DT parsing error: %d\n", ret);return ret;}}// In our image sensor there is an xclk block, so we don't need to send clock to the CIS/*sensor->xclk = devm_clk_get(dev, NULL);if (IS_ERR(sensor->xclk)) {dev_err(dev, "could not get xclk");return PTR_ERR(sensor->xclk);}xclk_freq = clk_get_rate(sensor->xclk);if (xclk_freq != 25000000) {dev_err(dev, "Unsupported clock frequency: %u\n", xclk_freq);return -EINVAL;}*/// CIS doesn't get power from the rpi board, later we will change that rpi board supplies power to CIS/* Request the power down GPIO asserted. *//*sensor->pwdn = devm_gpiod_get_optional(dev, "pwdn", GPIOD_OUT_HIGH);if (IS_ERR(sensor->pwdn)) {dev_err(dev, "Failed to get 'pwdn' gpio\n");return -EINVAL;}ret = PX6130_configure_regulators(dev, sensor);if (ret) {dev_err(dev, "Failed to get power regulators\n");return ret;}*/mutex_init(&sensor->lock);sensor->mode = PX6130_DEFAULT_MODE;ret = PX6130_init_controls(sensor, dev);dev_info(dev, "dbg_info: %s, line: %d, ret: %d, sensor: %x\n", __func__, __LINE__, ret, &sensor->sd);if (ret)goto mutex_destroy;sd = &sensor->sd;v4l2_i2c_subdev_init(sd, client, &PX6130_subdev_ops);sd->internal_ops = &PX6130_subdev_internal_ops;sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;sensor->pad.flags = MEDIA_PAD_FL_SOURCE;sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;ret = media_entity_pads_init(&sd->entity, 1, &sensor->pad);dev_info(dev, "dbg_info: %s, line: %d, ret: %d\n", __func__, __LINE__, ret);if (ret < 0)goto ctrl_handler_free;// power from an external power supply, we don't need it/*ret = PX6130_power_on(dev);if (ret)goto entity_cleanup;ret = PX6130_detect(sd);if (ret < 0)goto power_off;*/dev_info(dev, "dbg_info: %s, line: %d\n", __func__, __LINE__);ret = v4l2_async_register_subdev_sensor(sd);dev_info(dev, "dbg_info: %s, line: %d, ret: %d\n", __func__, __LINE__, ret);if (ret < 0)goto power_off;// power from an external power supply, we don't need it/* Enable runtime PM and turn off the device *//*pm_runtime_set_active(dev);pm_runtime_enable(dev);pm_runtime_idle(dev);*/dev_info(dev, "Pixelplus PX6130 camera driver probed\n");return 0;power_off:PX6130_power_off(dev);entity_cleanup:media_entity_cleanup(&sd->entity);ctrl_handler_free:v4l2_ctrl_handler_free(&sensor->ctrls);mutex_destroy:mutex_destroy(&sensor->lock);return ret;}static void PX6130_remove(struct i2c_client *client){struct v4l2_subdev *sd = i2c_get_clientdata(client);struct PX6130 *sensor = to_sensor(sd);v4l2_async_unregister_subdev(&sensor->sd);media_entity_cleanup(&sensor->sd.entity);v4l2_ctrl_handler_free(&sensor->ctrls);v4l2_device_unregister_subdev(sd);pm_runtime_disable(&client->dev);mutex_destroy(&sensor->lock);}static const struct dev_pm_ops PX6130_pm_ops = {SET_RUNTIME_PM_OPS(PX6130_power_off, PX6130_power_on, NULL)};static const struct i2c_device_id PX6130_id[] = {{ "px6130", 0 },{ /* sentinel */ }};MODULE_DEVICE_TABLE(i2c, PX6130_id);#if IS_ENABLED(CONFIG_OF)static const struct of_device_id PX6130_of_match[] = {{ .compatible = "pixelplus,px6130" },{ /* sentinel */ },};MODULE_DEVICE_TABLE(of, PX6130_of_match);#endifstatic struct i2c_driver PX6130_driver = {.driver = {.of_match_table = of_match_ptr(PX6130_of_match),.name= "px6130",//.pm= &PX6130_pm_ops,},.probe= PX6130_probe,.remove= PX6130_remove,.id_table= PX6130_id,};module_i2c_driver(PX6130_driver);MODULE_AUTHOR("Ramiro Oliveira <roliveir@synopsys.com>");MODULE_DESCRIPTION("A low-level driver for Pixelplus PX6130 sensors");MODULE_LICENSE("GPL v2");
Code:
raspi4@raspberrypi:~/px6130/driver $ sudo dtoverlay px6130
Code:
[ 146.919447] OF: overlay: WARNING: memory leak will occur if overlay removed, property: /soc/i2c0mux/i2c@1/status[ 146.919558] OF: overlay: WARNING: memory leak will occur if overlay removed, property: /soc/csi@7e801000/status[ 146.919592] OF: overlay: WARNING: memory leak will occur if overlay removed, property: /soc/i2c@7e205000/status[ 146.919613] OF: overlay: WARNING: memory leak will occur if overlay removed, property: /soc/i2c0mux/status[ 147.030756] i2c i2c-22: Added multiplexed i2c bus 0[ 147.031105] i2c i2c-22: Added multiplexed i2c bus 10
Code:
raspi4@raspberrypi:~/px6130/driver $ sudo insmod px6130.ko
Code:
[ 153.497572] px6130: loading out-of-tree module taints kernel.[ 153.498847] px6130 10-0036: dbg_info: PX6130_init_controls(775), blank(1195, 40), sensor_blank(1103197696, 1131448064)[ 153.498883] px6130 10-0036: dbg_info: PX6130_init_controls, line: 807[ 153.498894] px6130 10-0036: dbg_info: PX6130_init_controls, line: 811[ 153.498904] px6130 10-0036: dbg_info: PX6130_probe, line: 905, ret: 0, sensor: 6cee1c80[ 153.498921] px6130 10-0036: dbg_info: PX6130_probe, line: 917, ret: 0[ 153.498931] px6130 10-0036: dbg_info: PX6130_probe, line: 933[ 153.498969] px6130 10-0036: PX6130_get_pad_fmt(484), fmt: 1[ 153.498979] px6130 10-0036: size(1280, 960), code: 8200, fld: 1, color: 1, yuv_enc: 0, hsv_enc: 0[ 153.498993] px6130 10-0036: PX6130_get_mbus_code, 424[ 153.499002] px6130 10-0036: PX6130_get_pad_fmt(484), fmt: 1[ 153.499012] px6130 10-0036: size(1280, 960), code: 8200, fld: 1, color: 1, yuv_enc: 0, hsv_enc: 0[ 153.499024] px6130 10-0036: PX6130_get_mbus_code, 424[ 153.499772] px6130 10-0036: dbg_info: PX6130_probe, line: 935, ret: 0[ 153.499791] px6130 10-0036: Pixelplus PX6130 camera driver probed[ 153.584676] px6130 10-0036: PX6130_open, 658[ 153.591964] px6130 10-0036: PX6130_get_mbus_code, 424[ 153.591991] px6130 10-0036: PX6130_get_mbus_code, 424[ 153.591996] px6130 10-0036: dbg_info: PX6130_enum_frame_size(459), state:0, fse:813d3b88[ 153.592002] px6130 10-0036: PX6130_get_mbus_code, 424[ 153.592006] px6130 10-0036: dbg_info: PX6130_enum_frame_size(469) (1280, 960)[ 153.592022] px6130 10-0036: PX6130_get_mbus_code, 424[ 153.592026] px6130 10-0036: dbg_info: PX6130_enum_frame_size(459), state:0, fse:813d3b88[ 153.592030] px6130 10-0036: PX6130_get_mbus_code, 424[ 153.592033] px6130 10-0036: PX6130_get_mbus_code, 424[ 153.592036] px6130 10-0036: Err!! code: 8200, index: 2, mbus: 8200, size : 1[ 153.592041] px6130 10-0036: PX6130_get_mbus_code, 424[ 153.596291] px6130 10-0036: PX6130_get_mbus_code, 424[ 153.596314] px6130 10-0036: PX6130_get_mbus_code, 424[ 153.596318] px6130 10-0036: dbg_info: PX6130_enum_frame_size(459), state:0, fse:813d3b88[ 153.596323] px6130 10-0036: PX6130_get_mbus_code, 424[ 153.596326] px6130 10-0036: dbg_info: PX6130_enum_frame_size(469) (1280, 960)[ 153.596346] px6130 10-0036: PX6130_get_mbus_code, 424[ 153.596349] px6130 10-0036: dbg_info: PX6130_enum_frame_size(459), state:0, fse:813d3b88[ 153.596354] px6130 10-0036: PX6130_get_mbus_code, 424[ 153.596357] px6130 10-0036: PX6130_get_mbus_code, 424[ 153.596360] px6130 10-0036: Err!! code: 8200, index: 2, mbus: 8200, size : 1[ 153.596365] px6130 10-0036: PX6130_get_mbus_code, 424
Code:
raspi4@raspberrypi:~/px6130/driver $ v4l2-ctl --all -d 0Driver Info:Driver name : unicamCard type : unicamBus info : platform:fe801000.csiDriver version : 6.6.51Capabilities : 0x85a00001Video CaptureMetadata CaptureRead/WriteStreamingExtended Pix FormatDevice CapabilitiesDevice Caps : 0x05200001Video CaptureRead/WriteStreamingExtended Pix FormatMedia Driver Info:Driver name : unicamModel : unicamSerial : Bus info : platform:fe801000.csiMedia version : 6.6.51Hardware revision: 0x00000000 (0)Driver version : 6.6.51Interface Info:ID : 0x03000005Type : V4L VideoEntity Info:ID : 0x00000003 (3)Name : unicam-imageFunction : V4L2 I/OFlags : defaultPad 0x01000004 : 0: Sink Link 0x02000007: from remote pad 0x1000002 of entity 'px6130 10-0036' (Camera Sensor): Data, Enabled, ImmutablePriority: 2Video input : 0 (Camera 0: ok)Format Video Capture:Width/Height : 1280/960Pixel Format : 'YUYV' (YUYV 4:2:2)Field : NoneBytes per Line : 2560Size Image : 2457600Colorspace : SMPTE 170MTransfer Function : Default (maps to Rec. 709)YCbCr/HSV Encoding: Default (maps to ITU-R 601)Quantization : Default (maps to Limited Range)Flags : Crop Capability Video Capture:Bounds : Left 0, Top 0, Width 1280, Height 960Default : Left 0, Top 0, Width 1280, Height 960Pixel Aspect: 1/1Selection Video Capture: crop, Left 0, Top 0, Width 1280, Height 960, Flags: Selection Video Capture: crop_default, Left 0, Top 0, Width 1280, Height 960, Flags: Selection Video Capture: crop_bounds, Left 0, Top 0, Width 1280, Height 960, Flags: Selection Video Capture: native_size, Left 0, Top 0, Width 1280, Height 960, Flags: Streaming Parameters Video Capture:Capabilities : timeperframeFrames per second: 30.000 (30/1)Read buffers : 2User Controls horizontal_flip 0x00980914 (bool) : default=0 value=0 flags=modify-layout vertical_flip 0x00980915 (bool) : default=0 value=0 flags=modify-layoutImage Source Controls vertical_blanking 0x009e0901 (int) : min=0 max=31807 step=1 default=40 value=40 horizontal_blanking 0x009e0902 (int) : min=1195 max=6911 step=1 default=1195 value=1195Image Processing Controls pixel_rate 0x009f0902 (int64) : min=74250000 max=74250000 step=1 default=74250000 value=74250000 flags=read-only
Code:
[ 170.999868] px6130 10-0036: PX6130_get_selection(581), target: 0[ 170.999904] __PX6130_get_pad_crop(337): 1[ 171.000821] px6130 10-0036: PX6130_get_pad_fmt(484), fmt: 1[ 171.000843] px6130 10-0036: size(1280, 960), code: 8200, fld: 1, color: 1, yuv_enc: 0, hsv_enc: 0[ 171.000861] px6130 10-0036: PX6130_get_mbus_code, 424[ 171.000907] px6130 10-0036: PX6130_get_mbus_code, 424[ 171.001107] px6130 10-0036: PX6130_get_selection(581), target: 2[ 171.001119] px6130 10-0036: PX6130_get_selection(581), target: 1[ 171.001435] px6130 10-0036: PX6130_get_selection(581), target: 0[ 171.001452] __PX6130_get_pad_crop(337): 1[ 171.001487] px6130 10-0036: PX6130_get_selection(581), target: 1[ 171.001517] px6130 10-0036: PX6130_get_selection(581), target: 2[ 171.001546] px6130 10-0036: PX6130_get_selection(581), target: 256[ 171.001559] px6130 10-0036: PX6130_get_selection(581), target: 257[ 171.001571] px6130 10-0036: PX6130_get_selection(581), target: 258[ 171.001583] px6130 10-0036: PX6130_get_selection(581), target: 259[ 171.001595] px6130 10-0036: PX6130_get_selection(581), target: 3[ 171.001661] px6130 10-0036: PX6130_g_frame_interval, 409[ 171.001987] px6130 10-0036: PX6130_get_selection(581), target: 256
Code:
raspi4@raspberrypi:~/px6130/driver $ v4l2-ctl -d 0 --stream-mmap --stream-count=10 --stream-to=output.raw<<<<<<<<<<
Code:
[ 336.646032] px6130 10-0036: PX6130_get_selection(581), target: 0[ 336.646067] __PX6130_get_pad_crop(337): 1[ 336.646370] px6130 10-0036: PX6130_get_selection(581), target: 256[ 336.670597] px6130 10-0036: PX6130_get_pad_fmt(484), fmt: 1[ 336.670641] px6130 10-0036: size(1280, 960), code: 8200, fld: 1, color: 1, yuv_enc: 0, hsv_enc: 0[ 336.670656] px6130 10-0036: PX6130_get_mbus_code, 424[ 336.675232] px6130 10-0036: dbg_info: PX6130_s_stream, line: 354, enable: 1(0)[ 336.675261] px6130 10-0036: PX6130_s_stream(365) ret: 0[ 336.675273] px6130 10-0036: PX6130_set_mode(239) ret: 0[ 336.675284] px6130 10-0036: PX6130_stream_on(254) ret: 0[ 336.675299] px6130 10-0036: dbg_info: PX6130_s_ctrl, line: 685, id: 10356994[ 336.675310] px6130 10-0036: V4L2_CID_HBLANK val:1195[ 336.675319] px6130 10-0036: dbg_info: PX6130_s_ctrl, line: 685, id: 10356993[ 336.675329] px6130 10-0036: V4L2_CID_VBLANK val:40[ 336.675338] px6130 10-0036: dbg_info: PX6130_s_ctrl, line: 685, id: 9963796[ 336.675348] px6130 10-0036: dbg_info: PX6130_s_ctrl, line: 685, id: 9963797[ 336.675358] px6130 10-0036: V4L2_CID_VFLIP val:0[ 336.675366] px6130 10-0036: PX6130_stream_on(262): ret 0[ 336.675375] px6130 10-0036: PX6130_s_stream(372) ret: 0[ 336.675384] px6130 10-0036: dbg_info: PX6130_s_stream, line: 389[ 336.675429] px6130 10-0036: PX6130_get_pad_fmt(484), fmt: 1[ 336.675439] px6130 10-0036: size(1280, 960), code: 8200, fld: 1, color: 1, yuv_enc: 0, hsv_enc: 0[ 336.675452] px6130 10-0036: PX6130_get_mbus_code, 424[ 337.196646] px6130 10-0036: dbg_info: PX6130_s_stream, line: 354, enable: 0(1)[ 337.196668] px6130 10-0036: dbg_info: PX6130_s_stream, line: 378[ 337.196672] px6130 10-0036: PX6130_stream_off(273)[ 337.196676] px6130 10-0036: dbg_info: PX6130_s_stream, line: 389
Thank you.
Statistics: Posted by stupid dog — Fri Nov 29, 2024 10:56 am — Replies 0 — Views 32