From 8721f29e27f21111c439ee45f4ea76b56017d016 Mon Sep 17 00:00:00 2001 From: kgmt0 Date: Fri, 17 Jun 2022 02:08:44 +0700 Subject: [PATCH] WIP: Support Pinephone Pro Both cameras work now but the quality is very poor. The config file format is extended to include 2 new options "media-formats" and "media-crops" that can be used to set formats and crops on a per-pad basis. These are needed to initialize Pinephone Pro's media controller. Tested with Linux 6.0.8-1 from Arch Linux ARM (linux-megi). --- config/motorola,osprey.ini | 1 + config/pine64,pinephone-pro.ini | 51 ++++++++ config/xiaomi,scorpio.ini | 1 + meson.build | 1 + src/camera.c | 10 +- src/camera_config.c | 61 ++++++++++ src/camera_config.h | 23 ++++ src/device.c | 82 +++++++++++++ src/device.h | 9 ++ src/io_pipeline.c | 201 +++++++++++++++++++++++++------- 10 files changed, 392 insertions(+), 48 deletions(-) create mode 100644 config/pine64,pinephone-pro.ini diff --git a/config/motorola,osprey.ini b/config/motorola,osprey.ini index ab8990d..5809a8e 100644 --- a/config/motorola,osprey.ini +++ b/config/motorola,osprey.ini @@ -15,3 +15,4 @@ preview-rate=30 preview-fmt=RGGB10P rotate=270 media-links=msm_csiphy0:1->msm_csid0:0,msm_csid0:1->msm_ispif0:0,msm_ispif0:1->msm_vfe0_rdi0:0 +media-formats=msm_csiphy0:1:RGGB10P:4096:2304,msm_csid0:0:RGGB10P:4096:2304,msm_csid0:1:RGGB10P:4096:2304,msm_ispif0:0:RGGB10P:4096:2304,msm_ispif0:1:RGGB10P:4096:2304,msm_vfe0_rdi0:0:RGGB10P:4096:2304 diff --git a/config/pine64,pinephone-pro.ini b/config/pine64,pinephone-pro.ini new file mode 100644 index 0000000..3416ca0 --- /dev/null +++ b/config/pine64,pinephone-pro.ini @@ -0,0 +1,51 @@ +[device] +make=PINE64 +model=PinePhone + +[rear] +driver=imx258 1-001a +media-driver=rkisp1 +capture-width=1048 +capture-height=780 +capture-rate=30 +capture-fmt=RGGB8 +preview-width=1048 +preview-height=780 +preview-rate=30 +preview-fmt=RGGB8 +rotate=270 +mirrored=false +blacklevel=3 +whitelevel=255 +focallength=2.35 +cropfactor=10.81 +fnumber=2.2 +iso-min=100 +iso-max=64000 +flash-path=/sys/class/leds/white:flash +media-links=imx258 1-001a:0->rkisp1_csi:0,rkisp1_csi:1->rkisp1_isp:0,rkisp1_isp:2->rkisp1_resizer_mainpath:0 +media-formats=imx258 1-001a:0:RGGB10P:1048:780,rkisp1_csi:0:RGGB10P:1048:780,rkisp1_isp:0:RGGB10P:1048:780,rkisp1_isp:2:RGGB8:1048:780,rkisp1_resizer_mainpath:0:RGGB8:1048:780,rkisp1_resizer_mainpath:1:RGGB8:1048:780 +media-crops=rkisp1_isp:0:0:0:1048:780,rkisp1_isp:2:0:0:1048:780,rkisp1_resizer_mainpath:0:0:0:1048:768 + +[front] +driver=m00_f_ov8858 1-0036 +media-driver=rkisp1 +capture-width=3264 +capture-height=2448 +capture-rate=30 +;capture-fmt=BGGR10P +capture-fmt=BGGR8 +preview-width=3264 +preview-height=2448 +preview-rate=30 +;preview-fmt=BGGR10P +preview-fmt=BGGR8 +rotate=90 +mirrored=true +focallength=2.94 +cropfactor=12.7 +fnumber=2.4 +flash-display=true +media-links=m00_f_ov8858 1-0036:0->rkisp1_csi:0,rkisp1_csi:1->rkisp1_isp:0,rkisp1_isp:2->rkisp1_resizer_mainpath:0,rkisp1_resizer_mainpath:1->rkisp1_mainpath:0 +media-formats=m00_f_ov8858 1-0036:0:BGGR10P:3264:2448,rkisp1_csi:0:BGGR10P:3264:2448,rkisp1_isp:0:BGGR10P:3264:2448,rkisp1_isp:2:BGGR8:3264:2448,rkisp1_resizer_mainpath:0:BGGR8:3264:2448,rkisp1_resizer_mainpath:1:BGGR8:3264:2448 +media-crops=rkisp1_isp:0:0:0:3264:2448,rkisp1_isp:2:0:0:3264:2448,rkisp1_resizer_mainpath:0:0:0:3264:768 diff --git a/config/xiaomi,scorpio.ini b/config/xiaomi,scorpio.ini index cce6d49..14b7f29 100644 --- a/config/xiaomi,scorpio.ini +++ b/config/xiaomi,scorpio.ini @@ -15,3 +15,4 @@ preview-rate=30 preview-fmt=RGGB10P rotate=90 media-links=imx318 3-001a:0->msm_csiphy0:0,msm_csiphy0:1->msm_csid0:0,msm_csid0:1->msm_ispif0:0,msm_ispif0:1->msm_vfe0_rdi0:0 +media-formats=imx318 3-001a:0:RGGB10P:3840:2160,msm_csiphy0:0:RGGB10P:3840:2160,msm_csiphy0:1:RGGB10P:3840:2160,msm_csid0:0:RGGB10P:3840:2160,msm_csid0:1:RGGB10P:3840:2160,msm_ispif0:0:RGGB10P:3840:2160,msm_ispif0:1:RGGB10P:3840:2160,msm_vfe0_rdi0:0:RGGB10P:3840:2160 diff --git a/meson.build b/meson.build index 3b98f19..d53dec9 100644 --- a/meson.build +++ b/meson.build @@ -72,6 +72,7 @@ install_data( 'config/pine64,pinephone-1.0.ini', 'config/pine64,pinephone-1.1.ini', 'config/pine64,pinephone-1.2.ini', + 'config/pine64,pinephone-pro.ini', 'config/pine64,pinetab.ini', 'config/xiaomi,scorpio.ini', ], diff --git a/src/camera.c b/src/camera.c index 9a08f56..7368189 100644 --- a/src/camera.c +++ b/src/camera.c @@ -305,11 +305,11 @@ mp_camera_set_mode(MPCamera *camera, MPMode *mode) } // Update the mode - mode->pixel_format = - mp_pixel_format_from_v4l_bus_code(fmt.format.code); - mode->frame_interval = interval.interval; - mode->width = fmt.format.width; - mode->height = fmt.format.height; + //mode->pixel_format = + // mp_pixel_format_from_v4l_bus_code(fmt.format.code); + //mode->frame_interval = interval.interval; + //mode->width = fmt.format.width; + //mode->height = fmt.format.height; } // Set the mode for the video device diff --git a/src/camera_config.c b/src/camera_config.c index 6ff74d1..1102354 100644 --- a/src/camera_config.c +++ b/src/camera_config.c @@ -184,6 +184,67 @@ config_ini_handler(void *user, ++cc->num_media_links; } g_strfreev(linkdefs); + } else if (strcmp(name, "media-formats") == 0) { + struct mp_camera_config *cc = &cameras[index]; + char **formatdefs = g_strsplit(value, ",", 0); + + for (int i = 0; i < MP_MAX_FORMATS && formatdefs[i] != NULL; + ++i) { + char **entry = g_strsplit(formatdefs[i], ":", 5); + char *name = entry[0]; + int pad = strtoint(entry[1], NULL, 10); + char *format = entry[2]; + char *width = entry[3]; + char *height = entry[4]; + + const size_t name_size = + sizeof(cc->media_formats[i].name); + strncpy(cc->media_formats[i].name, + name, + name_size ); + + cc->media_formats[i].pad = pad; + + cc->media_formats[i].mode.pixel_format = + mp_pixel_format_from_str(format); + cc->media_formats[i].mode.width = + strtoint(width, NULL, 10); + cc->media_formats[i].mode.height = + strtoint(height, NULL, 10); + + cc->num_media_formats++; + + g_strfreev(entry); + } + } else if (strcmp(name, "media-crops") == 0) { + char **formatdefs = g_strsplit(value, ",", 0); + + for (int i = 0; i < MP_MAX_CROPS && formatdefs[i] != NULL; + ++i) { + char **entry = g_strsplit(formatdefs[i], ":", 6); + char *name = entry[0]; + int pad = strtoint(entry[1], NULL, 10); + int top = strtoint(entry[2], NULL, 10); + int left = strtoint(entry[3], NULL, 10); + int width = strtoint(entry[4], NULL, 10); + int height = strtoint(entry[5], NULL, 10); + + const size_t name_size = + sizeof(cc->media_crops[i].name); + strncpy(cc->media_crops[i].name, + name, + name_size ); + + cc->media_crops[i].pad = pad; + cc->media_crops[i].top = top; + cc->media_crops[i].left = left; + cc->media_crops[i].width = width; + cc->media_crops[i].height = height; + + cc->num_media_crops++; + + g_strfreev(entry); + } } else if (strcmp(name, "colormatrix") == 0) { sscanf(value, "%f,%f,%f,%f,%f,%f,%f,%f,%f", diff --git a/src/camera_config.h b/src/camera_config.h index d53d36f..b1bd5a5 100644 --- a/src/camera_config.h +++ b/src/camera_config.h @@ -7,6 +7,8 @@ #define MP_MAX_CAMERAS 5 #define MP_MAX_LINKS 10 +#define MP_MAX_FORMATS 10 +#define MP_MAX_CROPS 10 struct mp_media_link_config { char source_name[100]; @@ -15,6 +17,21 @@ struct mp_media_link_config { int target_port; }; +struct mp_media_format_config { + char name[100]; + int pad; + MPMode mode; +}; + +struct mp_media_crop_config { + char name[100]; + int pad; + int left; + int top; + int width; + int height; +}; + struct mp_camera_config { size_t index; @@ -30,6 +47,12 @@ struct mp_camera_config { struct mp_media_link_config media_links[MP_MAX_LINKS]; int num_media_links; + struct mp_media_format_config media_formats[MP_MAX_FORMATS]; + int num_media_formats; + + struct mp_media_crop_config media_crops[MP_MAX_CROPS]; + int num_media_crops; + float colormatrix[9]; float forwardmatrix[9]; float previewmatrix[9]; diff --git a/src/device.c b/src/device.c index 9e2db00..0defccb 100644 --- a/src/device.c +++ b/src/device.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include bool mp_find_device_path(struct media_v2_intf_devnode devnode, char *path, int length) @@ -237,6 +239,73 @@ mp_entity_pad_set_format(MPDevice *device, return true; } +const struct media_v2_pad * +mp_device_get_pad_at_index_from_entity(const MPDevice *device, uint32_t entity_id, uint32_t index) +{ + for (int i = 0; i < device->num_pads; ++i) { + if (device->pads[i].entity_id == entity_id && index-- == 0) { + return &device->pads[i]; + } + } + return NULL; +} + +bool +mp_device_setup_link_by_name(MPDevice *device, + const char *source_entity_name, + uint32_t source_pad_index, + const char *sink_entity_name, + uint32_t sink_pad_index, + bool enabled) +{ + const struct media_v2_entity *source_entity = + mp_device_find_entity + (device, source_entity_name); + const struct media_v2_entity *sink_entity = + mp_device_find_entity + (device, sink_entity_name); + + struct media_link_desc link = {}; + link.flags = enabled ? MEDIA_LNK_FL_ENABLED : 0; + link.source.entity = source_entity->id; + link.source.index = source_pad_index; + link.sink.entity = sink_entity->id; + link.sink.index = sink_pad_index; + if (xioctl(device->fd, MEDIA_IOC_SETUP_LINK, &link) == -1) { + errno_printerr("MEDIA_IOC_SETUP_LINK"); + return false; + } + + return true; +} + +int +mp_device_get_fd_by_name(const MPDevice *device, const char *driver_name) +{ + struct media_entity_desc desc = {}; + + desc.id |= MEDIA_ENT_ID_FLAG_NEXT; + + while(true) { + desc.id |= MEDIA_ENT_ID_FLAG_NEXT; + if(xioctl(device->fd, MEDIA_IOC_ENUM_ENTITIES, &desc) == -1) { + errno_printerr("MEDIA_IOC_ENUM_ENTITIES"); + return -1; + } + + if(strncmp(desc.name, driver_name, 32) == 0) { + const uint32_t major = desc.dev.major; + const uint32_t minor = desc.dev.minor; + char path[256]; + int rc = snprintf(path, 256, "/dev/char/%u:%u", major, minor); + + return rc > 0 ? open(path, O_RDWR) : -1; + } + } + + return -1; +} + const struct media_v2_entity * mp_device_find_entity(const MPDevice *device, const char *driver_name) { @@ -263,6 +332,19 @@ mp_device_find_entity_type(const MPDevice *device, const uint32_t type) return NULL; } +bool +mp_xioctl(const MPDevice *device, const char *driver_name, unsigned long request, void *argp) +{ + int fd = mp_device_get_fd_by_name(device, driver_name); + + if(fd < 0) + { + printf("ERROR: device with driver name %s not found\n", driver_name); + } + + return fd >= 0 && xioctl(fd, request, argp) != -1; +} + const struct media_device_info * mp_device_get_info(const MPDevice *device) { diff --git a/src/device.h b/src/device.h index 1894c67..a9e1b59 100644 --- a/src/device.h +++ b/src/device.h @@ -36,6 +36,15 @@ bool mp_entity_pad_set_format(MPDevice *device, uint32_t pad, MPMode *mode); +bool mp_xioctl(const MPDevice *device, const char *driver_name, unsigned long request, void *argp); + +bool mp_device_setup_link_by_name(MPDevice *device, + const char *source_entity_name, + uint32_t source_pad_index, + const char *sink_entity_name, + uint32_t sink_pad_index, + bool enabled); + const struct media_device_info *mp_device_get_info(const MPDevice *device); const struct media_v2_entity *mp_device_find_entity(const MPDevice *device, const char *driver_name); diff --git a/src/io_pipeline.c b/src/io_pipeline.c index 8434420..da95678 100644 --- a/src/io_pipeline.c +++ b/src/io_pipeline.c @@ -47,7 +47,8 @@ struct camera_info { // int media_fd; // struct mp_media_link media_links[MP_MAX_LINKS]; - // int num_media_links; + struct mp_media_link_config media_links[MP_MAX_LINKS]; + int num_media_links; // int gain_ctrl; }; @@ -100,36 +101,68 @@ static bool want_focus = false; static MPPipeline *pipeline; static GSource *capture_source; +// TODO: move to device.c +static void +mp_setup_media_link_pad_crops(struct device_info *dev_info, + const struct mp_media_crop_config media_crops[], + int num_media_crops) +{ + for(int i = 0; i < num_media_crops; i++) { + const struct mp_media_crop_config *crop = media_crops + i; + struct v4l2_subdev_crop v4l2_crop = {}; + v4l2_crop.pad = crop->pad; + v4l2_crop.which = V4L2_SUBDEV_FORMAT_ACTIVE; + v4l2_crop.rect.top = crop->top; + v4l2_crop.rect.left = crop->left; + v4l2_crop.rect.width = crop->width; + v4l2_crop.rect.height = crop->height; + + if(!mp_xioctl(dev_info->device, crop->name, VIDIOC_SUBDEV_S_CROP, &v4l2_crop)) { + //errno_printerr("VIDIOC_SUBDEV_S_CROP"); + } + } +} + static void mp_setup_media_link_pad_formats(struct device_info *dev_info, - const struct mp_media_link_config media_links[], - int num_media_links, - MPMode *mode) + const struct mp_media_format_config media_formats[], + int num_media_formats) { - const struct media_v2_entity *entities[2]; - int ports[2]; - for (int i = 0; i < num_media_links; i++) { - entities[0] = mp_device_find_entity( - dev_info->device, (const char *)media_links[i].source_name); - entities[1] = mp_device_find_entity( - dev_info->device, (const char *)media_links[i].target_name); - ports[0] = media_links[i].source_port; - ports[1] = media_links[i].target_port; - - for (int j = 0; j < 2; j++) - if (!mp_entity_pad_set_format( - dev_info->device, entities[j], ports[j], mode)) { - g_printerr("Failed to set %s:%d format\n", - entities[j]->name, - ports[j]); - exit(EXIT_FAILURE); - } + for(int i = 0; i < num_media_formats; i++) { + const struct mp_media_format_config *format = + media_formats + i; + const struct media_v2_entity *entity = + mp_device_find_entity + (dev_info->device, format->name); + MPMode *mode = + (MPMode *) + &format->mode; + bool successful = + mp_entity_pad_set_format + (dev_info->device, entity, format->pad, mode); + + if(!successful) { + g_printerr( "Failed to set %s:%d format\n", + entity->name, + format->pad ); + exit(EXIT_FAILURE); + } } } static void setup_camera(MPDeviceList **device_list, const struct mp_camera_config *config) { + char compat[512]; + FILE *fp = fopen("/proc/device-tree/compatible", "r"); + fgets(compat, 512, fp); + fclose(fp); + + printf("setup_camera()\n"); + printf("compatible: %s\n", compat); + printf("media_dev: %s\n", config->media_dev_name); + printf("dev: %s\n", config->dev_name); + // Find device info size_t device_index = 0; for (; device_index < num_devices; ++device_index) { @@ -144,6 +177,7 @@ setup_camera(MPDeviceList **device_list, const struct mp_camera_config *config) if (device_index == num_devices) { device_index = num_devices; + printf("initializing new device\n"); // Initialize new device struct device_info *info = &devices[device_index]; info->media_dev_name = config->media_dev_name; @@ -175,6 +209,8 @@ setup_camera(MPDeviceList **device_list, const struct mp_camera_config *config) exit(EXIT_FAILURE); } + printf("video path: %s\n", dev_name); + info->video_fd = open(dev_name, O_RDWR); if (info->video_fd == -1) { g_printerr("Could not open %s: %s\n", @@ -191,6 +227,9 @@ setup_camera(MPDeviceList **device_list, const struct mp_camera_config *config) struct device_info *dev_info = &devices[device_index]; info->device_index = device_index; + info->num_media_links = config->num_media_links; + + memcpy(info->media_links, config->media_links, MP_MAX_LINKS * sizeof(struct mp_media_link_config)); const struct media_v2_entity *entity = mp_device_find_entity(dev_info->device, config->dev_name); @@ -205,11 +244,48 @@ setup_camera(MPDeviceList **device_list, const struct mp_camera_config *config) info->pad_id = pad->id; + // Disable all links + const size_t num_links = + mp_device_get_num_links(dev_info->device); + const struct media_v2_link *links = + mp_device_get_links(dev_info->device); + + for(int i = 0; i < num_links; i++) { + const struct media_v2_link *link = links + i; + + if(!(link->flags & MEDIA_LNK_FL_IMMUTABLE)) { + mp_device_setup_link(dev_info->device, + link->source_id, + link->sink_id, + false); + } + } + // Make sure the camera starts out as disabled - mp_device_setup_link(dev_info->device, - info->pad_id, - dev_info->interface_pad_id, - false); + printf("making sure camera starts out disabled\n"); + if(config->num_media_links > 0) + { + const struct media_v2_entity *entity = + mp_device_find_entity(dev_info->device, config->media_links[0].source_name); + + // This gets the first pad for this entity, which is + // fine for Pinephone Pro, but does it really work for + // all devices? + const struct media_v2_pad* pad = + mp_device_get_pad_from_entity(dev_info->device, entity->id); + + mp_device_setup_link(dev_info->device, + info->pad_id, + pad->id, + false); + } + else + { + mp_device_setup_link(dev_info->device, + info->pad_id, + dev_info->interface_pad_id, + false); + } const struct media_v2_interface *interface = mp_device_find_entity_interface(dev_info->device, @@ -220,6 +296,7 @@ setup_camera(MPDeviceList **device_list, const struct mp_camera_config *config) exit(EXIT_FAILURE); } + printf("camera device path: %s\n", info->dev_fname); info->fd = open(info->dev_fname, O_RDWR); if (info->fd == -1) { g_printerr("Could not open %s: %s\n", @@ -234,11 +311,14 @@ setup_camera(MPDeviceList **device_list, const struct mp_camera_config *config) // the ov5640 driver where it won't allow setting the preview // format initially. MPMode mode = config->capture_mode; - if (config->num_media_links) + if (config->num_media_formats) mp_setup_media_link_pad_formats(dev_info, - config->media_links, - config->num_media_links, - &mode); + config->media_formats, + config->num_media_formats); + if (config->num_media_crops) + mp_setup_media_link_pad_crops(dev_info, + config->media_crops, + config->num_media_crops); mp_camera_set_mode(info->camera, &mode); // Trigger continuous auto focus if the sensor supports it @@ -400,9 +480,12 @@ capture(MPPipeline *pipeline, const void *data) mode = camera->capture_mode; if (camera->num_media_links) mp_setup_media_link_pad_formats(dev_info, - camera->media_links, - camera->num_media_links, - &mode); + camera->media_formats, + camera->num_media_formats); + if (camera->num_media_crops) + mp_setup_media_link_pad_crops(dev_info, + camera->media_crops, + camera->num_media_crops); mp_camera_set_mode(info->camera, &mode); just_switched_mode = true; @@ -569,9 +652,13 @@ on_frame(MPBuffer buffer, void *_data) if (camera->num_media_links) mp_setup_media_link_pad_formats( dev_info, - camera->media_links, - camera->num_media_links, - &mode); + camera->media_formats, + camera->num_media_formats); + if (camera->num_media_crops) + mp_setup_media_link_pad_crops( + dev_info, + camera->media_crops, + camera->num_media_crops); mp_camera_set_mode(info->camera, &mode); just_switched_mode = true; @@ -609,6 +696,8 @@ mp_setup_media_link(struct device_info *dev_info, static void update_state(MPPipeline *pipeline, const struct mp_io_pipeline_state *state) { + printf("update_state()\n"); + // Make sure the state isn't updated more than it needs to be by checking // whether this state change actually changes anything. bool has_changed = false; @@ -617,6 +706,7 @@ update_state(MPPipeline *pipeline, const struct mp_io_pipeline_state *state) has_changed = true; if (camera) { + printf("uninitializing current camera\n"); struct camera_info *info = &cameras[camera->index]; struct device_info *dev_info = &devices[info->device_index]; @@ -634,6 +724,7 @@ update_state(MPPipeline *pipeline, const struct mp_io_pipeline_state *state) } if (capture_source) { + printf("uninitializing current capture source\n"); g_source_destroy(capture_source); capture_source = NULL; } @@ -641,13 +732,33 @@ update_state(MPPipeline *pipeline, const struct mp_io_pipeline_state *state) camera = state->camera; if (camera) { + printf("initializing camera\n"); struct camera_info *info = &cameras[camera->index]; struct device_info *dev_info = &devices[info->device_index]; - mp_device_setup_link(dev_info->device, - info->pad_id, - dev_info->interface_pad_id, - true); + if(info->num_media_links > 0) + { + const struct media_v2_entity *entity = + mp_device_find_entity(dev_info->device, info->media_links[0].source_name); + + // This gets the first pad for this entity, which is + // fine for Pinephone Pro, but does it really work for + // all devices? + const struct media_v2_pad* pad = + mp_device_get_pad_from_entity(dev_info->device, entity->id); + + mp_device_setup_link(dev_info->device, + info->pad_id, + pad->id, + true); + } + else + { + mp_device_setup_link(dev_info->device, + info->pad_id, + dev_info->interface_pad_id, + true); + } // Enable media links for (int i = 0; i < camera->num_media_links; i++) @@ -658,9 +769,13 @@ update_state(MPPipeline *pipeline, const struct mp_io_pipeline_state *state) if (camera->num_media_links) mp_setup_media_link_pad_formats( dev_info, - camera->media_links, - camera->num_media_links, - &mode); + camera->media_formats, + camera->num_media_formats); + if (camera->num_media_crops) + mp_setup_media_link_pad_crops( + dev_info, + camera->media_crops, + camera->num_media_crops); mp_camera_set_mode(info->camera, &mode); mp_camera_start_capture(info->camera);