Quantcast
Channel: Raspberry Pi Forums
Viewing all articles
Browse latest Browse all 4060

Interfacing (DSI, CSI, I2C, etc.) • Re: MIPI CSI2 device driver for 1D iamge sensor

$
0
0
Hi, 6by9,
Thank you for your reply.

I copied some driver source code and overlay files.
then, I made "test_sensor.c" for driver, and "test_sensor.dts" for overlay.

I confirmed my driver can communicate with sensor via I2C, but I couldn't get any streams.
just I wanna get raw data from sensor.
What functions I need to implement?


Here's my code.

Code:

#include <asm/unaligned.h>#include <linux/clk-provider.h>#include <linux/clk.h>#include <linux/clkdev.h>#include <linux/delay.h>#include <linux/gpio/consumer.h>#include <linux/i2c.h>#include <linux/module.h>#include <linux/pm_runtime.h>#include <linux/regulator/consumer.h>#include <media/v4l2-ctrls.h>#include <media/v4l2-device.h>#include <media/v4l2-event.h>#include <media/v4l2-fwnode.h>#include <media/v4l2-mediabus.h>// #include <time.h>#define WIDTH 1024#define HEIGHT 1024struct s_test_sensor {  struct v4l2_subdev sd;  struct media_pad pad;  struct v4l2_mbus_framefmt format;  struct mutex mutex;};static int test_sensor_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) {  struct v4l2_mbus_framefmt *try_fmt =      v4l2_subdev_get_try_format(sd, fh->state, 0);  try_fmt->width = WIDTH;  try_fmt->height = HEIGHT;  try_fmt->code = MEDIA_BUS_FMT_UYVY8_1X16;  try_fmt->field = V4L2_FIELD_NONE;  return 0;}static void test_sensor_set_default_format(struct s_test_sensor *test_sensor) {  struct v4l2_mbus_framefmt *fmt = &test_sensor->format;  fmt->code = MEDIA_BUS_FMT_UYVY8_1X16;  fmt->colorspace = V4L2_COLORSPACE_RAW;  fmt->width = WIDTH;  fmt->height = HEIGHT;  fmt->field = V4L2_FIELD_NONE;}static inline struct s_test_sensor *to_test_sensor(struct v4l2_subdev *sd) {  return container_of(sd, struct s_test_sensor, sd);}static int test_sensor_start_stream(struct v4l2_subdev *sd, int is_on) {  struct s_test_sensor *test_sensor = to_test_sensor(sd);  // MEAS_START register is located in 0x0100  unsigned char send_data[2] = {0x01, 0x00};  // get i2c_client object  struct i2c_client *client = v4l2_get_subdevdata(&test_sensor->sd);  if (client == NULL) {    return -1;  }  // set address(0x0100) to write value  int command_res = i2c_master_send(client, send_data, 2);  unsigned char write_data[1] = {0x00};  if (is_on) {    write_data[0] = 0x01;    // if stream starts, set 0x01  } else {    write_data[0] = 0x00;  }  command_res = i2c_master_send(client, write_data, 1);  if (command_res < 0) {    return -1;  }  return 0;}static int test_sensor_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;  struct s_test_sensor *test_sensor = to_test_sensor(sd);  /* only supported format in the driver is Raw 10 bits SRGGB */  mutex_lock(&test_sensor->mutex);  code->code = MEDIA_BUS_FMT_UYVY8_1X16;  mutex_unlock(&test_sensor->mutex);  return 0;}static int test_sensor_get_fmt(struct v4l2_subdev *sd,                               struct v4l2_subdev_state *sd_state,                               struct v4l2_subdev_format *fmt) {  struct s_test_sensor *test_sensor = to_test_sensor(sd);  mutex_lock(&test_sensor->mutex);  fmt->format.width = WIDTH;  fmt->format.height = HEIGHT;  fmt->format.code = MEDIA_BUS_FMT_UYVY8_1X16;  fmt->format.field = V4L2_FIELD_NONE;  mutex_unlock(&test_sensor->mutex);  return 0;}static int test_sensor_set_fmt(struct v4l2_subdev *sd,                               struct v4l2_subdev_state *sd_state,                               struct v4l2_subdev_format *format) {  struct v4l2_mbus_framefmt *fmt = &format->format;  struct s_test_sensor *test_sensor = to_test_sensor(sd);  return 0;}static const struct v4l2_subdev_video_ops test_sensor_video_ops = {    .s_stream = test_sensor_start_stream,};static const struct v4l2_subdev_pad_ops test_sensor_pad_ops = {    .enum_mbus_code = test_sensor_enum_mbus_code,    .get_fmt = test_sensor_get_fmt,    .set_fmt = test_sensor_set_fmt,};static const struct v4l2_subdev_ops test_sensor_subdev_ops = {    .video = &test_sensor_video_ops,    .pad = &test_sensor_pad_ops,};static const struct v4l2_subdev_internal_ops test_sensor_internal_ops = {    .open = test_sensor_open,};static int test_sensor_probe(struct i2c_client *client) {  struct device *dev = &client->dev;  struct s_test_sensor *test_sensor;  test_sensor = devm_kzalloc(&client->dev, sizeof(*test_sensor), GFP_KERNEL);  if (!test_sensor) {    return -ENOMEM;  }  // initialize sub device options  v4l2_i2c_subdev_init(&test_sensor->sd, client, &test_sensor_subdev_ops);  usleep_range(100, 110);  test_sensor->sd.internal_ops = &test_sensor_internal_ops;  test_sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;  test_sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;  test_sensor->pad.flags = MEDIA_PAD_FL_SOURCE;  test_sensor->pad.index = 1;  test_sensor_set_default_format(test_sensor);  // intialize pad  int pad_res =      media_entity_pads_init(&test_sensor->sd.entity, 1, &test_sensor->pad);  // register test_sensor as v4l2 device  int register_res = v4l2_async_register_subdev_sensor(&test_sensor->sd);  return 0;}static void test_sensor_remove(struct i2c_client *client) {  struct v4l2_subdev *sd = i2c_get_clientdata(client);  struct s_test_sensor *test_sensor = to_test_sensor(sd);  v4l2_async_unregister_subdev(sd);}static const struct of_device_id test_sensor_dt_ids[] = {    {.compatible = "MillenniumScienceSchool,test_sensor"}, {}};static struct i2c_driver test_sensor_driver = {    .driver =        {            .name = "test_sensor",            .of_match_table = test_sensor_dt_ids,        },    .probe = test_sensor_probe,    .remove = test_sensor_remove,};MODULE_DEVICE_TABLE(of, test_sensor_dt_ids);module_i2c_driver(test_sensor_driver);MODULE_LICENSE("GPL");

