[{"id":5283,"web_url":"https://patchwork.libcamera.org/comment/5283/","msgid":"<61a74894-25ae-5ca1-d434-c360f4dba7eb@ideasonboard.com>","date":"2020-06-19T12:10:24","subject":"Re: [libcamera-devel] [PATCH] libcamera: pipelines: Add VIVID\n\tpipeline support","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Hi Kieran,\n\nOn 19/06/2020 13:02, Kieran Bingham wrote:\n> The VIVID driver supports more pixel formats and properties than the VIMC\n> driver, and can provide extended testing for libcamera.\n> \n> The VIMC pipeline handler is duplicated and simplified to support the\n> VIVID device.\n> \n> Unfortuantely, the VIVID device can not be handled by either of the\n\n\"Unfortunately\"\n\n> generic UVC or Simple pipeline handlers.\n> \n> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\nOf course I realise there is resistance to merging more pipeline\nhandlers at the moment, but this is now the *third* time I've sent this\npipeline out for individuals to use/see - so I'm sending it to the list.\n\n\n> ---\n>  meson_options.txt                        |   2 +-\n>  src/libcamera/pipeline/vivid/meson.build |   5 +\n>  src/libcamera/pipeline/vivid/vivid.cpp   | 441 +++++++++++++++++++++++\n>  3 files changed, 447 insertions(+), 1 deletion(-)\n>  create mode 100644 src/libcamera/pipeline/vivid/meson.build\n>  create mode 100644 src/libcamera/pipeline/vivid/vivid.cpp\n> \n> diff --git a/meson_options.txt b/meson_options.txt\n> index badace151bb6..dc4684df49b2 100644\n> --- a/meson_options.txt\n> +++ b/meson_options.txt\n> @@ -16,7 +16,7 @@ option('gstreamer',\n>  \n>  option('pipelines',\n>          type : 'array',\n> -        choices : ['ipu3', 'raspberrypi', 'rkisp1', 'simple', 'uvcvideo', 'vimc'],\n> +        choices : ['ipu3', 'raspberrypi', 'rkisp1', 'simple', 'uvcvideo', 'vimc', 'vivid'],\n>          description : 'Select which pipeline handlers to include')\n>  \n>  option('test',\n> diff --git a/src/libcamera/pipeline/vivid/meson.build b/src/libcamera/pipeline/vivid/meson.build\n> new file mode 100644\n> index 000000000000..086bb825387c\n> --- /dev/null\n> +++ b/src/libcamera/pipeline/vivid/meson.build\n> @@ -0,0 +1,5 @@\n> +# SPDX-License-Identifier: CC0-1.0\n> +\n> +libcamera_sources += files([\n> +    'vivid.cpp',\n> +])\n> diff --git a/src/libcamera/pipeline/vivid/vivid.cpp b/src/libcamera/pipeline/vivid/vivid.cpp\n> new file mode 100644\n> index 000000000000..b811e33a0299\n> --- /dev/null\n> +++ b/src/libcamera/pipeline/vivid/vivid.cpp\n> @@ -0,0 +1,441 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2018, Google Inc.\n\nThis should be 2020 of course,\n\n> + *\n> + * vivid.cpp - Pipeline handler for the vivid capture device\n> + */\n> +\n> +#include <algorithm>\n> +#include <iomanip>\n> +#include <map>\n> +#include <math.h>\n> +#include <tuple>\n> +\n> +#include <linux/media-bus-format.h>\n> +#include <linux/version.h>\n> +\n> +#include <libcamera/camera.h>\n> +#include <libcamera/control_ids.h>\n> +#include <libcamera/controls.h>\n\n> +#include <libcamera/ipa/ipa_interface.h>\n> +#include <libcamera/ipa/ipa_module_info.h>\n\nNo need for IPA headers of course, as there is no IPA.\n\n> +#include <libcamera/request.h>\n> +#include <libcamera/stream.h>\n> +\n> +#include \"libcamera/internal/camera_sensor.h\"\n> +#include \"libcamera/internal/device_enumerator.h\"\n> +#include \"libcamera/internal/ipa_manager.h\"\n> +#include \"libcamera/internal/log.h\"\n> +#include \"libcamera/internal/media_device.h\"\n> +#include \"libcamera/internal/pipeline_handler.h\"\n> +#include \"libcamera/internal/utils.h\"\n> +#include \"libcamera/internal/v4l2_controls.h\"\n> +#include \"libcamera/internal/v4l2_subdevice.h\"\n> +#include \"libcamera/internal/v4l2_videodevice.h\"\n> +\n> +#define VIVID_CID_CUSTOM_BASE           (V4L2_CID_USER_BASE | 0xf000)\n> +#define VIVID_CID_BUTTON                (VIVID_CID_CUSTOM_BASE + 0)\n> +#define VIVID_CID_BOOLEAN               (VIVID_CID_CUSTOM_BASE + 1)\n> +#define VIVID_CID_INTEGER               (VIVID_CID_CUSTOM_BASE + 2)\n> +#define VIVID_CID_INTEGER64             (VIVID_CID_CUSTOM_BASE + 3)\n> +#define VIVID_CID_MENU                  (VIVID_CID_CUSTOM_BASE + 4)\n> +#define VIVID_CID_STRING                (VIVID_CID_CUSTOM_BASE + 5)\n> +#define VIVID_CID_BITMASK               (VIVID_CID_CUSTOM_BASE + 6)\n> +#define VIVID_CID_INTMENU               (VIVID_CID_CUSTOM_BASE + 7)\n> +#define VIVID_CID_U32_ARRAY             (VIVID_CID_CUSTOM_BASE + 8)\n> +#define VIVID_CID_U16_MATRIX            (VIVID_CID_CUSTOM_BASE + 9)\n> +#define VIVID_CID_U8_4D_ARRAY           (VIVID_CID_CUSTOM_BASE + 10)\n> +\n> +#define VIVID_CID_VIVID_BASE            (0x00f00000 | 0xf000)\n> +#define VIVID_CID_VIVID_CLASS           (0x00f00000 | 1)\n> +#define VIVID_CID_TEST_PATTERN          (VIVID_CID_VIVID_BASE + 0)\n> +#define VIVID_CID_OSD_TEXT_MODE         (VIVID_CID_VIVID_BASE + 1)\n> +#define VIVID_CID_HOR_MOVEMENT          (VIVID_CID_VIVID_BASE + 2)\n> +#define VIVID_CID_VERT_MOVEMENT         (VIVID_CID_VIVID_BASE + 3)\n> +#define VIVID_CID_SHOW_BORDER           (VIVID_CID_VIVID_BASE + 4)\n> +#define VIVID_CID_SHOW_SQUARE           (VIVID_CID_VIVID_BASE + 5)\n> +#define VIVID_CID_INSERT_SAV            (VIVID_CID_VIVID_BASE + 6)\n> +#define VIVID_CID_INSERT_EAV            (VIVID_CID_VIVID_BASE + 7)\n> +#define VIVID_CID_VBI_CAP_INTERLACED    (VIVID_CID_VIVID_BASE + 8)\n> +\n> +#define VIVID_CID_HFLIP                 (VIVID_CID_VIVID_BASE + 20)\n> +#define VIVID_CID_VFLIP                 (VIVID_CID_VIVID_BASE + 21)\n> +#define VIVID_CID_STD_ASPECT_RATIO      (VIVID_CID_VIVID_BASE + 22)\n> +#define VIVID_CID_DV_TIMINGS_ASPECT_RATIO       (VIVID_CID_VIVID_BASE + 23)\n> +#define VIVID_CID_TSTAMP_SRC            (VIVID_CID_VIVID_BASE + 24)\n> +#define VIVID_CID_COLORSPACE            (VIVID_CID_VIVID_BASE + 25)\n> +#define VIVID_CID_XFER_FUNC             (VIVID_CID_VIVID_BASE + 26)\n> +#define VIVID_CID_YCBCR_ENC             (VIVID_CID_VIVID_BASE + 27)\n> +#define VIVID_CID_QUANTIZATION          (VIVID_CID_VIVID_BASE + 28)\n> +#define VIVID_CID_LIMITED_RGB_RANGE     (VIVID_CID_VIVID_BASE + 29)\n> +#define VIVID_CID_ALPHA_MODE            (VIVID_CID_VIVID_BASE + 30)\n> +#define VIVID_CID_HAS_CROP_CAP          (VIVID_CID_VIVID_BASE + 31)\n> +#define VIVID_CID_HAS_COMPOSE_CAP       (VIVID_CID_VIVID_BASE + 32)\n> +#define VIVID_CID_HAS_SCALER_CAP        (VIVID_CID_VIVID_BASE + 33)\n> +#define VIVID_CID_HAS_CROP_OUT          (VIVID_CID_VIVID_BASE + 34)\n> +#define VIVID_CID_HAS_COMPOSE_OUT       (VIVID_CID_VIVID_BASE + 35)\n> +#define VIVID_CID_HAS_SCALER_OUT        (VIVID_CID_VIVID_BASE + 36)\n> +#define VIVID_CID_LOOP_VIDEO            (VIVID_CID_VIVID_BASE + 37)\n> +#define VIVID_CID_SEQ_WRAP              (VIVID_CID_VIVID_BASE + 38)\n> +#define VIVID_CID_TIME_WRAP             (VIVID_CID_VIVID_BASE + 39)\n> +#define VIVID_CID_MAX_EDID_BLOCKS       (VIVID_CID_VIVID_BASE + 40)\n> +#define VIVID_CID_PERCENTAGE_FILL       (VIVID_CID_VIVID_BASE + 41)\n> +#define VIVID_CID_REDUCED_FPS           (VIVID_CID_VIVID_BASE + 42)\n> +#define VIVID_CID_HSV_ENC               (VIVID_CID_VIVID_BASE + 43)\n> +#define VIVID_CID_DISPLAY_PRESENT       (VIVID_CID_VIVID_BASE + 44)\n> +\n> +#define VIVID_CID_STD_SIGNAL_MODE       (VIVID_CID_VIVID_BASE + 60)\n> +#define VIVID_CID_STANDARD              (VIVID_CID_VIVID_BASE + 61)\n> +#define VIVID_CID_DV_TIMINGS_SIGNAL_MODE        (VIVID_CID_VIVID_BASE + 62)\n> +#define VIVID_CID_DV_TIMINGS            (VIVID_CID_VIVID_BASE + 63)\n> +#define VIVID_CID_PERC_DROPPED          (VIVID_CID_VIVID_BASE + 64)\n> +#define VIVID_CID_DISCONNECT            (VIVID_CID_VIVID_BASE + 65)\n> +#define VIVID_CID_DQBUF_ERROR           (VIVID_CID_VIVID_BASE + 66)\n> +#define VIVID_CID_QUEUE_SETUP_ERROR     (VIVID_CID_VIVID_BASE + 67)\n> +#define VIVID_CID_BUF_PREPARE_ERROR     (VIVID_CID_VIVID_BASE + 68)\n> +#define VIVID_CID_START_STR_ERROR       (VIVID_CID_VIVID_BASE + 69)\n> +#define VIVID_CID_QUEUE_ERROR           (VIVID_CID_VIVID_BASE + 70)\n> +#define VIVID_CID_CLEAR_FB              (VIVID_CID_VIVID_BASE + 71)\n> +#define VIVID_CID_REQ_VALIDATE_ERROR    (VIVID_CID_VIVID_BASE + 72)\n> +\n> +#define VIVID_CID_RADIO_SEEK_MODE       (VIVID_CID_VIVID_BASE + 90)\n> +#define VIVID_CID_RADIO_SEEK_PROG_LIM   (VIVID_CID_VIVID_BASE + 91)\n> +#define VIVID_CID_RADIO_RX_RDS_RBDS     (VIVID_CID_VIVID_BASE + 92)\n> +#define VIVID_CID_RADIO_RX_RDS_BLOCKIO  (VIVID_CID_VIVID_BASE + 93)\n> +\n> +#define VIVID_CID_RADIO_TX_RDS_BLOCKIO  (VIVID_CID_VIVID_BASE + 94)\n> +\n> +#define VIVID_CID_SDR_CAP_FM_DEVIATION  (VIVID_CID_VIVID_BASE + 110)\n> +\n> +\n> +namespace libcamera {\n> +\n> +LOG_DEFINE_CATEGORY(VIVID)\n> +\n> +class VividCameraData : public CameraData\n> +{\n> +public:\n> +\tVividCameraData(PipelineHandler *pipe, MediaDevice *media)\n> +\t\t: CameraData(pipe), media_(media), video_(nullptr)\n> +\t{\n> +\t}\n> +\n> +\t~VividCameraData()\n> +\t{\n> +\t\tdelete video_;\n> +\t}\n> +\n> +\tint init();\n> +\tvoid bufferReady(FrameBuffer *buffer);\n> +\n> +\tMediaDevice *media_;\n> +\tV4L2VideoDevice *video_;\n> +\tStream stream_;\n> +};\n> +\n> +class VividCameraConfiguration : public CameraConfiguration\n> +{\n> +public:\n> +\tVividCameraConfiguration();\n> +\n> +\tStatus validate() override;\n> +};\n> +\n> +class PipelineHandlerVivid : public PipelineHandler\n> +{\n> +public:\n> +\tPipelineHandlerVivid(CameraManager *manager);\n> +\n> +\tCameraConfiguration *generateConfiguration(Camera *camera,\n> +\t\t\t\t\t\t   const StreamRoles &roles) override;\n> +\tint configure(Camera *camera, CameraConfiguration *config) override;\n> +\n> +\tint exportFrameBuffers(Camera *camera, Stream *stream,\n> +\t\t\t       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;\n> +\n> +\tint start(Camera *camera) override;\n> +\tvoid stop(Camera *camera) override;\n> +\n> +\tint queueRequestDevice(Camera *camera, Request *request) override;\n> +\n> +\tbool match(DeviceEnumerator *enumerator) override;\n> +\n> +private:\n> +\tint processControls(VividCameraData *data, Request *request);\n> +\n> +\tVividCameraData *cameraData(const Camera *camera)\n> +\t{\n> +\t\treturn static_cast<VividCameraData *>(\n> +\t\t\tPipelineHandler::cameraData(camera));\n> +\t}\n> +};\n> +\n> +VividCameraConfiguration::VividCameraConfiguration()\n> +\t: CameraConfiguration()\n> +{\n> +}\n> +\n> +CameraConfiguration::Status VividCameraConfiguration::validate()\n> +{\n> +\tStatus status = Valid;\n> +\n> +\tif (config_.empty())\n> +\t\treturn Invalid;\n> +\n> +\t/* Cap the number of entries to the available streams. */\n> +\tif (config_.size() > 1) {\n> +\t\tconfig_.resize(1);\n> +\t\tstatus = Adjusted;\n> +\t}\n> +\n> +\tStreamConfiguration &cfg = config_[0];\n> +\n> +\t/* Adjust the pixel format. */\n> +\tconst std::vector<libcamera::PixelFormat> formats = cfg.formats().pixelformats();\n> +\tif (std::find(formats.begin(), formats.end(), cfg.pixelFormat) == formats.end()) {\n> +\t\tcfg.pixelFormat = cfg.formats().pixelformats()[0];\n> +\t\tLOG(VIVID, Debug) << \"Adjusting format to \" << cfg.pixelFormat.toString();\n> +\t\tstatus = Adjusted;\n> +\t}\n> +\n> +\tcfg.bufferCount = 4;\n> +\n> +\treturn status;\n> +}\n> +\n> +PipelineHandlerVivid::PipelineHandlerVivid(CameraManager *manager)\n> +\t: PipelineHandler(manager)\n> +{\n> +}\n> +\n> +CameraConfiguration *PipelineHandlerVivid::generateConfiguration(Camera *camera,\n> +\t\t\t\t\t\t\t\t const StreamRoles &roles)\n> +{\n> +\tCameraConfiguration *config = new VividCameraConfiguration();\n> +\tVividCameraData *data = cameraData(camera);\n> +\n> +\tif (roles.empty())\n> +\t\treturn config;\n> +\n> +\tstd::map<V4L2PixelFormat, std::vector<SizeRange>> v4l2Formats =\n> +\t\tdata->video_->formats();\n> +\tstd::map<PixelFormat, std::vector<SizeRange>> deviceFormats;\n> +\tstd::transform(v4l2Formats.begin(), v4l2Formats.end(),\n> +\t\t       std::inserter(deviceFormats, deviceFormats.begin()),\n> +\t\t       [&](const decltype(v4l2Formats)::value_type &format) {\n> +\t\t\t       return decltype(deviceFormats)::value_type{\n> +\t\t\t\t       format.first.toPixelFormat(),\n> +\t\t\t\t       format.second\n> +\t\t\t       };\n> +\t\t       });\n> +\n> +\tStreamFormats formats(deviceFormats);\n> +\tStreamConfiguration cfg(formats);\n> +\n> +\tcfg.pixelFormat = PixelFormat(DRM_FORMAT_BGR888);\n> +\tcfg.size = { 1280, 720 };\n> +\tcfg.bufferCount = 4;\n> +\n> +\tconfig->addConfiguration(cfg);\n> +\n> +\tconfig->validate();\n> +\n> +\treturn config;\n> +}\n> +\n> +int PipelineHandlerVivid::configure(Camera *camera, CameraConfiguration *config)\n> +{\n> +\tVividCameraData *data = cameraData(camera);\n> +\tStreamConfiguration &cfg = config->at(0);\n> +\tint ret;\n> +\n> +\tV4L2DeviceFormat format = {};\n> +\tformat.fourcc = data->video_->toV4L2PixelFormat(cfg.pixelFormat);\n> +\tformat.size = cfg.size;\n> +\n> +\tret = data->video_->setFormat(&format);\n> +\tif (ret)\n> +\t\treturn ret;\n> +\n> +\tif (format.size != cfg.size ||\n> +\t    format.fourcc != data->video_->toV4L2PixelFormat(cfg.pixelFormat))\n> +\t\treturn -EINVAL;\n> +\n> +\tcfg.setStream(&data->stream_);\n> +\tcfg.stride = format.planes[0].bpl;\n> +\n> +\treturn 0;\n> +}\n> +\n> +int PipelineHandlerVivid::exportFrameBuffers(Camera *camera, Stream *stream,\n> +\t\t\t\t\t     std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n> +{\n> +\tVividCameraData *data = cameraData(camera);\n> +\tunsigned int count = stream->configuration().bufferCount;\n> +\n> +\treturn data->video_->exportBuffers(count, buffers);\n> +}\n> +\n> +int PipelineHandlerVivid::start(Camera *camera)\n> +{\n> +\tVividCameraData *data = cameraData(camera);\n> +\tunsigned int count = data->stream_.configuration().bufferCount;\n> +\n> +\tint ret = data->video_->importBuffers(count);\n> +\tif (ret < 0)\n> +\t\treturn ret;\n> +\n> +\tret = data->video_->streamOn();\n> +\tif (ret < 0) {\n> +\t\tdata->ipa_->stop();\n> +\t\tdata->video_->releaseBuffers();\n> +\t\treturn ret;\n> +\t}\n> +\n> +\treturn 0;\n> +}\n> +\n> +void PipelineHandlerVivid::stop(Camera *camera)\n> +{\n> +\tVividCameraData *data = cameraData(camera);\n> +\tdata->video_->streamOff();\n> +\tdata->video_->releaseBuffers();\n> +}\n> +\n> +int PipelineHandlerVivid::processControls(VividCameraData *data, Request *request)\n> +{\n> +\tControlList controls(data->video_->controls());\n> +\n> +\tfor (auto it : request->controls()) {\n> +\t\tunsigned int id = it.first;\n> +\t\tunsigned int offset;\n> +\t\tuint32_t cid;\n> +\n> +\t\tif (id == controls::Brightness) {\n> +\t\t\tcid = V4L2_CID_BRIGHTNESS;\n> +\t\t\toffset = 128;\n> +\t\t} else if (id == controls::Contrast) {\n> +\t\t\tcid = V4L2_CID_CONTRAST;\n> +\t\t\toffset = 0;\n> +\t\t} else if (id == controls::Saturation) {\n> +\t\t\tcid = V4L2_CID_SATURATION;\n> +\t\t\toffset = 0;\n> +\t\t} else {\n> +\t\t\tcontinue;\n> +\t\t}\n> +\n> +\t\tint32_t value = lroundf(it.second.get<float>() * 128 + offset);\n> +\t\tcontrols.set(cid, utils::clamp(value, 0, 255));\n> +\t}\n> +\n> +\tfor (const auto &ctrl : controls)\n> +\t\tLOG(VIVID, Debug)\n> +\t\t\t<< \"Setting control \" << utils::hex(ctrl.first)\n> +\t\t\t<< \" to \" << ctrl.second.toString();\n> +\n> +\tint ret = data->video_->setControls(&controls);\n> +\tif (ret) {\n> +\t\tLOG(VIVID, Error) << \"Failed to set controls: \" << ret;\n> +\t\treturn ret < 0 ? ret : -EINVAL;\n> +\t}\n> +\n> +\treturn ret;\n> +}\n> +\n> +int PipelineHandlerVivid::queueRequestDevice(Camera *camera, Request *request)\n> +{\n> +\tVividCameraData *data = cameraData(camera);\n> +\tFrameBuffer *buffer = request->findBuffer(&data->stream_);\n> +\tif (!buffer) {\n> +\t\tLOG(VIVID, Error)\n> +\t\t\t<< \"Attempt to queue request with invalid stream\";\n> +\n> +\t\treturn -ENOENT;\n> +\t}\n> +\n> +\tint ret = processControls(data, request);\n> +\tif (ret < 0)\n> +\t\treturn ret;\n> +\n> +\tret = data->video_->queueBuffer(buffer);\n> +\tif (ret < 0)\n> +\t\treturn ret;\n> +\n> +\treturn 0;\n> +}\n> +\n> +bool PipelineHandlerVivid::match(DeviceEnumerator *enumerator)\n> +{\n> +\tDeviceMatch dm(\"vivid\");\n> +\tdm.add(\"vivid-000-vid-cap\");\n> +\n> +\tMediaDevice *media = acquireMediaDevice(enumerator, dm);\n> +\tif (!media)\n> +\t\treturn false;\n> +\n> +\tstd::unique_ptr<VividCameraData> data = std::make_unique<VividCameraData>(this, media);\n> +\n> +\t/* Locate and open the capture video node. */\n> +\tif (data->init())\n> +\t\treturn false;\n> +\n> +\t/* Create and register the camera. */\n> +\tstd::set<Stream *> streams{ &data->stream_ };\n> +\tstd::shared_ptr<Camera> camera = Camera::create(this, data->video_->deviceName(), streams);\n> +\tregisterCamera(std::move(camera), std::move(data));\n> +\n> +\treturn true;\n> +}\n> +\n> +int VividCameraData::init()\n> +{\n> +\tvideo_ = new V4L2VideoDevice(media_->getEntityByName(\"vivid-000-vid-cap\"));\n> +\tif (video_->open())\n> +\t\treturn -ENODEV;\n> +\n> +\tvideo_->bufferReady.connect(this, &VividCameraData::bufferReady);\n> +\n> +\t/* Initialise the supported controls. */\n> +\tconst ControlInfoMap &controls = video_->controls();\n> +\tControlInfoMap::Map ctrls;\n> +\n> +\tfor (const auto &ctrl : controls) {\n> +\t\tconst ControlId *id;\n> +\t\tControlInfo info;\n> +\n> +\t\tswitch (ctrl.first->id()) {\n> +\t\tcase V4L2_CID_BRIGHTNESS:\n> +\t\t\tid = &controls::Brightness;\n> +\t\t\tinfo = ControlInfo{ { -1.0f }, { 1.0f }, { 0.0f } };\n> +\t\t\tbreak;\n> +\t\tcase V4L2_CID_CONTRAST:\n> +\t\t\tid = &controls::Contrast;\n> +\t\t\tinfo = ControlInfo{ { 0.0f }, { 2.0f }, { 1.0f } };\n> +\t\t\tbreak;\n> +\t\tcase V4L2_CID_SATURATION:\n> +\t\t\tid = &controls::Saturation;\n> +\t\t\tinfo = ControlInfo{ { 0.0f }, { 2.0f }, { 1.0f } };\n> +\t\t\tbreak;\n> +\t\tdefault:\n> +\t\t\tcontinue;\n> +\t\t}\n> +\n> +\t\tctrls.emplace(id, info);\n> +\t}\n> +\n> +\tcontrolInfo_ = std::move(ctrls);\n> +\n> +\treturn 0;\n> +}\n> +\n> +void VividCameraData::bufferReady(FrameBuffer *buffer)\n> +{\n> +\tRequest *request = buffer->request();\n> +\n> +\tpipe_->completeBuffer(camera_, request, buffer);\n> +\tpipe_->completeRequest(camera_, request);\n> +}\n> +\n> +REGISTER_PIPELINE_HANDLER(PipelineHandlerVivid);\n> +\n> +} /* namespace libcamera */\n>","headers":{"Return-Path":"<kieran.bingham@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id BA47B60103\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 19 Jun 2020 14:10:28 +0200 (CEST)","from [192.168.0.20]\n\t(cpc89242-aztw30-2-0-cust488.18-1.cable.virginm.net [86.31.129.233])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 2B124556;\n\tFri, 19 Jun 2020 14:10:28 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"u65harZE\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1592568628;\n\tbh=KRHU6kDCKolO2htdhL9DUYoic43qPO+da5uKXplcoTg=;\n\th=Reply-To:Subject:To:References:From:Date:In-Reply-To:From;\n\tb=u65harZEtBkUFnwXVo7JOUEF4QxmfA1SOmSoObszbquvrHuPMj5CskVRTzCrxsQmL\n\tDOjC+GjEV36oUI2kTR8aOI0oGHlKf1OZlGgJzOYsvVR/wND9SaFafLj41+LuL4bK7q\n\t0cQ8P8zNVikRr+S7JdAedtLpaWrmQFhezRTCfAUs=","Reply-To":"kieran.bingham@ideasonboard.com","To":"Paul Elder <paul.elder@ideasonboard.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>","References":"<20200619120249.1084998-1-kieran.bingham@ideasonboard.com>","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Autocrypt":"addr=kieran.bingham@ideasonboard.com; keydata=\n\t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\n\tWZw01QYHU/GUV/zHJSFk","Organization":"Ideas on Board","Message-ID":"<61a74894-25ae-5ca1-d434-c360f4dba7eb@ideasonboard.com>","Date":"Fri, 19 Jun 2020 13:10:24 +0100","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101\n\tThunderbird/68.8.0","MIME-Version":"1.0","In-Reply-To":"<20200619120249.1084998-1-kieran.bingham@ideasonboard.com>","Content-Type":"text/plain; charset=utf-8","Content-Language":"en-GB","Content-Transfer-Encoding":"8bit","Subject":"Re: [libcamera-devel] [PATCH] libcamera: pipelines: Add VIVID\n\tpipeline support","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Fri, 19 Jun 2020 12:10:29 -0000"}},{"id":5284,"web_url":"https://patchwork.libcamera.org/comment/5284/","msgid":"<594cdc1d-3041-20c8-c26e-5cff845d2c52@ideasonboard.com>","date":"2020-06-19T12:14:09","subject":"Re: [libcamera-devel] [PATCH] libcamera: pipelines: Add VIVID\n\tpipeline support","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"On 19/06/2020 13:10, Kieran Bingham wrote:\n> Hi Kieran,\n> \n> On 19/06/2020 13:02, Kieran Bingham wrote:\n>> The VIVID driver supports more pixel formats and properties than the VIMC\n>> driver, and can provide extended testing for libcamera.\n>>\n>> The VIMC pipeline handler is duplicated and simplified to support the\n>> VIVID device.\n>>\n>> Unfortuantely, the VIVID device can not be handled by either of the\n> \n> \"Unfortunately\"\n> \n>> generic UVC or Simple pipeline handlers.\n\nPerhaps an alternative route to supporting this however would be to make\nthe simple pipeline handler support pipelines which *don't* have a\nsensor object... though the VIVID controls could be useful to support\ndirectly.\n\nPerhaps - inheriting the simple-pipeline handler should be possible, to\nprovide some 'device specific' adjustments for things like device\nspecific controls.\n\n\n>> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> \n> Of course I realise there is resistance to merging more pipeline\n> handlers at the moment, but this is now the *third* time I've sent this\n> pipeline out for individuals to use/see - so I'm sending it to the list.\n> \n> \n>> ---\n>>  meson_options.txt                        |   2 +-\n>>  src/libcamera/pipeline/vivid/meson.build |   5 +\n>>  src/libcamera/pipeline/vivid/vivid.cpp   | 441 +++++++++++++++++++++++\n>>  3 files changed, 447 insertions(+), 1 deletion(-)\n>>  create mode 100644 src/libcamera/pipeline/vivid/meson.build\n>>  create mode 100644 src/libcamera/pipeline/vivid/vivid.cpp\n>>\n>> diff --git a/meson_options.txt b/meson_options.txt\n>> index badace151bb6..dc4684df49b2 100644\n>> --- a/meson_options.txt\n>> +++ b/meson_options.txt\n>> @@ -16,7 +16,7 @@ option('gstreamer',\n>>  \n>>  option('pipelines',\n>>          type : 'array',\n>> -        choices : ['ipu3', 'raspberrypi', 'rkisp1', 'simple', 'uvcvideo', 'vimc'],\n>> +        choices : ['ipu3', 'raspberrypi', 'rkisp1', 'simple', 'uvcvideo', 'vimc', 'vivid'],\n>>          description : 'Select which pipeline handlers to include')\n>>  \n>>  option('test',\n>> diff --git a/src/libcamera/pipeline/vivid/meson.build b/src/libcamera/pipeline/vivid/meson.build\n>> new file mode 100644\n>> index 000000000000..086bb825387c\n>> --- /dev/null\n>> +++ b/src/libcamera/pipeline/vivid/meson.build\n>> @@ -0,0 +1,5 @@\n>> +# SPDX-License-Identifier: CC0-1.0\n>> +\n>> +libcamera_sources += files([\n>> +    'vivid.cpp',\n>> +])\n>> diff --git a/src/libcamera/pipeline/vivid/vivid.cpp b/src/libcamera/pipeline/vivid/vivid.cpp\n>> new file mode 100644\n>> index 000000000000..b811e33a0299\n>> --- /dev/null\n>> +++ b/src/libcamera/pipeline/vivid/vivid.cpp\n>> @@ -0,0 +1,441 @@\n>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>> +/*\n>> + * Copyright (C) 2018, Google Inc.\n> \n> This should be 2020 of course,\n> \n>> + *\n>> + * vivid.cpp - Pipeline handler for the vivid capture device\n>> + */\n>> +\n>> +#include <algorithm>\n>> +#include <iomanip>\n>> +#include <map>\n>> +#include <math.h>\n>> +#include <tuple>\n>> +\n>> +#include <linux/media-bus-format.h>\n>> +#include <linux/version.h>\n>> +\n>> +#include <libcamera/camera.h>\n>> +#include <libcamera/control_ids.h>\n>> +#include <libcamera/controls.h>\n> \n>> +#include <libcamera/ipa/ipa_interface.h>\n>> +#include <libcamera/ipa/ipa_module_info.h>\n> \n> No need for IPA headers of course, as there is no IPA.\n> \n>> +#include <libcamera/request.h>\n>> +#include <libcamera/stream.h>\n>> +\n>> +#include \"libcamera/internal/camera_sensor.h\"\n>> +#include \"libcamera/internal/device_enumerator.h\"\n>> +#include \"libcamera/internal/ipa_manager.h\"\n>> +#include \"libcamera/internal/log.h\"\n>> +#include \"libcamera/internal/media_device.h\"\n>> +#include \"libcamera/internal/pipeline_handler.h\"\n>> +#include \"libcamera/internal/utils.h\"\n>> +#include \"libcamera/internal/v4l2_controls.h\"\n>> +#include \"libcamera/internal/v4l2_subdevice.h\"\n>> +#include \"libcamera/internal/v4l2_videodevice.h\"\n>> +\n>> +#define VIVID_CID_CUSTOM_BASE           (V4L2_CID_USER_BASE | 0xf000)\n>> +#define VIVID_CID_BUTTON                (VIVID_CID_CUSTOM_BASE + 0)\n>> +#define VIVID_CID_BOOLEAN               (VIVID_CID_CUSTOM_BASE + 1)\n>> +#define VIVID_CID_INTEGER               (VIVID_CID_CUSTOM_BASE + 2)\n>> +#define VIVID_CID_INTEGER64             (VIVID_CID_CUSTOM_BASE + 3)\n>> +#define VIVID_CID_MENU                  (VIVID_CID_CUSTOM_BASE + 4)\n>> +#define VIVID_CID_STRING                (VIVID_CID_CUSTOM_BASE + 5)\n>> +#define VIVID_CID_BITMASK               (VIVID_CID_CUSTOM_BASE + 6)\n>> +#define VIVID_CID_INTMENU               (VIVID_CID_CUSTOM_BASE + 7)\n>> +#define VIVID_CID_U32_ARRAY             (VIVID_CID_CUSTOM_BASE + 8)\n>> +#define VIVID_CID_U16_MATRIX            (VIVID_CID_CUSTOM_BASE + 9)\n>> +#define VIVID_CID_U8_4D_ARRAY           (VIVID_CID_CUSTOM_BASE + 10)\n>> +\n>> +#define VIVID_CID_VIVID_BASE            (0x00f00000 | 0xf000)\n>> +#define VIVID_CID_VIVID_CLASS           (0x00f00000 | 1)\n>> +#define VIVID_CID_TEST_PATTERN          (VIVID_CID_VIVID_BASE + 0)\n>> +#define VIVID_CID_OSD_TEXT_MODE         (VIVID_CID_VIVID_BASE + 1)\n>> +#define VIVID_CID_HOR_MOVEMENT          (VIVID_CID_VIVID_BASE + 2)\n>> +#define VIVID_CID_VERT_MOVEMENT         (VIVID_CID_VIVID_BASE + 3)\n>> +#define VIVID_CID_SHOW_BORDER           (VIVID_CID_VIVID_BASE + 4)\n>> +#define VIVID_CID_SHOW_SQUARE           (VIVID_CID_VIVID_BASE + 5)\n>> +#define VIVID_CID_INSERT_SAV            (VIVID_CID_VIVID_BASE + 6)\n>> +#define VIVID_CID_INSERT_EAV            (VIVID_CID_VIVID_BASE + 7)\n>> +#define VIVID_CID_VBI_CAP_INTERLACED    (VIVID_CID_VIVID_BASE + 8)\n>> +\n>> +#define VIVID_CID_HFLIP                 (VIVID_CID_VIVID_BASE + 20)\n>> +#define VIVID_CID_VFLIP                 (VIVID_CID_VIVID_BASE + 21)\n>> +#define VIVID_CID_STD_ASPECT_RATIO      (VIVID_CID_VIVID_BASE + 22)\n>> +#define VIVID_CID_DV_TIMINGS_ASPECT_RATIO       (VIVID_CID_VIVID_BASE + 23)\n>> +#define VIVID_CID_TSTAMP_SRC            (VIVID_CID_VIVID_BASE + 24)\n>> +#define VIVID_CID_COLORSPACE            (VIVID_CID_VIVID_BASE + 25)\n>> +#define VIVID_CID_XFER_FUNC             (VIVID_CID_VIVID_BASE + 26)\n>> +#define VIVID_CID_YCBCR_ENC             (VIVID_CID_VIVID_BASE + 27)\n>> +#define VIVID_CID_QUANTIZATION          (VIVID_CID_VIVID_BASE + 28)\n>> +#define VIVID_CID_LIMITED_RGB_RANGE     (VIVID_CID_VIVID_BASE + 29)\n>> +#define VIVID_CID_ALPHA_MODE            (VIVID_CID_VIVID_BASE + 30)\n>> +#define VIVID_CID_HAS_CROP_CAP          (VIVID_CID_VIVID_BASE + 31)\n>> +#define VIVID_CID_HAS_COMPOSE_CAP       (VIVID_CID_VIVID_BASE + 32)\n>> +#define VIVID_CID_HAS_SCALER_CAP        (VIVID_CID_VIVID_BASE + 33)\n>> +#define VIVID_CID_HAS_CROP_OUT          (VIVID_CID_VIVID_BASE + 34)\n>> +#define VIVID_CID_HAS_COMPOSE_OUT       (VIVID_CID_VIVID_BASE + 35)\n>> +#define VIVID_CID_HAS_SCALER_OUT        (VIVID_CID_VIVID_BASE + 36)\n>> +#define VIVID_CID_LOOP_VIDEO            (VIVID_CID_VIVID_BASE + 37)\n>> +#define VIVID_CID_SEQ_WRAP              (VIVID_CID_VIVID_BASE + 38)\n>> +#define VIVID_CID_TIME_WRAP             (VIVID_CID_VIVID_BASE + 39)\n>> +#define VIVID_CID_MAX_EDID_BLOCKS       (VIVID_CID_VIVID_BASE + 40)\n>> +#define VIVID_CID_PERCENTAGE_FILL       (VIVID_CID_VIVID_BASE + 41)\n>> +#define VIVID_CID_REDUCED_FPS           (VIVID_CID_VIVID_BASE + 42)\n>> +#define VIVID_CID_HSV_ENC               (VIVID_CID_VIVID_BASE + 43)\n>> +#define VIVID_CID_DISPLAY_PRESENT       (VIVID_CID_VIVID_BASE + 44)\n>> +\n>> +#define VIVID_CID_STD_SIGNAL_MODE       (VIVID_CID_VIVID_BASE + 60)\n>> +#define VIVID_CID_STANDARD              (VIVID_CID_VIVID_BASE + 61)\n>> +#define VIVID_CID_DV_TIMINGS_SIGNAL_MODE        (VIVID_CID_VIVID_BASE + 62)\n>> +#define VIVID_CID_DV_TIMINGS            (VIVID_CID_VIVID_BASE + 63)\n>> +#define VIVID_CID_PERC_DROPPED          (VIVID_CID_VIVID_BASE + 64)\n>> +#define VIVID_CID_DISCONNECT            (VIVID_CID_VIVID_BASE + 65)\n>> +#define VIVID_CID_DQBUF_ERROR           (VIVID_CID_VIVID_BASE + 66)\n>> +#define VIVID_CID_QUEUE_SETUP_ERROR     (VIVID_CID_VIVID_BASE + 67)\n>> +#define VIVID_CID_BUF_PREPARE_ERROR     (VIVID_CID_VIVID_BASE + 68)\n>> +#define VIVID_CID_START_STR_ERROR       (VIVID_CID_VIVID_BASE + 69)\n>> +#define VIVID_CID_QUEUE_ERROR           (VIVID_CID_VIVID_BASE + 70)\n>> +#define VIVID_CID_CLEAR_FB              (VIVID_CID_VIVID_BASE + 71)\n>> +#define VIVID_CID_REQ_VALIDATE_ERROR    (VIVID_CID_VIVID_BASE + 72)\n>> +\n>> +#define VIVID_CID_RADIO_SEEK_MODE       (VIVID_CID_VIVID_BASE + 90)\n>> +#define VIVID_CID_RADIO_SEEK_PROG_LIM   (VIVID_CID_VIVID_BASE + 91)\n>> +#define VIVID_CID_RADIO_RX_RDS_RBDS     (VIVID_CID_VIVID_BASE + 92)\n>> +#define VIVID_CID_RADIO_RX_RDS_BLOCKIO  (VIVID_CID_VIVID_BASE + 93)\n>> +\n>> +#define VIVID_CID_RADIO_TX_RDS_BLOCKIO  (VIVID_CID_VIVID_BASE + 94)\n>> +\n>> +#define VIVID_CID_SDR_CAP_FM_DEVIATION  (VIVID_CID_VIVID_BASE + 110)\n>> +\n>> +\n>> +namespace libcamera {\n>> +\n>> +LOG_DEFINE_CATEGORY(VIVID)\n>> +\n>> +class VividCameraData : public CameraData\n>> +{\n>> +public:\n>> +\tVividCameraData(PipelineHandler *pipe, MediaDevice *media)\n>> +\t\t: CameraData(pipe), media_(media), video_(nullptr)\n>> +\t{\n>> +\t}\n>> +\n>> +\t~VividCameraData()\n>> +\t{\n>> +\t\tdelete video_;\n>> +\t}\n>> +\n>> +\tint init();\n>> +\tvoid bufferReady(FrameBuffer *buffer);\n>> +\n>> +\tMediaDevice *media_;\n>> +\tV4L2VideoDevice *video_;\n>> +\tStream stream_;\n>> +};\n>> +\n>> +class VividCameraConfiguration : public CameraConfiguration\n>> +{\n>> +public:\n>> +\tVividCameraConfiguration();\n>> +\n>> +\tStatus validate() override;\n>> +};\n>> +\n>> +class PipelineHandlerVivid : public PipelineHandler\n>> +{\n>> +public:\n>> +\tPipelineHandlerVivid(CameraManager *manager);\n>> +\n>> +\tCameraConfiguration *generateConfiguration(Camera *camera,\n>> +\t\t\t\t\t\t   const StreamRoles &roles) override;\n>> +\tint configure(Camera *camera, CameraConfiguration *config) override;\n>> +\n>> +\tint exportFrameBuffers(Camera *camera, Stream *stream,\n>> +\t\t\t       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;\n>> +\n>> +\tint start(Camera *camera) override;\n>> +\tvoid stop(Camera *camera) override;\n>> +\n>> +\tint queueRequestDevice(Camera *camera, Request *request) override;\n>> +\n>> +\tbool match(DeviceEnumerator *enumerator) override;\n>> +\n>> +private:\n>> +\tint processControls(VividCameraData *data, Request *request);\n>> +\n>> +\tVividCameraData *cameraData(const Camera *camera)\n>> +\t{\n>> +\t\treturn static_cast<VividCameraData *>(\n>> +\t\t\tPipelineHandler::cameraData(camera));\n>> +\t}\n>> +};\n>> +\n>> +VividCameraConfiguration::VividCameraConfiguration()\n>> +\t: CameraConfiguration()\n>> +{\n>> +}\n>> +\n>> +CameraConfiguration::Status VividCameraConfiguration::validate()\n>> +{\n>> +\tStatus status = Valid;\n>> +\n>> +\tif (config_.empty())\n>> +\t\treturn Invalid;\n>> +\n>> +\t/* Cap the number of entries to the available streams. */\n>> +\tif (config_.size() > 1) {\n>> +\t\tconfig_.resize(1);\n>> +\t\tstatus = Adjusted;\n>> +\t}\n>> +\n>> +\tStreamConfiguration &cfg = config_[0];\n>> +\n>> +\t/* Adjust the pixel format. */\n>> +\tconst std::vector<libcamera::PixelFormat> formats = cfg.formats().pixelformats();\n>> +\tif (std::find(formats.begin(), formats.end(), cfg.pixelFormat) == formats.end()) {\n>> +\t\tcfg.pixelFormat = cfg.formats().pixelformats()[0];\n>> +\t\tLOG(VIVID, Debug) << \"Adjusting format to \" << cfg.pixelFormat.toString();\n>> +\t\tstatus = Adjusted;\n>> +\t}\n>> +\n>> +\tcfg.bufferCount = 4;\n>> +\n>> +\treturn status;\n>> +}\n>> +\n>> +PipelineHandlerVivid::PipelineHandlerVivid(CameraManager *manager)\n>> +\t: PipelineHandler(manager)\n>> +{\n>> +}\n>> +\n>> +CameraConfiguration *PipelineHandlerVivid::generateConfiguration(Camera *camera,\n>> +\t\t\t\t\t\t\t\t const StreamRoles &roles)\n>> +{\n>> +\tCameraConfiguration *config = new VividCameraConfiguration();\n>> +\tVividCameraData *data = cameraData(camera);\n>> +\n>> +\tif (roles.empty())\n>> +\t\treturn config;\n>> +\n>> +\tstd::map<V4L2PixelFormat, std::vector<SizeRange>> v4l2Formats =\n>> +\t\tdata->video_->formats();\n>> +\tstd::map<PixelFormat, std::vector<SizeRange>> deviceFormats;\n>> +\tstd::transform(v4l2Formats.begin(), v4l2Formats.end(),\n>> +\t\t       std::inserter(deviceFormats, deviceFormats.begin()),\n>> +\t\t       [&](const decltype(v4l2Formats)::value_type &format) {\n>> +\t\t\t       return decltype(deviceFormats)::value_type{\n>> +\t\t\t\t       format.first.toPixelFormat(),\n>> +\t\t\t\t       format.second\n>> +\t\t\t       };\n>> +\t\t       });\n>> +\n>> +\tStreamFormats formats(deviceFormats);\n>> +\tStreamConfiguration cfg(formats);\n>> +\n>> +\tcfg.pixelFormat = PixelFormat(DRM_FORMAT_BGR888);\n>> +\tcfg.size = { 1280, 720 };\n>> +\tcfg.bufferCount = 4;\n>> +\n>> +\tconfig->addConfiguration(cfg);\n>> +\n>> +\tconfig->validate();\n>> +\n>> +\treturn config;\n>> +}\n>> +\n>> +int PipelineHandlerVivid::configure(Camera *camera, CameraConfiguration *config)\n>> +{\n>> +\tVividCameraData *data = cameraData(camera);\n>> +\tStreamConfiguration &cfg = config->at(0);\n>> +\tint ret;\n>> +\n>> +\tV4L2DeviceFormat format = {};\n>> +\tformat.fourcc = data->video_->toV4L2PixelFormat(cfg.pixelFormat);\n>> +\tformat.size = cfg.size;\n>> +\n>> +\tret = data->video_->setFormat(&format);\n>> +\tif (ret)\n>> +\t\treturn ret;\n>> +\n>> +\tif (format.size != cfg.size ||\n>> +\t    format.fourcc != data->video_->toV4L2PixelFormat(cfg.pixelFormat))\n>> +\t\treturn -EINVAL;\n>> +\n>> +\tcfg.setStream(&data->stream_);\n>> +\tcfg.stride = format.planes[0].bpl;\n>> +\n>> +\treturn 0;\n>> +}\n>> +\n>> +int PipelineHandlerVivid::exportFrameBuffers(Camera *camera, Stream *stream,\n>> +\t\t\t\t\t     std::vector<std::unique_ptr<FrameBuffer>> *buffers)\n>> +{\n>> +\tVividCameraData *data = cameraData(camera);\n>> +\tunsigned int count = stream->configuration().bufferCount;\n>> +\n>> +\treturn data->video_->exportBuffers(count, buffers);\n>> +}\n>> +\n>> +int PipelineHandlerVivid::start(Camera *camera)\n>> +{\n>> +\tVividCameraData *data = cameraData(camera);\n>> +\tunsigned int count = data->stream_.configuration().bufferCount;\n>> +\n>> +\tint ret = data->video_->importBuffers(count);\n>> +\tif (ret < 0)\n>> +\t\treturn ret;\n>> +\n>> +\tret = data->video_->streamOn();\n>> +\tif (ret < 0) {\n>> +\t\tdata->ipa_->stop();\n>> +\t\tdata->video_->releaseBuffers();\n>> +\t\treturn ret;\n>> +\t}\n>> +\n>> +\treturn 0;\n>> +}\n>> +\n>> +void PipelineHandlerVivid::stop(Camera *camera)\n>> +{\n>> +\tVividCameraData *data = cameraData(camera);\n>> +\tdata->video_->streamOff();\n>> +\tdata->video_->releaseBuffers();\n>> +}\n>> +\n>> +int PipelineHandlerVivid::processControls(VividCameraData *data, Request *request)\n>> +{\n>> +\tControlList controls(data->video_->controls());\n>> +\n>> +\tfor (auto it : request->controls()) {\n>> +\t\tunsigned int id = it.first;\n>> +\t\tunsigned int offset;\n>> +\t\tuint32_t cid;\n>> +\n>> +\t\tif (id == controls::Brightness) {\n>> +\t\t\tcid = V4L2_CID_BRIGHTNESS;\n>> +\t\t\toffset = 128;\n>> +\t\t} else if (id == controls::Contrast) {\n>> +\t\t\tcid = V4L2_CID_CONTRAST;\n>> +\t\t\toffset = 0;\n>> +\t\t} else if (id == controls::Saturation) {\n>> +\t\t\tcid = V4L2_CID_SATURATION;\n>> +\t\t\toffset = 0;\n>> +\t\t} else {\n>> +\t\t\tcontinue;\n>> +\t\t}\n>> +\n>> +\t\tint32_t value = lroundf(it.second.get<float>() * 128 + offset);\n>> +\t\tcontrols.set(cid, utils::clamp(value, 0, 255));\n>> +\t}\n>> +\n>> +\tfor (const auto &ctrl : controls)\n>> +\t\tLOG(VIVID, Debug)\n>> +\t\t\t<< \"Setting control \" << utils::hex(ctrl.first)\n>> +\t\t\t<< \" to \" << ctrl.second.toString();\n>> +\n>> +\tint ret = data->video_->setControls(&controls);\n>> +\tif (ret) {\n>> +\t\tLOG(VIVID, Error) << \"Failed to set controls: \" << ret;\n>> +\t\treturn ret < 0 ? ret : -EINVAL;\n>> +\t}\n>> +\n>> +\treturn ret;\n>> +}\n>> +\n>> +int PipelineHandlerVivid::queueRequestDevice(Camera *camera, Request *request)\n>> +{\n>> +\tVividCameraData *data = cameraData(camera);\n>> +\tFrameBuffer *buffer = request->findBuffer(&data->stream_);\n>> +\tif (!buffer) {\n>> +\t\tLOG(VIVID, Error)\n>> +\t\t\t<< \"Attempt to queue request with invalid stream\";\n>> +\n>> +\t\treturn -ENOENT;\n>> +\t}\n>> +\n>> +\tint ret = processControls(data, request);\n>> +\tif (ret < 0)\n>> +\t\treturn ret;\n>> +\n>> +\tret = data->video_->queueBuffer(buffer);\n>> +\tif (ret < 0)\n>> +\t\treturn ret;\n>> +\n>> +\treturn 0;\n>> +}\n>> +\n>> +bool PipelineHandlerVivid::match(DeviceEnumerator *enumerator)\n>> +{\n>> +\tDeviceMatch dm(\"vivid\");\n>> +\tdm.add(\"vivid-000-vid-cap\");\n>> +\n>> +\tMediaDevice *media = acquireMediaDevice(enumerator, dm);\n>> +\tif (!media)\n>> +\t\treturn false;\n>> +\n>> +\tstd::unique_ptr<VividCameraData> data = std::make_unique<VividCameraData>(this, media);\n>> +\n>> +\t/* Locate and open the capture video node. */\n>> +\tif (data->init())\n>> +\t\treturn false;\n>> +\n>> +\t/* Create and register the camera. */\n>> +\tstd::set<Stream *> streams{ &data->stream_ };\n>> +\tstd::shared_ptr<Camera> camera = Camera::create(this, data->video_->deviceName(), streams);\n>> +\tregisterCamera(std::move(camera), std::move(data));\n>> +\n>> +\treturn true;\n>> +}\n>> +\n>> +int VividCameraData::init()\n>> +{\n>> +\tvideo_ = new V4L2VideoDevice(media_->getEntityByName(\"vivid-000-vid-cap\"));\n>> +\tif (video_->open())\n>> +\t\treturn -ENODEV;\n>> +\n>> +\tvideo_->bufferReady.connect(this, &VividCameraData::bufferReady);\n>> +\n>> +\t/* Initialise the supported controls. */\n>> +\tconst ControlInfoMap &controls = video_->controls();\n>> +\tControlInfoMap::Map ctrls;\n>> +\n>> +\tfor (const auto &ctrl : controls) {\n>> +\t\tconst ControlId *id;\n>> +\t\tControlInfo info;\n>> +\n>> +\t\tswitch (ctrl.first->id()) {\n>> +\t\tcase V4L2_CID_BRIGHTNESS:\n>> +\t\t\tid = &controls::Brightness;\n>> +\t\t\tinfo = ControlInfo{ { -1.0f }, { 1.0f }, { 0.0f } };\n>> +\t\t\tbreak;\n>> +\t\tcase V4L2_CID_CONTRAST:\n>> +\t\t\tid = &controls::Contrast;\n>> +\t\t\tinfo = ControlInfo{ { 0.0f }, { 2.0f }, { 1.0f } };\n>> +\t\t\tbreak;\n>> +\t\tcase V4L2_CID_SATURATION:\n>> +\t\t\tid = &controls::Saturation;\n>> +\t\t\tinfo = ControlInfo{ { 0.0f }, { 2.0f }, { 1.0f } };\n>> +\t\t\tbreak;\n>> +\t\tdefault:\n>> +\t\t\tcontinue;\n>> +\t\t}\n>> +\n>> +\t\tctrls.emplace(id, info);\n>> +\t}\n>> +\n>> +\tcontrolInfo_ = std::move(ctrls);\n>> +\n>> +\treturn 0;\n>> +}\n>> +\n>> +void VividCameraData::bufferReady(FrameBuffer *buffer)\n>> +{\n>> +\tRequest *request = buffer->request();\n>> +\n>> +\tpipe_->completeBuffer(camera_, request, buffer);\n>> +\tpipe_->completeRequest(camera_, request);\n>> +}\n>> +\n>> +REGISTER_PIPELINE_HANDLER(PipelineHandlerVivid);\n>> +\n>> +} /* namespace libcamera */\n>>\n>","headers":{"Return-Path":"<kieran.bingham@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id CD0D460103\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 19 Jun 2020 14:14:12 +0200 (CEST)","from [192.168.0.20]\n\t(cpc89242-aztw30-2-0-cust488.18-1.cable.virginm.net [86.31.129.233])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 38A7B552;\n\tFri, 19 Jun 2020 14:14:12 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"dYE3ifr5\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1592568852;\n\tbh=zc1r23xOYlTtBNVTzI0qu5czyqU5Aju7buqd8RNBGtg=;\n\th=Reply-To:Subject:From:To:References:Date:In-Reply-To:From;\n\tb=dYE3ifr5wPBwWBNx0YOTtpEW1V35glCPMx5+0GLuUgcFLyepwO/f67ljSy6VDYPvi\n\tymzR+Hv+/9l0JYd1W7ijDyKJvpCqmrSqQ+bLMGXZjn2HHOI1WKvrLQ+3KLsJ5gekjr\n\tGztWOWJSG7YGb5U5vPKG3DJ5UgTKgk+S154dGL+E=","Reply-To":"kieran.bingham@ideasonboard.com","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","To":"Paul Elder <paul.elder@ideasonboard.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>","References":"<20200619120249.1084998-1-kieran.bingham@ideasonboard.com>\n\t<61a74894-25ae-5ca1-d434-c360f4dba7eb@ideasonboard.com>","Autocrypt":"addr=kieran.bingham@ideasonboard.com; keydata=\n\t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\n\tWZw01QYHU/GUV/zHJSFk","Organization":"Ideas on Board","Message-ID":"<594cdc1d-3041-20c8-c26e-5cff845d2c52@ideasonboard.com>","Date":"Fri, 19 Jun 2020 13:14:09 +0100","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101\n\tThunderbird/68.8.0","MIME-Version":"1.0","In-Reply-To":"<61a74894-25ae-5ca1-d434-c360f4dba7eb@ideasonboard.com>","Content-Type":"text/plain; charset=utf-8","Content-Language":"en-GB","Content-Transfer-Encoding":"7bit","Subject":"Re: [libcamera-devel] [PATCH] libcamera: pipelines: Add VIVID\n\tpipeline support","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Fri, 19 Jun 2020 12:14:13 -0000"}},{"id":5286,"web_url":"https://patchwork.libcamera.org/comment/5286/","msgid":"<95f92384c74975b52f1df3ee71911cfba1baadfc.camel@ndufresne.ca>","date":"2020-06-19T15:34:25","subject":"Re: [libcamera-devel] [PATCH] libcamera: pipelines: Add VIVID\n\tpipeline support","submitter":{"id":30,"url":"https://patchwork.libcamera.org/api/people/30/","name":"Nicolas Dufresne","email":"nicolas@ndufresne.ca"},"content":"Le vendredi 19 juin 2020 à 13:02 +0100, Kieran Bingham a écrit :\n> The VIVID driver supports more pixel formats and properties than the VIMC\n> driver, and can provide extended testing for libcamera.\n> \n> The VIMC pipeline handler is duplicated and simplified to support the\n> VIVID device.\n> \n> Unfortuantely, the VIVID device can not be handled by either of the\n> generic UVC or Simple pipeline handlers.\n\nCan you extend on that ?\n\n> \n> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> ---\n>  meson_options.txt                        |   2 +-\n>  src/libcamera/pipeline/vivid/meson.build |   5 +\n>  src/libcamera/pipeline/vivid/vivid.cpp   | 441 +++++++++++++++++++++++\n>  3 files changed, 447 insertions(+), 1 deletion(-)\n>  create mode 100644 src/libcamera/pipeline/vivid/meson.build\n>  create mode 100644 src/libcamera/pipeline/vivid/vivid.cpp\n> \n> diff --git a/meson_options.txt b/meson_options.txt\n> index badace151bb6..dc4684df49b2 100644\n> --- a/meson_options.txt\n> +++ b/meson_options.txt\n> @@ -16,7 +16,7 @@ option('gstreamer',\n>  \n>  option('pipelines',\n>          type : 'array',\n> -        choices : ['ipu3', 'raspberrypi', 'rkisp1', 'simple', 'uvcvideo',\n> 'vimc'],\n> +        choices : ['ipu3', 'raspberrypi', 'rkisp1', 'simple', 'uvcvideo',\n> 'vimc', 'vivid'],\n>          description : 'Select which pipeline handlers to include')\n>  \n>  option('test',\n> diff --git a/src/libcamera/pipeline/vivid/meson.build\n> b/src/libcamera/pipeline/vivid/meson.build\n> new file mode 100644\n> index 000000000000..086bb825387c\n> --- /dev/null\n> +++ b/src/libcamera/pipeline/vivid/meson.build\n> @@ -0,0 +1,5 @@\n> +# SPDX-License-Identifier: CC0-1.0\n> +\n> +libcamera_sources += files([\n> +    'vivid.cpp',\n> +])\n> diff --git a/src/libcamera/pipeline/vivid/vivid.cpp\n> b/src/libcamera/pipeline/vivid/vivid.cpp\n> new file mode 100644\n> index 000000000000..b811e33a0299\n> --- /dev/null\n> +++ b/src/libcamera/pipeline/vivid/vivid.cpp\n> @@ -0,0 +1,441 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2018, Google Inc.\n> + *\n> + * vivid.cpp - Pipeline handler for the vivid capture device\n> + */\n> +\n> +#include <algorithm>\n> +#include <iomanip>\n> +#include <map>\n> +#include <math.h>\n> +#include <tuple>\n> +\n> +#include <linux/media-bus-format.h>\n> +#include <linux/version.h>\n> +\n> +#include <libcamera/camera.h>\n> +#include <libcamera/control_ids.h>\n> +#include <libcamera/controls.h>\n> +#include <libcamera/ipa/ipa_interface.h>\n> +#include <libcamera/ipa/ipa_module_info.h>\n> +#include <libcamera/request.h>\n> +#include <libcamera/stream.h>\n> +\n> +#include \"libcamera/internal/camera_sensor.h\"\n> +#include \"libcamera/internal/device_enumerator.h\"\n> +#include \"libcamera/internal/ipa_manager.h\"\n> +#include \"libcamera/internal/log.h\"\n> +#include \"libcamera/internal/media_device.h\"\n> +#include \"libcamera/internal/pipeline_handler.h\"\n> +#include \"libcamera/internal/utils.h\"\n> +#include \"libcamera/internal/v4l2_controls.h\"\n> +#include \"libcamera/internal/v4l2_subdevice.h\"\n> +#include \"libcamera/internal/v4l2_videodevice.h\"\n> +\n> +#define VIVID_CID_CUSTOM_BASE           (V4L2_CID_USER_BASE | 0xf000)\n> +#define VIVID_CID_BUTTON                (VIVID_CID_CUSTOM_BASE + 0)\n> +#define VIVID_CID_BOOLEAN               (VIVID_CID_CUSTOM_BASE + 1)\n> +#define VIVID_CID_INTEGER               (VIVID_CID_CUSTOM_BASE + 2)\n> +#define VIVID_CID_INTEGER64             (VIVID_CID_CUSTOM_BASE + 3)\n> +#define VIVID_CID_MENU                  (VIVID_CID_CUSTOM_BASE + 4)\n> +#define VIVID_CID_STRING                (VIVID_CID_CUSTOM_BASE + 5)\n> +#define VIVID_CID_BITMASK               (VIVID_CID_CUSTOM_BASE + 6)\n> +#define VIVID_CID_INTMENU               (VIVID_CID_CUSTOM_BASE + 7)\n> +#define VIVID_CID_U32_ARRAY             (VIVID_CID_CUSTOM_BASE + 8)\n> +#define VIVID_CID_U16_MATRIX            (VIVID_CID_CUSTOM_BASE + 9)\n> +#define VIVID_CID_U8_4D_ARRAY           (VIVID_CID_CUSTOM_BASE + 10)\n> +\n> +#define VIVID_CID_VIVID_BASE            (0x00f00000 | 0xf000)\n> +#define VIVID_CID_VIVID_CLASS           (0x00f00000 | 1)\n> +#define VIVID_CID_TEST_PATTERN          (VIVID_CID_VIVID_BASE + 0)\n> +#define VIVID_CID_OSD_TEXT_MODE         (VIVID_CID_VIVID_BASE + 1)\n> +#define VIVID_CID_HOR_MOVEMENT          (VIVID_CID_VIVID_BASE + 2)\n> +#define VIVID_CID_VERT_MOVEMENT         (VIVID_CID_VIVID_BASE + 3)\n> +#define VIVID_CID_SHOW_BORDER           (VIVID_CID_VIVID_BASE + 4)\n> +#define VIVID_CID_SHOW_SQUARE           (VIVID_CID_VIVID_BASE + 5)\n> +#define VIVID_CID_INSERT_SAV            (VIVID_CID_VIVID_BASE + 6)\n> +#define VIVID_CID_INSERT_EAV            (VIVID_CID_VIVID_BASE + 7)\n> +#define VIVID_CID_VBI_CAP_INTERLACED    (VIVID_CID_VIVID_BASE + 8)\n> +\n> +#define VIVID_CID_HFLIP                 (VIVID_CID_VIVID_BASE + 20)\n> +#define VIVID_CID_VFLIP                 (VIVID_CID_VIVID_BASE + 21)\n> +#define VIVID_CID_STD_ASPECT_RATIO      (VIVID_CID_VIVID_BASE + 22)\n> +#define VIVID_CID_DV_TIMINGS_ASPECT_RATIO       (VIVID_CID_VIVID_BASE + 23)\n> +#define VIVID_CID_TSTAMP_SRC            (VIVID_CID_VIVID_BASE + 24)\n> +#define VIVID_CID_COLORSPACE            (VIVID_CID_VIVID_BASE + 25)\n> +#define VIVID_CID_XFER_FUNC             (VIVID_CID_VIVID_BASE + 26)\n> +#define VIVID_CID_YCBCR_ENC             (VIVID_CID_VIVID_BASE + 27)\n> +#define VIVID_CID_QUANTIZATION          (VIVID_CID_VIVID_BASE + 28)\n> +#define VIVID_CID_LIMITED_RGB_RANGE     (VIVID_CID_VIVID_BASE + 29)\n> +#define VIVID_CID_ALPHA_MODE            (VIVID_CID_VIVID_BASE + 30)\n> +#define VIVID_CID_HAS_CROP_CAP          (VIVID_CID_VIVID_BASE + 31)\n> +#define VIVID_CID_HAS_COMPOSE_CAP       (VIVID_CID_VIVID_BASE + 32)\n> +#define VIVID_CID_HAS_SCALER_CAP        (VIVID_CID_VIVID_BASE + 33)\n> +#define VIVID_CID_HAS_CROP_OUT          (VIVID_CID_VIVID_BASE + 34)\n> +#define VIVID_CID_HAS_COMPOSE_OUT       (VIVID_CID_VIVID_BASE + 35)\n> +#define VIVID_CID_HAS_SCALER_OUT        (VIVID_CID_VIVID_BASE + 36)\n> +#define VIVID_CID_LOOP_VIDEO            (VIVID_CID_VIVID_BASE + 37)\n> +#define VIVID_CID_SEQ_WRAP              (VIVID_CID_VIVID_BASE + 38)\n> +#define VIVID_CID_TIME_WRAP             (VIVID_CID_VIVID_BASE + 39)\n> +#define VIVID_CID_MAX_EDID_BLOCKS       (VIVID_CID_VIVID_BASE + 40)\n> +#define VIVID_CID_PERCENTAGE_FILL       (VIVID_CID_VIVID_BASE + 41)\n> +#define VIVID_CID_REDUCED_FPS           (VIVID_CID_VIVID_BASE + 42)\n> +#define VIVID_CID_HSV_ENC               (VIVID_CID_VIVID_BASE + 43)\n> +#define VIVID_CID_DISPLAY_PRESENT       (VIVID_CID_VIVID_BASE + 44)\n> +\n> +#define VIVID_CID_STD_SIGNAL_MODE       (VIVID_CID_VIVID_BASE + 60)\n> +#define VIVID_CID_STANDARD              (VIVID_CID_VIVID_BASE + 61)\n> +#define VIVID_CID_DV_TIMINGS_SIGNAL_MODE        (VIVID_CID_VIVID_BASE + 62)\n> +#define VIVID_CID_DV_TIMINGS            (VIVID_CID_VIVID_BASE + 63)\n> +#define VIVID_CID_PERC_DROPPED          (VIVID_CID_VIVID_BASE + 64)\n> +#define VIVID_CID_DISCONNECT            (VIVID_CID_VIVID_BASE + 65)\n> +#define VIVID_CID_DQBUF_ERROR           (VIVID_CID_VIVID_BASE + 66)\n> +#define VIVID_CID_QUEUE_SETUP_ERROR     (VIVID_CID_VIVID_BASE + 67)\n> +#define VIVID_CID_BUF_PREPARE_ERROR     (VIVID_CID_VIVID_BASE + 68)\n> +#define VIVID_CID_START_STR_ERROR       (VIVID_CID_VIVID_BASE + 69)\n> +#define VIVID_CID_QUEUE_ERROR           (VIVID_CID_VIVID_BASE + 70)\n> +#define VIVID_CID_CLEAR_FB              (VIVID_CID_VIVID_BASE + 71)\n> +#define VIVID_CID_REQ_VALIDATE_ERROR    (VIVID_CID_VIVID_BASE + 72)\n> +\n> +#define VIVID_CID_RADIO_SEEK_MODE       (VIVID_CID_VIVID_BASE + 90)\n> +#define VIVID_CID_RADIO_SEEK_PROG_LIM   (VIVID_CID_VIVID_BASE + 91)\n> +#define VIVID_CID_RADIO_RX_RDS_RBDS     (VIVID_CID_VIVID_BASE + 92)\n> +#define VIVID_CID_RADIO_RX_RDS_BLOCKIO  (VIVID_CID_VIVID_BASE + 93)\n> +\n> +#define VIVID_CID_RADIO_TX_RDS_BLOCKIO  (VIVID_CID_VIVID_BASE + 94)\n> +\n> +#define VIVID_CID_SDR_CAP_FM_DEVIATION  (VIVID_CID_VIVID_BASE + 110)\n> +\n> +\n> +namespace libcamera {\n> +\n> +LOG_DEFINE_CATEGORY(VIVID)\n> +\n> +class VividCameraData : public CameraData\n> +{\n> +public:\n> +\tVividCameraData(PipelineHandler *pipe, MediaDevice *media)\n> +\t\t: CameraData(pipe), media_(media), video_(nullptr)\n> +\t{\n> +\t}\n> +\n> +\t~VividCameraData()\n> +\t{\n> +\t\tdelete video_;\n> +\t}\n> +\n> +\tint init();\n> +\tvoid bufferReady(FrameBuffer *buffer);\n> +\n> +\tMediaDevice *media_;\n> +\tV4L2VideoDevice *video_;\n> +\tStream stream_;\n> +};\n> +\n> +class VividCameraConfiguration : public CameraConfiguration\n> +{\n> +public:\n> +\tVividCameraConfiguration();\n> +\n> +\tStatus validate() override;\n> +};\n> +\n> +class PipelineHandlerVivid : public PipelineHandler\n> +{\n> +public:\n> +\tPipelineHandlerVivid(CameraManager *manager);\n> +\n> +\tCameraConfiguration *generateConfiguration(Camera *camera,\n> +\t\t\t\t\t\t   const StreamRoles &roles)\n> override;\n> +\tint configure(Camera *camera, CameraConfiguration *config) override;\n> +\n> +\tint exportFrameBuffers(Camera *camera, Stream *stream,\n> +\t\t\t       std::vector<std::unique_ptr<FrameBuffer>>\n> *buffers) override;\n> +\n> +\tint start(Camera *camera) override;\n> +\tvoid stop(Camera *camera) override;\n> +\n> +\tint queueRequestDevice(Camera *camera, Request *request) override;\n> +\n> +\tbool match(DeviceEnumerator *enumerator) override;\n> +\n> +private:\n> +\tint processControls(VividCameraData *data, Request *request);\n> +\n> +\tVividCameraData *cameraData(const Camera *camera)\n> +\t{\n> +\t\treturn static_cast<VividCameraData *>(\n> +\t\t\tPipelineHandler::cameraData(camera));\n> +\t}\n> +};\n> +\n> +VividCameraConfiguration::VividCameraConfiguration()\n> +\t: CameraConfiguration()\n> +{\n> +}\n> +\n> +CameraConfiguration::Status VividCameraConfiguration::validate()\n> +{\n> +\tStatus status = Valid;\n> +\n> +\tif (config_.empty())\n> +\t\treturn Invalid;\n> +\n> +\t/* Cap the number of entries to the available streams. */\n> +\tif (config_.size() > 1) {\n> +\t\tconfig_.resize(1);\n> +\t\tstatus = Adjusted;\n> +\t}\n> +\n> +\tStreamConfiguration &cfg = config_[0];\n> +\n> +\t/* Adjust the pixel format. */\n> +\tconst std::vector<libcamera::PixelFormat> formats =\n> cfg.formats().pixelformats();\n> +\tif (std::find(formats.begin(), formats.end(), cfg.pixelFormat) ==\n> formats.end()) {\n> +\t\tcfg.pixelFormat = cfg.formats().pixelformats()[0];\n> +\t\tLOG(VIVID, Debug) << \"Adjusting format to \" <<\n> cfg.pixelFormat.toString();\n> +\t\tstatus = Adjusted;\n> +\t}\n> +\n> +\tcfg.bufferCount = 4;\n> +\n> +\treturn status;\n> +}\n> +\n> +PipelineHandlerVivid::PipelineHandlerVivid(CameraManager *manager)\n> +\t: PipelineHandler(manager)\n> +{\n> +}\n> +\n> +CameraConfiguration *PipelineHandlerVivid::generateConfiguration(Camera\n> *camera,\n> +\t\t\t\t\t\t\t\t const\n> StreamRoles &roles)\n> +{\n> +\tCameraConfiguration *config = new VividCameraConfiguration();\n> +\tVividCameraData *data = cameraData(camera);\n> +\n> +\tif (roles.empty())\n> +\t\treturn config;\n> +\n> +\tstd::map<V4L2PixelFormat, std::vector<SizeRange>> v4l2Formats =\n> +\t\tdata->video_->formats();\n> +\tstd::map<PixelFormat, std::vector<SizeRange>> deviceFormats;\n> +\tstd::transform(v4l2Formats.begin(), v4l2Formats.end(),\n> +\t\t       std::inserter(deviceFormats, deviceFormats.begin()),\n> +\t\t       [&](const decltype(v4l2Formats)::value_type &format) {\n> +\t\t\t       return decltype(deviceFormats)::value_type{\n> +\t\t\t\t       format.first.toPixelFormat(),\n> +\t\t\t\t       format.second\n> +\t\t\t       };\n> +\t\t       });\n> +\n> +\tStreamFormats formats(deviceFormats);\n> +\tStreamConfiguration cfg(formats);\n> +\n> +\tcfg.pixelFormat = PixelFormat(DRM_FORMAT_BGR888);\n> +\tcfg.size = { 1280, 720 };\n> +\tcfg.bufferCount = 4;\n> +\n> +\tconfig->addConfiguration(cfg);\n> +\n> +\tconfig->validate();\n> +\n> +\treturn config;\n> +}\n> +\n> +int PipelineHandlerVivid::configure(Camera *camera, CameraConfiguration\n> *config)\n> +{\n> +\tVividCameraData *data = cameraData(camera);\n> +\tStreamConfiguration &cfg = config->at(0);\n> +\tint ret;\n> +\n> +\tV4L2DeviceFormat format = {};\n> +\tformat.fourcc = data->video_->toV4L2PixelFormat(cfg.pixelFormat);\n> +\tformat.size = cfg.size;\n> +\n> +\tret = data->video_->setFormat(&format);\n> +\tif (ret)\n> +\t\treturn ret;\n> +\n> +\tif (format.size != cfg.size ||\n> +\t    format.fourcc != data->video_->toV4L2PixelFormat(cfg.pixelFormat))\n> +\t\treturn -EINVAL;\n> +\n> +\tcfg.setStream(&data->stream_);\n> +\tcfg.stride = format.planes[0].bpl;\n> +\n> +\treturn 0;\n> +}\n> +\n> +int PipelineHandlerVivid::exportFrameBuffers(Camera *camera, Stream *stream,\n> +\t\t\t\t\t     std::vector<std::unique_ptr<FrameBu\n> ffer>> *buffers)\n> +{\n> +\tVividCameraData *data = cameraData(camera);\n> +\tunsigned int count = stream->configuration().bufferCount;\n> +\n> +\treturn data->video_->exportBuffers(count, buffers);\n> +}\n> +\n> +int PipelineHandlerVivid::start(Camera *camera)\n> +{\n> +\tVividCameraData *data = cameraData(camera);\n> +\tunsigned int count = data->stream_.configuration().bufferCount;\n> +\n> +\tint ret = data->video_->importBuffers(count);\n> +\tif (ret < 0)\n> +\t\treturn ret;\n> +\n> +\tret = data->video_->streamOn();\n> +\tif (ret < 0) {\n> +\t\tdata->ipa_->stop();\n> +\t\tdata->video_->releaseBuffers();\n> +\t\treturn ret;\n> +\t}\n> +\n> +\treturn 0;\n> +}\n> +\n> +void PipelineHandlerVivid::stop(Camera *camera)\n> +{\n> +\tVividCameraData *data = cameraData(camera);\n> +\tdata->video_->streamOff();\n> +\tdata->video_->releaseBuffers();\n> +}\n> +\n> +int PipelineHandlerVivid::processControls(VividCameraData *data, Request\n> *request)\n> +{\n> +\tControlList controls(data->video_->controls());\n> +\n> +\tfor (auto it : request->controls()) {\n> +\t\tunsigned int id = it.first;\n> +\t\tunsigned int offset;\n> +\t\tuint32_t cid;\n> +\n> +\t\tif (id == controls::Brightness) {\n> +\t\t\tcid = V4L2_CID_BRIGHTNESS;\n> +\t\t\toffset = 128;\n> +\t\t} else if (id == controls::Contrast) {\n> +\t\t\tcid = V4L2_CID_CONTRAST;\n> +\t\t\toffset = 0;\n> +\t\t} else if (id == controls::Saturation) {\n> +\t\t\tcid = V4L2_CID_SATURATION;\n> +\t\t\toffset = 0;\n> +\t\t} else {\n> +\t\t\tcontinue;\n> +\t\t}\n> +\n> +\t\tint32_t value = lroundf(it.second.get<float>() * 128 + offset);\n> +\t\tcontrols.set(cid, utils::clamp(value, 0, 255));\n> +\t}\n> +\n> +\tfor (const auto &ctrl : controls)\n> +\t\tLOG(VIVID, Debug)\n> +\t\t\t<< \"Setting control \" << utils::hex(ctrl.first)\n> +\t\t\t<< \" to \" << ctrl.second.toString();\n> +\n> +\tint ret = data->video_->setControls(&controls);\n> +\tif (ret) {\n> +\t\tLOG(VIVID, Error) << \"Failed to set controls: \" << ret;\n> +\t\treturn ret < 0 ? ret : -EINVAL;\n> +\t}\n> +\n> +\treturn ret;\n> +}\n> +\n> +int PipelineHandlerVivid::queueRequestDevice(Camera *camera, Request\n> *request)\n> +{\n> +\tVividCameraData *data = cameraData(camera);\n> +\tFrameBuffer *buffer = request->findBuffer(&data->stream_);\n> +\tif (!buffer) {\n> +\t\tLOG(VIVID, Error)\n> +\t\t\t<< \"Attempt to queue request with invalid stream\";\n> +\n> +\t\treturn -ENOENT;\n> +\t}\n> +\n> +\tint ret = processControls(data, request);\n> +\tif (ret < 0)\n> +\t\treturn ret;\n> +\n> +\tret = data->video_->queueBuffer(buffer);\n> +\tif (ret < 0)\n> +\t\treturn ret;\n> +\n> +\treturn 0;\n> +}\n> +\n> +bool PipelineHandlerVivid::match(DeviceEnumerator *enumerator)\n> +{\n> +\tDeviceMatch dm(\"vivid\");\n> +\tdm.add(\"vivid-000-vid-cap\");\n> +\n> +\tMediaDevice *media = acquireMediaDevice(enumerator, dm);\n> +\tif (!media)\n> +\t\treturn false;\n> +\n> +\tstd::unique_ptr<VividCameraData> data =\n> std::make_unique<VividCameraData>(this, media);\n> +\n> +\t/* Locate and open the capture video node. */\n> +\tif (data->init())\n> +\t\treturn false;\n> +\n> +\t/* Create and register the camera. */\n> +\tstd::set<Stream *> streams{ &data->stream_ };\n> +\tstd::shared_ptr<Camera> camera = Camera::create(this, data->video_-\n> >deviceName(), streams);\n> +\tregisterCamera(std::move(camera), std::move(data));\n> +\n> +\treturn true;\n> +}\n> +\n> +int VividCameraData::init()\n> +{\n> +\tvideo_ = new V4L2VideoDevice(media_->getEntityByName(\"vivid-000-vid-\n> cap\"));\n> +\tif (video_->open())\n> +\t\treturn -ENODEV;\n> +\n> +\tvideo_->bufferReady.connect(this, &VividCameraData::bufferReady);\n> +\n> +\t/* Initialise the supported controls. */\n> +\tconst ControlInfoMap &controls = video_->controls();\n> +\tControlInfoMap::Map ctrls;\n> +\n> +\tfor (const auto &ctrl : controls) {\n> +\t\tconst ControlId *id;\n> +\t\tControlInfo info;\n> +\n> +\t\tswitch (ctrl.first->id()) {\n> +\t\tcase V4L2_CID_BRIGHTNESS:\n> +\t\t\tid = &controls::Brightness;\n> +\t\t\tinfo = ControlInfo{ { -1.0f }, { 1.0f }, { 0.0f } };\n> +\t\t\tbreak;\n> +\t\tcase V4L2_CID_CONTRAST:\n> +\t\t\tid = &controls::Contrast;\n> +\t\t\tinfo = ControlInfo{ { 0.0f }, { 2.0f }, { 1.0f } };\n> +\t\t\tbreak;\n> +\t\tcase V4L2_CID_SATURATION:\n> +\t\t\tid = &controls::Saturation;\n> +\t\t\tinfo = ControlInfo{ { 0.0f }, { 2.0f }, { 1.0f } };\n> +\t\t\tbreak;\n> +\t\tdefault:\n> +\t\t\tcontinue;\n> +\t\t}\n> +\n> +\t\tctrls.emplace(id, info);\n> +\t}\n> +\n> +\tcontrolInfo_ = std::move(ctrls);\n> +\n> +\treturn 0;\n> +}\n> +\n> +void VividCameraData::bufferReady(FrameBuffer *buffer)\n> +{\n> +\tRequest *request = buffer->request();\n> +\n> +\tpipe_->completeBuffer(camera_, request, buffer);\n> +\tpipe_->completeRequest(camera_, request);\n> +}\n> +\n> +REGISTER_PIPELINE_HANDLER(PipelineHandlerVivid);\n> +\n> +} /* namespace libcamera */","headers":{"Return-Path":"<nicolas@ndufresne.ca>","Received":["from mail-qt1-x829.google.com (mail-qt1-x829.google.com\n\t[IPv6:2607:f8b0:4864:20::829])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 9C4D3600FE\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 19 Jun 2020 17:34:28 +0200 (CEST)","by mail-qt1-x829.google.com with SMTP id z1so7492176qtn.2\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 19 Jun 2020 08:34:28 -0700 (PDT)","from nicolas-tpx395 ([192.222.193.21])\n\tby smtp.gmail.com with ESMTPSA id\n\ti4sm833732qkm.69.2020.06.19.08.34.26\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tFri, 19 Jun 2020 08:34:26 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key; \n\tunprotected)\n\theader.d=ndufresne-ca.20150623.gappssmtp.com\n\theader.i=@ndufresne-ca.20150623.gappssmtp.com header.b=\"p5Cxmxn4\"; \n\tdkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=ndufresne-ca.20150623.gappssmtp.com; s=20150623;\n\th=message-id:subject:from:to:date:in-reply-to:references:user-agent\n\t:mime-version:content-transfer-encoding;\n\tbh=DK3fJwEyH4yvK3c8TQPn++rUQv88+vpfELd7XgdCj4M=;\n\tb=p5Cxmxn4gYGRRQ64DKqmlkE948lfoEcQId2UvmppCXfC4p7gg08v6gHKIW8WDvywGG\n\tokcNd1qy08fVvDFSivXrsL8k4Ehy7bO4/bPGSWu9nc3H8oMhsV1kTqgqsdyKPGFi6b2e\n\tTmmOeV5P2xo87fmKiVtW69sjWOkWddvsVF7s54unRwbLCq1cBrtiWX5W8z3rYvsM7SQy\n\t9f9sQct+91RvZP+4+w5ZujErSnCSpwCG1TS73Qo3HLXQDqEf9xf4pNPIindbSDEFL1vj\n\tqa7ZCuaPBt3Fcq7VDD0dIdwwZKnQSYIgbHefGMxE0gGx1IpOrmg772JAR4YSDLwyWm2y\n\tdbfw==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:message-id:subject:from:to:date:in-reply-to\n\t:references:user-agent:mime-version:content-transfer-encoding;\n\tbh=DK3fJwEyH4yvK3c8TQPn++rUQv88+vpfELd7XgdCj4M=;\n\tb=NsBCvsoxm22XLnK5vLAePIHbc5VL+tWkordOIkvUWN4T27m9jM8b+wfnQ6krv4Lu5V\n\t3P07nJXvdrT/tokC6kRWBZ8zPrCsT9L6cnA2SM7zfwpOyevOEpoInaHLS03oyw9iRSi2\n\t8o8qTgBuyUTp1e85wpI3rQeRf8OkxoKK38AQGERND9lKAVXGWKMNJwOZmbbFRnqbm7u+\n\tXd+wFb9haq70LVOZR+iPDbuNxQce93yV5/MGp91s5h6Mi4l1ZpQOkBVT+hLRggErunyH\n\tiJp/FjQbCjgJ7vBN0gt+UXtaaH2WoTGZSfaQuyf2gvAcfIfCgHePQui0yO+1lA3ySK+K\n\tVUrA==","X-Gm-Message-State":"AOAM533uGW+cLxZ4LNCCYYAplquxrYqxMEVHYJvm3mj919J2G/pASzei\n\tHGuoMfJHUkFW1ieKglAdGAMOlA==","X-Google-Smtp-Source":"ABdhPJycs0QUxd3wWdTuAuWpfWw+Dsj8LjwpoaWsXWhCONaMjqDR+KplV8wdZNyfTtAxggo/DiYPaA==","X-Received":"by 2002:aed:34c7:: with SMTP id\n\tx65mr4119667qtd.340.1592580867217; \n\tFri, 19 Jun 2020 08:34:27 -0700 (PDT)","Message-ID":"<95f92384c74975b52f1df3ee71911cfba1baadfc.camel@ndufresne.ca>","From":"Nicolas Dufresne <nicolas@ndufresne.ca>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>, Paul Elder\n\t<paul.elder@ideasonboard.com>, libcamera devel\n\t<libcamera-devel@lists.libcamera.org>","Date":"Fri, 19 Jun 2020 11:34:25 -0400","In-Reply-To":"<20200619120249.1084998-1-kieran.bingham@ideasonboard.com>","References":"<20200619120249.1084998-1-kieran.bingham@ideasonboard.com>","Content-Type":"text/plain; charset=\"UTF-8\"","User-Agent":"Evolution 3.36.2 (3.36.2-1.fc32) ","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","Subject":"Re: [libcamera-devel] [PATCH] libcamera: pipelines: Add VIVID\n\tpipeline support","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Fri, 19 Jun 2020 15:34:28 -0000"}},{"id":5287,"web_url":"https://patchwork.libcamera.org/comment/5287/","msgid":"<40e70402-9e6b-f065-b24c-1e34ca3d9c99@ideasonboard.com>","date":"2020-06-19T16:19:55","subject":"Re: [libcamera-devel] [PATCH] libcamera: pipelines: Add VIVID\n\tpipeline support","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Hi Nicolas,\n\nOn 19/06/2020 16:34, Nicolas Dufresne wrote:\n> Le vendredi 19 juin 2020 à 13:02 +0100, Kieran Bingham a écrit :\n>> The VIVID driver supports more pixel formats and properties than the VIMC\n>> driver, and can provide extended testing for libcamera.\n>>\n>> The VIMC pipeline handler is duplicated and simplified to support the\n>> VIVID device.\n>>\n>> Unfortuantely, the VIVID device can not be handled by either of the\n>> generic UVC or Simple pipeline handlers.\n> \n> Can you extend on that ?\n\nI actually disagreed with myself in my latest reply (@13:14 in my timezone).\n\n\n> Perhaps an alternative route to supporting this however would be to make\n> the simple pipeline handler support pipelines which *don't* have a\n> sensor object... though the VIVID controls could be useful to support\n> directly.\n> \n> Perhaps - inheriting the simple-pipeline handler should be possible, to\n> provide some 'device specific' adjustments for things like device\n> specific controls.\n\nThe simple pipeline expects to walk a media graph and find a sensor.\nVIVID doesn't expose a sensor, nor desire to. It's just a plain video\ndevice node.\n\nPerhaps we could extend the simple pipeline handler to support devices\nwhich don't have a sensor ... or create a \"simple-simple-pipeline\nhandler\" ? :-D\n\nEqually - there could then be quite a lot of commonality between the UVC\npipeline handler, though we kept that separate because of expected\ndifferences with UVC devices ... (in particular the controls for UVC\nhave some specific variations already handled there).\n\nSo for testing with vivid, I took the easy option of creating a new\npipeline for VIVID. But there is so much code duplication already, I'm\nsure we could find some reuse somewhere ...\n\n--\nKieran\n\n\n>> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n>> ---\n>>  meson_options.txt                        |   2 +-\n>>  src/libcamera/pipeline/vivid/meson.build |   5 +\n>>  src/libcamera/pipeline/vivid/vivid.cpp   | 441 +++++++++++++++++++++++\n>>  3 files changed, 447 insertions(+), 1 deletion(-)\n>>  create mode 100644 src/libcamera/pipeline/vivid/meson.build\n>>  create mode 100644 src/libcamera/pipeline/vivid/vivid.cpp\n>>\n>> diff --git a/meson_options.txt b/meson_options.txt\n>> index badace151bb6..dc4684df49b2 100644\n>> --- a/meson_options.txt\n>> +++ b/meson_options.txt\n>> @@ -16,7 +16,7 @@ option('gstreamer',\n>>  \n>>  option('pipelines',\n>>          type : 'array',\n>> -        choices : ['ipu3', 'raspberrypi', 'rkisp1', 'simple', 'uvcvideo',\n>> 'vimc'],\n>> +        choices : ['ipu3', 'raspberrypi', 'rkisp1', 'simple', 'uvcvideo',\n>> 'vimc', 'vivid'],\n>>          description : 'Select which pipeline handlers to include')\n>>  \n>>  option('test',\n>> diff --git a/src/libcamera/pipeline/vivid/meson.build\n>> b/src/libcamera/pipeline/vivid/meson.build\n>> new file mode 100644\n>> index 000000000000..086bb825387c\n>> --- /dev/null\n>> +++ b/src/libcamera/pipeline/vivid/meson.build\n>> @@ -0,0 +1,5 @@\n>> +# SPDX-License-Identifier: CC0-1.0\n>> +\n>> +libcamera_sources += files([\n>> +    'vivid.cpp',\n>> +])\n>> diff --git a/src/libcamera/pipeline/vivid/vivid.cpp\n>> b/src/libcamera/pipeline/vivid/vivid.cpp\n>> new file mode 100644\n>> index 000000000000..b811e33a0299\n>> --- /dev/null\n>> +++ b/src/libcamera/pipeline/vivid/vivid.cpp\n>> @@ -0,0 +1,441 @@\n>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>> +/*\n>> + * Copyright (C) 2018, Google Inc.\n>> + *\n>> + * vivid.cpp - Pipeline handler for the vivid capture device\n>> + */\n>> +\n>> +#include <algorithm>\n>> +#include <iomanip>\n>> +#include <map>\n>> +#include <math.h>\n>> +#include <tuple>\n>> +\n>> +#include <linux/media-bus-format.h>\n>> +#include <linux/version.h>\n>> +\n>> +#include <libcamera/camera.h>\n>> +#include <libcamera/control_ids.h>\n>> +#include <libcamera/controls.h>\n>> +#include <libcamera/ipa/ipa_interface.h>\n>> +#include <libcamera/ipa/ipa_module_info.h>\n>> +#include <libcamera/request.h>\n>> +#include <libcamera/stream.h>\n>> +\n>> +#include \"libcamera/internal/camera_sensor.h\"\n>> +#include \"libcamera/internal/device_enumerator.h\"\n>> +#include \"libcamera/internal/ipa_manager.h\"\n>> +#include \"libcamera/internal/log.h\"\n>> +#include \"libcamera/internal/media_device.h\"\n>> +#include \"libcamera/internal/pipeline_handler.h\"\n>> +#include \"libcamera/internal/utils.h\"\n>> +#include \"libcamera/internal/v4l2_controls.h\"\n>> +#include \"libcamera/internal/v4l2_subdevice.h\"\n>> +#include \"libcamera/internal/v4l2_videodevice.h\"\n>> +\n>> +#define VIVID_CID_CUSTOM_BASE           (V4L2_CID_USER_BASE | 0xf000)\n>> +#define VIVID_CID_BUTTON                (VIVID_CID_CUSTOM_BASE + 0)\n>> +#define VIVID_CID_BOOLEAN               (VIVID_CID_CUSTOM_BASE + 1)\n>> +#define VIVID_CID_INTEGER               (VIVID_CID_CUSTOM_BASE + 2)\n>> +#define VIVID_CID_INTEGER64             (VIVID_CID_CUSTOM_BASE + 3)\n>> +#define VIVID_CID_MENU                  (VIVID_CID_CUSTOM_BASE + 4)\n>> +#define VIVID_CID_STRING                (VIVID_CID_CUSTOM_BASE + 5)\n>> +#define VIVID_CID_BITMASK               (VIVID_CID_CUSTOM_BASE + 6)\n>> +#define VIVID_CID_INTMENU               (VIVID_CID_CUSTOM_BASE + 7)\n>> +#define VIVID_CID_U32_ARRAY             (VIVID_CID_CUSTOM_BASE + 8)\n>> +#define VIVID_CID_U16_MATRIX            (VIVID_CID_CUSTOM_BASE + 9)\n>> +#define VIVID_CID_U8_4D_ARRAY           (VIVID_CID_CUSTOM_BASE + 10)\n>> +\n>> +#define VIVID_CID_VIVID_BASE            (0x00f00000 | 0xf000)\n>> +#define VIVID_CID_VIVID_CLASS           (0x00f00000 | 1)\n>> +#define VIVID_CID_TEST_PATTERN          (VIVID_CID_VIVID_BASE + 0)\n>> +#define VIVID_CID_OSD_TEXT_MODE         (VIVID_CID_VIVID_BASE + 1)\n>> +#define VIVID_CID_HOR_MOVEMENT          (VIVID_CID_VIVID_BASE + 2)\n>> +#define VIVID_CID_VERT_MOVEMENT         (VIVID_CID_VIVID_BASE + 3)\n>> +#define VIVID_CID_SHOW_BORDER           (VIVID_CID_VIVID_BASE + 4)\n>> +#define VIVID_CID_SHOW_SQUARE           (VIVID_CID_VIVID_BASE + 5)\n>> +#define VIVID_CID_INSERT_SAV            (VIVID_CID_VIVID_BASE + 6)\n>> +#define VIVID_CID_INSERT_EAV            (VIVID_CID_VIVID_BASE + 7)\n>> +#define VIVID_CID_VBI_CAP_INTERLACED    (VIVID_CID_VIVID_BASE + 8)\n>> +\n>> +#define VIVID_CID_HFLIP                 (VIVID_CID_VIVID_BASE + 20)\n>> +#define VIVID_CID_VFLIP                 (VIVID_CID_VIVID_BASE + 21)\n>> +#define VIVID_CID_STD_ASPECT_RATIO      (VIVID_CID_VIVID_BASE + 22)\n>> +#define VIVID_CID_DV_TIMINGS_ASPECT_RATIO       (VIVID_CID_VIVID_BASE + 23)\n>> +#define VIVID_CID_TSTAMP_SRC            (VIVID_CID_VIVID_BASE + 24)\n>> +#define VIVID_CID_COLORSPACE            (VIVID_CID_VIVID_BASE + 25)\n>> +#define VIVID_CID_XFER_FUNC             (VIVID_CID_VIVID_BASE + 26)\n>> +#define VIVID_CID_YCBCR_ENC             (VIVID_CID_VIVID_BASE + 27)\n>> +#define VIVID_CID_QUANTIZATION          (VIVID_CID_VIVID_BASE + 28)\n>> +#define VIVID_CID_LIMITED_RGB_RANGE     (VIVID_CID_VIVID_BASE + 29)\n>> +#define VIVID_CID_ALPHA_MODE            (VIVID_CID_VIVID_BASE + 30)\n>> +#define VIVID_CID_HAS_CROP_CAP          (VIVID_CID_VIVID_BASE + 31)\n>> +#define VIVID_CID_HAS_COMPOSE_CAP       (VIVID_CID_VIVID_BASE + 32)\n>> +#define VIVID_CID_HAS_SCALER_CAP        (VIVID_CID_VIVID_BASE + 33)\n>> +#define VIVID_CID_HAS_CROP_OUT          (VIVID_CID_VIVID_BASE + 34)\n>> +#define VIVID_CID_HAS_COMPOSE_OUT       (VIVID_CID_VIVID_BASE + 35)\n>> +#define VIVID_CID_HAS_SCALER_OUT        (VIVID_CID_VIVID_BASE + 36)\n>> +#define VIVID_CID_LOOP_VIDEO            (VIVID_CID_VIVID_BASE + 37)\n>> +#define VIVID_CID_SEQ_WRAP              (VIVID_CID_VIVID_BASE + 38)\n>> +#define VIVID_CID_TIME_WRAP             (VIVID_CID_VIVID_BASE + 39)\n>> +#define VIVID_CID_MAX_EDID_BLOCKS       (VIVID_CID_VIVID_BASE + 40)\n>> +#define VIVID_CID_PERCENTAGE_FILL       (VIVID_CID_VIVID_BASE + 41)\n>> +#define VIVID_CID_REDUCED_FPS           (VIVID_CID_VIVID_BASE + 42)\n>> +#define VIVID_CID_HSV_ENC               (VIVID_CID_VIVID_BASE + 43)\n>> +#define VIVID_CID_DISPLAY_PRESENT       (VIVID_CID_VIVID_BASE + 44)\n>> +\n>> +#define VIVID_CID_STD_SIGNAL_MODE       (VIVID_CID_VIVID_BASE + 60)\n>> +#define VIVID_CID_STANDARD              (VIVID_CID_VIVID_BASE + 61)\n>> +#define VIVID_CID_DV_TIMINGS_SIGNAL_MODE        (VIVID_CID_VIVID_BASE + 62)\n>> +#define VIVID_CID_DV_TIMINGS            (VIVID_CID_VIVID_BASE + 63)\n>> +#define VIVID_CID_PERC_DROPPED          (VIVID_CID_VIVID_BASE + 64)\n>> +#define VIVID_CID_DISCONNECT            (VIVID_CID_VIVID_BASE + 65)\n>> +#define VIVID_CID_DQBUF_ERROR           (VIVID_CID_VIVID_BASE + 66)\n>> +#define VIVID_CID_QUEUE_SETUP_ERROR     (VIVID_CID_VIVID_BASE + 67)\n>> +#define VIVID_CID_BUF_PREPARE_ERROR     (VIVID_CID_VIVID_BASE + 68)\n>> +#define VIVID_CID_START_STR_ERROR       (VIVID_CID_VIVID_BASE + 69)\n>> +#define VIVID_CID_QUEUE_ERROR           (VIVID_CID_VIVID_BASE + 70)\n>> +#define VIVID_CID_CLEAR_FB              (VIVID_CID_VIVID_BASE + 71)\n>> +#define VIVID_CID_REQ_VALIDATE_ERROR    (VIVID_CID_VIVID_BASE + 72)\n>> +\n>> +#define VIVID_CID_RADIO_SEEK_MODE       (VIVID_CID_VIVID_BASE + 90)\n>> +#define VIVID_CID_RADIO_SEEK_PROG_LIM   (VIVID_CID_VIVID_BASE + 91)\n>> +#define VIVID_CID_RADIO_RX_RDS_RBDS     (VIVID_CID_VIVID_BASE + 92)\n>> +#define VIVID_CID_RADIO_RX_RDS_BLOCKIO  (VIVID_CID_VIVID_BASE + 93)\n>> +\n>> +#define VIVID_CID_RADIO_TX_RDS_BLOCKIO  (VIVID_CID_VIVID_BASE + 94)\n>> +\n>> +#define VIVID_CID_SDR_CAP_FM_DEVIATION  (VIVID_CID_VIVID_BASE + 110)\n>> +\n>> +\n>> +namespace libcamera {\n>> +\n>> +LOG_DEFINE_CATEGORY(VIVID)\n>> +\n>> +class VividCameraData : public CameraData\n>> +{\n>> +public:\n>> +\tVividCameraData(PipelineHandler *pipe, MediaDevice *media)\n>> +\t\t: CameraData(pipe), media_(media), video_(nullptr)\n>> +\t{\n>> +\t}\n>> +\n>> +\t~VividCameraData()\n>> +\t{\n>> +\t\tdelete video_;\n>> +\t}\n>> +\n>> +\tint init();\n>> +\tvoid bufferReady(FrameBuffer *buffer);\n>> +\n>> +\tMediaDevice *media_;\n>> +\tV4L2VideoDevice *video_;\n>> +\tStream stream_;\n>> +};\n>> +\n>> +class VividCameraConfiguration : public CameraConfiguration\n>> +{\n>> +public:\n>> +\tVividCameraConfiguration();\n>> +\n>> +\tStatus validate() override;\n>> +};\n>> +\n>> +class PipelineHandlerVivid : public PipelineHandler\n>> +{\n>> +public:\n>> +\tPipelineHandlerVivid(CameraManager *manager);\n>> +\n>> +\tCameraConfiguration *generateConfiguration(Camera *camera,\n>> +\t\t\t\t\t\t   const StreamRoles &roles)\n>> override;\n>> +\tint configure(Camera *camera, CameraConfiguration *config) override;\n>> +\n>> +\tint exportFrameBuffers(Camera *camera, Stream *stream,\n>> +\t\t\t       std::vector<std::unique_ptr<FrameBuffer>>\n>> *buffers) override;\n>> +\n>> +\tint start(Camera *camera) override;\n>> +\tvoid stop(Camera *camera) override;\n>> +\n>> +\tint queueRequestDevice(Camera *camera, Request *request) override;\n>> +\n>> +\tbool match(DeviceEnumerator *enumerator) override;\n>> +\n>> +private:\n>> +\tint processControls(VividCameraData *data, Request *request);\n>> +\n>> +\tVividCameraData *cameraData(const Camera *camera)\n>> +\t{\n>> +\t\treturn static_cast<VividCameraData *>(\n>> +\t\t\tPipelineHandler::cameraData(camera));\n>> +\t}\n>> +};\n>> +\n>> +VividCameraConfiguration::VividCameraConfiguration()\n>> +\t: CameraConfiguration()\n>> +{\n>> +}\n>> +\n>> +CameraConfiguration::Status VividCameraConfiguration::validate()\n>> +{\n>> +\tStatus status = Valid;\n>> +\n>> +\tif (config_.empty())\n>> +\t\treturn Invalid;\n>> +\n>> +\t/* Cap the number of entries to the available streams. */\n>> +\tif (config_.size() > 1) {\n>> +\t\tconfig_.resize(1);\n>> +\t\tstatus = Adjusted;\n>> +\t}\n>> +\n>> +\tStreamConfiguration &cfg = config_[0];\n>> +\n>> +\t/* Adjust the pixel format. */\n>> +\tconst std::vector<libcamera::PixelFormat> formats =\n>> cfg.formats().pixelformats();\n>> +\tif (std::find(formats.begin(), formats.end(), cfg.pixelFormat) ==\n>> formats.end()) {\n>> +\t\tcfg.pixelFormat = cfg.formats().pixelformats()[0];\n>> +\t\tLOG(VIVID, Debug) << \"Adjusting format to \" <<\n>> cfg.pixelFormat.toString();\n>> +\t\tstatus = Adjusted;\n>> +\t}\n>> +\n>> +\tcfg.bufferCount = 4;\n>> +\n>> +\treturn status;\n>> +}\n>> +\n>> +PipelineHandlerVivid::PipelineHandlerVivid(CameraManager *manager)\n>> +\t: PipelineHandler(manager)\n>> +{\n>> +}\n>> +\n>> +CameraConfiguration *PipelineHandlerVivid::generateConfiguration(Camera\n>> *camera,\n>> +\t\t\t\t\t\t\t\t const\n>> StreamRoles &roles)\n>> +{\n>> +\tCameraConfiguration *config = new VividCameraConfiguration();\n>> +\tVividCameraData *data = cameraData(camera);\n>> +\n>> +\tif (roles.empty())\n>> +\t\treturn config;\n>> +\n>> +\tstd::map<V4L2PixelFormat, std::vector<SizeRange>> v4l2Formats =\n>> +\t\tdata->video_->formats();\n>> +\tstd::map<PixelFormat, std::vector<SizeRange>> deviceFormats;\n>> +\tstd::transform(v4l2Formats.begin(), v4l2Formats.end(),\n>> +\t\t       std::inserter(deviceFormats, deviceFormats.begin()),\n>> +\t\t       [&](const decltype(v4l2Formats)::value_type &format) {\n>> +\t\t\t       return decltype(deviceFormats)::value_type{\n>> +\t\t\t\t       format.first.toPixelFormat(),\n>> +\t\t\t\t       format.second\n>> +\t\t\t       };\n>> +\t\t       });\n>> +\n>> +\tStreamFormats formats(deviceFormats);\n>> +\tStreamConfiguration cfg(formats);\n>> +\n>> +\tcfg.pixelFormat = PixelFormat(DRM_FORMAT_BGR888);\n>> +\tcfg.size = { 1280, 720 };\n>> +\tcfg.bufferCount = 4;\n>> +\n>> +\tconfig->addConfiguration(cfg);\n>> +\n>> +\tconfig->validate();\n>> +\n>> +\treturn config;\n>> +}\n>> +\n>> +int PipelineHandlerVivid::configure(Camera *camera, CameraConfiguration\n>> *config)\n>> +{\n>> +\tVividCameraData *data = cameraData(camera);\n>> +\tStreamConfiguration &cfg = config->at(0);\n>> +\tint ret;\n>> +\n>> +\tV4L2DeviceFormat format = {};\n>> +\tformat.fourcc = data->video_->toV4L2PixelFormat(cfg.pixelFormat);\n>> +\tformat.size = cfg.size;\n>> +\n>> +\tret = data->video_->setFormat(&format);\n>> +\tif (ret)\n>> +\t\treturn ret;\n>> +\n>> +\tif (format.size != cfg.size ||\n>> +\t    format.fourcc != data->video_->toV4L2PixelFormat(cfg.pixelFormat))\n>> +\t\treturn -EINVAL;\n>> +\n>> +\tcfg.setStream(&data->stream_);\n>> +\tcfg.stride = format.planes[0].bpl;\n>> +\n>> +\treturn 0;\n>> +}\n>> +\n>> +int PipelineHandlerVivid::exportFrameBuffers(Camera *camera, Stream *stream,\n>> +\t\t\t\t\t     std::vector<std::unique_ptr<FrameBu\n>> ffer>> *buffers)\n>> +{\n>> +\tVividCameraData *data = cameraData(camera);\n>> +\tunsigned int count = stream->configuration().bufferCount;\n>> +\n>> +\treturn data->video_->exportBuffers(count, buffers);\n>> +}\n>> +\n>> +int PipelineHandlerVivid::start(Camera *camera)\n>> +{\n>> +\tVividCameraData *data = cameraData(camera);\n>> +\tunsigned int count = data->stream_.configuration().bufferCount;\n>> +\n>> +\tint ret = data->video_->importBuffers(count);\n>> +\tif (ret < 0)\n>> +\t\treturn ret;\n>> +\n>> +\tret = data->video_->streamOn();\n>> +\tif (ret < 0) {\n>> +\t\tdata->ipa_->stop();\n>> +\t\tdata->video_->releaseBuffers();\n>> +\t\treturn ret;\n>> +\t}\n>> +\n>> +\treturn 0;\n>> +}\n>> +\n>> +void PipelineHandlerVivid::stop(Camera *camera)\n>> +{\n>> +\tVividCameraData *data = cameraData(camera);\n>> +\tdata->video_->streamOff();\n>> +\tdata->video_->releaseBuffers();\n>> +}\n>> +\n>> +int PipelineHandlerVivid::processControls(VividCameraData *data, Request\n>> *request)\n>> +{\n>> +\tControlList controls(data->video_->controls());\n>> +\n>> +\tfor (auto it : request->controls()) {\n>> +\t\tunsigned int id = it.first;\n>> +\t\tunsigned int offset;\n>> +\t\tuint32_t cid;\n>> +\n>> +\t\tif (id == controls::Brightness) {\n>> +\t\t\tcid = V4L2_CID_BRIGHTNESS;\n>> +\t\t\toffset = 128;\n>> +\t\t} else if (id == controls::Contrast) {\n>> +\t\t\tcid = V4L2_CID_CONTRAST;\n>> +\t\t\toffset = 0;\n>> +\t\t} else if (id == controls::Saturation) {\n>> +\t\t\tcid = V4L2_CID_SATURATION;\n>> +\t\t\toffset = 0;\n>> +\t\t} else {\n>> +\t\t\tcontinue;\n>> +\t\t}\n>> +\n>> +\t\tint32_t value = lroundf(it.second.get<float>() * 128 + offset);\n>> +\t\tcontrols.set(cid, utils::clamp(value, 0, 255));\n>> +\t}\n>> +\n>> +\tfor (const auto &ctrl : controls)\n>> +\t\tLOG(VIVID, Debug)\n>> +\t\t\t<< \"Setting control \" << utils::hex(ctrl.first)\n>> +\t\t\t<< \" to \" << ctrl.second.toString();\n>> +\n>> +\tint ret = data->video_->setControls(&controls);\n>> +\tif (ret) {\n>> +\t\tLOG(VIVID, Error) << \"Failed to set controls: \" << ret;\n>> +\t\treturn ret < 0 ? ret : -EINVAL;\n>> +\t}\n>> +\n>> +\treturn ret;\n>> +}\n>> +\n>> +int PipelineHandlerVivid::queueRequestDevice(Camera *camera, Request\n>> *request)\n>> +{\n>> +\tVividCameraData *data = cameraData(camera);\n>> +\tFrameBuffer *buffer = request->findBuffer(&data->stream_);\n>> +\tif (!buffer) {\n>> +\t\tLOG(VIVID, Error)\n>> +\t\t\t<< \"Attempt to queue request with invalid stream\";\n>> +\n>> +\t\treturn -ENOENT;\n>> +\t}\n>> +\n>> +\tint ret = processControls(data, request);\n>> +\tif (ret < 0)\n>> +\t\treturn ret;\n>> +\n>> +\tret = data->video_->queueBuffer(buffer);\n>> +\tif (ret < 0)\n>> +\t\treturn ret;\n>> +\n>> +\treturn 0;\n>> +}\n>> +\n>> +bool PipelineHandlerVivid::match(DeviceEnumerator *enumerator)\n>> +{\n>> +\tDeviceMatch dm(\"vivid\");\n>> +\tdm.add(\"vivid-000-vid-cap\");\n>> +\n>> +\tMediaDevice *media = acquireMediaDevice(enumerator, dm);\n>> +\tif (!media)\n>> +\t\treturn false;\n>> +\n>> +\tstd::unique_ptr<VividCameraData> data =\n>> std::make_unique<VividCameraData>(this, media);\n>> +\n>> +\t/* Locate and open the capture video node. */\n>> +\tif (data->init())\n>> +\t\treturn false;\n>> +\n>> +\t/* Create and register the camera. */\n>> +\tstd::set<Stream *> streams{ &data->stream_ };\n>> +\tstd::shared_ptr<Camera> camera = Camera::create(this, data->video_-\n>>> deviceName(), streams);\n>> +\tregisterCamera(std::move(camera), std::move(data));\n>> +\n>> +\treturn true;\n>> +}\n>> +\n>> +int VividCameraData::init()\n>> +{\n>> +\tvideo_ = new V4L2VideoDevice(media_->getEntityByName(\"vivid-000-vid-\n>> cap\"));\n>> +\tif (video_->open())\n>> +\t\treturn -ENODEV;\n>> +\n>> +\tvideo_->bufferReady.connect(this, &VividCameraData::bufferReady);\n>> +\n>> +\t/* Initialise the supported controls. */\n>> +\tconst ControlInfoMap &controls = video_->controls();\n>> +\tControlInfoMap::Map ctrls;\n>> +\n>> +\tfor (const auto &ctrl : controls) {\n>> +\t\tconst ControlId *id;\n>> +\t\tControlInfo info;\n>> +\n>> +\t\tswitch (ctrl.first->id()) {\n>> +\t\tcase V4L2_CID_BRIGHTNESS:\n>> +\t\t\tid = &controls::Brightness;\n>> +\t\t\tinfo = ControlInfo{ { -1.0f }, { 1.0f }, { 0.0f } };\n>> +\t\t\tbreak;\n>> +\t\tcase V4L2_CID_CONTRAST:\n>> +\t\t\tid = &controls::Contrast;\n>> +\t\t\tinfo = ControlInfo{ { 0.0f }, { 2.0f }, { 1.0f } };\n>> +\t\t\tbreak;\n>> +\t\tcase V4L2_CID_SATURATION:\n>> +\t\t\tid = &controls::Saturation;\n>> +\t\t\tinfo = ControlInfo{ { 0.0f }, { 2.0f }, { 1.0f } };\n>> +\t\t\tbreak;\n>> +\t\tdefault:\n>> +\t\t\tcontinue;\n>> +\t\t}\n>> +\n>> +\t\tctrls.emplace(id, info);\n>> +\t}\n>> +\n>> +\tcontrolInfo_ = std::move(ctrls);\n>> +\n>> +\treturn 0;\n>> +}\n>> +\n>> +void VividCameraData::bufferReady(FrameBuffer *buffer)\n>> +{\n>> +\tRequest *request = buffer->request();\n>> +\n>> +\tpipe_->completeBuffer(camera_, request, buffer);\n>> +\tpipe_->completeRequest(camera_, request);\n>> +}\n>> +\n>> +REGISTER_PIPELINE_HANDLER(PipelineHandlerVivid);\n>> +\n>> +} /* namespace libcamera */\n>","headers":{"Return-Path":"<kieran.bingham@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 93351600FE\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 19 Jun 2020 18:19:59 +0200 (CEST)","from [192.168.0.20]\n\t(cpc89242-aztw30-2-0-cust488.18-1.cable.virginm.net [86.31.129.233])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id EF2EC560;\n\tFri, 19 Jun 2020 18:19:58 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"uVoHv8nq\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1592583599;\n\tbh=4eVl1B8zeNFoaTEhFWlxct8ptj2/xuuccmq7JYofNk8=;\n\th=Reply-To:Subject:To:References:From:Date:In-Reply-To:From;\n\tb=uVoHv8nqSspQ9ZmD56eRnGWbGzXOKkL76U9/qcr3abnKXgN536qcFmwR6cBR3DGZA\n\tGXB0IcBbpshpBW/SszdK6yIagoK1D+LPrgXVKb4MB6qqJ3HUOrH4tuLzhbjn8wCiyq\n\tr6LckSzEjcBUw2qfDaLskx8MqG2ThfP4o512+LZs=","Reply-To":"kieran.bingham@ideasonboard.com","To":"Nicolas Dufresne <nicolas@ndufresne.ca>,\n\tPaul Elder <paul.elder@ideasonboard.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>","References":"<20200619120249.1084998-1-kieran.bingham@ideasonboard.com>\n\t<95f92384c74975b52f1df3ee71911cfba1baadfc.camel@ndufresne.ca>","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Autocrypt":"addr=kieran.bingham@ideasonboard.com; keydata=\n\t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\n\tWZw01QYHU/GUV/zHJSFk","Organization":"Ideas on Board","Message-ID":"<40e70402-9e6b-f065-b24c-1e34ca3d9c99@ideasonboard.com>","Date":"Fri, 19 Jun 2020 17:19:55 +0100","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101\n\tThunderbird/68.8.0","MIME-Version":"1.0","In-Reply-To":"<95f92384c74975b52f1df3ee71911cfba1baadfc.camel@ndufresne.ca>","Content-Type":"text/plain; charset=utf-8","Content-Language":"en-GB","Content-Transfer-Encoding":"8bit","Subject":"Re: [libcamera-devel] [PATCH] libcamera: pipelines: Add VIVID\n\tpipeline support","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Fri, 19 Jun 2020 16:19:59 -0000"}},{"id":5288,"web_url":"https://patchwork.libcamera.org/comment/5288/","msgid":"<1073f243556c26abe24ff51319307b854656b19a.camel@ndufresne.ca>","date":"2020-06-19T16:35:49","subject":"Re: [libcamera-devel] [PATCH] libcamera: pipelines: Add VIVID\n\tpipeline support","submitter":{"id":30,"url":"https://patchwork.libcamera.org/api/people/30/","name":"Nicolas Dufresne","email":"nicolas@ndufresne.ca"},"content":"Le vendredi 19 juin 2020 à 17:19 +0100, Kieran Bingham a écrit :\n> Hi Nicolas,\n> \n> On 19/06/2020 16:34, Nicolas Dufresne wrote:\n> > Le vendredi 19 juin 2020 à 13:02 +0100, Kieran Bingham a écrit :\n> > > The VIVID driver supports more pixel formats and properties than the VIMC\n> > > driver, and can provide extended testing for libcamera.\n> > > \n> > > The VIMC pipeline handler is duplicated and simplified to support the\n> > > VIVID device.\n> > > \n> > > Unfortuantely, the VIVID device can not be handled by either of the\n> > > generic UVC or Simple pipeline handlers.\n> > \n> > Can you extend on that ?\n> \n> I actually disagreed with myself in my latest reply (@13:14 in my timezone).\n> \n> \n> > Perhaps an alternative route to supporting this however would be to make\n> > the simple pipeline handler support pipelines which *don't* have a\n> > sensor object... though the VIVID controls could be useful to support\n> > directly.\n> > \n> > Perhaps - inheriting the simple-pipeline handler should be possible, to\n> > provide some 'device specific' adjustments for things like device\n> > specific controls.\n> \n> The simple pipeline expects to walk a media graph and find a sensor.\n> VIVID doesn't expose a sensor, nor desire to. It's just a plain video\n> device node.\n\nIndeed, the simulated input is meant to be HDMI, SDI, RCA etc. Making these\nsensor would be mis-leading. But it has a \"webcam\" mode too, but I think all\nthis control clashes too much.\n\n> \n> Perhaps we could extend the simple pipeline handler to support devices\n> which don't have a sensor ... or create a \"simple-simple-pipeline\n> handler\" ? :-D\n> \n> Equally - there could then be quite a lot of commonality between the UVC\n> pipeline handler, though we kept that separate because of expected\n> differences with UVC devices ... (in particular the controls for UVC\n> have some specific variations already handled there).\n> \n> So for testing with vivid, I took the easy option of creating a new\n> pipeline for VIVID. But there is so much code duplication already, I'm\n> sure we could find some reuse somewhere ...\n\nThanks, seems fair, often my question are just to make sure we leave a trace, so\nin few years for now, if we want to merge it we know exactly why it was split in\nthe first place.\n\n> \n> --\n> Kieran\n> \n> \n> > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > > ---\n> > >  meson_options.txt                        |   2 +-\n> > >  src/libcamera/pipeline/vivid/meson.build |   5 +\n> > >  src/libcamera/pipeline/vivid/vivid.cpp   | 441 +++++++++++++++++++++++\n> > >  3 files changed, 447 insertions(+), 1 deletion(-)\n> > >  create mode 100644 src/libcamera/pipeline/vivid/meson.build\n> > >  create mode 100644 src/libcamera/pipeline/vivid/vivid.cpp\n> > > \n> > > diff --git a/meson_options.txt b/meson_options.txt\n> > > index badace151bb6..dc4684df49b2 100644\n> > > --- a/meson_options.txt\n> > > +++ b/meson_options.txt\n> > > @@ -16,7 +16,7 @@ option('gstreamer',\n> > >  \n> > >  option('pipelines',\n> > >          type : 'array',\n> > > -        choices : ['ipu3', 'raspberrypi', 'rkisp1', 'simple', 'uvcvideo',\n> > > 'vimc'],\n> > > +        choices : ['ipu3', 'raspberrypi', 'rkisp1', 'simple', 'uvcvideo',\n> > > 'vimc', 'vivid'],\n> > >          description : 'Select which pipeline handlers to include')\n> > >  \n> > >  option('test',\n> > > diff --git a/src/libcamera/pipeline/vivid/meson.build\n> > > b/src/libcamera/pipeline/vivid/meson.build\n> > > new file mode 100644\n> > > index 000000000000..086bb825387c\n> > > --- /dev/null\n> > > +++ b/src/libcamera/pipeline/vivid/meson.build\n> > > @@ -0,0 +1,5 @@\n> > > +# SPDX-License-Identifier: CC0-1.0\n> > > +\n> > > +libcamera_sources += files([\n> > > +    'vivid.cpp',\n> > > +])\n> > > diff --git a/src/libcamera/pipeline/vivid/vivid.cpp\n> > > b/src/libcamera/pipeline/vivid/vivid.cpp\n> > > new file mode 100644\n> > > index 000000000000..b811e33a0299\n> > > --- /dev/null\n> > > +++ b/src/libcamera/pipeline/vivid/vivid.cpp\n> > > @@ -0,0 +1,441 @@\n> > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > > +/*\n> > > + * Copyright (C) 2018, Google Inc.\n> > > + *\n> > > + * vivid.cpp - Pipeline handler for the vivid capture device\n> > > + */\n> > > +\n> > > +#include <algorithm>\n> > > +#include <iomanip>\n> > > +#include <map>\n> > > +#include <math.h>\n> > > +#include <tuple>\n> > > +\n> > > +#include <linux/media-bus-format.h>\n> > > +#include <linux/version.h>\n> > > +\n> > > +#include <libcamera/camera.h>\n> > > +#include <libcamera/control_ids.h>\n> > > +#include <libcamera/controls.h>\n> > > +#include <libcamera/ipa/ipa_interface.h>\n> > > +#include <libcamera/ipa/ipa_module_info.h>\n> > > +#include <libcamera/request.h>\n> > > +#include <libcamera/stream.h>\n> > > +\n> > > +#include \"libcamera/internal/camera_sensor.h\"\n> > > +#include \"libcamera/internal/device_enumerator.h\"\n> > > +#include \"libcamera/internal/ipa_manager.h\"\n> > > +#include \"libcamera/internal/log.h\"\n> > > +#include \"libcamera/internal/media_device.h\"\n> > > +#include \"libcamera/internal/pipeline_handler.h\"\n> > > +#include \"libcamera/internal/utils.h\"\n> > > +#include \"libcamera/internal/v4l2_controls.h\"\n> > > +#include \"libcamera/internal/v4l2_subdevice.h\"\n> > > +#include \"libcamera/internal/v4l2_videodevice.h\"\n> > > +\n> > > +#define VIVID_CID_CUSTOM_BASE           (V4L2_CID_USER_BASE | 0xf000)\n> > > +#define VIVID_CID_BUTTON                (VIVID_CID_CUSTOM_BASE + 0)\n> > > +#define VIVID_CID_BOOLEAN               (VIVID_CID_CUSTOM_BASE + 1)\n> > > +#define VIVID_CID_INTEGER               (VIVID_CID_CUSTOM_BASE + 2)\n> > > +#define VIVID_CID_INTEGER64             (VIVID_CID_CUSTOM_BASE + 3)\n> > > +#define VIVID_CID_MENU                  (VIVID_CID_CUSTOM_BASE + 4)\n> > > +#define VIVID_CID_STRING                (VIVID_CID_CUSTOM_BASE + 5)\n> > > +#define VIVID_CID_BITMASK               (VIVID_CID_CUSTOM_BASE + 6)\n> > > +#define VIVID_CID_INTMENU               (VIVID_CID_CUSTOM_BASE + 7)\n> > > +#define VIVID_CID_U32_ARRAY             (VIVID_CID_CUSTOM_BASE + 8)\n> > > +#define VIVID_CID_U16_MATRIX            (VIVID_CID_CUSTOM_BASE + 9)\n> > > +#define VIVID_CID_U8_4D_ARRAY           (VIVID_CID_CUSTOM_BASE + 10)\n> > > +\n> > > +#define VIVID_CID_VIVID_BASE            (0x00f00000 | 0xf000)\n> > > +#define VIVID_CID_VIVID_CLASS           (0x00f00000 | 1)\n> > > +#define VIVID_CID_TEST_PATTERN          (VIVID_CID_VIVID_BASE + 0)\n> > > +#define VIVID_CID_OSD_TEXT_MODE         (VIVID_CID_VIVID_BASE + 1)\n> > > +#define VIVID_CID_HOR_MOVEMENT          (VIVID_CID_VIVID_BASE + 2)\n> > > +#define VIVID_CID_VERT_MOVEMENT         (VIVID_CID_VIVID_BASE + 3)\n> > > +#define VIVID_CID_SHOW_BORDER           (VIVID_CID_VIVID_BASE + 4)\n> > > +#define VIVID_CID_SHOW_SQUARE           (VIVID_CID_VIVID_BASE + 5)\n> > > +#define VIVID_CID_INSERT_SAV            (VIVID_CID_VIVID_BASE + 6)\n> > > +#define VIVID_CID_INSERT_EAV            (VIVID_CID_VIVID_BASE + 7)\n> > > +#define VIVID_CID_VBI_CAP_INTERLACED    (VIVID_CID_VIVID_BASE + 8)\n> > > +\n> > > +#define VIVID_CID_HFLIP                 (VIVID_CID_VIVID_BASE + 20)\n> > > +#define VIVID_CID_VFLIP                 (VIVID_CID_VIVID_BASE + 21)\n> > > +#define VIVID_CID_STD_ASPECT_RATIO      (VIVID_CID_VIVID_BASE + 22)\n> > > +#define VIVID_CID_DV_TIMINGS_ASPECT_RATIO       (VIVID_CID_VIVID_BASE +\n> > > 23)\n> > > +#define VIVID_CID_TSTAMP_SRC            (VIVID_CID_VIVID_BASE + 24)\n> > > +#define VIVID_CID_COLORSPACE            (VIVID_CID_VIVID_BASE + 25)\n> > > +#define VIVID_CID_XFER_FUNC             (VIVID_CID_VIVID_BASE + 26)\n> > > +#define VIVID_CID_YCBCR_ENC             (VIVID_CID_VIVID_BASE + 27)\n> > > +#define VIVID_CID_QUANTIZATION          (VIVID_CID_VIVID_BASE + 28)\n> > > +#define VIVID_CID_LIMITED_RGB_RANGE     (VIVID_CID_VIVID_BASE + 29)\n> > > +#define VIVID_CID_ALPHA_MODE            (VIVID_CID_VIVID_BASE + 30)\n> > > +#define VIVID_CID_HAS_CROP_CAP          (VIVID_CID_VIVID_BASE + 31)\n> > > +#define VIVID_CID_HAS_COMPOSE_CAP       (VIVID_CID_VIVID_BASE + 32)\n> > > +#define VIVID_CID_HAS_SCALER_CAP        (VIVID_CID_VIVID_BASE + 33)\n> > > +#define VIVID_CID_HAS_CROP_OUT          (VIVID_CID_VIVID_BASE + 34)\n> > > +#define VIVID_CID_HAS_COMPOSE_OUT       (VIVID_CID_VIVID_BASE + 35)\n> > > +#define VIVID_CID_HAS_SCALER_OUT        (VIVID_CID_VIVID_BASE + 36)\n> > > +#define VIVID_CID_LOOP_VIDEO            (VIVID_CID_VIVID_BASE + 37)\n> > > +#define VIVID_CID_SEQ_WRAP              (VIVID_CID_VIVID_BASE + 38)\n> > > +#define VIVID_CID_TIME_WRAP             (VIVID_CID_VIVID_BASE + 39)\n> > > +#define VIVID_CID_MAX_EDID_BLOCKS       (VIVID_CID_VIVID_BASE + 40)\n> > > +#define VIVID_CID_PERCENTAGE_FILL       (VIVID_CID_VIVID_BASE + 41)\n> > > +#define VIVID_CID_REDUCED_FPS           (VIVID_CID_VIVID_BASE + 42)\n> > > +#define VIVID_CID_HSV_ENC               (VIVID_CID_VIVID_BASE + 43)\n> > > +#define VIVID_CID_DISPLAY_PRESENT       (VIVID_CID_VIVID_BASE + 44)\n> > > +\n> > > +#define VIVID_CID_STD_SIGNAL_MODE       (VIVID_CID_VIVID_BASE + 60)\n> > > +#define VIVID_CID_STANDARD              (VIVID_CID_VIVID_BASE + 61)\n> > > +#define VIVID_CID_DV_TIMINGS_SIGNAL_MODE        (VIVID_CID_VIVID_BASE +\n> > > 62)\n> > > +#define VIVID_CID_DV_TIMINGS            (VIVID_CID_VIVID_BASE + 63)\n> > > +#define VIVID_CID_PERC_DROPPED          (VIVID_CID_VIVID_BASE + 64)\n> > > +#define VIVID_CID_DISCONNECT            (VIVID_CID_VIVID_BASE + 65)\n> > > +#define VIVID_CID_DQBUF_ERROR           (VIVID_CID_VIVID_BASE + 66)\n> > > +#define VIVID_CID_QUEUE_SETUP_ERROR     (VIVID_CID_VIVID_BASE + 67)\n> > > +#define VIVID_CID_BUF_PREPARE_ERROR     (VIVID_CID_VIVID_BASE + 68)\n> > > +#define VIVID_CID_START_STR_ERROR       (VIVID_CID_VIVID_BASE + 69)\n> > > +#define VIVID_CID_QUEUE_ERROR           (VIVID_CID_VIVID_BASE + 70)\n> > > +#define VIVID_CID_CLEAR_FB              (VIVID_CID_VIVID_BASE + 71)\n> > > +#define VIVID_CID_REQ_VALIDATE_ERROR    (VIVID_CID_VIVID_BASE + 72)\n> > > +\n> > > +#define VIVID_CID_RADIO_SEEK_MODE       (VIVID_CID_VIVID_BASE + 90)\n> > > +#define VIVID_CID_RADIO_SEEK_PROG_LIM   (VIVID_CID_VIVID_BASE + 91)\n> > > +#define VIVID_CID_RADIO_RX_RDS_RBDS     (VIVID_CID_VIVID_BASE + 92)\n> > > +#define VIVID_CID_RADIO_RX_RDS_BLOCKIO  (VIVID_CID_VIVID_BASE + 93)\n> > > +\n> > > +#define VIVID_CID_RADIO_TX_RDS_BLOCKIO  (VIVID_CID_VIVID_BASE + 94)\n> > > +\n> > > +#define VIVID_CID_SDR_CAP_FM_DEVIATION  (VIVID_CID_VIVID_BASE + 110)\n> > > +\n> > > +\n> > > +namespace libcamera {\n> > > +\n> > > +LOG_DEFINE_CATEGORY(VIVID)\n> > > +\n> > > +class VividCameraData : public CameraData\n> > > +{\n> > > +public:\n> > > +\tVividCameraData(PipelineHandler *pipe, MediaDevice *media)\n> > > +\t\t: CameraData(pipe), media_(media), video_(nullptr)\n> > > +\t{\n> > > +\t}\n> > > +\n> > > +\t~VividCameraData()\n> > > +\t{\n> > > +\t\tdelete video_;\n> > > +\t}\n> > > +\n> > > +\tint init();\n> > > +\tvoid bufferReady(FrameBuffer *buffer);\n> > > +\n> > > +\tMediaDevice *media_;\n> > > +\tV4L2VideoDevice *video_;\n> > > +\tStream stream_;\n> > > +};\n> > > +\n> > > +class VividCameraConfiguration : public CameraConfiguration\n> > > +{\n> > > +public:\n> > > +\tVividCameraConfiguration();\n> > > +\n> > > +\tStatus validate() override;\n> > > +};\n> > > +\n> > > +class PipelineHandlerVivid : public PipelineHandler\n> > > +{\n> > > +public:\n> > > +\tPipelineHandlerVivid(CameraManager *manager);\n> > > +\n> > > +\tCameraConfiguration *generateConfiguration(Camera *camera,\n> > > +\t\t\t\t\t\t   const StreamRoles &roles)\n> > > override;\n> > > +\tint configure(Camera *camera, CameraConfiguration *config) override;\n> > > +\n> > > +\tint exportFrameBuffers(Camera *camera, Stream *stream,\n> > > +\t\t\t       std::vector<std::unique_ptr<FrameBuffer>>\n> > > *buffers) override;\n> > > +\n> > > +\tint start(Camera *camera) override;\n> > > +\tvoid stop(Camera *camera) override;\n> > > +\n> > > +\tint queueRequestDevice(Camera *camera, Request *request) override;\n> > > +\n> > > +\tbool match(DeviceEnumerator *enumerator) override;\n> > > +\n> > > +private:\n> > > +\tint processControls(VividCameraData *data, Request *request);\n> > > +\n> > > +\tVividCameraData *cameraData(const Camera *camera)\n> > > +\t{\n> > > +\t\treturn static_cast<VividCameraData *>(\n> > > +\t\t\tPipelineHandler::cameraData(camera));\n> > > +\t}\n> > > +};\n> > > +\n> > > +VividCameraConfiguration::VividCameraConfiguration()\n> > > +\t: CameraConfiguration()\n> > > +{\n> > > +}\n> > > +\n> > > +CameraConfiguration::Status VividCameraConfiguration::validate()\n> > > +{\n> > > +\tStatus status = Valid;\n> > > +\n> > > +\tif (config_.empty())\n> > > +\t\treturn Invalid;\n> > > +\n> > > +\t/* Cap the number of entries to the available streams. */\n> > > +\tif (config_.size() > 1) {\n> > > +\t\tconfig_.resize(1);\n> > > +\t\tstatus = Adjusted;\n> > > +\t}\n> > > +\n> > > +\tStreamConfiguration &cfg = config_[0];\n> > > +\n> > > +\t/* Adjust the pixel format. */\n> > > +\tconst std::vector<libcamera::PixelFormat> formats =\n> > > cfg.formats().pixelformats();\n> > > +\tif (std::find(formats.begin(), formats.end(), cfg.pixelFormat) ==\n> > > formats.end()) {\n> > > +\t\tcfg.pixelFormat = cfg.formats().pixelformats()[0];\n> > > +\t\tLOG(VIVID, Debug) << \"Adjusting format to \" <<\n> > > cfg.pixelFormat.toString();\n> > > +\t\tstatus = Adjusted;\n> > > +\t}\n> > > +\n> > > +\tcfg.bufferCount = 4;\n> > > +\n> > > +\treturn status;\n> > > +}\n> > > +\n> > > +PipelineHandlerVivid::PipelineHandlerVivid(CameraManager *manager)\n> > > +\t: PipelineHandler(manager)\n> > > +{\n> > > +}\n> > > +\n> > > +CameraConfiguration *PipelineHandlerVivid::generateConfiguration(Camera\n> > > *camera,\n> > > +\t\t\t\t\t\t\t\t const\n> > > StreamRoles &roles)\n> > > +{\n> > > +\tCameraConfiguration *config = new VividCameraConfiguration();\n> > > +\tVividCameraData *data = cameraData(camera);\n> > > +\n> > > +\tif (roles.empty())\n> > > +\t\treturn config;\n> > > +\n> > > +\tstd::map<V4L2PixelFormat, std::vector<SizeRange>> v4l2Formats =\n> > > +\t\tdata->video_->formats();\n> > > +\tstd::map<PixelFormat, std::vector<SizeRange>> deviceFormats;\n> > > +\tstd::transform(v4l2Formats.begin(), v4l2Formats.end(),\n> > > +\t\t       std::inserter(deviceFormats, deviceFormats.begin()),\n> > > +\t\t       [&](const decltype(v4l2Formats)::value_type &format) {\n> > > +\t\t\t       return decltype(deviceFormats)::value_type{\n> > > +\t\t\t\t       format.first.toPixelFormat(),\n> > > +\t\t\t\t       format.second\n> > > +\t\t\t       };\n> > > +\t\t       });\n> > > +\n> > > +\tStreamFormats formats(deviceFormats);\n> > > +\tStreamConfiguration cfg(formats);\n> > > +\n> > > +\tcfg.pixelFormat = PixelFormat(DRM_FORMAT_BGR888);\n> > > +\tcfg.size = { 1280, 720 };\n> > > +\tcfg.bufferCount = 4;\n> > > +\n> > > +\tconfig->addConfiguration(cfg);\n> > > +\n> > > +\tconfig->validate();\n> > > +\n> > > +\treturn config;\n> > > +}\n> > > +\n> > > +int PipelineHandlerVivid::configure(Camera *camera, CameraConfiguration\n> > > *config)\n> > > +{\n> > > +\tVividCameraData *data = cameraData(camera);\n> > > +\tStreamConfiguration &cfg = config->at(0);\n> > > +\tint ret;\n> > > +\n> > > +\tV4L2DeviceFormat format = {};\n> > > +\tformat.fourcc = data->video_->toV4L2PixelFormat(cfg.pixelFormat);\n> > > +\tformat.size = cfg.size;\n> > > +\n> > > +\tret = data->video_->setFormat(&format);\n> > > +\tif (ret)\n> > > +\t\treturn ret;\n> > > +\n> > > +\tif (format.size != cfg.size ||\n> > > +\t    format.fourcc != data->video_->toV4L2PixelFormat(cfg.pixelFormat))\n> > > +\t\treturn -EINVAL;\n> > > +\n> > > +\tcfg.setStream(&data->stream_);\n> > > +\tcfg.stride = format.planes[0].bpl;\n> > > +\n> > > +\treturn 0;\n> > > +}\n> > > +\n> > > +int PipelineHandlerVivid::exportFrameBuffers(Camera *camera, Stream\n> > > *stream,\n> > > +\t\t\t\t\t     std::vector<std::unique_ptr<FrameBu\n> > > ffer>> *buffers)\n> > > +{\n> > > +\tVividCameraData *data = cameraData(camera);\n> > > +\tunsigned int count = stream->configuration().bufferCount;\n> > > +\n> > > +\treturn data->video_->exportBuffers(count, buffers);\n> > > +}\n> > > +\n> > > +int PipelineHandlerVivid::start(Camera *camera)\n> > > +{\n> > > +\tVividCameraData *data = cameraData(camera);\n> > > +\tunsigned int count = data->stream_.configuration().bufferCount;\n> > > +\n> > > +\tint ret = data->video_->importBuffers(count);\n> > > +\tif (ret < 0)\n> > > +\t\treturn ret;\n> > > +\n> > > +\tret = data->video_->streamOn();\n> > > +\tif (ret < 0) {\n> > > +\t\tdata->ipa_->stop();\n> > > +\t\tdata->video_->releaseBuffers();\n> > > +\t\treturn ret;\n> > > +\t}\n> > > +\n> > > +\treturn 0;\n> > > +}\n> > > +\n> > > +void PipelineHandlerVivid::stop(Camera *camera)\n> > > +{\n> > > +\tVividCameraData *data = cameraData(camera);\n> > > +\tdata->video_->streamOff();\n> > > +\tdata->video_->releaseBuffers();\n> > > +}\n> > > +\n> > > +int PipelineHandlerVivid::processControls(VividCameraData *data, Request\n> > > *request)\n> > > +{\n> > > +\tControlList controls(data->video_->controls());\n> > > +\n> > > +\tfor (auto it : request->controls()) {\n> > > +\t\tunsigned int id = it.first;\n> > > +\t\tunsigned int offset;\n> > > +\t\tuint32_t cid;\n> > > +\n> > > +\t\tif (id == controls::Brightness) {\n> > > +\t\t\tcid = V4L2_CID_BRIGHTNESS;\n> > > +\t\t\toffset = 128;\n> > > +\t\t} else if (id == controls::Contrast) {\n> > > +\t\t\tcid = V4L2_CID_CONTRAST;\n> > > +\t\t\toffset = 0;\n> > > +\t\t} else if (id == controls::Saturation) {\n> > > +\t\t\tcid = V4L2_CID_SATURATION;\n> > > +\t\t\toffset = 0;\n> > > +\t\t} else {\n> > > +\t\t\tcontinue;\n> > > +\t\t}\n> > > +\n> > > +\t\tint32_t value = lroundf(it.second.get<float>() * 128 + offset);\n> > > +\t\tcontrols.set(cid, utils::clamp(value, 0, 255));\n> > > +\t}\n> > > +\n> > > +\tfor (const auto &ctrl : controls)\n> > > +\t\tLOG(VIVID, Debug)\n> > > +\t\t\t<< \"Setting control \" << utils::hex(ctrl.first)\n> > > +\t\t\t<< \" to \" << ctrl.second.toString();\n> > > +\n> > > +\tint ret = data->video_->setControls(&controls);\n> > > +\tif (ret) {\n> > > +\t\tLOG(VIVID, Error) << \"Failed to set controls: \" << ret;\n> > > +\t\treturn ret < 0 ? ret : -EINVAL;\n> > > +\t}\n> > > +\n> > > +\treturn ret;\n> > > +}\n> > > +\n> > > +int PipelineHandlerVivid::queueRequestDevice(Camera *camera, Request\n> > > *request)\n> > > +{\n> > > +\tVividCameraData *data = cameraData(camera);\n> > > +\tFrameBuffer *buffer = request->findBuffer(&data->stream_);\n> > > +\tif (!buffer) {\n> > > +\t\tLOG(VIVID, Error)\n> > > +\t\t\t<< \"Attempt to queue request with invalid stream\";\n> > > +\n> > > +\t\treturn -ENOENT;\n> > > +\t}\n> > > +\n> > > +\tint ret = processControls(data, request);\n> > > +\tif (ret < 0)\n> > > +\t\treturn ret;\n> > > +\n> > > +\tret = data->video_->queueBuffer(buffer);\n> > > +\tif (ret < 0)\n> > > +\t\treturn ret;\n> > > +\n> > > +\treturn 0;\n> > > +}\n> > > +\n> > > +bool PipelineHandlerVivid::match(DeviceEnumerator *enumerator)\n> > > +{\n> > > +\tDeviceMatch dm(\"vivid\");\n> > > +\tdm.add(\"vivid-000-vid-cap\");\n> > > +\n> > > +\tMediaDevice *media = acquireMediaDevice(enumerator, dm);\n> > > +\tif (!media)\n> > > +\t\treturn false;\n> > > +\n> > > +\tstd::unique_ptr<VividCameraData> data =\n> > > std::make_unique<VividCameraData>(this, media);\n> > > +\n> > > +\t/* Locate and open the capture video node. */\n> > > +\tif (data->init())\n> > > +\t\treturn false;\n> > > +\n> > > +\t/* Create and register the camera. */\n> > > +\tstd::set<Stream *> streams{ &data->stream_ };\n> > > +\tstd::shared_ptr<Camera> camera = Camera::create(this, data->video_-\n> > > > deviceName(), streams);\n> > > +\tregisterCamera(std::move(camera), std::move(data));\n> > > +\n> > > +\treturn true;\n> > > +}\n> > > +\n> > > +int VividCameraData::init()\n> > > +{\n> > > +\tvideo_ = new V4L2VideoDevice(media_->getEntityByName(\"vivid-000-vid-\n> > > cap\"));\n> > > +\tif (video_->open())\n> > > +\t\treturn -ENODEV;\n> > > +\n> > > +\tvideo_->bufferReady.connect(this, &VividCameraData::bufferReady);\n> > > +\n> > > +\t/* Initialise the supported controls. */\n> > > +\tconst ControlInfoMap &controls = video_->controls();\n> > > +\tControlInfoMap::Map ctrls;\n> > > +\n> > > +\tfor (const auto &ctrl : controls) {\n> > > +\t\tconst ControlId *id;\n> > > +\t\tControlInfo info;\n> > > +\n> > > +\t\tswitch (ctrl.first->id()) {\n> > > +\t\tcase V4L2_CID_BRIGHTNESS:\n> > > +\t\t\tid = &controls::Brightness;\n> > > +\t\t\tinfo = ControlInfo{ { -1.0f }, { 1.0f }, { 0.0f } };\n> > > +\t\t\tbreak;\n> > > +\t\tcase V4L2_CID_CONTRAST:\n> > > +\t\t\tid = &controls::Contrast;\n> > > +\t\t\tinfo = ControlInfo{ { 0.0f }, { 2.0f }, { 1.0f } };\n> > > +\t\t\tbreak;\n> > > +\t\tcase V4L2_CID_SATURATION:\n> > > +\t\t\tid = &controls::Saturation;\n> > > +\t\t\tinfo = ControlInfo{ { 0.0f }, { 2.0f }, { 1.0f } };\n> > > +\t\t\tbreak;\n> > > +\t\tdefault:\n> > > +\t\t\tcontinue;\n> > > +\t\t}\n> > > +\n> > > +\t\tctrls.emplace(id, info);\n> > > +\t}\n> > > +\n> > > +\tcontrolInfo_ = std::move(ctrls);\n> > > +\n> > > +\treturn 0;\n> > > +}\n> > > +\n> > > +void VividCameraData::bufferReady(FrameBuffer *buffer)\n> > > +{\n> > > +\tRequest *request = buffer->request();\n> > > +\n> > > +\tpipe_->completeBuffer(camera_, request, buffer);\n> > > +\tpipe_->completeRequest(camera_, request);\n> > > +}\n> > > +\n> > > +REGISTER_PIPELINE_HANDLER(PipelineHandlerVivid);\n> > > +\n> > > +} /* namespace libcamera */","headers":{"Return-Path":"<nicolas@ndufresne.ca>","Received":["from mail-qt1-x834.google.com (mail-qt1-x834.google.com\n\t[IPv6:2607:f8b0:4864:20::834])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 4994C600FE\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 19 Jun 2020 18:35:52 +0200 (CEST)","by mail-qt1-x834.google.com with SMTP id e3so1222197qts.12\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 19 Jun 2020 09:35:52 -0700 (PDT)","from nicolas-tpx395 ([192.222.193.21])\n\tby smtp.gmail.com with ESMTPSA id\n\tw1sm897450qkf.73.2020.06.19.09.35.49\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tFri, 19 Jun 2020 09:35:50 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key; \n\tunprotected)\n\theader.d=ndufresne-ca.20150623.gappssmtp.com\n\theader.i=@ndufresne-ca.20150623.gappssmtp.com header.b=\"OweboA5C\"; \n\tdkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=ndufresne-ca.20150623.gappssmtp.com; s=20150623;\n\th=message-id:subject:from:to:date:in-reply-to:references:user-agent\n\t:mime-version:content-transfer-encoding;\n\tbh=DgORD1oZqyrhnGR9+f+W8R9WHqoi+uqYmw2VFeoWl+8=;\n\tb=OweboA5C8XUTcZcUpeXOCa38mSzuqdMsOvvlsWxEJXl7Gl34rgwi8Yzqc7wmgstdJ2\n\tHvXwO3JAqucouu8je/+oNvq1gctLm07+L8ObJqDTm/GU92Nc+uTizNFT4W4SvwXBrlhz\n\tKNgE0S+/xZ09McTALvV4FlM2PAmxRfb97KjjLNltxT6OaMoidZlm8LxEDWX3bQdxlMmJ\n\t/l3hnXYoNfwoRWG96BOleQniFCdFybWVGg1wAhGTtLmwrKn1o8TQqes69uAb4+RgVmb8\n\tBIYtNgICfKjxksVt/nIcW4lGRWzmEeASktiZ0JNj7UmiWgZsNS1K4HNXE9HBhtSvqfFB\n\t59kQ==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:message-id:subject:from:to:date:in-reply-to\n\t:references:user-agent:mime-version:content-transfer-encoding;\n\tbh=DgORD1oZqyrhnGR9+f+W8R9WHqoi+uqYmw2VFeoWl+8=;\n\tb=SW3A1mGpddQk16pYodSUpWLMKn1wSkEgb3Fb+sSGqvh8Md55oSlx6dz599u2hW5DEe\n\tswQEa9nJEjFbWyYSYMDbLNISbkCU47XTnWqKbNQmFAJa0l9C2LI+S4JWWF9JOZZJJqr6\n\tRKzu4cEm9wK0MFYtTlb1iMX9hVzOAis4w9fncDaPWH5W1Zoiw8mq2SVr97WmMfRfX0+Q\n\t1srxkQzLgTpCPG3pxMoTI/l5taEJOxTBxPbUnhe/7PtUfNgak/Ml+iYPdw0TrU8k1s4o\n\teXB25olQ3jOcWPGw97hArxLYWY/7UzEzBGK8hU4AL5eeP5BfMKb9z/ZOVHsgyGZ1XJtW\n\tr9Gw==","X-Gm-Message-State":"AOAM532LGY9ZjTseSGHsHLY9IyP/zXWNrIgcCsBparA8L77AgGDSDv5o\n\t8/eA4gcNdaSGgInGyWAm3KSN297NxNc=","X-Google-Smtp-Source":"ABdhPJxgnegYMLn/eRmBp5XnXR2hfeDZnMzebNfCM3qsHwK18VYLBLLNrsxUFV0yalat8719SiAzew==","X-Received":"by 2002:ac8:4e81:: with SMTP id 1mr4468527qtp.364.1592584551009; \n\tFri, 19 Jun 2020 09:35:51 -0700 (PDT)","Message-ID":"<1073f243556c26abe24ff51319307b854656b19a.camel@ndufresne.ca>","From":"Nicolas Dufresne <nicolas@ndufresne.ca>","To":"kieran.bingham@ideasonboard.com, Paul Elder\n\t<paul.elder@ideasonboard.com>,  libcamera devel\n\t<libcamera-devel@lists.libcamera.org>","Date":"Fri, 19 Jun 2020 12:35:49 -0400","In-Reply-To":"<40e70402-9e6b-f065-b24c-1e34ca3d9c99@ideasonboard.com>","References":"<20200619120249.1084998-1-kieran.bingham@ideasonboard.com>\n\t<95f92384c74975b52f1df3ee71911cfba1baadfc.camel@ndufresne.ca>\n\t<40e70402-9e6b-f065-b24c-1e34ca3d9c99@ideasonboard.com>","Content-Type":"text/plain; charset=\"UTF-8\"","User-Agent":"Evolution 3.36.2 (3.36.2-1.fc32) ","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","Subject":"Re: [libcamera-devel] [PATCH] libcamera: pipelines: Add VIVID\n\tpipeline support","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Fri, 19 Jun 2020 16:35:52 -0000"}},{"id":5308,"web_url":"https://patchwork.libcamera.org/comment/5308/","msgid":"<20200622014517.GB25355@pendragon.ideasonboard.com>","date":"2020-06-22T01:45:17","subject":"Re: [libcamera-devel] [PATCH] libcamera: pipelines: Add VIVID\n\tpipeline support","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Kieran,\n\nOn Fri, Jun 19, 2020 at 05:19:55PM +0100, Kieran Bingham wrote:\n> On 19/06/2020 16:34, Nicolas Dufresne wrote:\n> > Le vendredi 19 juin 2020 à 13:02 +0100, Kieran Bingham a écrit :\n> >> The VIVID driver supports more pixel formats and properties than the VIMC\n> >> driver, and can provide extended testing for libcamera.\n> >>\n> >> The VIMC pipeline handler is duplicated and simplified to support the\n> >> VIVID device.\n> >>\n> >> Unfortuantely, the VIVID device can not be handled by either of the\n> >> generic UVC or Simple pipeline handlers.\n> > \n> > Can you extend on that ?\n> \n> I actually disagreed with myself in my latest reply (@13:14 in my timezone).\n> \n> > Perhaps an alternative route to supporting this however would be to make\n> > the simple pipeline handler support pipelines which *don't* have a\n> > sensor object... though the VIVID controls could be useful to support\n> > directly.\n> > \n> > Perhaps - inheriting the simple-pipeline handler should be possible, to\n> > provide some 'device specific' adjustments for things like device\n> > specific controls.\n> \n> The simple pipeline expects to walk a media graph and find a sensor.\n> VIVID doesn't expose a sensor, nor desire to. It's just a plain video\n> device node.\n> \n> Perhaps we could extend the simple pipeline handler to support devices\n> which don't have a sensor ... or create a \"simple-simple-pipeline\n> handler\" ? :-D\n> \n> Equally - there could then be quite a lot of commonality between the UVC\n> pipeline handler, though we kept that separate because of expected\n> differences with UVC devices ... (in particular the controls for UVC\n> have some specific variations already handled there).\n> \n> So for testing with vivid, I took the easy option of creating a new\n> pipeline for VIVID. But there is so much code duplication already, I'm\n> sure we could find some reuse somewhere ...\n\nThank you for posting this patch. I think it's useful, as vimc currently\nhas a fairly limited set of features compared to vivid. In particular,\nthe lack of YUV format support in vimc (when configuring the pipeline\nwith a Bayer camera sensor as a source) limits our ability to test any\nuse case that requires YUV without real hardware.\n\nIdeally, I would like vimc to gain support for these features, and avoid\nadding another pipeline handler for vivid that would duplicate code. We\ncan discuss how to minimize code duplication, either with the simple\npipeline handler or the UVC pipeline handler, but the effort to add YUV\nsupport in vimc seems fairly low to me, possibly even lower than\ndeveloping vivid support in libcamera with reduced code duplication. I'm\ncurious to know your opinion on long-term support for vivid in\nlibcamera, compared to enhancing the vimc driver.\n\nWe could merge this pipeline handler as-is and deal with code sharing\nlater (or possibly even drop it later when vimc support will be better),\nbut we also need to consider that every new pipeline handler increases\nthe maintenance effort, and makes changes to the pipeline handler API\nmore difficult. We need to consider the pros and cons.\n\nRegarding code sharing, without having studied the question in details,\nI feel that the UVC pipeline handler would be a better target, as vimc\nis media controller-centric, while UVC and vivid are video node-centric.\nI'm even considering supporting vimc with the simple pipeline handler\ninstead of a dedicated pipeline handler, but that would likely require\nchanges in both the simple pipeline handler and the vimc driver (most\nnotably the vimc scaler, which hardcodes a downscaling ratio of 3).\n\n> >> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> >> ---\n> >>  meson_options.txt                        |   2 +-\n> >>  src/libcamera/pipeline/vivid/meson.build |   5 +\n> >>  src/libcamera/pipeline/vivid/vivid.cpp   | 441 +++++++++++++++++++++++\n> >>  3 files changed, 447 insertions(+), 1 deletion(-)\n> >>  create mode 100644 src/libcamera/pipeline/vivid/meson.build\n> >>  create mode 100644 src/libcamera/pipeline/vivid/vivid.cpp\n> >>\n> >> diff --git a/meson_options.txt b/meson_options.txt\n> >> index badace151bb6..dc4684df49b2 100644\n> >> --- a/meson_options.txt\n> >> +++ b/meson_options.txt\n> >> @@ -16,7 +16,7 @@ option('gstreamer',\n> >>  \n> >>  option('pipelines',\n> >>          type : 'array',\n> >> -        choices : ['ipu3', 'raspberrypi', 'rkisp1', 'simple', 'uvcvideo',\n> >> 'vimc'],\n> >> +        choices : ['ipu3', 'raspberrypi', 'rkisp1', 'simple', 'uvcvideo',\n> >> 'vimc', 'vivid'],\n> >>          description : 'Select which pipeline handlers to include')\n> >>  \n> >>  option('test',\n> >> diff --git a/src/libcamera/pipeline/vivid/meson.build\n> >> b/src/libcamera/pipeline/vivid/meson.build\n> >> new file mode 100644\n> >> index 000000000000..086bb825387c\n> >> --- /dev/null\n> >> +++ b/src/libcamera/pipeline/vivid/meson.build\n> >> @@ -0,0 +1,5 @@\n> >> +# SPDX-License-Identifier: CC0-1.0\n> >> +\n> >> +libcamera_sources += files([\n> >> +    'vivid.cpp',\n> >> +])\n> >> diff --git a/src/libcamera/pipeline/vivid/vivid.cpp\n> >> b/src/libcamera/pipeline/vivid/vivid.cpp\n> >> new file mode 100644\n> >> index 000000000000..b811e33a0299\n> >> --- /dev/null\n> >> +++ b/src/libcamera/pipeline/vivid/vivid.cpp\n> >> @@ -0,0 +1,441 @@\n> >> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> >> +/*\n> >> + * Copyright (C) 2018, Google Inc.\n> >> + *\n> >> + * vivid.cpp - Pipeline handler for the vivid capture device\n> >> + */\n> >> +\n> >> +#include <algorithm>\n> >> +#include <iomanip>\n> >> +#include <map>\n> >> +#include <math.h>\n> >> +#include <tuple>\n> >> +\n> >> +#include <linux/media-bus-format.h>\n> >> +#include <linux/version.h>\n> >> +\n> >> +#include <libcamera/camera.h>\n> >> +#include <libcamera/control_ids.h>\n> >> +#include <libcamera/controls.h>\n> >> +#include <libcamera/ipa/ipa_interface.h>\n> >> +#include <libcamera/ipa/ipa_module_info.h>\n> >> +#include <libcamera/request.h>\n> >> +#include <libcamera/stream.h>\n> >> +\n> >> +#include \"libcamera/internal/camera_sensor.h\"\n> >> +#include \"libcamera/internal/device_enumerator.h\"\n> >> +#include \"libcamera/internal/ipa_manager.h\"\n> >> +#include \"libcamera/internal/log.h\"\n> >> +#include \"libcamera/internal/media_device.h\"\n> >> +#include \"libcamera/internal/pipeline_handler.h\"\n> >> +#include \"libcamera/internal/utils.h\"\n> >> +#include \"libcamera/internal/v4l2_controls.h\"\n> >> +#include \"libcamera/internal/v4l2_subdevice.h\"\n> >> +#include \"libcamera/internal/v4l2_videodevice.h\"\n> >> +\n> >> +#define VIVID_CID_CUSTOM_BASE           (V4L2_CID_USER_BASE | 0xf000)\n> >> +#define VIVID_CID_BUTTON                (VIVID_CID_CUSTOM_BASE + 0)\n> >> +#define VIVID_CID_BOOLEAN               (VIVID_CID_CUSTOM_BASE + 1)\n> >> +#define VIVID_CID_INTEGER               (VIVID_CID_CUSTOM_BASE + 2)\n> >> +#define VIVID_CID_INTEGER64             (VIVID_CID_CUSTOM_BASE + 3)\n> >> +#define VIVID_CID_MENU                  (VIVID_CID_CUSTOM_BASE + 4)\n> >> +#define VIVID_CID_STRING                (VIVID_CID_CUSTOM_BASE + 5)\n> >> +#define VIVID_CID_BITMASK               (VIVID_CID_CUSTOM_BASE + 6)\n> >> +#define VIVID_CID_INTMENU               (VIVID_CID_CUSTOM_BASE + 7)\n> >> +#define VIVID_CID_U32_ARRAY             (VIVID_CID_CUSTOM_BASE + 8)\n> >> +#define VIVID_CID_U16_MATRIX            (VIVID_CID_CUSTOM_BASE + 9)\n> >> +#define VIVID_CID_U8_4D_ARRAY           (VIVID_CID_CUSTOM_BASE + 10)\n> >> +\n> >> +#define VIVID_CID_VIVID_BASE            (0x00f00000 | 0xf000)\n> >> +#define VIVID_CID_VIVID_CLASS           (0x00f00000 | 1)\n> >> +#define VIVID_CID_TEST_PATTERN          (VIVID_CID_VIVID_BASE + 0)\n> >> +#define VIVID_CID_OSD_TEXT_MODE         (VIVID_CID_VIVID_BASE + 1)\n> >> +#define VIVID_CID_HOR_MOVEMENT          (VIVID_CID_VIVID_BASE + 2)\n> >> +#define VIVID_CID_VERT_MOVEMENT         (VIVID_CID_VIVID_BASE + 3)\n> >> +#define VIVID_CID_SHOW_BORDER           (VIVID_CID_VIVID_BASE + 4)\n> >> +#define VIVID_CID_SHOW_SQUARE           (VIVID_CID_VIVID_BASE + 5)\n> >> +#define VIVID_CID_INSERT_SAV            (VIVID_CID_VIVID_BASE + 6)\n> >> +#define VIVID_CID_INSERT_EAV            (VIVID_CID_VIVID_BASE + 7)\n> >> +#define VIVID_CID_VBI_CAP_INTERLACED    (VIVID_CID_VIVID_BASE + 8)\n> >> +\n> >> +#define VIVID_CID_HFLIP                 (VIVID_CID_VIVID_BASE + 20)\n> >> +#define VIVID_CID_VFLIP                 (VIVID_CID_VIVID_BASE + 21)\n> >> +#define VIVID_CID_STD_ASPECT_RATIO      (VIVID_CID_VIVID_BASE + 22)\n> >> +#define VIVID_CID_DV_TIMINGS_ASPECT_RATIO       (VIVID_CID_VIVID_BASE + 23)\n> >> +#define VIVID_CID_TSTAMP_SRC            (VIVID_CID_VIVID_BASE + 24)\n> >> +#define VIVID_CID_COLORSPACE            (VIVID_CID_VIVID_BASE + 25)\n> >> +#define VIVID_CID_XFER_FUNC             (VIVID_CID_VIVID_BASE + 26)\n> >> +#define VIVID_CID_YCBCR_ENC             (VIVID_CID_VIVID_BASE + 27)\n> >> +#define VIVID_CID_QUANTIZATION          (VIVID_CID_VIVID_BASE + 28)\n> >> +#define VIVID_CID_LIMITED_RGB_RANGE     (VIVID_CID_VIVID_BASE + 29)\n> >> +#define VIVID_CID_ALPHA_MODE            (VIVID_CID_VIVID_BASE + 30)\n> >> +#define VIVID_CID_HAS_CROP_CAP          (VIVID_CID_VIVID_BASE + 31)\n> >> +#define VIVID_CID_HAS_COMPOSE_CAP       (VIVID_CID_VIVID_BASE + 32)\n> >> +#define VIVID_CID_HAS_SCALER_CAP        (VIVID_CID_VIVID_BASE + 33)\n> >> +#define VIVID_CID_HAS_CROP_OUT          (VIVID_CID_VIVID_BASE + 34)\n> >> +#define VIVID_CID_HAS_COMPOSE_OUT       (VIVID_CID_VIVID_BASE + 35)\n> >> +#define VIVID_CID_HAS_SCALER_OUT        (VIVID_CID_VIVID_BASE + 36)\n> >> +#define VIVID_CID_LOOP_VIDEO            (VIVID_CID_VIVID_BASE + 37)\n> >> +#define VIVID_CID_SEQ_WRAP              (VIVID_CID_VIVID_BASE + 38)\n> >> +#define VIVID_CID_TIME_WRAP             (VIVID_CID_VIVID_BASE + 39)\n> >> +#define VIVID_CID_MAX_EDID_BLOCKS       (VIVID_CID_VIVID_BASE + 40)\n> >> +#define VIVID_CID_PERCENTAGE_FILL       (VIVID_CID_VIVID_BASE + 41)\n> >> +#define VIVID_CID_REDUCED_FPS           (VIVID_CID_VIVID_BASE + 42)\n> >> +#define VIVID_CID_HSV_ENC               (VIVID_CID_VIVID_BASE + 43)\n> >> +#define VIVID_CID_DISPLAY_PRESENT       (VIVID_CID_VIVID_BASE + 44)\n> >> +\n> >> +#define VIVID_CID_STD_SIGNAL_MODE       (VIVID_CID_VIVID_BASE + 60)\n> >> +#define VIVID_CID_STANDARD              (VIVID_CID_VIVID_BASE + 61)\n> >> +#define VIVID_CID_DV_TIMINGS_SIGNAL_MODE        (VIVID_CID_VIVID_BASE + 62)\n> >> +#define VIVID_CID_DV_TIMINGS            (VIVID_CID_VIVID_BASE + 63)\n> >> +#define VIVID_CID_PERC_DROPPED          (VIVID_CID_VIVID_BASE + 64)\n> >> +#define VIVID_CID_DISCONNECT            (VIVID_CID_VIVID_BASE + 65)\n> >> +#define VIVID_CID_DQBUF_ERROR           (VIVID_CID_VIVID_BASE + 66)\n> >> +#define VIVID_CID_QUEUE_SETUP_ERROR     (VIVID_CID_VIVID_BASE + 67)\n> >> +#define VIVID_CID_BUF_PREPARE_ERROR     (VIVID_CID_VIVID_BASE + 68)\n> >> +#define VIVID_CID_START_STR_ERROR       (VIVID_CID_VIVID_BASE + 69)\n> >> +#define VIVID_CID_QUEUE_ERROR           (VIVID_CID_VIVID_BASE + 70)\n> >> +#define VIVID_CID_CLEAR_FB              (VIVID_CID_VIVID_BASE + 71)\n> >> +#define VIVID_CID_REQ_VALIDATE_ERROR    (VIVID_CID_VIVID_BASE + 72)\n> >> +\n> >> +#define VIVID_CID_RADIO_SEEK_MODE       (VIVID_CID_VIVID_BASE + 90)\n> >> +#define VIVID_CID_RADIO_SEEK_PROG_LIM   (VIVID_CID_VIVID_BASE + 91)\n> >> +#define VIVID_CID_RADIO_RX_RDS_RBDS     (VIVID_CID_VIVID_BASE + 92)\n> >> +#define VIVID_CID_RADIO_RX_RDS_BLOCKIO  (VIVID_CID_VIVID_BASE + 93)\n> >> +\n> >> +#define VIVID_CID_RADIO_TX_RDS_BLOCKIO  (VIVID_CID_VIVID_BASE + 94)\n> >> +\n> >> +#define VIVID_CID_SDR_CAP_FM_DEVIATION  (VIVID_CID_VIVID_BASE + 110)\n> >> +\n> >> +\n> >> +namespace libcamera {\n> >> +\n> >> +LOG_DEFINE_CATEGORY(VIVID)\n> >> +\n> >> +class VividCameraData : public CameraData\n> >> +{\n> >> +public:\n> >> +\tVividCameraData(PipelineHandler *pipe, MediaDevice *media)\n> >> +\t\t: CameraData(pipe), media_(media), video_(nullptr)\n> >> +\t{\n> >> +\t}\n> >> +\n> >> +\t~VividCameraData()\n> >> +\t{\n> >> +\t\tdelete video_;\n> >> +\t}\n> >> +\n> >> +\tint init();\n> >> +\tvoid bufferReady(FrameBuffer *buffer);\n> >> +\n> >> +\tMediaDevice *media_;\n> >> +\tV4L2VideoDevice *video_;\n> >> +\tStream stream_;\n> >> +};\n> >> +\n> >> +class VividCameraConfiguration : public CameraConfiguration\n> >> +{\n> >> +public:\n> >> +\tVividCameraConfiguration();\n> >> +\n> >> +\tStatus validate() override;\n> >> +};\n> >> +\n> >> +class PipelineHandlerVivid : public PipelineHandler\n> >> +{\n> >> +public:\n> >> +\tPipelineHandlerVivid(CameraManager *manager);\n> >> +\n> >> +\tCameraConfiguration *generateConfiguration(Camera *camera,\n> >> +\t\t\t\t\t\t   const StreamRoles &roles)\n> >> override;\n> >> +\tint configure(Camera *camera, CameraConfiguration *config) override;\n> >> +\n> >> +\tint exportFrameBuffers(Camera *camera, Stream *stream,\n> >> +\t\t\t       std::vector<std::unique_ptr<FrameBuffer>>\n> >> *buffers) override;\n> >> +\n> >> +\tint start(Camera *camera) override;\n> >> +\tvoid stop(Camera *camera) override;\n> >> +\n> >> +\tint queueRequestDevice(Camera *camera, Request *request) override;\n> >> +\n> >> +\tbool match(DeviceEnumerator *enumerator) override;\n> >> +\n> >> +private:\n> >> +\tint processControls(VividCameraData *data, Request *request);\n> >> +\n> >> +\tVividCameraData *cameraData(const Camera *camera)\n> >> +\t{\n> >> +\t\treturn static_cast<VividCameraData *>(\n> >> +\t\t\tPipelineHandler::cameraData(camera));\n> >> +\t}\n> >> +};\n> >> +\n> >> +VividCameraConfiguration::VividCameraConfiguration()\n> >> +\t: CameraConfiguration()\n> >> +{\n> >> +}\n> >> +\n> >> +CameraConfiguration::Status VividCameraConfiguration::validate()\n> >> +{\n> >> +\tStatus status = Valid;\n> >> +\n> >> +\tif (config_.empty())\n> >> +\t\treturn Invalid;\n> >> +\n> >> +\t/* Cap the number of entries to the available streams. */\n> >> +\tif (config_.size() > 1) {\n> >> +\t\tconfig_.resize(1);\n> >> +\t\tstatus = Adjusted;\n> >> +\t}\n> >> +\n> >> +\tStreamConfiguration &cfg = config_[0];\n> >> +\n> >> +\t/* Adjust the pixel format. */\n> >> +\tconst std::vector<libcamera::PixelFormat> formats =\n> >> cfg.formats().pixelformats();\n> >> +\tif (std::find(formats.begin(), formats.end(), cfg.pixelFormat) ==\n> >> formats.end()) {\n> >> +\t\tcfg.pixelFormat = cfg.formats().pixelformats()[0];\n> >> +\t\tLOG(VIVID, Debug) << \"Adjusting format to \" <<\n> >> cfg.pixelFormat.toString();\n> >> +\t\tstatus = Adjusted;\n> >> +\t}\n> >> +\n> >> +\tcfg.bufferCount = 4;\n> >> +\n> >> +\treturn status;\n> >> +}\n> >> +\n> >> +PipelineHandlerVivid::PipelineHandlerVivid(CameraManager *manager)\n> >> +\t: PipelineHandler(manager)\n> >> +{\n> >> +}\n> >> +\n> >> +CameraConfiguration *PipelineHandlerVivid::generateConfiguration(Camera\n> >> *camera,\n> >> +\t\t\t\t\t\t\t\t const\n> >> StreamRoles &roles)\n> >> +{\n> >> +\tCameraConfiguration *config = new VividCameraConfiguration();\n> >> +\tVividCameraData *data = cameraData(camera);\n> >> +\n> >> +\tif (roles.empty())\n> >> +\t\treturn config;\n> >> +\n> >> +\tstd::map<V4L2PixelFormat, std::vector<SizeRange>> v4l2Formats =\n> >> +\t\tdata->video_->formats();\n> >> +\tstd::map<PixelFormat, std::vector<SizeRange>> deviceFormats;\n> >> +\tstd::transform(v4l2Formats.begin(), v4l2Formats.end(),\n> >> +\t\t       std::inserter(deviceFormats, deviceFormats.begin()),\n> >> +\t\t       [&](const decltype(v4l2Formats)::value_type &format) {\n> >> +\t\t\t       return decltype(deviceFormats)::value_type{\n> >> +\t\t\t\t       format.first.toPixelFormat(),\n> >> +\t\t\t\t       format.second\n> >> +\t\t\t       };\n> >> +\t\t       });\n> >> +\n> >> +\tStreamFormats formats(deviceFormats);\n> >> +\tStreamConfiguration cfg(formats);\n> >> +\n> >> +\tcfg.pixelFormat = PixelFormat(DRM_FORMAT_BGR888);\n> >> +\tcfg.size = { 1280, 720 };\n> >> +\tcfg.bufferCount = 4;\n> >> +\n> >> +\tconfig->addConfiguration(cfg);\n> >> +\n> >> +\tconfig->validate();\n> >> +\n> >> +\treturn config;\n> >> +}\n> >> +\n> >> +int PipelineHandlerVivid::configure(Camera *camera, CameraConfiguration\n> >> *config)\n> >> +{\n> >> +\tVividCameraData *data = cameraData(camera);\n> >> +\tStreamConfiguration &cfg = config->at(0);\n> >> +\tint ret;\n> >> +\n> >> +\tV4L2DeviceFormat format = {};\n> >> +\tformat.fourcc = data->video_->toV4L2PixelFormat(cfg.pixelFormat);\n> >> +\tformat.size = cfg.size;\n> >> +\n> >> +\tret = data->video_->setFormat(&format);\n> >> +\tif (ret)\n> >> +\t\treturn ret;\n> >> +\n> >> +\tif (format.size != cfg.size ||\n> >> +\t    format.fourcc != data->video_->toV4L2PixelFormat(cfg.pixelFormat))\n> >> +\t\treturn -EINVAL;\n> >> +\n> >> +\tcfg.setStream(&data->stream_);\n> >> +\tcfg.stride = format.planes[0].bpl;\n> >> +\n> >> +\treturn 0;\n> >> +}\n> >> +\n> >> +int PipelineHandlerVivid::exportFrameBuffers(Camera *camera, Stream *stream,\n> >> +\t\t\t\t\t     std::vector<std::unique_ptr<FrameBu\n> >> ffer>> *buffers)\n> >> +{\n> >> +\tVividCameraData *data = cameraData(camera);\n> >> +\tunsigned int count = stream->configuration().bufferCount;\n> >> +\n> >> +\treturn data->video_->exportBuffers(count, buffers);\n> >> +}\n> >> +\n> >> +int PipelineHandlerVivid::start(Camera *camera)\n> >> +{\n> >> +\tVividCameraData *data = cameraData(camera);\n> >> +\tunsigned int count = data->stream_.configuration().bufferCount;\n> >> +\n> >> +\tint ret = data->video_->importBuffers(count);\n> >> +\tif (ret < 0)\n> >> +\t\treturn ret;\n> >> +\n> >> +\tret = data->video_->streamOn();\n> >> +\tif (ret < 0) {\n> >> +\t\tdata->ipa_->stop();\n> >> +\t\tdata->video_->releaseBuffers();\n> >> +\t\treturn ret;\n> >> +\t}\n> >> +\n> >> +\treturn 0;\n> >> +}\n> >> +\n> >> +void PipelineHandlerVivid::stop(Camera *camera)\n> >> +{\n> >> +\tVividCameraData *data = cameraData(camera);\n> >> +\tdata->video_->streamOff();\n> >> +\tdata->video_->releaseBuffers();\n> >> +}\n> >> +\n> >> +int PipelineHandlerVivid::processControls(VividCameraData *data, Request\n> >> *request)\n> >> +{\n> >> +\tControlList controls(data->video_->controls());\n> >> +\n> >> +\tfor (auto it : request->controls()) {\n> >> +\t\tunsigned int id = it.first;\n> >> +\t\tunsigned int offset;\n> >> +\t\tuint32_t cid;\n> >> +\n> >> +\t\tif (id == controls::Brightness) {\n> >> +\t\t\tcid = V4L2_CID_BRIGHTNESS;\n> >> +\t\t\toffset = 128;\n> >> +\t\t} else if (id == controls::Contrast) {\n> >> +\t\t\tcid = V4L2_CID_CONTRAST;\n> >> +\t\t\toffset = 0;\n> >> +\t\t} else if (id == controls::Saturation) {\n> >> +\t\t\tcid = V4L2_CID_SATURATION;\n> >> +\t\t\toffset = 0;\n> >> +\t\t} else {\n> >> +\t\t\tcontinue;\n> >> +\t\t}\n> >> +\n> >> +\t\tint32_t value = lroundf(it.second.get<float>() * 128 + offset);\n> >> +\t\tcontrols.set(cid, utils::clamp(value, 0, 255));\n> >> +\t}\n> >> +\n> >> +\tfor (const auto &ctrl : controls)\n> >> +\t\tLOG(VIVID, Debug)\n> >> +\t\t\t<< \"Setting control \" << utils::hex(ctrl.first)\n> >> +\t\t\t<< \" to \" << ctrl.second.toString();\n> >> +\n> >> +\tint ret = data->video_->setControls(&controls);\n> >> +\tif (ret) {\n> >> +\t\tLOG(VIVID, Error) << \"Failed to set controls: \" << ret;\n> >> +\t\treturn ret < 0 ? ret : -EINVAL;\n> >> +\t}\n> >> +\n> >> +\treturn ret;\n> >> +}\n> >> +\n> >> +int PipelineHandlerVivid::queueRequestDevice(Camera *camera, Request\n> >> *request)\n> >> +{\n> >> +\tVividCameraData *data = cameraData(camera);\n> >> +\tFrameBuffer *buffer = request->findBuffer(&data->stream_);\n> >> +\tif (!buffer) {\n> >> +\t\tLOG(VIVID, Error)\n> >> +\t\t\t<< \"Attempt to queue request with invalid stream\";\n> >> +\n> >> +\t\treturn -ENOENT;\n> >> +\t}\n> >> +\n> >> +\tint ret = processControls(data, request);\n> >> +\tif (ret < 0)\n> >> +\t\treturn ret;\n> >> +\n> >> +\tret = data->video_->queueBuffer(buffer);\n> >> +\tif (ret < 0)\n> >> +\t\treturn ret;\n> >> +\n> >> +\treturn 0;\n> >> +}\n> >> +\n> >> +bool PipelineHandlerVivid::match(DeviceEnumerator *enumerator)\n> >> +{\n> >> +\tDeviceMatch dm(\"vivid\");\n> >> +\tdm.add(\"vivid-000-vid-cap\");\n> >> +\n> >> +\tMediaDevice *media = acquireMediaDevice(enumerator, dm);\n> >> +\tif (!media)\n> >> +\t\treturn false;\n> >> +\n> >> +\tstd::unique_ptr<VividCameraData> data =\n> >> std::make_unique<VividCameraData>(this, media);\n> >> +\n> >> +\t/* Locate and open the capture video node. */\n> >> +\tif (data->init())\n> >> +\t\treturn false;\n> >> +\n> >> +\t/* Create and register the camera. */\n> >> +\tstd::set<Stream *> streams{ &data->stream_ };\n> >> +\tstd::shared_ptr<Camera> camera = Camera::create(this, data->video_-\n> >>> deviceName(), streams);\n> >> +\tregisterCamera(std::move(camera), std::move(data));\n> >> +\n> >> +\treturn true;\n> >> +}\n> >> +\n> >> +int VividCameraData::init()\n> >> +{\n> >> +\tvideo_ = new V4L2VideoDevice(media_->getEntityByName(\"vivid-000-vid-\n> >> cap\"));\n> >> +\tif (video_->open())\n> >> +\t\treturn -ENODEV;\n> >> +\n> >> +\tvideo_->bufferReady.connect(this, &VividCameraData::bufferReady);\n> >> +\n> >> +\t/* Initialise the supported controls. */\n> >> +\tconst ControlInfoMap &controls = video_->controls();\n> >> +\tControlInfoMap::Map ctrls;\n> >> +\n> >> +\tfor (const auto &ctrl : controls) {\n> >> +\t\tconst ControlId *id;\n> >> +\t\tControlInfo info;\n> >> +\n> >> +\t\tswitch (ctrl.first->id()) {\n> >> +\t\tcase V4L2_CID_BRIGHTNESS:\n> >> +\t\t\tid = &controls::Brightness;\n> >> +\t\t\tinfo = ControlInfo{ { -1.0f }, { 1.0f }, { 0.0f } };\n> >> +\t\t\tbreak;\n> >> +\t\tcase V4L2_CID_CONTRAST:\n> >> +\t\t\tid = &controls::Contrast;\n> >> +\t\t\tinfo = ControlInfo{ { 0.0f }, { 2.0f }, { 1.0f } };\n> >> +\t\t\tbreak;\n> >> +\t\tcase V4L2_CID_SATURATION:\n> >> +\t\t\tid = &controls::Saturation;\n> >> +\t\t\tinfo = ControlInfo{ { 0.0f }, { 2.0f }, { 1.0f } };\n> >> +\t\t\tbreak;\n> >> +\t\tdefault:\n> >> +\t\t\tcontinue;\n> >> +\t\t}\n> >> +\n> >> +\t\tctrls.emplace(id, info);\n> >> +\t}\n> >> +\n> >> +\tcontrolInfo_ = std::move(ctrls);\n> >> +\n> >> +\treturn 0;\n> >> +}\n> >> +\n> >> +void VividCameraData::bufferReady(FrameBuffer *buffer)\n> >> +{\n> >> +\tRequest *request = buffer->request();\n> >> +\n> >> +\tpipe_->completeBuffer(camera_, request, buffer);\n> >> +\tpipe_->completeRequest(camera_, request);\n> >> +}\n> >> +\n> >> +REGISTER_PIPELINE_HANDLER(PipelineHandlerVivid);\n> >> +\n> >> +} /* namespace libcamera */","headers":{"Return-Path":"<laurent.pinchart@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 5104B609A3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 22 Jun 2020 03:45:42 +0200 (CEST)","from pendragon.ideasonboard.com (81-175-216-236.bb.dnainternet.fi\n\t[81.175.216.236])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 53A4FBB8;\n\tMon, 22 Jun 2020 03:45:41 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"LaJ7Aev2\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1592790341;\n\tbh=UyCCMpaKSY+J7YIWyK2lUiPc1ibs2b2D7wNTHwFgiig=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=LaJ7Aev2HKTCH8OnY/tUSuOICnSRR9uN6DgZ2nkJ63KDPexEjmYx7XzTMmASgiXg0\n\tOc2ljzcP3KeUhfP+alQFANzFqD7yAK0L55WYxNvDDbpd7Drk0xR8UmfCoIodXlyQY0\n\tK5Oh0aCafGwc/pEfU5G4qAToD8LhT+8MtA8fZhKg=","Date":"Mon, 22 Jun 2020 04:45:17 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"Nicolas Dufresne <nicolas@ndufresne.ca>,\n\tPaul Elder <paul.elder@ideasonboard.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>","Message-ID":"<20200622014517.GB25355@pendragon.ideasonboard.com>","References":"<20200619120249.1084998-1-kieran.bingham@ideasonboard.com>\n\t<95f92384c74975b52f1df3ee71911cfba1baadfc.camel@ndufresne.ca>\n\t<40e70402-9e6b-f065-b24c-1e34ca3d9c99@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<40e70402-9e6b-f065-b24c-1e34ca3d9c99@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH] libcamera: pipelines: Add VIVID\n\tpipeline support","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Mon, 22 Jun 2020 01:45:42 -0000"}},{"id":5325,"web_url":"https://patchwork.libcamera.org/comment/5325/","msgid":"<a4ef15f7-9462-997d-e756-b5f8103ed79b@ideasonboard.com>","date":"2020-06-22T09:47:39","subject":"Re: [libcamera-devel] [PATCH] libcamera: pipelines: Add VIVID\n\tpipeline support","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Hi Laurent,\n\nOn 22/06/2020 02:45, Laurent Pinchart wrote:\n> Hi Kieran,\n> \n> On Fri, Jun 19, 2020 at 05:19:55PM +0100, Kieran Bingham wrote:\n>> On 19/06/2020 16:34, Nicolas Dufresne wrote:\n>>> Le vendredi 19 juin 2020 à 13:02 +0100, Kieran Bingham a écrit :\n>>>> The VIVID driver supports more pixel formats and properties than the VIMC\n>>>> driver, and can provide extended testing for libcamera.\n>>>>\n>>>> The VIMC pipeline handler is duplicated and simplified to support the\n>>>> VIVID device.\n>>>>\n>>>> Unfortuantely, the VIVID device can not be handled by either of the\n>>>> generic UVC or Simple pipeline handlers.\n>>>\n>>> Can you extend on that ?\n>>\n>> I actually disagreed with myself in my latest reply (@13:14 in my timezone).\n>>\n>>> Perhaps an alternative route to supporting this however would be to make\n>>> the simple pipeline handler support pipelines which *don't* have a\n>>> sensor object... though the VIVID controls could be useful to support\n>>> directly.\n>>>\n>>> Perhaps - inheriting the simple-pipeline handler should be possible, to\n>>> provide some 'device specific' adjustments for things like device\n>>> specific controls.\n>>\n>> The simple pipeline expects to walk a media graph and find a sensor.\n>> VIVID doesn't expose a sensor, nor desire to. It's just a plain video\n>> device node.\n>>\n>> Perhaps we could extend the simple pipeline handler to support devices\n>> which don't have a sensor ... or create a \"simple-simple-pipeline\n>> handler\" ? :-D\n>>\n>> Equally - there could then be quite a lot of commonality between the UVC\n>> pipeline handler, though we kept that separate because of expected\n>> differences with UVC devices ... (in particular the controls for UVC\n>> have some specific variations already handled there).\n>>\n>> So for testing with vivid, I took the easy option of creating a new\n>> pipeline for VIVID. But there is so much code duplication already, I'm\n>> sure we could find some reuse somewhere ...\n> \n> Thank you for posting this patch. I think it's useful, as vimc currently\n> has a fairly limited set of features compared to vivid. In particular,\n> the lack of YUV format support in vimc (when configuring the pipeline\n> with a Bayer camera sensor as a source) limits our ability to test any\n> use case that requires YUV without real hardware.\n\nThe extra format support really was the driving factor here, and vivid\nhas much greater coverage for V4L2, so I suspect it might have\nfurther/different benefits for testing the v4l2 compatibility layers ...\n\nWhich is of course why it got posted, because Paul had asked there, and\nI had the vivid handler ready to hand.\n\n> Ideally, I would like vimc to gain support for these features, and avoid\n> adding another pipeline handler for vivid that would duplicate code. We\n> can discuss how to minimize code duplication, either with the simple\n> pipeline handler or the UVC pipeline handler, but the effort to add YUV\n> support in vimc seems fairly low to me, possibly even lower than\n> developing vivid support in libcamera with reduced code duplication. I'm\n\nI don't yet know what's involved in adding extra format support to VIMC,\nbut weren't Collabora also looking at such a topic ?\n\n\n> curious to know your opinion on long-term support for vivid in\n> libcamera, compared to enhancing the vimc driver.\n> \n> We could merge this pipeline handler as-is and deal with code sharing\n> later (or possibly even drop it later when vimc support will be better),\n\nI think that's an important consideration. - We can 'drop' it later too\nif required test functionality becomes available elsewhere...\n\n\n> but we also need to consider that every new pipeline handler increases\n> the maintenance effort, and makes changes to the pipeline handler API\n> more difficult. We need to consider the pros and cons.\n\nOf course, that's why initially I hadn't posted it when /I/ needed it.\nBut upon the third send out - I figured that was enough motivation to\ninclude the mailing-list.\n\n\n> Regarding code sharing, without having studied the question in details,\n> I feel that the UVC pipeline handler would be a better target, as vimc\n> is media controller-centric, while UVC and vivid are video node-centric.\n\nYes, there are definitely similarities with UVC, but there are likely\ndifferences in controls ... but I suspect for VIVID we don't /actually/\ncare about those differences anyway.\n\n\n> I'm even considering supporting vimc with the simple pipeline handler\n> instead of a dedicated pipeline handler, but that would likely require\n\nThat's interesting, but how would you handle having the 'test vimc' IPA?\nI suspect VIMC would end up staying external unless perhaps the simple\npipeline handler would also support IPA matching?\n\n\n> changes in both the simple pipeline handler and the vimc driver (most\n> notably the vimc scaler, which hardcodes a downscaling ratio of 3).\n\nYes, that's a painful arbitrary limitation.\n\n\nOverall, I don't particularly mind too much if this gets integrated or not.\n\nI think it can provide some help for development of libcamera in virtual\nsystems which, while we have low hardware support might be more beneficial.\n\nIt might be an easy way to provide testing of things like 'multiple\ncameras' on a single pipeline handler too - as you can easily have extra\ncapture devices with fairly arbitrary sizes for testing.\n\nWithout wide format coverage in VIMC it could help with other plumbing\naspects too - but equally even if we do integrate it - I could easily\nsee a point in the future where it becomes simpler to remove it.\n\n--\nKieran\n\n\n>>>> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n>>>> ---\n>>>>  meson_options.txt                        |   2 +-\n>>>>  src/libcamera/pipeline/vivid/meson.build |   5 +\n>>>>  src/libcamera/pipeline/vivid/vivid.cpp   | 441 +++++++++++++++++++++++\n>>>>  3 files changed, 447 insertions(+), 1 deletion(-)\n>>>>  create mode 100644 src/libcamera/pipeline/vivid/meson.build\n>>>>  create mode 100644 src/libcamera/pipeline/vivid/vivid.cpp\n>>>>\n>>>> diff --git a/meson_options.txt b/meson_options.txt\n>>>> index badace151bb6..dc4684df49b2 100644\n>>>> --- a/meson_options.txt\n>>>> +++ b/meson_options.txt\n>>>> @@ -16,7 +16,7 @@ option('gstreamer',\n>>>>  \n>>>>  option('pipelines',\n>>>>          type : 'array',\n>>>> -        choices : ['ipu3', 'raspberrypi', 'rkisp1', 'simple', 'uvcvideo',\n>>>> 'vimc'],\n>>>> +        choices : ['ipu3', 'raspberrypi', 'rkisp1', 'simple', 'uvcvideo',\n>>>> 'vimc', 'vivid'],\n>>>>          description : 'Select which pipeline handlers to include')\n>>>>  \n>>>>  option('test',\n>>>> diff --git a/src/libcamera/pipeline/vivid/meson.build\n>>>> b/src/libcamera/pipeline/vivid/meson.build\n>>>> new file mode 100644\n>>>> index 000000000000..086bb825387c\n>>>> --- /dev/null\n>>>> +++ b/src/libcamera/pipeline/vivid/meson.build\n>>>> @@ -0,0 +1,5 @@\n>>>> +# SPDX-License-Identifier: CC0-1.0\n>>>> +\n>>>> +libcamera_sources += files([\n>>>> +    'vivid.cpp',\n>>>> +])\n>>>> diff --git a/src/libcamera/pipeline/vivid/vivid.cpp\n>>>> b/src/libcamera/pipeline/vivid/vivid.cpp\n>>>> new file mode 100644\n>>>> index 000000000000..b811e33a0299\n>>>> --- /dev/null\n>>>> +++ b/src/libcamera/pipeline/vivid/vivid.cpp\n>>>> @@ -0,0 +1,441 @@\n>>>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>>>> +/*\n>>>> + * Copyright (C) 2018, Google Inc.\n>>>> + *\n>>>> + * vivid.cpp - Pipeline handler for the vivid capture device\n>>>> + */\n>>>> +\n>>>> +#include <algorithm>\n>>>> +#include <iomanip>\n>>>> +#include <map>\n>>>> +#include <math.h>\n>>>> +#include <tuple>\n>>>> +\n>>>> +#include <linux/media-bus-format.h>\n>>>> +#include <linux/version.h>\n>>>> +\n>>>> +#include <libcamera/camera.h>\n>>>> +#include <libcamera/control_ids.h>\n>>>> +#include <libcamera/controls.h>\n>>>> +#include <libcamera/ipa/ipa_interface.h>\n>>>> +#include <libcamera/ipa/ipa_module_info.h>\n>>>> +#include <libcamera/request.h>\n>>>> +#include <libcamera/stream.h>\n>>>> +\n>>>> +#include \"libcamera/internal/camera_sensor.h\"\n>>>> +#include \"libcamera/internal/device_enumerator.h\"\n>>>> +#include \"libcamera/internal/ipa_manager.h\"\n>>>> +#include \"libcamera/internal/log.h\"\n>>>> +#include \"libcamera/internal/media_device.h\"\n>>>> +#include \"libcamera/internal/pipeline_handler.h\"\n>>>> +#include \"libcamera/internal/utils.h\"\n>>>> +#include \"libcamera/internal/v4l2_controls.h\"\n>>>> +#include \"libcamera/internal/v4l2_subdevice.h\"\n>>>> +#include \"libcamera/internal/v4l2_videodevice.h\"\n>>>> +\n>>>> +#define VIVID_CID_CUSTOM_BASE           (V4L2_CID_USER_BASE | 0xf000)\n>>>> +#define VIVID_CID_BUTTON                (VIVID_CID_CUSTOM_BASE + 0)\n>>>> +#define VIVID_CID_BOOLEAN               (VIVID_CID_CUSTOM_BASE + 1)\n>>>> +#define VIVID_CID_INTEGER               (VIVID_CID_CUSTOM_BASE + 2)\n>>>> +#define VIVID_CID_INTEGER64             (VIVID_CID_CUSTOM_BASE + 3)\n>>>> +#define VIVID_CID_MENU                  (VIVID_CID_CUSTOM_BASE + 4)\n>>>> +#define VIVID_CID_STRING                (VIVID_CID_CUSTOM_BASE + 5)\n>>>> +#define VIVID_CID_BITMASK               (VIVID_CID_CUSTOM_BASE + 6)\n>>>> +#define VIVID_CID_INTMENU               (VIVID_CID_CUSTOM_BASE + 7)\n>>>> +#define VIVID_CID_U32_ARRAY             (VIVID_CID_CUSTOM_BASE + 8)\n>>>> +#define VIVID_CID_U16_MATRIX            (VIVID_CID_CUSTOM_BASE + 9)\n>>>> +#define VIVID_CID_U8_4D_ARRAY           (VIVID_CID_CUSTOM_BASE + 10)\n>>>> +\n>>>> +#define VIVID_CID_VIVID_BASE            (0x00f00000 | 0xf000)\n>>>> +#define VIVID_CID_VIVID_CLASS           (0x00f00000 | 1)\n>>>> +#define VIVID_CID_TEST_PATTERN          (VIVID_CID_VIVID_BASE + 0)\n>>>> +#define VIVID_CID_OSD_TEXT_MODE         (VIVID_CID_VIVID_BASE + 1)\n>>>> +#define VIVID_CID_HOR_MOVEMENT          (VIVID_CID_VIVID_BASE + 2)\n>>>> +#define VIVID_CID_VERT_MOVEMENT         (VIVID_CID_VIVID_BASE + 3)\n>>>> +#define VIVID_CID_SHOW_BORDER           (VIVID_CID_VIVID_BASE + 4)\n>>>> +#define VIVID_CID_SHOW_SQUARE           (VIVID_CID_VIVID_BASE + 5)\n>>>> +#define VIVID_CID_INSERT_SAV            (VIVID_CID_VIVID_BASE + 6)\n>>>> +#define VIVID_CID_INSERT_EAV            (VIVID_CID_VIVID_BASE + 7)\n>>>> +#define VIVID_CID_VBI_CAP_INTERLACED    (VIVID_CID_VIVID_BASE + 8)\n>>>> +\n>>>> +#define VIVID_CID_HFLIP                 (VIVID_CID_VIVID_BASE + 20)\n>>>> +#define VIVID_CID_VFLIP                 (VIVID_CID_VIVID_BASE + 21)\n>>>> +#define VIVID_CID_STD_ASPECT_RATIO      (VIVID_CID_VIVID_BASE + 22)\n>>>> +#define VIVID_CID_DV_TIMINGS_ASPECT_RATIO       (VIVID_CID_VIVID_BASE + 23)\n>>>> +#define VIVID_CID_TSTAMP_SRC            (VIVID_CID_VIVID_BASE + 24)\n>>>> +#define VIVID_CID_COLORSPACE            (VIVID_CID_VIVID_BASE + 25)\n>>>> +#define VIVID_CID_XFER_FUNC             (VIVID_CID_VIVID_BASE + 26)\n>>>> +#define VIVID_CID_YCBCR_ENC             (VIVID_CID_VIVID_BASE + 27)\n>>>> +#define VIVID_CID_QUANTIZATION          (VIVID_CID_VIVID_BASE + 28)\n>>>> +#define VIVID_CID_LIMITED_RGB_RANGE     (VIVID_CID_VIVID_BASE + 29)\n>>>> +#define VIVID_CID_ALPHA_MODE            (VIVID_CID_VIVID_BASE + 30)\n>>>> +#define VIVID_CID_HAS_CROP_CAP          (VIVID_CID_VIVID_BASE + 31)\n>>>> +#define VIVID_CID_HAS_COMPOSE_CAP       (VIVID_CID_VIVID_BASE + 32)\n>>>> +#define VIVID_CID_HAS_SCALER_CAP        (VIVID_CID_VIVID_BASE + 33)\n>>>> +#define VIVID_CID_HAS_CROP_OUT          (VIVID_CID_VIVID_BASE + 34)\n>>>> +#define VIVID_CID_HAS_COMPOSE_OUT       (VIVID_CID_VIVID_BASE + 35)\n>>>> +#define VIVID_CID_HAS_SCALER_OUT        (VIVID_CID_VIVID_BASE + 36)\n>>>> +#define VIVID_CID_LOOP_VIDEO            (VIVID_CID_VIVID_BASE + 37)\n>>>> +#define VIVID_CID_SEQ_WRAP              (VIVID_CID_VIVID_BASE + 38)\n>>>> +#define VIVID_CID_TIME_WRAP             (VIVID_CID_VIVID_BASE + 39)\n>>>> +#define VIVID_CID_MAX_EDID_BLOCKS       (VIVID_CID_VIVID_BASE + 40)\n>>>> +#define VIVID_CID_PERCENTAGE_FILL       (VIVID_CID_VIVID_BASE + 41)\n>>>> +#define VIVID_CID_REDUCED_FPS           (VIVID_CID_VIVID_BASE + 42)\n>>>> +#define VIVID_CID_HSV_ENC               (VIVID_CID_VIVID_BASE + 43)\n>>>> +#define VIVID_CID_DISPLAY_PRESENT       (VIVID_CID_VIVID_BASE + 44)\n>>>> +\n>>>> +#define VIVID_CID_STD_SIGNAL_MODE       (VIVID_CID_VIVID_BASE + 60)\n>>>> +#define VIVID_CID_STANDARD              (VIVID_CID_VIVID_BASE + 61)\n>>>> +#define VIVID_CID_DV_TIMINGS_SIGNAL_MODE        (VIVID_CID_VIVID_BASE + 62)\n>>>> +#define VIVID_CID_DV_TIMINGS            (VIVID_CID_VIVID_BASE + 63)\n>>>> +#define VIVID_CID_PERC_DROPPED          (VIVID_CID_VIVID_BASE + 64)\n>>>> +#define VIVID_CID_DISCONNECT            (VIVID_CID_VIVID_BASE + 65)\n>>>> +#define VIVID_CID_DQBUF_ERROR           (VIVID_CID_VIVID_BASE + 66)\n>>>> +#define VIVID_CID_QUEUE_SETUP_ERROR     (VIVID_CID_VIVID_BASE + 67)\n>>>> +#define VIVID_CID_BUF_PREPARE_ERROR     (VIVID_CID_VIVID_BASE + 68)\n>>>> +#define VIVID_CID_START_STR_ERROR       (VIVID_CID_VIVID_BASE + 69)\n>>>> +#define VIVID_CID_QUEUE_ERROR           (VIVID_CID_VIVID_BASE + 70)\n>>>> +#define VIVID_CID_CLEAR_FB              (VIVID_CID_VIVID_BASE + 71)\n>>>> +#define VIVID_CID_REQ_VALIDATE_ERROR    (VIVID_CID_VIVID_BASE + 72)\n>>>> +\n>>>> +#define VIVID_CID_RADIO_SEEK_MODE       (VIVID_CID_VIVID_BASE + 90)\n>>>> +#define VIVID_CID_RADIO_SEEK_PROG_LIM   (VIVID_CID_VIVID_BASE + 91)\n>>>> +#define VIVID_CID_RADIO_RX_RDS_RBDS     (VIVID_CID_VIVID_BASE + 92)\n>>>> +#define VIVID_CID_RADIO_RX_RDS_BLOCKIO  (VIVID_CID_VIVID_BASE + 93)\n>>>> +\n>>>> +#define VIVID_CID_RADIO_TX_RDS_BLOCKIO  (VIVID_CID_VIVID_BASE + 94)\n>>>> +\n>>>> +#define VIVID_CID_SDR_CAP_FM_DEVIATION  (VIVID_CID_VIVID_BASE + 110)\n>>>> +\n>>>> +\n>>>> +namespace libcamera {\n>>>> +\n>>>> +LOG_DEFINE_CATEGORY(VIVID)\n>>>> +\n>>>> +class VividCameraData : public CameraData\n>>>> +{\n>>>> +public:\n>>>> +\tVividCameraData(PipelineHandler *pipe, MediaDevice *media)\n>>>> +\t\t: CameraData(pipe), media_(media), video_(nullptr)\n>>>> +\t{\n>>>> +\t}\n>>>> +\n>>>> +\t~VividCameraData()\n>>>> +\t{\n>>>> +\t\tdelete video_;\n>>>> +\t}\n>>>> +\n>>>> +\tint init();\n>>>> +\tvoid bufferReady(FrameBuffer *buffer);\n>>>> +\n>>>> +\tMediaDevice *media_;\n>>>> +\tV4L2VideoDevice *video_;\n>>>> +\tStream stream_;\n>>>> +};\n>>>> +\n>>>> +class VividCameraConfiguration : public CameraConfiguration\n>>>> +{\n>>>> +public:\n>>>> +\tVividCameraConfiguration();\n>>>> +\n>>>> +\tStatus validate() override;\n>>>> +};\n>>>> +\n>>>> +class PipelineHandlerVivid : public PipelineHandler\n>>>> +{\n>>>> +public:\n>>>> +\tPipelineHandlerVivid(CameraManager *manager);\n>>>> +\n>>>> +\tCameraConfiguration *generateConfiguration(Camera *camera,\n>>>> +\t\t\t\t\t\t   const StreamRoles &roles)\n>>>> override;\n>>>> +\tint configure(Camera *camera, CameraConfiguration *config) override;\n>>>> +\n>>>> +\tint exportFrameBuffers(Camera *camera, Stream *stream,\n>>>> +\t\t\t       std::vector<std::unique_ptr<FrameBuffer>>\n>>>> *buffers) override;\n>>>> +\n>>>> +\tint start(Camera *camera) override;\n>>>> +\tvoid stop(Camera *camera) override;\n>>>> +\n>>>> +\tint queueRequestDevice(Camera *camera, Request *request) override;\n>>>> +\n>>>> +\tbool match(DeviceEnumerator *enumerator) override;\n>>>> +\n>>>> +private:\n>>>> +\tint processControls(VividCameraData *data, Request *request);\n>>>> +\n>>>> +\tVividCameraData *cameraData(const Camera *camera)\n>>>> +\t{\n>>>> +\t\treturn static_cast<VividCameraData *>(\n>>>> +\t\t\tPipelineHandler::cameraData(camera));\n>>>> +\t}\n>>>> +};\n>>>> +\n>>>> +VividCameraConfiguration::VividCameraConfiguration()\n>>>> +\t: CameraConfiguration()\n>>>> +{\n>>>> +}\n>>>> +\n>>>> +CameraConfiguration::Status VividCameraConfiguration::validate()\n>>>> +{\n>>>> +\tStatus status = Valid;\n>>>> +\n>>>> +\tif (config_.empty())\n>>>> +\t\treturn Invalid;\n>>>> +\n>>>> +\t/* Cap the number of entries to the available streams. */\n>>>> +\tif (config_.size() > 1) {\n>>>> +\t\tconfig_.resize(1);\n>>>> +\t\tstatus = Adjusted;\n>>>> +\t}\n>>>> +\n>>>> +\tStreamConfiguration &cfg = config_[0];\n>>>> +\n>>>> +\t/* Adjust the pixel format. */\n>>>> +\tconst std::vector<libcamera::PixelFormat> formats =\n>>>> cfg.formats().pixelformats();\n>>>> +\tif (std::find(formats.begin(), formats.end(), cfg.pixelFormat) ==\n>>>> formats.end()) {\n>>>> +\t\tcfg.pixelFormat = cfg.formats().pixelformats()[0];\n>>>> +\t\tLOG(VIVID, Debug) << \"Adjusting format to \" <<\n>>>> cfg.pixelFormat.toString();\n>>>> +\t\tstatus = Adjusted;\n>>>> +\t}\n>>>> +\n>>>> +\tcfg.bufferCount = 4;\n>>>> +\n>>>> +\treturn status;\n>>>> +}\n>>>> +\n>>>> +PipelineHandlerVivid::PipelineHandlerVivid(CameraManager *manager)\n>>>> +\t: PipelineHandler(manager)\n>>>> +{\n>>>> +}\n>>>> +\n>>>> +CameraConfiguration *PipelineHandlerVivid::generateConfiguration(Camera\n>>>> *camera,\n>>>> +\t\t\t\t\t\t\t\t const\n>>>> StreamRoles &roles)\n>>>> +{\n>>>> +\tCameraConfiguration *config = new VividCameraConfiguration();\n>>>> +\tVividCameraData *data = cameraData(camera);\n>>>> +\n>>>> +\tif (roles.empty())\n>>>> +\t\treturn config;\n>>>> +\n>>>> +\tstd::map<V4L2PixelFormat, std::vector<SizeRange>> v4l2Formats =\n>>>> +\t\tdata->video_->formats();\n>>>> +\tstd::map<PixelFormat, std::vector<SizeRange>> deviceFormats;\n>>>> +\tstd::transform(v4l2Formats.begin(), v4l2Formats.end(),\n>>>> +\t\t       std::inserter(deviceFormats, deviceFormats.begin()),\n>>>> +\t\t       [&](const decltype(v4l2Formats)::value_type &format) {\n>>>> +\t\t\t       return decltype(deviceFormats)::value_type{\n>>>> +\t\t\t\t       format.first.toPixelFormat(),\n>>>> +\t\t\t\t       format.second\n>>>> +\t\t\t       };\n>>>> +\t\t       });\n>>>> +\n>>>> +\tStreamFormats formats(deviceFormats);\n>>>> +\tStreamConfiguration cfg(formats);\n>>>> +\n>>>> +\tcfg.pixelFormat = PixelFormat(DRM_FORMAT_BGR888);\n>>>> +\tcfg.size = { 1280, 720 };\n>>>> +\tcfg.bufferCount = 4;\n>>>> +\n>>>> +\tconfig->addConfiguration(cfg);\n>>>> +\n>>>> +\tconfig->validate();\n>>>> +\n>>>> +\treturn config;\n>>>> +}\n>>>> +\n>>>> +int PipelineHandlerVivid::configure(Camera *camera, CameraConfiguration\n>>>> *config)\n>>>> +{\n>>>> +\tVividCameraData *data = cameraData(camera);\n>>>> +\tStreamConfiguration &cfg = config->at(0);\n>>>> +\tint ret;\n>>>> +\n>>>> +\tV4L2DeviceFormat format = {};\n>>>> +\tformat.fourcc = data->video_->toV4L2PixelFormat(cfg.pixelFormat);\n>>>> +\tformat.size = cfg.size;\n>>>> +\n>>>> +\tret = data->video_->setFormat(&format);\n>>>> +\tif (ret)\n>>>> +\t\treturn ret;\n>>>> +\n>>>> +\tif (format.size != cfg.size ||\n>>>> +\t    format.fourcc != data->video_->toV4L2PixelFormat(cfg.pixelFormat))\n>>>> +\t\treturn -EINVAL;\n>>>> +\n>>>> +\tcfg.setStream(&data->stream_);\n>>>> +\tcfg.stride = format.planes[0].bpl;\n>>>> +\n>>>> +\treturn 0;\n>>>> +}\n>>>> +\n>>>> +int PipelineHandlerVivid::exportFrameBuffers(Camera *camera, Stream *stream,\n>>>> +\t\t\t\t\t     std::vector<std::unique_ptr<FrameBu\n>>>> ffer>> *buffers)\n>>>> +{\n>>>> +\tVividCameraData *data = cameraData(camera);\n>>>> +\tunsigned int count = stream->configuration().bufferCount;\n>>>> +\n>>>> +\treturn data->video_->exportBuffers(count, buffers);\n>>>> +}\n>>>> +\n>>>> +int PipelineHandlerVivid::start(Camera *camera)\n>>>> +{\n>>>> +\tVividCameraData *data = cameraData(camera);\n>>>> +\tunsigned int count = data->stream_.configuration().bufferCount;\n>>>> +\n>>>> +\tint ret = data->video_->importBuffers(count);\n>>>> +\tif (ret < 0)\n>>>> +\t\treturn ret;\n>>>> +\n>>>> +\tret = data->video_->streamOn();\n>>>> +\tif (ret < 0) {\n>>>> +\t\tdata->ipa_->stop();\n>>>> +\t\tdata->video_->releaseBuffers();\n>>>> +\t\treturn ret;\n>>>> +\t}\n>>>> +\n>>>> +\treturn 0;\n>>>> +}\n>>>> +\n>>>> +void PipelineHandlerVivid::stop(Camera *camera)\n>>>> +{\n>>>> +\tVividCameraData *data = cameraData(camera);\n>>>> +\tdata->video_->streamOff();\n>>>> +\tdata->video_->releaseBuffers();\n>>>> +}\n>>>> +\n>>>> +int PipelineHandlerVivid::processControls(VividCameraData *data, Request\n>>>> *request)\n>>>> +{\n>>>> +\tControlList controls(data->video_->controls());\n>>>> +\n>>>> +\tfor (auto it : request->controls()) {\n>>>> +\t\tunsigned int id = it.first;\n>>>> +\t\tunsigned int offset;\n>>>> +\t\tuint32_t cid;\n>>>> +\n>>>> +\t\tif (id == controls::Brightness) {\n>>>> +\t\t\tcid = V4L2_CID_BRIGHTNESS;\n>>>> +\t\t\toffset = 128;\n>>>> +\t\t} else if (id == controls::Contrast) {\n>>>> +\t\t\tcid = V4L2_CID_CONTRAST;\n>>>> +\t\t\toffset = 0;\n>>>> +\t\t} else if (id == controls::Saturation) {\n>>>> +\t\t\tcid = V4L2_CID_SATURATION;\n>>>> +\t\t\toffset = 0;\n>>>> +\t\t} else {\n>>>> +\t\t\tcontinue;\n>>>> +\t\t}\n>>>> +\n>>>> +\t\tint32_t value = lroundf(it.second.get<float>() * 128 + offset);\n>>>> +\t\tcontrols.set(cid, utils::clamp(value, 0, 255));\n>>>> +\t}\n>>>> +\n>>>> +\tfor (const auto &ctrl : controls)\n>>>> +\t\tLOG(VIVID, Debug)\n>>>> +\t\t\t<< \"Setting control \" << utils::hex(ctrl.first)\n>>>> +\t\t\t<< \" to \" << ctrl.second.toString();\n>>>> +\n>>>> +\tint ret = data->video_->setControls(&controls);\n>>>> +\tif (ret) {\n>>>> +\t\tLOG(VIVID, Error) << \"Failed to set controls: \" << ret;\n>>>> +\t\treturn ret < 0 ? ret : -EINVAL;\n>>>> +\t}\n>>>> +\n>>>> +\treturn ret;\n>>>> +}\n>>>> +\n>>>> +int PipelineHandlerVivid::queueRequestDevice(Camera *camera, Request\n>>>> *request)\n>>>> +{\n>>>> +\tVividCameraData *data = cameraData(camera);\n>>>> +\tFrameBuffer *buffer = request->findBuffer(&data->stream_);\n>>>> +\tif (!buffer) {\n>>>> +\t\tLOG(VIVID, Error)\n>>>> +\t\t\t<< \"Attempt to queue request with invalid stream\";\n>>>> +\n>>>> +\t\treturn -ENOENT;\n>>>> +\t}\n>>>> +\n>>>> +\tint ret = processControls(data, request);\n>>>> +\tif (ret < 0)\n>>>> +\t\treturn ret;\n>>>> +\n>>>> +\tret = data->video_->queueBuffer(buffer);\n>>>> +\tif (ret < 0)\n>>>> +\t\treturn ret;\n>>>> +\n>>>> +\treturn 0;\n>>>> +}\n>>>> +\n>>>> +bool PipelineHandlerVivid::match(DeviceEnumerator *enumerator)\n>>>> +{\n>>>> +\tDeviceMatch dm(\"vivid\");\n>>>> +\tdm.add(\"vivid-000-vid-cap\");\n>>>> +\n>>>> +\tMediaDevice *media = acquireMediaDevice(enumerator, dm);\n>>>> +\tif (!media)\n>>>> +\t\treturn false;\n>>>> +\n>>>> +\tstd::unique_ptr<VividCameraData> data =\n>>>> std::make_unique<VividCameraData>(this, media);\n>>>> +\n>>>> +\t/* Locate and open the capture video node. */\n>>>> +\tif (data->init())\n>>>> +\t\treturn false;\n>>>> +\n>>>> +\t/* Create and register the camera. */\n>>>> +\tstd::set<Stream *> streams{ &data->stream_ };\n>>>> +\tstd::shared_ptr<Camera> camera = Camera::create(this, data->video_-\n>>>>> deviceName(), streams);\n>>>> +\tregisterCamera(std::move(camera), std::move(data));\n>>>> +\n>>>> +\treturn true;\n>>>> +}\n>>>> +\n>>>> +int VividCameraData::init()\n>>>> +{\n>>>> +\tvideo_ = new V4L2VideoDevice(media_->getEntityByName(\"vivid-000-vid-\n>>>> cap\"));\n>>>> +\tif (video_->open())\n>>>> +\t\treturn -ENODEV;\n>>>> +\n>>>> +\tvideo_->bufferReady.connect(this, &VividCameraData::bufferReady);\n>>>> +\n>>>> +\t/* Initialise the supported controls. */\n>>>> +\tconst ControlInfoMap &controls = video_->controls();\n>>>> +\tControlInfoMap::Map ctrls;\n>>>> +\n>>>> +\tfor (const auto &ctrl : controls) {\n>>>> +\t\tconst ControlId *id;\n>>>> +\t\tControlInfo info;\n>>>> +\n>>>> +\t\tswitch (ctrl.first->id()) {\n>>>> +\t\tcase V4L2_CID_BRIGHTNESS:\n>>>> +\t\t\tid = &controls::Brightness;\n>>>> +\t\t\tinfo = ControlInfo{ { -1.0f }, { 1.0f }, { 0.0f } };\n>>>> +\t\t\tbreak;\n>>>> +\t\tcase V4L2_CID_CONTRAST:\n>>>> +\t\t\tid = &controls::Contrast;\n>>>> +\t\t\tinfo = ControlInfo{ { 0.0f }, { 2.0f }, { 1.0f } };\n>>>> +\t\t\tbreak;\n>>>> +\t\tcase V4L2_CID_SATURATION:\n>>>> +\t\t\tid = &controls::Saturation;\n>>>> +\t\t\tinfo = ControlInfo{ { 0.0f }, { 2.0f }, { 1.0f } };\n>>>> +\t\t\tbreak;\n>>>> +\t\tdefault:\n>>>> +\t\t\tcontinue;\n>>>> +\t\t}\n>>>> +\n>>>> +\t\tctrls.emplace(id, info);\n>>>> +\t}\n>>>> +\n>>>> +\tcontrolInfo_ = std::move(ctrls);\n>>>> +\n>>>> +\treturn 0;\n>>>> +}\n>>>> +\n>>>> +void VividCameraData::bufferReady(FrameBuffer *buffer)\n>>>> +{\n>>>> +\tRequest *request = buffer->request();\n>>>> +\n>>>> +\tpipe_->completeBuffer(camera_, request, buffer);\n>>>> +\tpipe_->completeRequest(camera_, request);\n>>>> +}\n>>>> +\n>>>> +REGISTER_PIPELINE_HANDLER(PipelineHandlerVivid);\n>>>> +\n>>>> +} /* namespace libcamera */\n>","headers":{"Return-Path":"<kieran.bingham@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 2146F603BB\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 22 Jun 2020 11:47:43 +0200 (CEST)","from [192.168.0.20]\n\t(cpc89242-aztw30-2-0-cust488.18-1.cable.virginm.net [86.31.129.233])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 5B91130D;\n\tMon, 22 Jun 2020 11:47:42 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"MxWpTPU3\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1592819262;\n\tbh=feFZMQr3CKhpWiv48TctovLy/rrx45If3sEp3ycZBKg=;\n\th=Reply-To:Subject:To:Cc:References:From:Date:In-Reply-To:From;\n\tb=MxWpTPU3Dihk5+SmloESAH6MtaZPuBHBicvaP9QJAcVAugDMNecAEr8g6QfQqCTOH\n\tSW0rLufMyR/TiwQ1eu74rLs+JFCa7bEOwwfFggeZB699nt+2dfjP5OHfqfP02AgazN\n\tv5H/W/x8DEinfLF0Zsy4g3zHGO1+9rL8yU364KeU=","Reply-To":"kieran.bingham@ideasonboard.com","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"Nicolas Dufresne <nicolas@ndufresne.ca>,\n\tPaul Elder <paul.elder@ideasonboard.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>","References":"<20200619120249.1084998-1-kieran.bingham@ideasonboard.com>\n\t<95f92384c74975b52f1df3ee71911cfba1baadfc.camel@ndufresne.ca>\n\t<40e70402-9e6b-f065-b24c-1e34ca3d9c99@ideasonboard.com>\n\t<20200622014517.GB25355@pendragon.ideasonboard.com>","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Autocrypt":"addr=kieran.bingham@ideasonboard.com; keydata=\n\t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\n\tWZw01QYHU/GUV/zHJSFk","Organization":"Ideas on Board","Message-ID":"<a4ef15f7-9462-997d-e756-b5f8103ed79b@ideasonboard.com>","Date":"Mon, 22 Jun 2020 10:47:39 +0100","User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101\n\tThunderbird/68.8.0","MIME-Version":"1.0","In-Reply-To":"<20200622014517.GB25355@pendragon.ideasonboard.com>","Content-Type":"text/plain; charset=utf-8","Content-Language":"en-GB","Content-Transfer-Encoding":"8bit","Subject":"Re: [libcamera-devel] [PATCH] libcamera: pipelines: Add VIVID\n\tpipeline support","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Mon, 22 Jun 2020 09:47:43 -0000"}},{"id":5332,"web_url":"https://patchwork.libcamera.org/comment/5332/","msgid":"<20200622222907.GH5852@pendragon.ideasonboard.com>","date":"2020-06-22T22:29:07","subject":"Re: [libcamera-devel] [PATCH] libcamera: pipelines: Add VIVID\n\tpipeline support","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Kieran,\n\nOn Mon, Jun 22, 2020 at 10:47:39AM +0100, Kieran Bingham wrote:\n> On 22/06/2020 02:45, Laurent Pinchart wrote:\n> > On Fri, Jun 19, 2020 at 05:19:55PM +0100, Kieran Bingham wrote:\n> >> On 19/06/2020 16:34, Nicolas Dufresne wrote:\n> >>> Le vendredi 19 juin 2020 à 13:02 +0100, Kieran Bingham a écrit :\n> >>>> The VIVID driver supports more pixel formats and properties than the VIMC\n> >>>> driver, and can provide extended testing for libcamera.\n> >>>>\n> >>>> The VIMC pipeline handler is duplicated and simplified to support the\n> >>>> VIVID device.\n> >>>>\n> >>>> Unfortuantely, the VIVID device can not be handled by either of the\n> >>>> generic UVC or Simple pipeline handlers.\n> >>>\n> >>> Can you extend on that ?\n> >>\n> >> I actually disagreed with myself in my latest reply (@13:14 in my timezone).\n> >>\n> >>> Perhaps an alternative route to supporting this however would be to make\n> >>> the simple pipeline handler support pipelines which *don't* have a\n> >>> sensor object... though the VIVID controls could be useful to support\n> >>> directly.\n> >>>\n> >>> Perhaps - inheriting the simple-pipeline handler should be possible, to\n> >>> provide some 'device specific' adjustments for things like device\n> >>> specific controls.\n> >>\n> >> The simple pipeline expects to walk a media graph and find a sensor.\n> >> VIVID doesn't expose a sensor, nor desire to. It's just a plain video\n> >> device node.\n> >>\n> >> Perhaps we could extend the simple pipeline handler to support devices\n> >> which don't have a sensor ... or create a \"simple-simple-pipeline\n> >> handler\" ? :-D\n> >>\n> >> Equally - there could then be quite a lot of commonality between the UVC\n> >> pipeline handler, though we kept that separate because of expected\n> >> differences with UVC devices ... (in particular the controls for UVC\n> >> have some specific variations already handled there).\n> >>\n> >> So for testing with vivid, I took the easy option of creating a new\n> >> pipeline for VIVID. But there is so much code duplication already, I'm\n> >> sure we could find some reuse somewhere ...\n> > \n> > Thank you for posting this patch. I think it's useful, as vimc currently\n> > has a fairly limited set of features compared to vivid. In particular,\n> > the lack of YUV format support in vimc (when configuring the pipeline\n> > with a Bayer camera sensor as a source) limits our ability to test any\n> > use case that requires YUV without real hardware.\n> \n> The extra format support really was the driving factor here, and vivid\n> has much greater coverage for V4L2, so I suspect it might have\n> further/different benefits for testing the v4l2 compatibility layers ...\n> \n> Which is of course why it got posted, because Paul had asked there, and\n> I had the vivid handler ready to hand.\n> \n> > Ideally, I would like vimc to gain support for these features, and avoid\n> > adding another pipeline handler for vivid that would duplicate code. We\n> > can discuss how to minimize code duplication, either with the simple\n> > pipeline handler or the UVC pipeline handler, but the effort to add YUV\n> > support in vimc seems fairly low to me, possibly even lower than\n> > developing vivid support in libcamera with reduced code duplication. I'm\n> \n> I don't yet know what's involved in adding extra format support to VIMC,\n> but weren't Collabora also looking at such a topic ?\n\nWe need to synchronize with Helen and Shuah here.\n\n> > curious to know your opinion on long-term support for vivid in\n> > libcamera, compared to enhancing the vimc driver.\n> > \n> > We could merge this pipeline handler as-is and deal with code sharing\n> > later (or possibly even drop it later when vimc support will be better),\n> \n> I think that's an important consideration. - We can 'drop' it later too\n> if required test functionality becomes available elsewhere...\n> \n> > but we also need to consider that every new pipeline handler increases\n> > the maintenance effort, and makes changes to the pipeline handler API\n> > more difficult. We need to consider the pros and cons.\n> \n> Of course, that's why initially I hadn't posted it when /I/ needed it.\n> But upon the third send out - I figured that was enough motivation to\n> include the mailing-list.\n> \n> > Regarding code sharing, without having studied the question in details,\n> > I feel that the UVC pipeline handler would be a better target, as vimc\n> > is media controller-centric, while UVC and vivid are video node-centric.\n> \n> Yes, there are definitely similarities with UVC, but there are likely\n> differences in controls ... but I suspect for VIVID we don't /actually/\n> care about those differences anyway.\n> \n> > I'm even considering supporting vimc with the simple pipeline handler\n> > instead of a dedicated pipeline handler, but that would likely require\n> \n> That's interesting, but how would you handle having the 'test vimc' IPA?\n> I suspect VIMC would end up staying external unless perhaps the simple\n> pipeline handler would also support IPA matching?\n\nI foresee a need for IPAs for the simple pipeline handler, coupled with\nGPU processing of Bayer data (a poor man's ISP in a way). The vimc IPA\ncould then be integrated with whatever mechanism we implement in the\nsimple pipeline handler to load IPAs.\n\n> > changes in both the simple pipeline handler and the vimc driver (most\n> > notably the vimc scaler, which hardcodes a downscaling ratio of 3).\n> \n> Yes, that's a painful arbitrary limitation.\n> \n> Overall, I don't particularly mind too much if this gets integrated or not.\n> \n> I think it can provide some help for development of libcamera in virtual\n> systems which, while we have low hardware support might be more beneficial.\n> \n> It might be an easy way to provide testing of things like 'multiple\n> cameras' on a single pipeline handler too - as you can easily have extra\n> capture devices with fairly arbitrary sizes for testing.\n> \n> Without wide format coverage in VIMC it could help with other plumbing\n> aspects too - but equally even if we do integrate it - I could easily\n> see a point in the future where it becomes simpler to remove it.\n> \n> >>>> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> >>>> ---\n> >>>>  meson_options.txt                        |   2 +-\n> >>>>  src/libcamera/pipeline/vivid/meson.build |   5 +\n> >>>>  src/libcamera/pipeline/vivid/vivid.cpp   | 441 +++++++++++++++++++++++\n> >>>>  3 files changed, 447 insertions(+), 1 deletion(-)\n> >>>>  create mode 100644 src/libcamera/pipeline/vivid/meson.build\n> >>>>  create mode 100644 src/libcamera/pipeline/vivid/vivid.cpp\n> >>>>\n> >>>> diff --git a/meson_options.txt b/meson_options.txt\n> >>>> index badace151bb6..dc4684df49b2 100644\n> >>>> --- a/meson_options.txt\n> >>>> +++ b/meson_options.txt\n> >>>> @@ -16,7 +16,7 @@ option('gstreamer',\n> >>>>  \n> >>>>  option('pipelines',\n> >>>>          type : 'array',\n> >>>> -        choices : ['ipu3', 'raspberrypi', 'rkisp1', 'simple', 'uvcvideo',\n> >>>> 'vimc'],\n> >>>> +        choices : ['ipu3', 'raspberrypi', 'rkisp1', 'simple', 'uvcvideo',\n> >>>> 'vimc', 'vivid'],\n> >>>>          description : 'Select which pipeline handlers to include')\n> >>>>  \n> >>>>  option('test',\n> >>>> diff --git a/src/libcamera/pipeline/vivid/meson.build\n> >>>> b/src/libcamera/pipeline/vivid/meson.build\n> >>>> new file mode 100644\n> >>>> index 000000000000..086bb825387c\n> >>>> --- /dev/null\n> >>>> +++ b/src/libcamera/pipeline/vivid/meson.build\n> >>>> @@ -0,0 +1,5 @@\n> >>>> +# SPDX-License-Identifier: CC0-1.0\n> >>>> +\n> >>>> +libcamera_sources += files([\n> >>>> +    'vivid.cpp',\n> >>>> +])\n> >>>> diff --git a/src/libcamera/pipeline/vivid/vivid.cpp\n> >>>> b/src/libcamera/pipeline/vivid/vivid.cpp\n> >>>> new file mode 100644\n> >>>> index 000000000000..b811e33a0299\n> >>>> --- /dev/null\n> >>>> +++ b/src/libcamera/pipeline/vivid/vivid.cpp\n> >>>> @@ -0,0 +1,441 @@\n> >>>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> >>>> +/*\n> >>>> + * Copyright (C) 2018, Google Inc.\n> >>>> + *\n> >>>> + * vivid.cpp - Pipeline handler for the vivid capture device\n> >>>> + */\n> >>>> +\n> >>>> +#include <algorithm>\n> >>>> +#include <iomanip>\n> >>>> +#include <map>\n> >>>> +#include <math.h>\n> >>>> +#include <tuple>\n> >>>> +\n> >>>> +#include <linux/media-bus-format.h>\n> >>>> +#include <linux/version.h>\n> >>>> +\n> >>>> +#include <libcamera/camera.h>\n> >>>> +#include <libcamera/control_ids.h>\n> >>>> +#include <libcamera/controls.h>\n> >>>> +#include <libcamera/ipa/ipa_interface.h>\n> >>>> +#include <libcamera/ipa/ipa_module_info.h>\n> >>>> +#include <libcamera/request.h>\n> >>>> +#include <libcamera/stream.h>\n> >>>> +\n> >>>> +#include \"libcamera/internal/camera_sensor.h\"\n> >>>> +#include \"libcamera/internal/device_enumerator.h\"\n> >>>> +#include \"libcamera/internal/ipa_manager.h\"\n> >>>> +#include \"libcamera/internal/log.h\"\n> >>>> +#include \"libcamera/internal/media_device.h\"\n> >>>> +#include \"libcamera/internal/pipeline_handler.h\"\n> >>>> +#include \"libcamera/internal/utils.h\"\n> >>>> +#include \"libcamera/internal/v4l2_controls.h\"\n> >>>> +#include \"libcamera/internal/v4l2_subdevice.h\"\n> >>>> +#include \"libcamera/internal/v4l2_videodevice.h\"\n> >>>> +\n> >>>> +#define VIVID_CID_CUSTOM_BASE           (V4L2_CID_USER_BASE | 0xf000)\n> >>>> +#define VIVID_CID_BUTTON                (VIVID_CID_CUSTOM_BASE + 0)\n> >>>> +#define VIVID_CID_BOOLEAN               (VIVID_CID_CUSTOM_BASE + 1)\n> >>>> +#define VIVID_CID_INTEGER               (VIVID_CID_CUSTOM_BASE + 2)\n> >>>> +#define VIVID_CID_INTEGER64             (VIVID_CID_CUSTOM_BASE + 3)\n> >>>> +#define VIVID_CID_MENU                  (VIVID_CID_CUSTOM_BASE + 4)\n> >>>> +#define VIVID_CID_STRING                (VIVID_CID_CUSTOM_BASE + 5)\n> >>>> +#define VIVID_CID_BITMASK               (VIVID_CID_CUSTOM_BASE + 6)\n> >>>> +#define VIVID_CID_INTMENU               (VIVID_CID_CUSTOM_BASE + 7)\n> >>>> +#define VIVID_CID_U32_ARRAY             (VIVID_CID_CUSTOM_BASE + 8)\n> >>>> +#define VIVID_CID_U16_MATRIX            (VIVID_CID_CUSTOM_BASE + 9)\n> >>>> +#define VIVID_CID_U8_4D_ARRAY           (VIVID_CID_CUSTOM_BASE + 10)\n> >>>> +\n> >>>> +#define VIVID_CID_VIVID_BASE            (0x00f00000 | 0xf000)\n> >>>> +#define VIVID_CID_VIVID_CLASS           (0x00f00000 | 1)\n> >>>> +#define VIVID_CID_TEST_PATTERN          (VIVID_CID_VIVID_BASE + 0)\n> >>>> +#define VIVID_CID_OSD_TEXT_MODE         (VIVID_CID_VIVID_BASE + 1)\n> >>>> +#define VIVID_CID_HOR_MOVEMENT          (VIVID_CID_VIVID_BASE + 2)\n> >>>> +#define VIVID_CID_VERT_MOVEMENT         (VIVID_CID_VIVID_BASE + 3)\n> >>>> +#define VIVID_CID_SHOW_BORDER           (VIVID_CID_VIVID_BASE + 4)\n> >>>> +#define VIVID_CID_SHOW_SQUARE           (VIVID_CID_VIVID_BASE + 5)\n> >>>> +#define VIVID_CID_INSERT_SAV            (VIVID_CID_VIVID_BASE + 6)\n> >>>> +#define VIVID_CID_INSERT_EAV            (VIVID_CID_VIVID_BASE + 7)\n> >>>> +#define VIVID_CID_VBI_CAP_INTERLACED    (VIVID_CID_VIVID_BASE + 8)\n> >>>> +\n> >>>> +#define VIVID_CID_HFLIP                 (VIVID_CID_VIVID_BASE + 20)\n> >>>> +#define VIVID_CID_VFLIP                 (VIVID_CID_VIVID_BASE + 21)\n> >>>> +#define VIVID_CID_STD_ASPECT_RATIO      (VIVID_CID_VIVID_BASE + 22)\n> >>>> +#define VIVID_CID_DV_TIMINGS_ASPECT_RATIO       (VIVID_CID_VIVID_BASE + 23)\n> >>>> +#define VIVID_CID_TSTAMP_SRC            (VIVID_CID_VIVID_BASE + 24)\n> >>>> +#define VIVID_CID_COLORSPACE            (VIVID_CID_VIVID_BASE + 25)\n> >>>> +#define VIVID_CID_XFER_FUNC             (VIVID_CID_VIVID_BASE + 26)\n> >>>> +#define VIVID_CID_YCBCR_ENC             (VIVID_CID_VIVID_BASE + 27)\n> >>>> +#define VIVID_CID_QUANTIZATION          (VIVID_CID_VIVID_BASE + 28)\n> >>>> +#define VIVID_CID_LIMITED_RGB_RANGE     (VIVID_CID_VIVID_BASE + 29)\n> >>>> +#define VIVID_CID_ALPHA_MODE            (VIVID_CID_VIVID_BASE + 30)\n> >>>> +#define VIVID_CID_HAS_CROP_CAP          (VIVID_CID_VIVID_BASE + 31)\n> >>>> +#define VIVID_CID_HAS_COMPOSE_CAP       (VIVID_CID_VIVID_BASE + 32)\n> >>>> +#define VIVID_CID_HAS_SCALER_CAP        (VIVID_CID_VIVID_BASE + 33)\n> >>>> +#define VIVID_CID_HAS_CROP_OUT          (VIVID_CID_VIVID_BASE + 34)\n> >>>> +#define VIVID_CID_HAS_COMPOSE_OUT       (VIVID_CID_VIVID_BASE + 35)\n> >>>> +#define VIVID_CID_HAS_SCALER_OUT        (VIVID_CID_VIVID_BASE + 36)\n> >>>> +#define VIVID_CID_LOOP_VIDEO            (VIVID_CID_VIVID_BASE + 37)\n> >>>> +#define VIVID_CID_SEQ_WRAP              (VIVID_CID_VIVID_BASE + 38)\n> >>>> +#define VIVID_CID_TIME_WRAP             (VIVID_CID_VIVID_BASE + 39)\n> >>>> +#define VIVID_CID_MAX_EDID_BLOCKS       (VIVID_CID_VIVID_BASE + 40)\n> >>>> +#define VIVID_CID_PERCENTAGE_FILL       (VIVID_CID_VIVID_BASE + 41)\n> >>>> +#define VIVID_CID_REDUCED_FPS           (VIVID_CID_VIVID_BASE + 42)\n> >>>> +#define VIVID_CID_HSV_ENC               (VIVID_CID_VIVID_BASE + 43)\n> >>>> +#define VIVID_CID_DISPLAY_PRESENT       (VIVID_CID_VIVID_BASE + 44)\n> >>>> +\n> >>>> +#define VIVID_CID_STD_SIGNAL_MODE       (VIVID_CID_VIVID_BASE + 60)\n> >>>> +#define VIVID_CID_STANDARD              (VIVID_CID_VIVID_BASE + 61)\n> >>>> +#define VIVID_CID_DV_TIMINGS_SIGNAL_MODE        (VIVID_CID_VIVID_BASE + 62)\n> >>>> +#define VIVID_CID_DV_TIMINGS            (VIVID_CID_VIVID_BASE + 63)\n> >>>> +#define VIVID_CID_PERC_DROPPED          (VIVID_CID_VIVID_BASE + 64)\n> >>>> +#define VIVID_CID_DISCONNECT            (VIVID_CID_VIVID_BASE + 65)\n> >>>> +#define VIVID_CID_DQBUF_ERROR           (VIVID_CID_VIVID_BASE + 66)\n> >>>> +#define VIVID_CID_QUEUE_SETUP_ERROR     (VIVID_CID_VIVID_BASE + 67)\n> >>>> +#define VIVID_CID_BUF_PREPARE_ERROR     (VIVID_CID_VIVID_BASE + 68)\n> >>>> +#define VIVID_CID_START_STR_ERROR       (VIVID_CID_VIVID_BASE + 69)\n> >>>> +#define VIVID_CID_QUEUE_ERROR           (VIVID_CID_VIVID_BASE + 70)\n> >>>> +#define VIVID_CID_CLEAR_FB              (VIVID_CID_VIVID_BASE + 71)\n> >>>> +#define VIVID_CID_REQ_VALIDATE_ERROR    (VIVID_CID_VIVID_BASE + 72)\n> >>>> +\n> >>>> +#define VIVID_CID_RADIO_SEEK_MODE       (VIVID_CID_VIVID_BASE + 90)\n> >>>> +#define VIVID_CID_RADIO_SEEK_PROG_LIM   (VIVID_CID_VIVID_BASE + 91)\n> >>>> +#define VIVID_CID_RADIO_RX_RDS_RBDS     (VIVID_CID_VIVID_BASE + 92)\n> >>>> +#define VIVID_CID_RADIO_RX_RDS_BLOCKIO  (VIVID_CID_VIVID_BASE + 93)\n> >>>> +\n> >>>> +#define VIVID_CID_RADIO_TX_RDS_BLOCKIO  (VIVID_CID_VIVID_BASE + 94)\n> >>>> +\n> >>>> +#define VIVID_CID_SDR_CAP_FM_DEVIATION  (VIVID_CID_VIVID_BASE + 110)\n> >>>> +\n> >>>> +\n> >>>> +namespace libcamera {\n> >>>> +\n> >>>> +LOG_DEFINE_CATEGORY(VIVID)\n> >>>> +\n> >>>> +class VividCameraData : public CameraData\n> >>>> +{\n> >>>> +public:\n> >>>> +\tVividCameraData(PipelineHandler *pipe, MediaDevice *media)\n> >>>> +\t\t: CameraData(pipe), media_(media), video_(nullptr)\n> >>>> +\t{\n> >>>> +\t}\n> >>>> +\n> >>>> +\t~VividCameraData()\n> >>>> +\t{\n> >>>> +\t\tdelete video_;\n> >>>> +\t}\n> >>>> +\n> >>>> +\tint init();\n> >>>> +\tvoid bufferReady(FrameBuffer *buffer);\n> >>>> +\n> >>>> +\tMediaDevice *media_;\n> >>>> +\tV4L2VideoDevice *video_;\n> >>>> +\tStream stream_;\n> >>>> +};\n> >>>> +\n> >>>> +class VividCameraConfiguration : public CameraConfiguration\n> >>>> +{\n> >>>> +public:\n> >>>> +\tVividCameraConfiguration();\n> >>>> +\n> >>>> +\tStatus validate() override;\n> >>>> +};\n> >>>> +\n> >>>> +class PipelineHandlerVivid : public PipelineHandler\n> >>>> +{\n> >>>> +public:\n> >>>> +\tPipelineHandlerVivid(CameraManager *manager);\n> >>>> +\n> >>>> +\tCameraConfiguration *generateConfiguration(Camera *camera,\n> >>>> +\t\t\t\t\t\t   const StreamRoles &roles)\n> >>>> override;\n> >>>> +\tint configure(Camera *camera, CameraConfiguration *config) override;\n> >>>> +\n> >>>> +\tint exportFrameBuffers(Camera *camera, Stream *stream,\n> >>>> +\t\t\t       std::vector<std::unique_ptr<FrameBuffer>>\n> >>>> *buffers) override;\n> >>>> +\n> >>>> +\tint start(Camera *camera) override;\n> >>>> +\tvoid stop(Camera *camera) override;\n> >>>> +\n> >>>> +\tint queueRequestDevice(Camera *camera, Request *request) override;\n> >>>> +\n> >>>> +\tbool match(DeviceEnumerator *enumerator) override;\n> >>>> +\n> >>>> +private:\n> >>>> +\tint processControls(VividCameraData *data, Request *request);\n> >>>> +\n> >>>> +\tVividCameraData *cameraData(const Camera *camera)\n> >>>> +\t{\n> >>>> +\t\treturn static_cast<VividCameraData *>(\n> >>>> +\t\t\tPipelineHandler::cameraData(camera));\n> >>>> +\t}\n> >>>> +};\n> >>>> +\n> >>>> +VividCameraConfiguration::VividCameraConfiguration()\n> >>>> +\t: CameraConfiguration()\n> >>>> +{\n> >>>> +}\n> >>>> +\n> >>>> +CameraConfiguration::Status VividCameraConfiguration::validate()\n> >>>> +{\n> >>>> +\tStatus status = Valid;\n> >>>> +\n> >>>> +\tif (config_.empty())\n> >>>> +\t\treturn Invalid;\n> >>>> +\n> >>>> +\t/* Cap the number of entries to the available streams. */\n> >>>> +\tif (config_.size() > 1) {\n> >>>> +\t\tconfig_.resize(1);\n> >>>> +\t\tstatus = Adjusted;\n> >>>> +\t}\n> >>>> +\n> >>>> +\tStreamConfiguration &cfg = config_[0];\n> >>>> +\n> >>>> +\t/* Adjust the pixel format. */\n> >>>> +\tconst std::vector<libcamera::PixelFormat> formats =\n> >>>> cfg.formats().pixelformats();\n> >>>> +\tif (std::find(formats.begin(), formats.end(), cfg.pixelFormat) ==\n> >>>> formats.end()) {\n> >>>> +\t\tcfg.pixelFormat = cfg.formats().pixelformats()[0];\n> >>>> +\t\tLOG(VIVID, Debug) << \"Adjusting format to \" <<\n> >>>> cfg.pixelFormat.toString();\n> >>>> +\t\tstatus = Adjusted;\n> >>>> +\t}\n> >>>> +\n> >>>> +\tcfg.bufferCount = 4;\n> >>>> +\n> >>>> +\treturn status;\n> >>>> +}\n> >>>> +\n> >>>> +PipelineHandlerVivid::PipelineHandlerVivid(CameraManager *manager)\n> >>>> +\t: PipelineHandler(manager)\n> >>>> +{\n> >>>> +}\n> >>>> +\n> >>>> +CameraConfiguration *PipelineHandlerVivid::generateConfiguration(Camera\n> >>>> *camera,\n> >>>> +\t\t\t\t\t\t\t\t const\n> >>>> StreamRoles &roles)\n> >>>> +{\n> >>>> +\tCameraConfiguration *config = new VividCameraConfiguration();\n> >>>> +\tVividCameraData *data = cameraData(camera);\n> >>>> +\n> >>>> +\tif (roles.empty())\n> >>>> +\t\treturn config;\n> >>>> +\n> >>>> +\tstd::map<V4L2PixelFormat, std::vector<SizeRange>> v4l2Formats =\n> >>>> +\t\tdata->video_->formats();\n> >>>> +\tstd::map<PixelFormat, std::vector<SizeRange>> deviceFormats;\n> >>>> +\tstd::transform(v4l2Formats.begin(), v4l2Formats.end(),\n> >>>> +\t\t       std::inserter(deviceFormats, deviceFormats.begin()),\n> >>>> +\t\t       [&](const decltype(v4l2Formats)::value_type &format) {\n> >>>> +\t\t\t       return decltype(deviceFormats)::value_type{\n> >>>> +\t\t\t\t       format.first.toPixelFormat(),\n> >>>> +\t\t\t\t       format.second\n> >>>> +\t\t\t       };\n> >>>> +\t\t       });\n> >>>> +\n> >>>> +\tStreamFormats formats(deviceFormats);\n> >>>> +\tStreamConfiguration cfg(formats);\n> >>>> +\n> >>>> +\tcfg.pixelFormat = PixelFormat(DRM_FORMAT_BGR888);\n> >>>> +\tcfg.size = { 1280, 720 };\n> >>>> +\tcfg.bufferCount = 4;\n> >>>> +\n> >>>> +\tconfig->addConfiguration(cfg);\n> >>>> +\n> >>>> +\tconfig->validate();\n> >>>> +\n> >>>> +\treturn config;\n> >>>> +}\n> >>>> +\n> >>>> +int PipelineHandlerVivid::configure(Camera *camera, CameraConfiguration\n> >>>> *config)\n> >>>> +{\n> >>>> +\tVividCameraData *data = cameraData(camera);\n> >>>> +\tStreamConfiguration &cfg = config->at(0);\n> >>>> +\tint ret;\n> >>>> +\n> >>>> +\tV4L2DeviceFormat format = {};\n> >>>> +\tformat.fourcc = data->video_->toV4L2PixelFormat(cfg.pixelFormat);\n> >>>> +\tformat.size = cfg.size;\n> >>>> +\n> >>>> +\tret = data->video_->setFormat(&format);\n> >>>> +\tif (ret)\n> >>>> +\t\treturn ret;\n> >>>> +\n> >>>> +\tif (format.size != cfg.size ||\n> >>>> +\t    format.fourcc != data->video_->toV4L2PixelFormat(cfg.pixelFormat))\n> >>>> +\t\treturn -EINVAL;\n> >>>> +\n> >>>> +\tcfg.setStream(&data->stream_);\n> >>>> +\tcfg.stride = format.planes[0].bpl;\n> >>>> +\n> >>>> +\treturn 0;\n> >>>> +}\n> >>>> +\n> >>>> +int PipelineHandlerVivid::exportFrameBuffers(Camera *camera, Stream *stream,\n> >>>> +\t\t\t\t\t     std::vector<std::unique_ptr<FrameBu\n> >>>> ffer>> *buffers)\n> >>>> +{\n> >>>> +\tVividCameraData *data = cameraData(camera);\n> >>>> +\tunsigned int count = stream->configuration().bufferCount;\n> >>>> +\n> >>>> +\treturn data->video_->exportBuffers(count, buffers);\n> >>>> +}\n> >>>> +\n> >>>> +int PipelineHandlerVivid::start(Camera *camera)\n> >>>> +{\n> >>>> +\tVividCameraData *data = cameraData(camera);\n> >>>> +\tunsigned int count = data->stream_.configuration().bufferCount;\n> >>>> +\n> >>>> +\tint ret = data->video_->importBuffers(count);\n> >>>> +\tif (ret < 0)\n> >>>> +\t\treturn ret;\n> >>>> +\n> >>>> +\tret = data->video_->streamOn();\n> >>>> +\tif (ret < 0) {\n> >>>> +\t\tdata->ipa_->stop();\n> >>>> +\t\tdata->video_->releaseBuffers();\n> >>>> +\t\treturn ret;\n> >>>> +\t}\n> >>>> +\n> >>>> +\treturn 0;\n> >>>> +}\n> >>>> +\n> >>>> +void PipelineHandlerVivid::stop(Camera *camera)\n> >>>> +{\n> >>>> +\tVividCameraData *data = cameraData(camera);\n> >>>> +\tdata->video_->streamOff();\n> >>>> +\tdata->video_->releaseBuffers();\n> >>>> +}\n> >>>> +\n> >>>> +int PipelineHandlerVivid::processControls(VividCameraData *data, Request\n> >>>> *request)\n> >>>> +{\n> >>>> +\tControlList controls(data->video_->controls());\n> >>>> +\n> >>>> +\tfor (auto it : request->controls()) {\n> >>>> +\t\tunsigned int id = it.first;\n> >>>> +\t\tunsigned int offset;\n> >>>> +\t\tuint32_t cid;\n> >>>> +\n> >>>> +\t\tif (id == controls::Brightness) {\n> >>>> +\t\t\tcid = V4L2_CID_BRIGHTNESS;\n> >>>> +\t\t\toffset = 128;\n> >>>> +\t\t} else if (id == controls::Contrast) {\n> >>>> +\t\t\tcid = V4L2_CID_CONTRAST;\n> >>>> +\t\t\toffset = 0;\n> >>>> +\t\t} else if (id == controls::Saturation) {\n> >>>> +\t\t\tcid = V4L2_CID_SATURATION;\n> >>>> +\t\t\toffset = 0;\n> >>>> +\t\t} else {\n> >>>> +\t\t\tcontinue;\n> >>>> +\t\t}\n> >>>> +\n> >>>> +\t\tint32_t value = lroundf(it.second.get<float>() * 128 + offset);\n> >>>> +\t\tcontrols.set(cid, utils::clamp(value, 0, 255));\n> >>>> +\t}\n> >>>> +\n> >>>> +\tfor (const auto &ctrl : controls)\n> >>>> +\t\tLOG(VIVID, Debug)\n> >>>> +\t\t\t<< \"Setting control \" << utils::hex(ctrl.first)\n> >>>> +\t\t\t<< \" to \" << ctrl.second.toString();\n> >>>> +\n> >>>> +\tint ret = data->video_->setControls(&controls);\n> >>>> +\tif (ret) {\n> >>>> +\t\tLOG(VIVID, Error) << \"Failed to set controls: \" << ret;\n> >>>> +\t\treturn ret < 0 ? ret : -EINVAL;\n> >>>> +\t}\n> >>>> +\n> >>>> +\treturn ret;\n> >>>> +}\n> >>>> +\n> >>>> +int PipelineHandlerVivid::queueRequestDevice(Camera *camera, Request\n> >>>> *request)\n> >>>> +{\n> >>>> +\tVividCameraData *data = cameraData(camera);\n> >>>> +\tFrameBuffer *buffer = request->findBuffer(&data->stream_);\n> >>>> +\tif (!buffer) {\n> >>>> +\t\tLOG(VIVID, Error)\n> >>>> +\t\t\t<< \"Attempt to queue request with invalid stream\";\n> >>>> +\n> >>>> +\t\treturn -ENOENT;\n> >>>> +\t}\n> >>>> +\n> >>>> +\tint ret = processControls(data, request);\n> >>>> +\tif (ret < 0)\n> >>>> +\t\treturn ret;\n> >>>> +\n> >>>> +\tret = data->video_->queueBuffer(buffer);\n> >>>> +\tif (ret < 0)\n> >>>> +\t\treturn ret;\n> >>>> +\n> >>>> +\treturn 0;\n> >>>> +}\n> >>>> +\n> >>>> +bool PipelineHandlerVivid::match(DeviceEnumerator *enumerator)\n> >>>> +{\n> >>>> +\tDeviceMatch dm(\"vivid\");\n> >>>> +\tdm.add(\"vivid-000-vid-cap\");\n> >>>> +\n> >>>> +\tMediaDevice *media = acquireMediaDevice(enumerator, dm);\n> >>>> +\tif (!media)\n> >>>> +\t\treturn false;\n> >>>> +\n> >>>> +\tstd::unique_ptr<VividCameraData> data =\n> >>>> std::make_unique<VividCameraData>(this, media);\n> >>>> +\n> >>>> +\t/* Locate and open the capture video node. */\n> >>>> +\tif (data->init())\n> >>>> +\t\treturn false;\n> >>>> +\n> >>>> +\t/* Create and register the camera. */\n> >>>> +\tstd::set<Stream *> streams{ &data->stream_ };\n> >>>> +\tstd::shared_ptr<Camera> camera = Camera::create(this, data->video_-\n> >>>>> deviceName(), streams);\n> >>>> +\tregisterCamera(std::move(camera), std::move(data));\n> >>>> +\n> >>>> +\treturn true;\n> >>>> +}\n> >>>> +\n> >>>> +int VividCameraData::init()\n> >>>> +{\n> >>>> +\tvideo_ = new V4L2VideoDevice(media_->getEntityByName(\"vivid-000-vid-\n> >>>> cap\"));\n> >>>> +\tif (video_->open())\n> >>>> +\t\treturn -ENODEV;\n> >>>> +\n> >>>> +\tvideo_->bufferReady.connect(this, &VividCameraData::bufferReady);\n> >>>> +\n> >>>> +\t/* Initialise the supported controls. */\n> >>>> +\tconst ControlInfoMap &controls = video_->controls();\n> >>>> +\tControlInfoMap::Map ctrls;\n> >>>> +\n> >>>> +\tfor (const auto &ctrl : controls) {\n> >>>> +\t\tconst ControlId *id;\n> >>>> +\t\tControlInfo info;\n> >>>> +\n> >>>> +\t\tswitch (ctrl.first->id()) {\n> >>>> +\t\tcase V4L2_CID_BRIGHTNESS:\n> >>>> +\t\t\tid = &controls::Brightness;\n> >>>> +\t\t\tinfo = ControlInfo{ { -1.0f }, { 1.0f }, { 0.0f } };\n> >>>> +\t\t\tbreak;\n> >>>> +\t\tcase V4L2_CID_CONTRAST:\n> >>>> +\t\t\tid = &controls::Contrast;\n> >>>> +\t\t\tinfo = ControlInfo{ { 0.0f }, { 2.0f }, { 1.0f } };\n> >>>> +\t\t\tbreak;\n> >>>> +\t\tcase V4L2_CID_SATURATION:\n> >>>> +\t\t\tid = &controls::Saturation;\n> >>>> +\t\t\tinfo = ControlInfo{ { 0.0f }, { 2.0f }, { 1.0f } };\n> >>>> +\t\t\tbreak;\n> >>>> +\t\tdefault:\n> >>>> +\t\t\tcontinue;\n> >>>> +\t\t}\n> >>>> +\n> >>>> +\t\tctrls.emplace(id, info);\n> >>>> +\t}\n> >>>> +\n> >>>> +\tcontrolInfo_ = std::move(ctrls);\n> >>>> +\n> >>>> +\treturn 0;\n> >>>> +}\n> >>>> +\n> >>>> +void VividCameraData::bufferReady(FrameBuffer *buffer)\n> >>>> +{\n> >>>> +\tRequest *request = buffer->request();\n> >>>> +\n> >>>> +\tpipe_->completeBuffer(camera_, request, buffer);\n> >>>> +\tpipe_->completeRequest(camera_, request);\n> >>>> +}\n> >>>> +\n> >>>> +REGISTER_PIPELINE_HANDLER(PipelineHandlerVivid);\n> >>>> +\n> >>>> +} /* namespace libcamera */","headers":{"Return-Path":"<laurent.pinchart@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id A8F29603BE\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 23 Jun 2020 00:29:32 +0200 (CEST)","from pendragon.ideasonboard.com (81-175-216-236.bb.dnainternet.fi\n\t[81.175.216.236])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id F39F1327;\n\tTue, 23 Jun 2020 00:29:31 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"hMQDaaI0\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1592864972;\n\tbh=BLjOoZgh1k/WB+9ExxvYsbER3vAbDuRk7NP1VTIydp8=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=hMQDaaI0dFZxHzya2sVqLSGNN5r+nEU0KWWBe1U+8xm9iiOzkgkakP5XuM5AdQkzz\n\tGOY+UO6LTTqcA6LajN9N/t8dibpl6MBRtDPniosA5BChnDLMneQarEqWVPZ0eGMMwb\n\tA9anVpXC2lF3NTQW+5DNXWAjqRJNJNHx+aY5gtnk=","Date":"Tue, 23 Jun 2020 01:29:07 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"Nicolas Dufresne <nicolas@ndufresne.ca>,\n\tPaul Elder <paul.elder@ideasonboard.com>,\n\tlibcamera devel <libcamera-devel@lists.libcamera.org>","Message-ID":"<20200622222907.GH5852@pendragon.ideasonboard.com>","References":"<20200619120249.1084998-1-kieran.bingham@ideasonboard.com>\n\t<95f92384c74975b52f1df3ee71911cfba1baadfc.camel@ndufresne.ca>\n\t<40e70402-9e6b-f065-b24c-1e34ca3d9c99@ideasonboard.com>\n\t<20200622014517.GB25355@pendragon.ideasonboard.com>\n\t<a4ef15f7-9462-997d-e756-b5f8103ed79b@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<a4ef15f7-9462-997d-e756-b5f8103ed79b@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH] libcamera: pipelines: Add VIVID\n\tpipeline support","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Mon, 22 Jun 2020 22:29:32 -0000"}}]