From patchwork Fri Oct 22 14:39:03 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Naushir Patuck X-Patchwork-Id: 14261 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 41A9FBDB1C for ; Fri, 22 Oct 2021 14:40:18 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 0461268F61; Fri, 22 Oct 2021 16:40:18 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="GF8jST3s"; dkim-atps=neutral Received: from mail-wm1-x329.google.com (mail-wm1-x329.google.com [IPv6:2a00:1450:4864:20::329]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 33D7A6012A for ; Fri, 22 Oct 2021 16:40:15 +0200 (CEST) Received: by mail-wm1-x329.google.com with SMTP id j205so2443434wmj.3 for ; Fri, 22 Oct 2021 07:40:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=wJpyur6FGxUGBGkl1qE65WWe1yICqyvTwggHyrkXtb8=; b=GF8jST3stXA8+VnkDBriDE5zE7JNjtPjDkzojOOyf99L7i4yTNHvgup8EP38vcXUNv soSNPQ2Ey7U6KF25Gt4RuyYmD5E/iTXXNIw5eD1tAMXcowtpYDzd3rXwBVMNHdRoa1qq 27uQBZbqQVnvw2pjgzHN/6qNSRSi+Ct/myak8sfCA3K1B7Q4pH5w/T3IOeTRbCFlS6zO MAclMvcQlI1ffAGy5DdHyZt2jdOo07G/86kV+u4Usn9B44Hk11NrZ1vy8GvppPV40jeC /FIM74Zcbo6ZM6yx2K7sFt4AGUqGZKp4VdvMoLIRKyZfLH4UXoIKQOXaH5oXyPFgWvt+ BUeQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=wJpyur6FGxUGBGkl1qE65WWe1yICqyvTwggHyrkXtb8=; b=K9oScpjsu6yDoPlAHi2E6ZsdBzL8OktNFkkFzDJnKw4FrYOzWXxho80ARv+GhqiV6C PYzX2O+QAGIxc3wgLai3/1ZkI24bkokFztY9JaxWkTc3rrYtYm3J/SG+aYH2YWd8rCJS tPaE4uL7Wib7r2AaATQ3OmSux5Dib0sIXIQFhtrESGlDvTiXp0DvOWtS79NlwSQRFmxe uR4RAyvxfLz3zP2bQ3sVZaUoVGZnTRF6PpSboN+zct4/3b61MMrV7fSTRGuoyuzz73Ok 5iYkiwBzCo2AOMH6wUOfNYOu3oTybzYRTRu9avNJAyhPBV4oPQVpv2mwl5v/qlRGAhAl mhGg== X-Gm-Message-State: AOAM53196zbBMGwCmFitcjlSBVqiXJ1/U2n5+/QKf5wFviItGGinBrRv W+UiwlY0EtyIM4FN/00KSJU4WaoT8428fBUE X-Google-Smtp-Source: ABdhPJzb+TpjEkb7PRbNgfB7Hn+2hsEK2DkWetEEm+shaQofaFxN4moIDRGodx+N9yuHMtJRRPPdPQ== X-Received: by 2002:a1c:3b44:: with SMTP id i65mr107502wma.161.1634913614133; Fri, 22 Oct 2021 07:40:14 -0700 (PDT) Received: from naush-laptop.pitowers.org ([2a00:1098:3142:14:f00d:ddf0:61b0:a4cd]) by smtp.gmail.com with ESMTPSA id r205sm8577878wma.3.2021.10.22.07.40.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 22 Oct 2021 07:40:13 -0700 (PDT) From: Naushir Patuck To: libcamera-devel@lists.libcamera.org Date: Fri, 22 Oct 2021 15:39:03 +0100 Message-Id: <20211022143907.3089419-3-naush@raspberrypi.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20211022143907.3089419-1-naush@raspberrypi.com> References: <20211022143907.3089419-1-naush@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v2 2/6] pipeline: raspberrypi: Convert the pipeline handler to use media controller X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Switch the pipeline handler to use the new Unicam media controller based driver. With this change, we directly talk to the sensor device driver to set controls and set/get formats in the pipeline handler. This change requires the accompanying Raspberry Pi linux kernel change at https://github.com/raspberrypi/linux/pull/4645. If this kernel change is not present, the pipeline handler will fail to run with an error message informing the user to update the kernel build. Signed-off-by: Naushir Patuck Reviewed-by: David Plowman --- .../pipeline/raspberrypi/raspberrypi.cpp | 146 +++++++++++------- 1 file changed, 90 insertions(+), 56 deletions(-) diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp index 1634ca98f481..a31b0f81eba7 100644 --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp @@ -48,6 +48,29 @@ LOG_DEFINE_CATEGORY(RPI) namespace { +/* Map of mbus codes to supported sizes reported by the sensor. */ +using SensorFormats = std::map>; + +SensorFormats populateSensorFormats(std::unique_ptr &sensor) +{ + SensorFormats formats; + + for (auto const mbusCode : sensor->mbusCodes()) + formats.emplace(mbusCode, sensor->sizes(mbusCode)); + + return formats; +} + +inline V4L2DeviceFormat toV4L2DeviceFormat(V4L2SubdeviceFormat &mode) +{ + V4L2DeviceFormat deviceFormat; + BayerFormat bayer = BayerFormat::fromMbusCode(mode.mbus_code); + + deviceFormat.fourcc = bayer.toV4L2PixelFormat(); + deviceFormat.size = mode.size; + return deviceFormat; +} + bool isRaw(PixelFormat &pixFmt) { /* @@ -74,11 +97,10 @@ double scoreFormat(double desired, double actual) return score; } -V4L2DeviceFormat findBestMode(V4L2VideoDevice::Formats &formatsMap, - const Size &req) +V4L2SubdeviceFormat findBestMode(const SensorFormats &formatsMap, const Size &req) { double bestScore = std::numeric_limits::max(), score; - V4L2DeviceFormat bestMode; + V4L2SubdeviceFormat bestMode; #define PENALTY_AR 1500.0 #define PENALTY_8BIT 2000.0 @@ -88,18 +110,17 @@ V4L2DeviceFormat findBestMode(V4L2VideoDevice::Formats &formatsMap, /* Calculate the closest/best mode from the user requested size. */ for (const auto &iter : formatsMap) { - V4L2PixelFormat v4l2Format = iter.first; - const PixelFormatInfo &info = PixelFormatInfo::info(v4l2Format); + const unsigned int mbus_code = iter.first; + const PixelFormat format = BayerFormat::fromMbusCode(mbus_code).toPixelFormat(); + const PixelFormatInfo &info = PixelFormatInfo::info(format); - for (const SizeRange &sz : iter.second) { - double modeWidth = sz.contains(req) ? req.width : sz.max.width; - double modeHeight = sz.contains(req) ? req.height : sz.max.height; + for (const Size &sz : iter.second) { double reqAr = static_cast(req.width) / req.height; - double modeAr = modeWidth / modeHeight; + double modeAr = sz.width / sz.height; /* Score the dimensions for closeness. */ - score = scoreFormat(req.width, modeWidth); - score += scoreFormat(req.height, modeHeight); + score = scoreFormat(req.width, sz.width); + score += scoreFormat(req.height, sz.height); score += PENALTY_AR * scoreFormat(reqAr, modeAr); /* Add any penalties... this is not an exact science! */ @@ -115,12 +136,12 @@ V4L2DeviceFormat findBestMode(V4L2VideoDevice::Formats &formatsMap, if (score <= bestScore) { bestScore = score; - bestMode.fourcc = v4l2Format; - bestMode.size = Size(modeWidth, modeHeight); + bestMode.mbus_code = mbus_code; + bestMode.size = sz; } - LOG(RPI, Info) << "Mode: " << modeWidth << "x" << modeHeight - << " fmt " << v4l2Format.toString() + LOG(RPI, Info) << "Mode: " << sz.width << "x" << sz.height + << " fmt " << format.toString() << " Score: " << score << " (best " << bestScore << ")"; } @@ -170,6 +191,7 @@ public: std::unique_ptr ipa_; std::unique_ptr sensor_; + SensorFormats sensorFormats_; /* Array of Unicam and ISP device streams and associated buffers/streams. */ RPi::Device unicam_; RPi::Device isp_; @@ -352,9 +374,9 @@ CameraConfiguration::Status RPiCameraConfiguration::validate() * Calculate the best sensor mode we can use based on * the user request. */ - V4L2VideoDevice::Formats fmts = data_->unicam_[Unicam::Image].dev()->formats(); - V4L2DeviceFormat sensorFormat = findBestMode(fmts, cfg.size); - int ret = data_->unicam_[Unicam::Image].dev()->tryFormat(&sensorFormat); + V4L2SubdeviceFormat sensorFormat = findBestMode(data_->sensorFormats_, cfg.size); + V4L2DeviceFormat unicamFormat = toV4L2DeviceFormat(sensorFormat); + int ret = data_->unicam_[Unicam::Image].dev()->tryFormat(&unicamFormat); if (ret) return Invalid; @@ -366,7 +388,7 @@ CameraConfiguration::Status RPiCameraConfiguration::validate() * fetch the "native" (i.e. untransformed) Bayer order, * because the sensor may currently be flipped! */ - V4L2PixelFormat fourcc = sensorFormat.fourcc; + V4L2PixelFormat fourcc = unicamFormat.fourcc; if (data_->flipsAlterBayerOrder_) { BayerFormat bayer = BayerFormat::fromV4L2PixelFormat(fourcc); bayer.order = data_->nativeBayerOrder_; @@ -375,15 +397,15 @@ CameraConfiguration::Status RPiCameraConfiguration::validate() } PixelFormat sensorPixFormat = fourcc.toPixelFormat(); - if (cfg.size != sensorFormat.size || + if (cfg.size != unicamFormat.size || cfg.pixelFormat != sensorPixFormat) { - cfg.size = sensorFormat.size; + cfg.size = unicamFormat.size; cfg.pixelFormat = sensorPixFormat; status = Adjusted; } - cfg.stride = sensorFormat.planes[0].bpl; - cfg.frameSize = sensorFormat.planes[0].size; + cfg.stride = unicamFormat.planes[0].bpl; + cfg.frameSize = unicamFormat.planes[0].size; rawCount++; } else { @@ -472,7 +494,8 @@ CameraConfiguration *PipelineHandlerRPi::generateConfiguration(Camera *camera, { RPiCameraData *data = cameraData(camera); CameraConfiguration *config = new RPiCameraConfiguration(data); - V4L2DeviceFormat sensorFormat; + V4L2SubdeviceFormat sensorFormat; + V4L2DeviceFormat unicamFormat; unsigned int bufferCount; PixelFormat pixelFormat; V4L2VideoDevice::Formats fmts; @@ -487,9 +510,9 @@ CameraConfiguration *PipelineHandlerRPi::generateConfiguration(Camera *camera, switch (role) { case StreamRole::Raw: size = data->sensor_->resolution(); - fmts = data->unicam_[Unicam::Image].dev()->formats(); - sensorFormat = findBestMode(fmts, size); - pixelFormat = sensorFormat.fourcc.toPixelFormat(); + sensorFormat = findBestMode(data->sensorFormats_, size); + unicamFormat = toV4L2DeviceFormat(sensorFormat); + pixelFormat = BayerFormat::fromMbusCode(sensorFormat.mbus_code).toPixelFormat(); ASSERT(pixelFormat.isValid()); bufferCount = 2; rawCount++; @@ -599,32 +622,30 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config) } /* First calculate the best sensor mode we can use based on the user request. */ - V4L2VideoDevice::Formats fmts = data->unicam_[Unicam::Image].dev()->formats(); - V4L2DeviceFormat sensorFormat = findBestMode(fmts, rawStream ? sensorSize : maxSize); + V4L2SubdeviceFormat sensorFormat = findBestMode(data->sensorFormats_, rawStream ? sensorSize : maxSize); + V4L2DeviceFormat unicamFormat = toV4L2DeviceFormat(sensorFormat); + + ret = data->sensor_->setFormat(&sensorFormat); + if (ret) + return ret; /* * Unicam image output format. The ISP input format gets set at start, * just in case we have swapped bayer orders due to flips. */ - ret = data->unicam_[Unicam::Image].dev()->setFormat(&sensorFormat); + ret = data->unicam_[Unicam::Image].dev()->setFormat(&unicamFormat); if (ret) return ret; - /* - * The control ranges associated with the sensor may need updating - * after a format change. - * \todo Use the CameraSensor::setFormat API instead. - */ - data->sensor_->updateControlInfo(); - LOG(RPI, Info) << "Sensor: " << camera->id() - << " - Selected mode: " << sensorFormat.toString(); + << " - Selected sensor mode: " << sensorFormat.toString() + << " - Selected unicam mode: " << unicamFormat.toString(); /* * This format may be reset on start() if the bayer order has changed * because of flips in the sensor. */ - ret = data->isp_[Isp::Input].dev()->setFormat(&sensorFormat); + ret = data->isp_[Isp::Input].dev()->setFormat(&unicamFormat); if (ret) return ret; @@ -746,8 +767,8 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config) data->ispMinCropSize_ = testCrop.size(); /* Adjust aspect ratio by providing crops on the input image. */ - Size size = sensorFormat.size.boundedToAspectRatio(maxSize); - Rectangle crop = size.centeredTo(Rectangle(sensorFormat.size).center()); + Size size = unicamFormat.size.boundedToAspectRatio(maxSize); + Rectangle crop = size.centeredTo(Rectangle(unicamFormat.size).center()); data->ispCrop_ = crop; data->isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &crop); @@ -761,8 +782,11 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config) * supports it. */ if (data->sensorMetadata_) { - format = {}; + V4L2SubdeviceFormat embeddedFormat; + + data->sensor_->device()->getFormat(1, &embeddedFormat); format.fourcc = V4L2PixelFormat(V4L2_META_FMT_SENSOR_DATA); + format.planes[0].size = embeddedFormat.size.width * embeddedFormat.size.height; LOG(RPI, Debug) << "Setting embedded data format."; ret = data->unicam_[Unicam::Embedded].dev()->setFormat(&format); @@ -847,9 +871,14 @@ int PipelineHandlerRPi::start(Camera *camera, const ControlList *controls) * IPA configure may have changed the sensor flips - hence the bayer * order. Get the sensor format and set the ISP input now. */ - V4L2DeviceFormat sensorFormat; - data->unicam_[Unicam::Image].dev()->getFormat(&sensorFormat); - ret = data->isp_[Isp::Input].dev()->setFormat(&sensorFormat); + V4L2SubdeviceFormat sensorFormat; + data->sensor_->device()->getFormat(0, &sensorFormat); + + V4L2DeviceFormat ispFormat; + ispFormat.fourcc = BayerFormat::fromMbusCode(sensorFormat.mbus_code).toV4L2PixelFormat(); + ispFormat.size = sensorFormat.size; + + ret = data->isp_[Isp::Input].dev()->setFormat(&ispFormat); if (ret) { stop(camera); return ret; @@ -1004,6 +1033,8 @@ bool PipelineHandlerRPi::match(DeviceEnumerator *enumerator) if (data->sensor_->init()) return false; + data->sensorFormats_ = populateSensorFormats(data->sensor_); + ipa::RPi::SensorConfig sensorConfig; if (data->loadIPA(&sensorConfig)) { LOG(RPI, Error) << "Failed to load a suitable IPA library"; @@ -1030,6 +1061,11 @@ bool PipelineHandlerRPi::match(DeviceEnumerator *enumerator) return false; } + if (!(data->unicam_[Unicam::Image].dev()->caps().device_caps() & V4L2_CAP_IO_MC)) { + LOG(RPI, Error) << "Unicam driver did not advertise V4L2_CAP_IO_MC, please update your kernel!"; + return false; + } + /* * Setup our delayed control writer with the sensor default * gain and exposure delays. Mark VBLANK for priority write. @@ -1039,7 +1075,7 @@ bool PipelineHandlerRPi::match(DeviceEnumerator *enumerator) { V4L2_CID_EXPOSURE, { sensorConfig.exposureDelay, false } }, { V4L2_CID_VBLANK, { sensorConfig.vblankDelay, true } } }; - data->delayedCtrls_ = std::make_unique(data->unicam_[Unicam::Image].dev(), params); + data->delayedCtrls_ = std::make_unique(data->sensor_->device(), params); data->sensorMetadata_ = sensorConfig.sensorMetadata; /* Register the controls that the Raspberry Pi IPA can handle. */ @@ -1066,15 +1102,14 @@ bool PipelineHandlerRPi::match(DeviceEnumerator *enumerator) * As part of answering the final question, we reset the camera to * no transform at all. */ - - V4L2VideoDevice *dev = data->unicam_[Unicam::Image].dev(); - const struct v4l2_query_ext_ctrl *hflipCtrl = dev->controlInfo(V4L2_CID_HFLIP); + const V4L2Subdevice *sensor = data->sensor_->device(); + const struct v4l2_query_ext_ctrl *hflipCtrl = sensor->controlInfo(V4L2_CID_HFLIP); if (hflipCtrl) { /* We assume it will support vflips too... */ data->supportsFlips_ = true; data->flipsAlterBayerOrder_ = hflipCtrl->flags & V4L2_CTRL_FLAG_MODIFY_LAYOUT; - ControlList ctrls(dev->controls()); + ControlList ctrls(data->sensor_->controls()); ctrls.set(V4L2_CID_HFLIP, 0); ctrls.set(V4L2_CID_VFLIP, 0); data->setSensorControls(ctrls); @@ -1082,9 +1117,8 @@ bool PipelineHandlerRPi::match(DeviceEnumerator *enumerator) /* Look for a valid Bayer format. */ BayerFormat bayerFormat; - for (const auto &iter : dev->formats()) { - V4L2PixelFormat v4l2Format = iter.first; - bayerFormat = BayerFormat::fromV4L2PixelFormat(v4l2Format); + for (const auto &iter : data->sensorFormats_) { + bayerFormat = BayerFormat::fromMbusCode(iter.first); if (bayerFormat.isValid()) break; } @@ -1271,7 +1305,7 @@ int RPiCameraData::configureIPA(const CameraConfiguration *config) } } - entityControls.emplace(0, unicam_[Unicam::Image].dev()->controls()); + entityControls.emplace(0, sensor_->controls()); entityControls.emplace(1, isp_[Isp::Input].dev()->controls()); /* Always send the user transform to the IPA. */ @@ -1406,10 +1440,10 @@ void RPiCameraData::setSensorControls(ControlList &controls) ControlList vblank_ctrl; vblank_ctrl.set(V4L2_CID_VBLANK, controls.get(V4L2_CID_VBLANK)); - unicam_[Unicam::Image].dev()->setControls(&vblank_ctrl); + sensor_->setControls(&vblank_ctrl); } - unicam_[Unicam::Image].dev()->setControls(&controls); + sensor_->setControls(&controls); } void RPiCameraData::unicamBufferDequeue(FrameBuffer *buffer)