Code:

/dts-v1/;/ {        compatible = "brcm,bcm2835";        fragment@0 {                target = <0xffffffff>;                phandle = <0x05>;                __overlay__ {                        #address-cells = <0x01>;                        #size-cells = <0x00>;                        status = "okay";                        test_sensor@2a {                                compatible = "MillenniumScienceSchool,test_sensor";                                                                reg = <0x2a>;                                status = "okay";                                clocks = <0xffffffff>;                                clock-names = "xclk";                                VANA-supply = <0xffffffff>;                                VDIG-supply = <0xffffffff>;                                VDDL-supply = <0xffffffff>;                                rotation = <0x00>;                                orientation = <0x02>;                                phandle = <0x04>;                                port {                                        endpoint {                                                remote-endpoint = <0x01>;                                                clock-lanes = <0x00>;                                                data-lanes = <0x01 0x02>;                                                clock-noncontinuous;                                                link-frequencies = <0x00 0x11b3dc40>;                                                phandle = <0x02>;                                        };                                };                        };                };        };        fragment@1 {                target = <0xffffffff>;                phandle = <0x06>;                __overlay__ {                        status = "okay";                        brcm,media-controller;                        phandle = <0x03>;                        port {                                endpoint {                                        remote-endpoint = <0x02>;                                        clock-lanes = <0x00>;                                        data-lanes = <0x01 0x02>;                                        clock-noncontinuous;                                        phandle = <0x01>;                                };                        };                };        };        fragment@2 {                target = <0xffffffff>;                __overlay__ {                        status = "okay";                };        };        fragment@3 {                target = <0xffffffff>;                phandle = <0x07>;                __overlay__ {                        clock-frequency = <0x17d78400>;                        status = "okay";                };        };        fragment@4 {                target = <0xffffffff>;                __overlay__ {                        status = "okay";                };        };        __overrides__ {                media-controller = [00 00 00 03 62 72 63 6d 2c 6d 65 64 69 61 2d 63 6f 6e 74 72 6f 6c 6c 65 72 3f 00];                rotation = [00 00 00 04 72 6f 74 61 74 69 6f 6e 3a 30 00];                orientation = [00 00 00 04 6f 72 69 65 6e 74 61 74 69 6f 6e 3a 30 00];                cam0 = [00 00 00 05 74 61 72 67 65 74 3a 30 3d 00 ff ff ff ff 00 00 00 06 74 61 72 67 65 74 3a 30 3d 00 ff ff ff ff 00 00 00 07 74 61 72 67 65 74 3a 30 3d 00 ff ff ff ff 00 00 00 04 63 6c 6f 63 6b 73 3a 30 3d 00 ff ff ff ff 00 00 00 04 56 41 4e 41 2d 73 75 70 70 6c 79 3a 30 3d 00 ff ff ff ff];        };        __fixups__ {                i2c_csi_dsi = "/fragment@0:target:0";                cam1_clk = "/fragment@0/__overlay__/test_sensor@2a:clocks:0\0/fragment@3:target:0";                cam1_reg = "/fragment@0/__overlay__/test_sensor@2a:VANA-supply:0";                cam_dummy_reg = "/fragment@0/__overlay__/test_sensor@2a:VDIG-supply:0\0/fragment@0/__overlay__/test_sensor@2a:VDDL-supply:0";                csi1 = "/fragment@1:target:0";                i2c0if = "/fragment@2:target:0";                i2c0mux = "/fragment@4:target:0";                i2c_csi_dsi0 = "/__overrides__:cam0:14";                csi0 = "/__overrides__:cam0:32";                cam0_clk = "/__overrides__:cam0:50\0/__overrides__:cam0:68";                cam0_reg = "/__overrides__:cam0:91";        };        __local_fixups__ {                fragment@0 {                        __overlay__ {                                test_sensor@2a {                                        port {                                                endpoint {                                                        remote-endpoint = <0x00>;                                                };                                        };                                };                        };                };                fragment@1 {                        __overlay__ {                                port {                                        endpoint {                                                remote-endpoint = <0x00>;                                        };                                };                        };                };                __overrides__ {                        media-controller = <0x00>;                        rotation = <0x00>;                        orientation = <0x00>;                        cam0 = <0x00 0x12 0x24 0x36 0x48>;                };        };};
The I2C address of my sensor is 0x2a.

The process I followed is in below.
1. compile "test_sensor.dts" to "test_sensor.dtbo" and locate it in "/boot/firmware/overlay/test_sensor.dtbo"
2. modify "/boot/firmware/config.txt" for overlay

Code:

dtoverlay=vc4-kms-v3ddtoverlay=test_sensor
3. compile `test_sensor.c` to `test_sensor.ko` and insmod it


I tried some commands to get stream.
First, I set sensor's resolution and pixel format.

v4l2-ctl -v width=1024,height=1024,pixelformat=Y10P

Code:

[ 1648.856060] unicam fe801000.csi: unicam_calc_format_size_bpl: fourcc: 50303159 size: 1024x1024 bpl:1280 img_size:1310720[ 1648.856089] unicam fe801000.csi: unicam_mc_try_fmt: 50303159 1024x1024 (bytesperline 1280 sizeimage 1310720)
Then, I tried some commands.

libcamera-hello

Code:

[0:23:39.147303486] [3607]  INFO Camera camera_manager.cpp:313 libcamera v0.3.0+65-6ddd79b5[0:23:39.162325264] [3610] ERROR CameraSensor camera_sensor.cpp:127 'test_sensor 10-002a': No image format found[0:23:39.162423153] [3610] ERROR RPI vc4.cpp:215 Failed to register camera test_sensor 10-002a: -22Preview window unavailableERROR: *** no cameras available ***

v4l2-ctl --stream-mmap=3 --stream-count=1000 --stream-to=test.raw

Code:

VIDIOC_STREAMON returned -1 (Invalid argument)[ 1696.000263] unicam fe801000.csi: Failed to start media pipeline: -22
Is there any lists of functions that driver need to implement?
I've found sensor driver requirements by libcamera.
https://github.com/raspberrypi/libcamer ... ements.rst

Thanks,

Statistics: Posted by Koyuki — Wed Sep 25, 2024 2:38 am



Viewing all articles
Browse latest Browse all 4060

Trending Articles