From patchwork Mon May 4 09:25:38 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525249 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id ECC751668 for ; Mon, 4 May 2020 09:26:26 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id AEFFA20757 for ; Mon, 4 May 2020 09:26:26 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Dv6n+ETM" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728332AbgEDJ00 (ORCPT ); Mon, 4 May 2020 05:26:26 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33122 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S1727781AbgEDJ00 (ORCPT ); Mon, 4 May 2020 05:26:26 -0400 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id D5FF0C061A0F for ; Mon, 4 May 2020 02:26:25 -0700 (PDT) Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 267D14F7; Mon, 4 May 2020 11:26:20 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584382; bh=OCIMooeZ0RGXlcgPRdjTxKPthxAq74qkmawbKsM920c=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Dv6n+ETMPdDPfPl4JkHjx9Dj6VfuVnZAqjmTvxwm68aa10kL4pUPWMEprw5lga1N2 6ig8/vdJhzv+j7V1Dc8cDSb0y8U7EQwsIbAg6WH7ypVLmry+1AIAdxQK7oGR8040TB CwHSlE7JB/CuMBhCcQ8mpUVULDYLWT2E+MlZaggM= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson Subject: [PATCH v2 01/34] media: uapi: v4l2-core: Add sensor ancillary data V4L2 fourcc type Date: Mon, 4 May 2020 12:25:38 +0300 Message-Id: <20200504092611.9798-2-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Naushir Patuck Add V4L2_META_FMT_SENSOR_DATA format 4CC. This new format will be used by the BCM2835 Unicam device to return out camera sensor embedded data. Signed-off-by: Naushir Patuck --- Documentation/media/uapi/v4l/meta-formats.rst | 1 + .../uapi/v4l/pixfmt-meta-sensor-data.rst | 32 +++++++++++++++++++ drivers/media/v4l2-core/v4l2-ioctl.c | 1 + include/uapi/linux/videodev2.h | 1 + 4 files changed, 35 insertions(+) create mode 100644 Documentation/media/uapi/v4l/pixfmt-meta-sensor-data.rst diff --git a/Documentation/media/uapi/v4l/meta-formats.rst b/Documentation/media/uapi/v4l/meta-formats.rst index 74c8659ee9d6..5474086ef6f0 100644 --- a/Documentation/media/uapi/v4l/meta-formats.rst +++ b/Documentation/media/uapi/v4l/meta-formats.rst @@ -21,6 +21,7 @@ These formats are used for the :ref:`metadata` interface only. pixfmt-meta-d4xx pixfmt-meta-intel-ipu3 + pixfmt-meta-sensor-data pixfmt-meta-uvc pixfmt-meta-vsp1-hgo pixfmt-meta-vsp1-hgt diff --git a/Documentation/media/uapi/v4l/pixfmt-meta-sensor-data.rst b/Documentation/media/uapi/v4l/pixfmt-meta-sensor-data.rst new file mode 100644 index 000000000000..4a67e204d08a --- /dev/null +++ b/Documentation/media/uapi/v4l/pixfmt-meta-sensor-data.rst @@ -0,0 +1,32 @@ +.. Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/media/uapi/fdl-appendix.rst. +.. +.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections + +.. _v4l2-meta-fmt-sensor-data: + +*********************************** +V4L2_META_FMT_SENSOR_DATA ('SENS') +*********************************** + +Sensor Ancillary Metadata + +Description +=========== + +This format describes ancillary data generated by a camera sensor and +transmitted over a stream on the camera bus. Sensor vendors generally have their +own custom format for this ancillary data. Some vendors follow a generic +CSI-2/SMIA embedded data format as described in the `CSI-2 specification. +`_ + +The size of the embedded buffer is defined as a single line with a pixel width +width specified in bytes. This is obtained by a call to the +:c:type:`VIDIOC_SUBDEV_G_FMT` ioctl on the sensor subdevice where the ``pad`` +field in :c:type:`v4l2_subdev_format` is set to 1. Note that this size is fixed +and cannot be modified with a call to :c:type:`VIDIOC_SUBDEV_S_FMT`. + diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index b2ef8e60ea7d..faf5a0f5eb6b 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1346,6 +1346,7 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_META_FMT_UVC: descr = "UVC Payload Header Metadata"; break; case V4L2_META_FMT_D4XX: descr = "Intel D4xx UVC Metadata"; break; case V4L2_META_FMT_VIVID: descr = "Vivid Metadata"; break; + case V4L2_META_FMT_SENSOR_DATA: descr = "Sensor Ancillary Metadata"; break; default: /* Compressed formats */ diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index 9817b7e2c968..a96146223843 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -766,6 +766,7 @@ struct v4l2_pix_format { #define V4L2_META_FMT_UVC v4l2_fourcc('U', 'V', 'C', 'H') /* UVC Payload Header metadata */ #define V4L2_META_FMT_D4XX v4l2_fourcc('D', '4', 'X', 'X') /* D4XX Payload Header metadata */ #define V4L2_META_FMT_VIVID v4l2_fourcc('V', 'I', 'V', 'D') /* Vivid Metadata */ +#define V4L2_META_FMT_SENSOR_DATA v4l2_fourcc('S', 'E', 'N', 'S') /* Sensor Ancillary metadata */ /* priv field value to indicates that subsequent fields are valid. */ #define V4L2_PIX_FMT_PRIV_MAGIC 0xfeedcafe From patchwork Mon May 4 09:25:39 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525253 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 40B60139A for ; Mon, 4 May 2020 09:26:28 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 2546B2075B for ; Mon, 4 May 2020 09:26:28 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="SOZDuujb" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728338AbgEDJ01 (ORCPT ); Mon, 4 May 2020 05:26:27 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33132 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S1727799AbgEDJ01 (ORCPT ); Mon, 4 May 2020 05:26:27 -0400 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 45142C061A0E for ; Mon, 4 May 2020 02:26:27 -0700 (PDT) Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 45DB211F9; Mon, 4 May 2020 11:26:22 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584383; bh=BlQSMuAfbRgjsXaR+o2a399vxP98SecNe4kDjpCilg0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=SOZDuujbcPDQQRSIsZo2BltNq69ftAkoI1NOXc96P2k+pvWxLR6Rz37DbuYOhtVva FLSWKlu43UdgHvAEzgOszI4yp2andx4vE43p+xq78dKpepe9ezCrBoe9cmIKAL5+Nl loJvOQKJCafJDPzQGXAlq1Cj27LTCD2XeKXewg4U= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson Subject: [PATCH v2 02/34] media: uapi: Add MEDIA_BUS_FMT_SENSOR_DATA media bus format Date: Mon, 4 May 2020 12:25:39 +0300 Message-Id: <20200504092611.9798-3-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Naushir Patuck This patch adds MEDIA_BUS_FMT_SENSOR_DATA used by the bcm2835-unicam driver to support CSI-2 embedded data streams from camera sensors. Signed-off-by: Naushir Patuck --- .../media/uapi/v4l/subdev-formats.rst | 33 +++++++++++++++++++ include/uapi/linux/media-bus-format.h | 3 ++ 2 files changed, 36 insertions(+) diff --git a/Documentation/media/uapi/v4l/subdev-formats.rst b/Documentation/media/uapi/v4l/subdev-formats.rst index 17bfb2beaa6a..e79caa61b9ce 100644 --- a/Documentation/media/uapi/v4l/subdev-formats.rst +++ b/Documentation/media/uapi/v4l/subdev-formats.rst @@ -7831,3 +7831,36 @@ formats. - 0x5001 - Interleaved raw UYVY and JPEG image format with embedded meta-data used by Samsung S3C73MX camera sensors. + + + +.. _v4l2-mbus-sensor-data: + +Sensor Ancillary Metadata Formats +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This section lists ancillary data generated by a camera sensor and +transmitted over a stream on the camera bus. + +The following table lists the existing sensor ancillary metadata formats: + + +.. _v4l2-mbus-pixelcode-sensor-metadata: + +.. tabularcolumns:: |p{8.0cm}|p{1.4cm}|p{7.7cm}| + +.. flat-table:: Sensor ancillary metadata formats + :header-rows: 1 + :stub-columns: 0 + + * - Identifier + - Code + - Comments + * .. _MEDIA_BUS_FMT_SENSOR_DATA: + + - MEDIA_BUS_FMT_SENSOR_DATA + - 0x7001 + - Sensor vendor specific ancillary metadata. Some vendors follow a generic + CSI-2/SMIA embedded data format as described in the `CSI-2 specification. + `_ + diff --git a/include/uapi/linux/media-bus-format.h b/include/uapi/linux/media-bus-format.h index 84fa53ffb13f..3c2848e91c1b 100644 --- a/include/uapi/linux/media-bus-format.h +++ b/include/uapi/linux/media-bus-format.h @@ -156,4 +156,7 @@ /* HSV - next is 0x6002 */ #define MEDIA_BUS_FMT_AHSV8888_1X32 0x6001 +/* Sensor ancillary metadata formats - next is 0x7002 */ +#define MEDIA_BUS_FMT_SENSOR_DATA 0x7001 + #endif /* __LINUX_MEDIA_BUS_FORMAT_H */ From patchwork Mon May 4 09:25:40 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525255 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 9F05317EF for ; Mon, 4 May 2020 09:26:28 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 86FEF20757 for ; Mon, 4 May 2020 09:26:28 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="DONeIDYY" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728339AbgEDJ02 (ORCPT ); Mon, 4 May 2020 05:26:28 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33134 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S1728335AbgEDJ01 (ORCPT ); Mon, 4 May 2020 05:26:27 -0400 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 7DD90C061A0F for ; Mon, 4 May 2020 02:26:27 -0700 (PDT) Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 1847011FC; Mon, 4 May 2020 11:26:23 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584385; bh=dkYtlCRVNSZRWandxSnugeev2fN87AO1Q4xcgIhzCU4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=DONeIDYYONJzOHBivnfUhA5J83w0CW/SWpo+XlNo7pJhIj4jTzM0SXNCEUkSMo9Xe gzcK0B2VdJcfQy5WG+I/Kwa0EKJ7slNGrEjuPGkjClMFfqS4dX/a5bJ+Ngq0awY/J7 4AcsDdf8o+zDso5Y7HbAJ5S60jQxmQsmM9luE1DQ= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson Subject: [PATCH v2 03/34] dt-bindings: media: Document BCM283x CSI2/CCP2 receiver Date: Mon, 4 May 2020 12:25:40 +0300 Message-Id: <20200504092611.9798-4-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Dave Stevenson Document the DT bindings for the CSI2/CCP2 receiver peripheral (known as Unicam) on BCM283x SoCs. Signed-off-by: Dave Stevenson Signed-off-by: Laurent Pinchart --- .../bindings/media/brcm,bcm2835-unicam.yaml | 155 ++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 Documentation/devicetree/bindings/media/brcm,bcm2835-unicam.yaml diff --git a/Documentation/devicetree/bindings/media/brcm,bcm2835-unicam.yaml b/Documentation/devicetree/bindings/media/brcm,bcm2835-unicam.yaml new file mode 100644 index 000000000000..6ffc900e8ae8 --- /dev/null +++ b/Documentation/devicetree/bindings/media/brcm,bcm2835-unicam.yaml @@ -0,0 +1,155 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2020 Raspberry Pi (Trading) Ltd. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/brcm,bcm2835-unicam.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Broadcom BCM283x Camera Interface (Unicam) + +maintainers: + - Dave Stevenson + - Raspberry Pi kernel list + +description: + The Unicam block on BCM283x SoCs is the receiver for either CSI-2 or CCP2 + data from image sensors or similar devices. + + The main platform using this SoC is the Raspberry Pi family of boards. On the + Pi the VideoCore firmware can also control this hardware block, and driving + it from two different processors will cause issues. To avoid this, the + firmware checks the device tree configuration during boot. If it finds device + tree nodes whose name starts with "csi" then it will stop the firmware + accessing the block, and it can then safely be used via the device tree + binding. + +properties: + compatible: + const: brcm,bcm2835-unicam + + reg: + items: + - description: Main registers block + - description: Clock registers block + + interrupts: + maxItems: 1 + + clocks: + minItems: 1 + maxItems: 2 + + clock-names: + minItems: 1 + items: + - const: lp + - const: core + + power-domains: + maxItems: 1 + + brcm,num-data-lanes: + description: + The number of data lanes supported by this Unicam instance. It may be + larger than the number of data lanes routed on the board, as described by + the data-lanes property of the endpoint. + allOf: + - $ref: "/schemas/types.yaml#/definitions/uint32" + - enum: [1, 2, 4] + + port: + type: object + description: + Input port node, as described in video-interfaces.txt. + + properties: + endpoint: + type: object + + properties: + clock-lanes: + items: + - const: 0 + + data-lanes: + description: + Lane reordering is not supported, items shall be in order, + starting at 1. + allOf: + - $ref: "/schemas/types.yaml#/definitions/uint32-array" + - maxItems: 1 + items: + minItems: 1 + maxItems: 4 + items: + - const: 1 + - const: 2 + - const: 3 + - const: 4 + + lane-polarities: + description: + Lane inversion is not supported. If the property is specified, it + shall contain all 0's. + allOf: + - $ref: "/schemas/types.yaml#/definitions/uint32-array" + - maxItems: 1 + items: + minItems: 2 + maxItems: 5 + items: + - const: 0 + - const: 0 + - const: 0 + - const: 0 + - const: 0 + + remote-endpoint: true + + required: + - data-lanes + - remote-endpoint + + additionalProperties: false + + required: + - endpoint + + additionalProperties: false + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - power-domains + - brcm,num-data-lanes + - port + +additionalProperties: false + +examples: + - | + #include + #include + #include + + csi@7e801000 { + compatible = "brcm,bcm2835-unicam"; + reg = <0x7e801000 0x800>, + <0x7e802004 0x4>; + interrupts = <2 7>; + clocks = <&clocks BCM2835_CLOCK_CAM1>; + clock-names = "lp"; + power-domains = <&power RPI_POWER_DOMAIN_UNICAM1>; + brcm,num-data-lanes = <4>; + + port { + csi1_ep: endpoint { + remote-endpoint = <&imx219_0>; + data-lanes = <1 2>; + }; + }; + }; +... From patchwork Mon May 4 09:25:41 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525257 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 09374139A for ; Mon, 4 May 2020 09:26:31 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id BB35320757 for ; Mon, 4 May 2020 09:26:30 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Li/ADdCK" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728347AbgEDJ0a (ORCPT ); Mon, 4 May 2020 05:26:30 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33136 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S1728341AbgEDJ03 (ORCPT ); Mon, 4 May 2020 05:26:29 -0400 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A5E3EC061A0E for ; Mon, 4 May 2020 02:26:28 -0700 (PDT) Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id E79891209; Mon, 4 May 2020 11:26:25 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584387; bh=x98hgKmdwZyivnkMGOH+atrg2tkbtGw65ayFB2scGn0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Li/ADdCKIAd/VuZa5G+KWudQPQEMl45Mjk21hmmgyHUdU/3VZZ46nMw8dCh3Zsoe7 g3L/Kp7ZWeQZsYaMSEDgyv+frk/xAOQg54XWdmb4EEujABEbOzX73esR8ZXK2MW00J tlFzkfHJ9M99NS7By8OjwVzc5rmeuVSAgv7pl9Aw= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson Subject: [PATCH v2 04/34] media: bcm2835-unicam: Driver for CCP2/CSI2 camera interface Date: Mon, 4 May 2020 12:25:41 +0300 Message-Id: <20200504092611.9798-5-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Naushir Patuck Add a driver for the Unicam camera receiver block on BCM283x processors. Compared to the bcm2835-camera driver present in staging, this driver handles the Unicam block only (CSI-2 receiver), and doesn't depend on the VC4 firmware running on the VPU. The commit is made up of a series of changes cherry-picked from the rpi-5.4.y branch of https://github.com/raspberrypi/linux/ with additional enhancements, forward-ported to the mainline kernel. Signed-off-by: Dave Stevenson Signed-off-by: Naushir Patuck Signed-off-by: Laurent Pinchart Reported-by: kbuild test robot --- Changes since v1: - Re-fetch mbus code from subdev on a g_fmt call - Group all ioctl disabling together - Fix reference counting in unicam_open - Add support for VIDIOC_[S|G]_SELECTION --- MAINTAINERS | 7 + drivers/media/platform/Kconfig | 1 + drivers/media/platform/Makefile | 2 + drivers/media/platform/bcm2835/Kconfig | 15 + drivers/media/platform/bcm2835/Makefile | 3 + .../media/platform/bcm2835/bcm2835-unicam.c | 2825 +++++++++++++++++ .../media/platform/bcm2835/vc4-regs-unicam.h | 253 ++ 7 files changed, 3106 insertions(+) create mode 100644 drivers/media/platform/bcm2835/Kconfig create mode 100644 drivers/media/platform/bcm2835/Makefile create mode 100644 drivers/media/platform/bcm2835/bcm2835-unicam.c create mode 100644 drivers/media/platform/bcm2835/vc4-regs-unicam.h diff --git a/MAINTAINERS b/MAINTAINERS index b816a453b10e..edf5b8d9c98a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3341,6 +3341,13 @@ N: bcm113* N: bcm216* N: kona +BROADCOM BCM2835 CAMERA DRIVER +M: Raspberry Pi Kernel Maintenance +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/platform/bcm2835/ +F: Documentation/devicetree/bindings/media/brcm,bcm2835-unicam.yaml + BROADCOM BCM47XX MIPS ARCHITECTURE M: Hauke Mehrtens M: Rafał Miłecki diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig index e01bbb9dd1c1..98721a4e0be1 100644 --- a/drivers/media/platform/Kconfig +++ b/drivers/media/platform/Kconfig @@ -146,6 +146,7 @@ source "drivers/media/platform/am437x/Kconfig" source "drivers/media/platform/xilinx/Kconfig" source "drivers/media/platform/rcar-vin/Kconfig" source "drivers/media/platform/atmel/Kconfig" +source "drivers/media/platform/bcm2835/Kconfig" source "drivers/media/platform/sunxi/Kconfig" config VIDEO_TI_CAL diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile index d13db96e3015..a425e4d2e3f3 100644 --- a/drivers/media/platform/Makefile +++ b/drivers/media/platform/Makefile @@ -98,4 +98,6 @@ obj-y += meson/ obj-y += cros-ec-cec/ +obj-y += bcm2835/ + obj-y += sunxi/ diff --git a/drivers/media/platform/bcm2835/Kconfig b/drivers/media/platform/bcm2835/Kconfig new file mode 100644 index 000000000000..ec46e3ef053c --- /dev/null +++ b/drivers/media/platform/bcm2835/Kconfig @@ -0,0 +1,15 @@ +# Broadcom VideoCore4 V4L2 camera support + +config VIDEO_BCM2835_UNICAM + tristate "Broadcom BCM2835 Unicam video capture driver" + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && MEDIA_CONTROLLER + depends on ARCH_BCM2835 || COMPILE_TEST + select VIDEOBUF2_DMA_CONTIG + select V4L2_FWNODE + help + Say Y here to enable support for the BCM2835 CSI-2 receiver. This is a + V4L2 driver that controls the CSI-2 receiver directly, independently + from the VC4 firmware. + + To compile this driver as a module, choose M here. The module will be + called bcm2835-unicam. diff --git a/drivers/media/platform/bcm2835/Makefile b/drivers/media/platform/bcm2835/Makefile new file mode 100644 index 000000000000..a98aba03598a --- /dev/null +++ b/drivers/media/platform/bcm2835/Makefile @@ -0,0 +1,3 @@ +# Makefile for BCM2835 Unicam driver + +obj-$(CONFIG_VIDEO_BCM2835_UNICAM) += bcm2835-unicam.o diff --git a/drivers/media/platform/bcm2835/bcm2835-unicam.c b/drivers/media/platform/bcm2835/bcm2835-unicam.c new file mode 100644 index 000000000000..2e9387cbc1e0 --- /dev/null +++ b/drivers/media/platform/bcm2835/bcm2835-unicam.c @@ -0,0 +1,2825 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * BCM2835 Unicam Capture Driver + * + * Copyright (C) 2017-2020 - Raspberry Pi (Trading) Ltd. + * + * Dave Stevenson + * + * Based on TI am437x driver by + * Benoit Parrot + * Lad, Prabhakar + * + * and TI CAL camera interface driver by + * Benoit Parrot + * + * + * There are two camera drivers in the kernel for BCM283x - this one + * and bcm2835-camera (currently in staging). + * + * This driver directly controls the Unicam peripheral - there is no + * involvement with the VideoCore firmware. Unicam receives CSI-2 or + * CCP2 data and writes it into SDRAM. + * The only potential processing options are to repack Bayer data into an + * alternate format, and applying windowing. + * The repacking does not shift the data, so can repack V4L2_PIX_FMT_Sxxxx10P + * to V4L2_PIX_FMT_Sxxxx10, or V4L2_PIX_FMT_Sxxxx12P to V4L2_PIX_FMT_Sxxxx12, + * but not generically up to V4L2_PIX_FMT_Sxxxx16. The driver will add both + * formats where the relevant formats are defined, and will automatically + * configure the repacking as required. + * Support for windowing may be added later. + * + * It should be possible to connect this driver to any sensor with a + * suitable output interface and V4L2 subdevice driver. + * + * bcm2835-camera uses the VideoCore firmware to control the sensor, + * Unicam, ISP, and all tuner control loops. Fully processed frames are + * delivered to the driver by the firmware. It only has sensor drivers + * for Omnivision OV5647, and Sony IMX219 sensors. + * + * The two drivers are mutually exclusive for the same Unicam instance. + * The VideoCore firmware checks the device tree configuration during boot. + * If it finds device tree nodes called csi0 or csi1 it will block the + * firmware from accessing the peripheral, and bcm2835-camera will + * not be able to stream data. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vc4-regs-unicam.h" + +#define UNICAM_MODULE_NAME "unicam" +#define UNICAM_VERSION "0.1.0" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level 0-3"); + +#define unicam_dbg(level, dev, fmt, arg...) \ + v4l2_dbg(level, debug, &(dev)->v4l2_dev, fmt, ##arg) +#define unicam_info(dev, fmt, arg...) \ + v4l2_info(&(dev)->v4l2_dev, fmt, ##arg) +#define unicam_err(dev, fmt, arg...) \ + v4l2_err(&(dev)->v4l2_dev, fmt, ##arg) + +/* + * To protect against a dodgy sensor driver never returning an error from + * enum_mbus_code, set a maximum index value to be used. + */ +#define MAX_ENUM_MBUS_CODE 128 + +/* + * Stride is a 16 bit register, but also has to be a multiple of 32. + */ +#define BPL_ALIGNMENT 32 +#define MAX_BYTESPERLINE ((1 << 16) - BPL_ALIGNMENT) +/* + * Max width is therefore determined by the max stride divided by + * the number of bits per pixel. Take 32bpp as a + * worst case. + * No imposed limit on the height, so adopt a square image for want + * of anything better. + */ +#define MAX_WIDTH (MAX_BYTESPERLINE / 4) +#define MAX_HEIGHT MAX_WIDTH +/* Define a nominal minimum image size */ +#define MIN_WIDTH 16 +#define MIN_HEIGHT 16 +/* Default size of the embedded buffer */ +#define UNICAM_EMBEDDED_SIZE 8192 + +/* + * Size of the dummy buffer. Can be any size really, but the DMA + * allocation works in units of page sizes. + */ +#define DUMMY_BUF_SIZE (PAGE_SIZE) + +enum pad_types { + IMAGE_PAD, + METADATA_PAD, + MAX_NODES +}; + +/* + * struct unicam_fmt - Unicam media bus format information + * @pixelformat: V4L2 pixel format FCC identifier. 0 if n/a. + * @repacked_fourcc: V4L2 pixel format FCC identifier if the data is expanded + * out to 16bpp. 0 if n/a. + * @code: V4L2 media bus format code. + * @depth: Bits per pixel as delivered from the source. + * @csi_dt: CSI data type. + * @check_variants: Flag to denote that there are multiple mediabus formats + * still in the list that could match this V4L2 format. + */ +struct unicam_fmt { + u32 fourcc; + u32 repacked_fourcc; + u32 code; + u8 depth; + u8 csi_dt; + u8 check_variants; +}; + +static const struct unicam_fmt formats[] = { + /* YUV Formats */ + { + .fourcc = V4L2_PIX_FMT_YUYV, + .code = MEDIA_BUS_FMT_YUYV8_2X8, + .depth = 16, + .csi_dt = 0x1e, + .check_variants = 1, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .depth = 16, + .csi_dt = 0x1e, + .check_variants = 1, + }, { + .fourcc = V4L2_PIX_FMT_YVYU, + .code = MEDIA_BUS_FMT_YVYU8_2X8, + .depth = 16, + .csi_dt = 0x1e, + .check_variants = 1, + }, { + .fourcc = V4L2_PIX_FMT_VYUY, + .code = MEDIA_BUS_FMT_VYUY8_2X8, + .depth = 16, + .csi_dt = 0x1e, + .check_variants = 1, + }, { + .fourcc = V4L2_PIX_FMT_YUYV, + .code = MEDIA_BUS_FMT_YUYV8_1X16, + .depth = 16, + .csi_dt = 0x1e, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .depth = 16, + .csi_dt = 0x1e, + }, { + .fourcc = V4L2_PIX_FMT_YVYU, + .code = MEDIA_BUS_FMT_YVYU8_1X16, + .depth = 16, + .csi_dt = 0x1e, + }, { + .fourcc = V4L2_PIX_FMT_VYUY, + .code = MEDIA_BUS_FMT_VYUY8_1X16, + .depth = 16, + .csi_dt = 0x1e, + }, { + /* RGB Formats */ + .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .depth = 16, + .csi_dt = 0x22, + }, { + .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ + .code = MEDIA_BUS_FMT_RGB565_2X8_BE, + .depth = 16, + .csi_dt = 0x22 + }, { + .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */ + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, + .depth = 16, + .csi_dt = 0x21, + }, { + .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ + .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, + .depth = 16, + .csi_dt = 0x21, + }, { + .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ + .code = MEDIA_BUS_FMT_RGB888_1X24, + .depth = 24, + .csi_dt = 0x24, + }, { + .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ + .code = MEDIA_BUS_FMT_BGR888_1X24, + .depth = 24, + .csi_dt = 0x24, + }, { + .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ + .code = MEDIA_BUS_FMT_ARGB8888_1X32, + .depth = 32, + .csi_dt = 0x0, + }, { + /* Bayer Formats */ + .fourcc = V4L2_PIX_FMT_SBGGR8, + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .depth = 8, + .csi_dt = 0x2a, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .depth = 8, + .csi_dt = 0x2a, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .depth = 8, + .csi_dt = 0x2a, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB8, + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .depth = 8, + .csi_dt = 0x2a, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR10P, + .repacked_fourcc = V4L2_PIX_FMT_SBGGR10, + .code = MEDIA_BUS_FMT_SBGGR10_1X10, + .depth = 10, + .csi_dt = 0x2b, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG10P, + .repacked_fourcc = V4L2_PIX_FMT_SGBRG10, + .code = MEDIA_BUS_FMT_SGBRG10_1X10, + .depth = 10, + .csi_dt = 0x2b, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG10P, + .repacked_fourcc = V4L2_PIX_FMT_SGRBG10, + .code = MEDIA_BUS_FMT_SGRBG10_1X10, + .depth = 10, + .csi_dt = 0x2b, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB10P, + .repacked_fourcc = V4L2_PIX_FMT_SRGGB10, + .code = MEDIA_BUS_FMT_SRGGB10_1X10, + .depth = 10, + .csi_dt = 0x2b, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR12P, + .repacked_fourcc = V4L2_PIX_FMT_SBGGR12, + .code = MEDIA_BUS_FMT_SBGGR12_1X12, + .depth = 12, + .csi_dt = 0x2c, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG12P, + .repacked_fourcc = V4L2_PIX_FMT_SGBRG12, + .code = MEDIA_BUS_FMT_SGBRG12_1X12, + .depth = 12, + .csi_dt = 0x2c, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG12P, + .repacked_fourcc = V4L2_PIX_FMT_SGRBG12, + .code = MEDIA_BUS_FMT_SGRBG12_1X12, + .depth = 12, + .csi_dt = 0x2c, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB12P, + .repacked_fourcc = V4L2_PIX_FMT_SRGGB12, + .code = MEDIA_BUS_FMT_SRGGB12_1X12, + .depth = 12, + .csi_dt = 0x2c, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR14P, + .code = MEDIA_BUS_FMT_SBGGR14_1X14, + .depth = 14, + .csi_dt = 0x2d, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG14P, + .code = MEDIA_BUS_FMT_SGBRG14_1X14, + .depth = 14, + .csi_dt = 0x2d, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG14P, + .code = MEDIA_BUS_FMT_SGRBG14_1X14, + .depth = 14, + .csi_dt = 0x2d, + }, { + .fourcc = V4L2_PIX_FMT_SRGGB14P, + .code = MEDIA_BUS_FMT_SRGGB14_1X14, + .depth = 14, + .csi_dt = 0x2d, + }, { + /* + * 16 bit Bayer formats could be supported, but there is no CSI2 + * data_type defined for raw 16, and no sensors that produce it at + * present. + */ + + /* Greyscale formats */ + .fourcc = V4L2_PIX_FMT_GREY, + .code = MEDIA_BUS_FMT_Y8_1X8, + .depth = 8, + .csi_dt = 0x2a, + }, { + .fourcc = V4L2_PIX_FMT_Y10P, + .repacked_fourcc = V4L2_PIX_FMT_Y10, + .code = MEDIA_BUS_FMT_Y10_1X10, + .depth = 10, + .csi_dt = 0x2b, + }, { + /* NB There is no packed V4L2 fourcc for this format. */ + .repacked_fourcc = V4L2_PIX_FMT_Y12, + .code = MEDIA_BUS_FMT_Y12_1X12, + .depth = 12, + .csi_dt = 0x2c, + }, + /* Embedded data format */ + { + .fourcc = V4L2_META_FMT_SENSOR_DATA, + .code = MEDIA_BUS_FMT_SENSOR_DATA, + .depth = 8, + } +}; + +struct unicam_buffer { + struct vb2_v4l2_buffer vb; + struct list_head list; +}; + +static inline struct unicam_buffer *to_unicam_buffer(struct vb2_buffer *vb) +{ + return container_of(vb, struct unicam_buffer, vb.vb2_buf); +} + +struct unicam_node { + bool registered; + int open; + bool streaming; + unsigned int pad_id; + /* Pointer pointing to current v4l2_buffer */ + struct unicam_buffer *cur_frm; + /* Pointer pointing to next v4l2_buffer */ + struct unicam_buffer *next_frm; + /* video capture */ + const struct unicam_fmt *fmt; + /* Used to store current pixel format */ + struct v4l2_format v_fmt; + /* Used to store current mbus frame format */ + struct v4l2_mbus_framefmt m_fmt; + /* Buffer queue used in video-buf */ + struct vb2_queue buffer_queue; + /* Queue of filled frames */ + struct list_head dma_queue; + /* IRQ lock for DMA queue */ + spinlock_t dma_queue_lock; + /* lock used to access this structure */ + struct mutex lock; + /* Identifies video device for this channel */ + struct video_device video_dev; + /* Pointer to the parent handle */ + struct unicam_device *dev; + struct media_pad pad; + unsigned int embedded_lines; + /* + * Dummy buffer intended to be used by unicam + * if we have no other queued buffers to swap to. + */ + void *dummy_buf_cpu_addr; + dma_addr_t dummy_buf_dma_addr; +}; + +struct unicam_device { + struct kref kref; + + /* V4l2 specific parameters */ + struct v4l2_async_subdev asd; + + /* peripheral base address */ + void __iomem *base; + /* clock gating base address */ + void __iomem *clk_gate_base; + /* clock handle */ + struct clk *clock; + /* V4l2 device */ + struct v4l2_device v4l2_dev; + struct media_device mdev; + + /* parent device */ + struct platform_device *pdev; + /* subdevice async Notifier */ + struct v4l2_async_notifier notifier; + unsigned int sequence; + + /* ptr to sub device */ + struct v4l2_subdev *sensor; + /* Pad config for the sensor */ + struct v4l2_subdev_pad_config *sensor_config; + + enum v4l2_mbus_type bus_type; + /* + * Stores bus.mipi_csi2.flags for CSI2 sensors, or + * bus.mipi_csi1.strobe for CCP2. + */ + unsigned int bus_flags; + unsigned int max_data_lanes; + unsigned int active_data_lanes; + bool sensor_embedded_data; + + struct unicam_node node[MAX_NODES]; + struct v4l2_ctrl_handler ctrl_handler; +}; + +static inline struct unicam_device * +to_unicam_device(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct unicam_device, v4l2_dev); +} + +/* Hardware access */ +static inline void clk_write(struct unicam_device *dev, u32 val) +{ + writel(val | 0x5a000000, dev->clk_gate_base); +} + +static inline u32 reg_read(struct unicam_device *dev, u32 offset) +{ + return readl(dev->base + offset); +} + +static inline void reg_write(struct unicam_device *dev, u32 offset, u32 val) +{ + writel(val, dev->base + offset); +} + +static inline int get_field(u32 value, u32 mask) +{ + return (value & mask) >> __ffs(mask); +} + +static inline void set_field(u32 *valp, u32 field, u32 mask) +{ + u32 val = *valp; + + val &= ~mask; + val |= (field << __ffs(mask)) & mask; + *valp = val; +} + +static inline u32 reg_read_field(struct unicam_device *dev, u32 offset, + u32 mask) +{ + return get_field(reg_read(dev, offset), mask); +} + +static inline void reg_write_field(struct unicam_device *dev, u32 offset, + u32 field, u32 mask) +{ + u32 val = reg_read(dev, offset); + + set_field(&val, field, mask); + reg_write(dev, offset, val); +} + +/* Power management functions */ +static inline int unicam_runtime_get(struct unicam_device *dev) +{ + return pm_runtime_get_sync(&dev->pdev->dev); +} + +static inline void unicam_runtime_put(struct unicam_device *dev) +{ + pm_runtime_put_sync(&dev->pdev->dev); +} + +/* Format setup functions */ +static const struct unicam_fmt *find_format_by_code(u32 code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + if (formats[i].code == code) + return &formats[i]; + } + + return NULL; +} + +static int check_mbus_format(struct unicam_device *dev, + const struct unicam_fmt *format) +{ + unsigned int i; + int ret = 0; + + for (i = 0; !ret && i < MAX_ENUM_MBUS_CODE; i++) { + struct v4l2_subdev_mbus_code_enum mbus_code = { + .index = i, + .pad = IMAGE_PAD, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + + ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, + NULL, &mbus_code); + + if (!ret && mbus_code.code == format->code) + return 1; + } + + return 0; +} + +static const struct unicam_fmt *find_format_by_pix(struct unicam_device *dev, + u32 pixelformat) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(formats); i++) { + if (formats[i].fourcc == pixelformat || + formats[i].repacked_fourcc == pixelformat) { + if (formats[i].check_variants && + !check_mbus_format(dev, &formats[i])) + continue; + return &formats[i]; + } + } + + return NULL; +} + +static inline unsigned int bytes_per_line(u32 width, + const struct unicam_fmt *fmt, + u32 v4l2_fourcc) +{ + if (v4l2_fourcc == fmt->repacked_fourcc) + /* Repacking always goes to 16bpp */ + return ALIGN(width << 1, BPL_ALIGNMENT); + else + return ALIGN((width * fmt->depth) >> 3, BPL_ALIGNMENT); +} + +static int __subdev_get_format(struct unicam_device *dev, + struct v4l2_mbus_framefmt *fmt, int pad_id) +{ + struct v4l2_subdev_format sd_fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = pad_id + }; + int ret; + + ret = v4l2_subdev_call(dev->sensor, pad, get_fmt, dev->sensor_config, + &sd_fmt); + if (ret < 0) + return ret; + + *fmt = sd_fmt.format; + + unicam_dbg(1, dev, "%s %dx%d code:%04x\n", __func__, + fmt->width, fmt->height, fmt->code); + + return 0; +} + +static int __subdev_set_format(struct unicam_device *dev, + struct v4l2_mbus_framefmt *fmt, int pad_id) +{ + struct v4l2_subdev_format sd_fmt = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = pad_id + }; + int ret; + + sd_fmt.format = *fmt; + + ret = v4l2_subdev_call(dev->sensor, pad, set_fmt, dev->sensor_config, + &sd_fmt); + if (ret < 0) + return ret; + + *fmt = sd_fmt.format; + + if (pad_id == IMAGE_PAD) + unicam_dbg(1, dev, "%s %dx%d code:%04x\n", __func__, fmt->width, + fmt->height, fmt->code); + else + unicam_dbg(1, dev, "%s Embedded data code:%04x\n", __func__, + sd_fmt.format.code); + + return 0; +} + +static int unicam_calc_format_size_bpl(struct unicam_device *dev, + const struct unicam_fmt *fmt, + struct v4l2_format *f) +{ + unsigned int min_bytesperline; + + v4l_bound_align_image(&f->fmt.pix.width, MIN_WIDTH, MAX_WIDTH, 2, + &f->fmt.pix.height, MIN_HEIGHT, MAX_HEIGHT, 0, + 0); + + min_bytesperline = bytes_per_line(f->fmt.pix.width, fmt, + f->fmt.pix.pixelformat); + + if (f->fmt.pix.bytesperline > min_bytesperline && + f->fmt.pix.bytesperline <= MAX_BYTESPERLINE) + f->fmt.pix.bytesperline = ALIGN(f->fmt.pix.bytesperline, + BPL_ALIGNMENT); + else + f->fmt.pix.bytesperline = min_bytesperline; + + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + + unicam_dbg(3, dev, "%s: fourcc: %08X size: %dx%d bpl:%d img_size:%d\n", + __func__, + f->fmt.pix.pixelformat, + f->fmt.pix.width, f->fmt.pix.height, + f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); + + return 0; +} + +static int unicam_reset_format(struct unicam_node *node) +{ + struct unicam_device *dev = node->dev; + struct v4l2_mbus_framefmt mbus_fmt; + int ret; + + if (dev->sensor_embedded_data || node->pad_id != METADATA_PAD) { + ret = __subdev_get_format(dev, &mbus_fmt, node->pad_id); + if (ret) { + unicam_err(dev, "Failed to get_format - ret %d\n", ret); + return ret; + } + + if (mbus_fmt.code != node->fmt->code) { + unicam_err(dev, "code mismatch - fmt->code %08x, mbus_fmt.code %08x\n", + node->fmt->code, mbus_fmt.code); + return ret; + } + } + + if (node->pad_id == IMAGE_PAD) { + v4l2_fill_pix_format(&node->v_fmt.fmt.pix, &mbus_fmt); + node->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + unicam_calc_format_size_bpl(dev, node->fmt, &node->v_fmt); + } else { + node->v_fmt.type = V4L2_BUF_TYPE_META_CAPTURE; + node->v_fmt.fmt.meta.dataformat = V4L2_META_FMT_SENSOR_DATA; + if (dev->sensor_embedded_data) { + node->v_fmt.fmt.meta.buffersize = + mbus_fmt.width * mbus_fmt.height; + node->embedded_lines = mbus_fmt.height; + } else { + node->v_fmt.fmt.meta.buffersize = UNICAM_EMBEDDED_SIZE; + node->embedded_lines = 1; + } + } + + node->m_fmt = mbus_fmt; + return 0; +} + +static void unicam_wr_dma_addr(struct unicam_device *dev, dma_addr_t dmaaddr, + unsigned int buffer_size, int pad_id) +{ + dma_addr_t endaddr = dmaaddr + buffer_size; + + /* + * dmaaddr and endaddr should be a 32-bit address with the top two bits + * set to 0x3 to signify uncached access through the Videocore memory + * controller. + */ + WARN_ON((dmaaddr >> 30) != 0x3 || (endaddr >> 30) != 0x3); + + if (pad_id == IMAGE_PAD) { + reg_write(dev, UNICAM_IBSA0, dmaaddr); + reg_write(dev, UNICAM_IBEA0, endaddr); + } else { + reg_write(dev, UNICAM_DBSA0, dmaaddr); + reg_write(dev, UNICAM_DBEA0, endaddr); + } +} + +static inline unsigned int unicam_get_lines_done(struct unicam_device *dev) +{ + dma_addr_t start_addr, cur_addr; + unsigned int stride = dev->node[IMAGE_PAD].v_fmt.fmt.pix.bytesperline; + struct unicam_buffer *frm = dev->node[IMAGE_PAD].cur_frm; + + if (!frm) + return 0; + + start_addr = vb2_dma_contig_plane_dma_addr(&frm->vb.vb2_buf, 0); + cur_addr = reg_read(dev, UNICAM_IBWP); + return (unsigned int)(cur_addr - start_addr) / stride; +} + +static inline void unicam_schedule_next_buffer(struct unicam_node *node) +{ + struct unicam_device *dev = node->dev; + struct unicam_buffer *buf; + unsigned int size; + dma_addr_t addr; + + buf = list_first_entry(&node->dma_queue, struct unicam_buffer, list); + node->next_frm = buf; + list_del(&buf->list); + + addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + size = (node->pad_id == IMAGE_PAD) ? + node->v_fmt.fmt.pix.sizeimage : + node->v_fmt.fmt.meta.buffersize; + + unicam_wr_dma_addr(dev, addr, size, node->pad_id); +} + +static inline void unicam_schedule_dummy_buffer(struct unicam_node *node) +{ + struct unicam_device *dev = node->dev; + + unicam_dbg(3, dev, "Scheduling dummy buffer for node %d\n", + node->pad_id); + + unicam_wr_dma_addr(dev, node->dummy_buf_dma_addr, DUMMY_BUF_SIZE, + node->pad_id); + node->next_frm = NULL; +} + +static inline void unicam_process_buffer_complete(struct unicam_node *node, + unsigned int sequence) +{ + node->cur_frm->vb.field = node->m_fmt.field; + node->cur_frm->vb.sequence = sequence; + + vb2_buffer_done(&node->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE); +} + +static bool unicam_all_nodes_streaming(struct unicam_device *dev) +{ + bool ret; + + ret = dev->node[IMAGE_PAD].open && dev->node[IMAGE_PAD].streaming; + ret &= !dev->node[METADATA_PAD].open || + dev->node[METADATA_PAD].streaming; + return ret; +} + +static bool unicam_all_nodes_disabled(struct unicam_device *dev) +{ + return !dev->node[IMAGE_PAD].streaming && + !dev->node[METADATA_PAD].streaming; +} + +static void unicam_queue_event_sof(struct unicam_device *unicam) +{ + struct v4l2_event event = { + .type = V4L2_EVENT_FRAME_SYNC, + .u.frame_sync.frame_sequence = unicam->sequence, + }; + + v4l2_event_queue(&unicam->node[IMAGE_PAD].video_dev, &event); +} + +/* + * unicam_isr : ISR handler for unicam capture + * @irq: irq number + * @dev_id: dev_id ptr + * + * It changes status of the captured buffer, takes next buffer from the queue + * and sets its address in unicam registers + */ +static irqreturn_t unicam_isr(int irq, void *dev) +{ + struct unicam_device *unicam = dev; + unsigned int lines_done = unicam_get_lines_done(dev); + unsigned int sequence = unicam->sequence; + unsigned int i; + u32 ista, sta; + u64 ts; + + /* + * Don't service interrupts if not streaming. + * Avoids issues if the VPU should enable the + * peripheral without the kernel knowing (that + * shouldn't happen, but causes issues if it does). + */ + if (unicam_all_nodes_disabled(unicam)) + return IRQ_NONE; + + sta = reg_read(unicam, UNICAM_STA); + /* Write value back to clear the interrupts */ + reg_write(unicam, UNICAM_STA, sta); + + ista = reg_read(unicam, UNICAM_ISTA); + /* Write value back to clear the interrupts */ + reg_write(unicam, UNICAM_ISTA, ista); + + unicam_dbg(3, unicam, "ISR: ISTA: 0x%X, STA: 0x%X, sequence %d, lines done %d", + ista, sta, sequence, lines_done); + + if (!(sta & (UNICAM_IS | UNICAM_PI0))) + return IRQ_HANDLED; + + /* + * We must run the frame end handler first. If we have a valid next_frm + * and we get a simultaneout FE + FS interrupt, running the FS handler + * first would null out the next_frm ptr and we would have lost the + * buffer forever. + */ + if (ista & UNICAM_FEI || sta & UNICAM_PI0) { + /* + * Ensure we have swapped buffers already as we can't + * stop the peripheral. If no buffer is available, use a + * dummy buffer to dump out frames until we get a new buffer + * to use. + */ + for (i = 0; i < ARRAY_SIZE(unicam->node); i++) { + if (!unicam->node[i].streaming) + continue; + + if (unicam->node[i].cur_frm) + unicam_process_buffer_complete(&unicam->node[i], + sequence); + unicam->node[i].cur_frm = unicam->node[i].next_frm; + } + unicam->sequence++; + } + + if (ista & UNICAM_FSI) { + /* + * Timestamp is to be when the first data byte was captured, + * aka frame start. + */ + ts = ktime_get_ns(); + for (i = 0; i < ARRAY_SIZE(unicam->node); i++) { + if (!unicam->node[i].streaming) + continue; + + if (unicam->node[i].cur_frm) + unicam->node[i].cur_frm->vb.vb2_buf.timestamp = + ts; + /* + * Set the next frame output to go to a dummy frame + * if we have not managed to obtain another frame + * from the queue. + */ + unicam_schedule_dummy_buffer(&unicam->node[i]); + } + + unicam_queue_event_sof(unicam); + } + + /* + * Cannot swap buffer at frame end, there may be a race condition + * where the HW does not actually swap it if the new frame has + * already started. + */ + if (ista & (UNICAM_FSI | UNICAM_LCI) && !(ista & UNICAM_FEI)) { + for (i = 0; i < ARRAY_SIZE(unicam->node); i++) { + if (!unicam->node[i].streaming) + continue; + + spin_lock(&unicam->node[i].dma_queue_lock); + if (!list_empty(&unicam->node[i].dma_queue) && + !unicam->node[i].next_frm) + unicam_schedule_next_buffer(&unicam->node[i]); + spin_unlock(&unicam->node[i].dma_queue_lock); + } + } + + if (reg_read(unicam, UNICAM_ICTL) & UNICAM_FCM) { + /* Switch out of trigger mode if selected */ + reg_write_field(unicam, UNICAM_ICTL, 1, UNICAM_TFC); + reg_write_field(unicam, UNICAM_ICTL, 0, UNICAM_FCM); + } + return IRQ_HANDLED; +} + +static int unicam_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; + + strlcpy(cap->driver, UNICAM_MODULE_NAME, sizeof(cap->driver)); + strlcpy(cap->card, UNICAM_MODULE_NAME, sizeof(cap->card)); + + snprintf(cap->bus_info, sizeof(cap->bus_info), + "platform:%s", dev_name(&dev->pdev->dev)); + + cap->capabilities |= V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_META_CAPTURE; + + return 0; +} + +static int unicam_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; + unsigned int index = 0; + unsigned int i; + int ret = 0; + + if (node->pad_id != IMAGE_PAD) + return -EINVAL; + + for (i = 0; !ret && i < MAX_ENUM_MBUS_CODE; i++) { + struct v4l2_subdev_mbus_code_enum mbus_code = { + .index = i, + .pad = IMAGE_PAD, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + const struct unicam_fmt *fmt; + + ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, + NULL, &mbus_code); + if (ret < 0) { + unicam_dbg(2, dev, + "subdev->enum_mbus_code idx %d returned %d - index invalid\n", + i, ret); + return -EINVAL; + } + + fmt = find_format_by_code(mbus_code.code); + if (fmt) { + if (fmt->fourcc) { + if (index == f->index) { + f->pixelformat = fmt->fourcc; + break; + } + index++; + } + if (fmt->repacked_fourcc) { + if (index == f->index) { + f->pixelformat = fmt->repacked_fourcc; + break; + } + index++; + } + } + } + + return 0; +} + +static int unicam_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct v4l2_mbus_framefmt mbus_fmt = {0}; + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; + const struct unicam_fmt *fmt = NULL; + int ret; + + if (node->pad_id != IMAGE_PAD) + return -EINVAL; + + /* + * If a flip has occurred in the sensor, the fmt code might have + * changed. So we will need to re-fetch the format from the subdevice. + */ + ret = __subdev_get_format(dev, &mbus_fmt, node->pad_id); + if (ret) + return -EINVAL; + + /* Find the V4L2 format from mbus code. We must match a known format. */ + fmt = find_format_by_code(mbus_fmt.code); + if (!fmt) + return -EINVAL; + + node->fmt = fmt; + node->v_fmt.fmt.pix.pixelformat = fmt->fourcc; + *f = node->v_fmt; + + return 0; +} + +static +const struct unicam_fmt *get_first_supported_format(struct unicam_device *dev) +{ + struct v4l2_subdev_mbus_code_enum mbus_code; + const struct unicam_fmt *fmt = NULL; + unsigned int i; + int ret; + + for (i = 0; ret != -EINVAL && ret != -ENOIOCTLCMD; ++i) { + memset(&mbus_code, 0, sizeof(mbus_code)); + mbus_code.index = i; + mbus_code.pad = IMAGE_PAD; + mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; + + ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, NULL, + &mbus_code); + if (ret < 0) { + unicam_dbg(2, dev, + "subdev->enum_mbus_code idx %u returned %d - continue\n", + i, ret); + continue; + } + + unicam_dbg(2, dev, "subdev %s: code: 0x%08x idx: %u\n", + dev->sensor->name, mbus_code.code, i); + + fmt = find_format_by_code(mbus_code.code); + unicam_dbg(2, dev, "fmt 0x%08x returned as %p, V4L2 FOURCC 0x%08x, csi_dt 0x%02x\n", + mbus_code.code, fmt, fmt ? fmt->fourcc : 0, + fmt ? fmt->csi_dt : 0); + if (fmt) + return fmt; + } + + return NULL; +} + +static int unicam_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; + struct v4l2_subdev_format sd_fmt = { + .which = V4L2_SUBDEV_FORMAT_TRY, + .pad = IMAGE_PAD + }; + struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; + const struct unicam_fmt *fmt; + int ret; + + if (node->pad_id != IMAGE_PAD) + return -EINVAL; + + fmt = find_format_by_pix(dev, f->fmt.pix.pixelformat); + if (!fmt) { + /* + * Pixel format not supported by unicam. Choose the first + * supported format, and let the sensor choose something else. + */ + unicam_dbg(3, dev, "Fourcc format (0x%08x) not found. Use first format.\n", + f->fmt.pix.pixelformat); + + fmt = &formats[0]; + f->fmt.pix.pixelformat = fmt->fourcc; + } + + v4l2_fill_mbus_format(mbus_fmt, &f->fmt.pix, fmt->code); + /* + * No support for receiving interlaced video, so never + * request it from the sensor subdev. + */ + mbus_fmt->field = V4L2_FIELD_NONE; + + ret = v4l2_subdev_call(dev->sensor, pad, set_fmt, dev->sensor_config, + &sd_fmt); + if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + + if (mbus_fmt->field != V4L2_FIELD_NONE) + unicam_info(dev, "Sensor trying to send interlaced video - results may be unpredictable\n"); + + v4l2_fill_pix_format(&f->fmt.pix, &sd_fmt.format); + if (mbus_fmt->code != fmt->code) { + /* Sensor has returned an alternate format */ + fmt = find_format_by_code(mbus_fmt->code); + if (!fmt) { + /* + * The alternate format is one unicam can't support. + * Find the first format that is supported by both, and + * then set that. + */ + fmt = get_first_supported_format(dev); + mbus_fmt->code = fmt->code; + + ret = v4l2_subdev_call(dev->sensor, pad, set_fmt, + dev->sensor_config, &sd_fmt); + if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + + if (mbus_fmt->field != V4L2_FIELD_NONE) + unicam_info(dev, "Sensor trying to send interlaced video - results may be unpredictable\n"); + + v4l2_fill_pix_format(&f->fmt.pix, &sd_fmt.format); + + if (mbus_fmt->code != fmt->code) { + /* + * We've set a format that the sensor reports + * as being supported, but it refuses to set it. + * Not much else we can do. + * Assume that the sensor driver may accept the + * format when it is set (rather than tried). + */ + unicam_err(dev, "Sensor won't accept default format, and Unicam can't support sensor default\n"); + } + } + + if (fmt->fourcc) + f->fmt.pix.pixelformat = fmt->fourcc; + else + f->fmt.pix.pixelformat = fmt->repacked_fourcc; + } + + return unicam_calc_format_size_bpl(dev, fmt, f); +} + +static int unicam_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; + struct vb2_queue *q = &node->buffer_queue; + struct v4l2_mbus_framefmt mbus_fmt = {0}; + const struct unicam_fmt *fmt; + int ret; + + if (vb2_is_busy(q)) + return -EBUSY; + + ret = unicam_try_fmt_vid_cap(file, priv, f); + if (ret < 0) + return ret; + + fmt = find_format_by_pix(dev, f->fmt.pix.pixelformat); + if (!fmt) { + /* + * Unknown pixel format - adopt a default. + * This shouldn't happen as try_fmt should have resolved any + * issues first. + */ + fmt = get_first_supported_format(dev); + if (!fmt) + /* + * It shouldn't be possible to get here with no + * supported formats + */ + return -EINVAL; + f->fmt.pix.pixelformat = fmt->fourcc; + return -EINVAL; + } + + v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code); + + ret = __subdev_set_format(dev, &mbus_fmt, node->pad_id); + if (ret) { + unicam_dbg(3, dev, "%s __subdev_set_format failed %d\n", + __func__, ret); + return ret; + } + + /* Just double check nothing has gone wrong */ + if (mbus_fmt.code != fmt->code) { + unicam_dbg(3, dev, + "%s subdev changed format on us, this should not happen\n", + __func__); + return -EINVAL; + } + + node->fmt = fmt; + node->v_fmt.fmt.pix.pixelformat = f->fmt.pix.pixelformat; + node->v_fmt.fmt.pix.bytesperline = f->fmt.pix.bytesperline; + unicam_reset_format(node); + + unicam_dbg(3, dev, + "%s %dx%d, mbus_fmt 0x%08X, V4L2 pix 0x%08X.\n", + __func__, node->v_fmt.fmt.pix.width, + node->v_fmt.fmt.pix.height, mbus_fmt.code, + node->v_fmt.fmt.pix.pixelformat); + + *f = node->v_fmt; + + return 0; +} + +static int unicam_enum_fmt_meta_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; + const struct unicam_fmt *fmt; + u32 code; + int ret = 0; + + if (node->pad_id != METADATA_PAD || f->index != 0) + return -EINVAL; + + if (dev->sensor_embedded_data) { + struct v4l2_subdev_mbus_code_enum mbus_code = { + .index = f->index, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .pad = METADATA_PAD, + }; + + ret = v4l2_subdev_call(dev->sensor, pad, enum_mbus_code, NULL, + &mbus_code); + if (ret < 0) { + unicam_dbg(2, dev, + "subdev->enum_mbus_code idx 0 returned %d - index invalid\n", + ret); + return -EINVAL; + } + + code = mbus_code.code; + } else { + code = MEDIA_BUS_FMT_SENSOR_DATA; + } + + fmt = find_format_by_code(code); + if (fmt) + f->pixelformat = fmt->fourcc; + + return 0; +} + +static int unicam_g_fmt_meta_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct unicam_node *node = video_drvdata(file); + + if (node->pad_id != METADATA_PAD) + return -EINVAL; + + *f = node->v_fmt; + + return 0; +} + +static int unicam_queue_setup(struct vb2_queue *vq, + unsigned int *nbuffers, + unsigned int *nplanes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct unicam_node *node = vb2_get_drv_priv(vq); + struct unicam_device *dev = node->dev; + unsigned int size = node->pad_id == IMAGE_PAD ? + node->v_fmt.fmt.pix.sizeimage : + node->v_fmt.fmt.meta.buffersize; + + if (vq->num_buffers + *nbuffers < 3) + *nbuffers = 3 - vq->num_buffers; + + if (*nplanes) { + if (sizes[0] < size) { + unicam_err(dev, "sizes[0] %i < size %u\n", sizes[0], + size); + return -EINVAL; + } + size = sizes[0]; + } + + *nplanes = 1; + sizes[0] = size; + + return 0; +} + +static int unicam_buffer_prepare(struct vb2_buffer *vb) +{ + struct unicam_node *node = vb2_get_drv_priv(vb->vb2_queue); + struct unicam_device *dev = node->dev; + struct unicam_buffer *buf = to_unicam_buffer(vb); + unsigned long size; + + if (WARN_ON(!node->fmt)) + return -EINVAL; + + size = node->pad_id == IMAGE_PAD ? node->v_fmt.fmt.pix.sizeimage : + node->v_fmt.fmt.meta.buffersize; + if (vb2_plane_size(vb, 0) < size) { + unicam_err(dev, "data will not fit into plane (%lu < %lu)\n", + vb2_plane_size(vb, 0), size); + return -EINVAL; + } + + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); + return 0; +} + +static void unicam_buffer_queue(struct vb2_buffer *vb) +{ + struct unicam_node *node = vb2_get_drv_priv(vb->vb2_queue); + struct unicam_buffer *buf = to_unicam_buffer(vb); + unsigned long flags; + + spin_lock_irqsave(&node->dma_queue_lock, flags); + list_add_tail(&buf->list, &node->dma_queue); + spin_unlock_irqrestore(&node->dma_queue_lock, flags); +} + +static void unicam_set_packing_config(struct unicam_device *dev) +{ + u32 pack, unpack; + u32 val; + + if (dev->node[IMAGE_PAD].v_fmt.fmt.pix.pixelformat == + dev->node[IMAGE_PAD].fmt->fourcc) { + unpack = UNICAM_PUM_NONE; + pack = UNICAM_PPM_NONE; + } else { + switch (dev->node[IMAGE_PAD].fmt->depth) { + case 8: + unpack = UNICAM_PUM_UNPACK8; + break; + case 10: + unpack = UNICAM_PUM_UNPACK10; + break; + case 12: + unpack = UNICAM_PUM_UNPACK12; + break; + case 14: + unpack = UNICAM_PUM_UNPACK14; + break; + case 16: + unpack = UNICAM_PUM_UNPACK16; + break; + default: + unpack = UNICAM_PUM_NONE; + break; + } + + /* Repacking is always to 16bpp */ + pack = UNICAM_PPM_PACK16; + } + + val = 0; + set_field(&val, unpack, UNICAM_PUM_MASK); + set_field(&val, pack, UNICAM_PPM_MASK); + reg_write(dev, UNICAM_IPIPE, val); +} + +static void unicam_cfg_image_id(struct unicam_device *dev) +{ + if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) { + /* CSI2 mode, hardcode VC 0 for now. */ + reg_write(dev, UNICAM_IDI0, + (0 << 6) | dev->node[IMAGE_PAD].fmt->csi_dt); + } else { + /* CCP2 mode */ + reg_write(dev, UNICAM_IDI0, + 0x80 | dev->node[IMAGE_PAD].fmt->csi_dt); + } +} + +static void unicam_enable_ed(struct unicam_device *dev) +{ + u32 val = reg_read(dev, UNICAM_DCS); + + set_field(&val, 2, UNICAM_EDL_MASK); + /* Do not wrap at the end of the embedded data buffer */ + set_field(&val, 0, UNICAM_DBOB); + + reg_write(dev, UNICAM_DCS, val); +} + +static void unicam_start_rx(struct unicam_device *dev, dma_addr_t *addr) +{ + int line_int_freq = dev->node[IMAGE_PAD].v_fmt.fmt.pix.height >> 2; + unsigned int size, i; + u32 val; + + if (line_int_freq < 128) + line_int_freq = 128; + + /* Enable lane clocks */ + val = 1; + for (i = 0; i < dev->active_data_lanes; i++) + val = val << 2 | 1; + clk_write(dev, val); + + /* Basic init */ + reg_write(dev, UNICAM_CTRL, UNICAM_MEM); + + /* Enable analogue control, and leave in reset. */ + val = UNICAM_AR; + set_field(&val, 7, UNICAM_CTATADJ_MASK); + set_field(&val, 7, UNICAM_PTATADJ_MASK); + reg_write(dev, UNICAM_ANA, val); + usleep_range(1000, 2000); + + /* Come out of reset */ + reg_write_field(dev, UNICAM_ANA, 0, UNICAM_AR); + + /* Peripheral reset */ + reg_write_field(dev, UNICAM_CTRL, 1, UNICAM_CPR); + reg_write_field(dev, UNICAM_CTRL, 0, UNICAM_CPR); + + reg_write_field(dev, UNICAM_CTRL, 0, UNICAM_CPE); + + /* Enable Rx control. */ + val = reg_read(dev, UNICAM_CTRL); + if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) { + set_field(&val, UNICAM_CPM_CSI2, UNICAM_CPM_MASK); + set_field(&val, UNICAM_DCM_STROBE, UNICAM_DCM_MASK); + } else { + set_field(&val, UNICAM_CPM_CCP2, UNICAM_CPM_MASK); + set_field(&val, dev->bus_flags, UNICAM_DCM_MASK); + } + /* Packet framer timeout */ + set_field(&val, 0xf, UNICAM_PFT_MASK); + set_field(&val, 128, UNICAM_OET_MASK); + reg_write(dev, UNICAM_CTRL, val); + + reg_write(dev, UNICAM_IHWIN, 0); + reg_write(dev, UNICAM_IVWIN, 0); + + /* AXI bus access QoS setup */ + val = reg_read(dev, UNICAM_PRI); + set_field(&val, 0, UNICAM_BL_MASK); + set_field(&val, 0, UNICAM_BS_MASK); + set_field(&val, 0xe, UNICAM_PP_MASK); + set_field(&val, 8, UNICAM_NP_MASK); + set_field(&val, 2, UNICAM_PT_MASK); + set_field(&val, 1, UNICAM_PE); + reg_write(dev, UNICAM_PRI, val); + + reg_write_field(dev, UNICAM_ANA, 0, UNICAM_DDL); + + /* Always start in trigger frame capture mode (UNICAM_FCM set) */ + val = UNICAM_FSIE | UNICAM_FEIE | UNICAM_FCM | UNICAM_IBOB; + set_field(&val, line_int_freq, UNICAM_LCIE_MASK); + reg_write(dev, UNICAM_ICTL, val); + reg_write(dev, UNICAM_STA, UNICAM_STA_MASK_ALL); + reg_write(dev, UNICAM_ISTA, UNICAM_ISTA_MASK_ALL); + + /* tclk_term_en */ + reg_write_field(dev, UNICAM_CLT, 2, UNICAM_CLT1_MASK); + /* tclk_settle */ + reg_write_field(dev, UNICAM_CLT, 6, UNICAM_CLT2_MASK); + /* td_term_en */ + reg_write_field(dev, UNICAM_DLT, 2, UNICAM_DLT1_MASK); + /* ths_settle */ + reg_write_field(dev, UNICAM_DLT, 6, UNICAM_DLT2_MASK); + /* trx_enable */ + reg_write_field(dev, UNICAM_DLT, 0, UNICAM_DLT3_MASK); + + reg_write_field(dev, UNICAM_CTRL, 0, UNICAM_SOE); + + /* Packet compare setup - required to avoid missing frame ends */ + val = 0; + set_field(&val, 1, UNICAM_PCE); + set_field(&val, 1, UNICAM_GI); + set_field(&val, 1, UNICAM_CPH); + set_field(&val, 0, UNICAM_PCVC_MASK); + set_field(&val, 1, UNICAM_PCDT_MASK); + reg_write(dev, UNICAM_CMP0, val); + + /* Enable clock lane and set up terminations */ + val = 0; + if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) { + /* CSI2 */ + set_field(&val, 1, UNICAM_CLE); + set_field(&val, 1, UNICAM_CLLPE); + if (dev->bus_flags & V4L2_MBUS_CSI2_CONTINUOUS_CLOCK) { + set_field(&val, 1, UNICAM_CLTRE); + set_field(&val, 1, UNICAM_CLHSE); + } + } else { + /* CCP2 */ + set_field(&val, 1, UNICAM_CLE); + set_field(&val, 1, UNICAM_CLHSE); + set_field(&val, 1, UNICAM_CLTRE); + } + reg_write(dev, UNICAM_CLK, val); + + /* + * Enable required data lanes with appropriate terminations. + * The same value needs to be written to UNICAM_DATn registers for + * the active lanes, and 0 for inactive ones. + */ + val = 0; + if (dev->bus_type == V4L2_MBUS_CSI2_DPHY) { + /* CSI2 */ + set_field(&val, 1, UNICAM_DLE); + set_field(&val, 1, UNICAM_DLLPE); + if (dev->bus_flags & V4L2_MBUS_CSI2_CONTINUOUS_CLOCK) { + set_field(&val, 1, UNICAM_DLTRE); + set_field(&val, 1, UNICAM_DLHSE); + } + } else { + /* CCP2 */ + set_field(&val, 1, UNICAM_DLE); + set_field(&val, 1, UNICAM_DLHSE); + set_field(&val, 1, UNICAM_DLTRE); + } + reg_write(dev, UNICAM_DAT0, val); + + if (dev->active_data_lanes == 1) + val = 0; + reg_write(dev, UNICAM_DAT1, val); + + if (dev->max_data_lanes > 2) { + /* + * Registers UNICAM_DAT2 and UNICAM_DAT3 only valid if the + * instance supports more than 2 data lanes. + */ + if (dev->active_data_lanes == 2) + val = 0; + reg_write(dev, UNICAM_DAT2, val); + + if (dev->active_data_lanes == 3) + val = 0; + reg_write(dev, UNICAM_DAT3, val); + } + + reg_write(dev, UNICAM_IBLS, + dev->node[IMAGE_PAD].v_fmt.fmt.pix.bytesperline); + size = dev->node[IMAGE_PAD].v_fmt.fmt.pix.sizeimage; + unicam_wr_dma_addr(dev, addr[IMAGE_PAD], size, IMAGE_PAD); + unicam_set_packing_config(dev); + unicam_cfg_image_id(dev); + + val = reg_read(dev, UNICAM_MISC); + set_field(&val, 1, UNICAM_FL0); + set_field(&val, 1, UNICAM_FL1); + reg_write(dev, UNICAM_MISC, val); + + if (dev->node[METADATA_PAD].streaming && dev->sensor_embedded_data) { + size = dev->node[METADATA_PAD].v_fmt.fmt.meta.buffersize; + unicam_enable_ed(dev); + unicam_wr_dma_addr(dev, addr[METADATA_PAD], size, METADATA_PAD); + } + + /* Enable peripheral */ + reg_write_field(dev, UNICAM_CTRL, 1, UNICAM_CPE); + + /* Load image pointers */ + reg_write_field(dev, UNICAM_ICTL, 1, UNICAM_LIP_MASK); + + /* Load embedded data buffer pointers if needed */ + if (dev->node[METADATA_PAD].streaming && dev->sensor_embedded_data) + reg_write_field(dev, UNICAM_DCS, 1, UNICAM_LDP); + + /* + * Enable trigger only for the first frame to + * sync correctly to the FS from the source. + */ + reg_write_field(dev, UNICAM_ICTL, 1, UNICAM_TFC); +} + +static void unicam_disable(struct unicam_device *dev) +{ + /* Analogue lane control disable */ + reg_write_field(dev, UNICAM_ANA, 1, UNICAM_DDL); + + /* Stop the output engine */ + reg_write_field(dev, UNICAM_CTRL, 1, UNICAM_SOE); + + /* Disable the data lanes. */ + reg_write(dev, UNICAM_DAT0, 0); + reg_write(dev, UNICAM_DAT1, 0); + + if (dev->max_data_lanes > 2) { + reg_write(dev, UNICAM_DAT2, 0); + reg_write(dev, UNICAM_DAT3, 0); + } + + /* Peripheral reset */ + reg_write_field(dev, UNICAM_CTRL, 1, UNICAM_CPR); + usleep_range(50, 100); + reg_write_field(dev, UNICAM_CTRL, 0, UNICAM_CPR); + + /* Disable peripheral */ + reg_write_field(dev, UNICAM_CTRL, 0, UNICAM_CPE); + + /* Clear ED setup */ + reg_write(dev, UNICAM_DCS, 0); + + /* Disable all lane clocks */ + clk_write(dev, 0); +} + +static void unicam_return_buffers(struct unicam_node *node) +{ + struct unicam_buffer *buf, *tmp; + unsigned long flags; + + spin_lock_irqsave(&node->dma_queue_lock, flags); + list_for_each_entry_safe(buf, tmp, &node->dma_queue, list) { + list_del(&buf->list); + vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); + } + + if (node->cur_frm) + vb2_buffer_done(&node->cur_frm->vb.vb2_buf, + VB2_BUF_STATE_ERROR); + if (node->next_frm && node->cur_frm != node->next_frm) + vb2_buffer_done(&node->next_frm->vb.vb2_buf, + VB2_BUF_STATE_ERROR); + + node->cur_frm = NULL; + node->next_frm = NULL; + spin_unlock_irqrestore(&node->dma_queue_lock, flags); +} + +static int unicam_start_streaming(struct vb2_queue *vq, unsigned int count) +{ + struct unicam_node *node = vb2_get_drv_priv(vq); + struct unicam_device *dev = node->dev; + dma_addr_t buffer_addr[MAX_NODES] = { 0 }; + unsigned long flags; + unsigned int i; + int ret; + + node->streaming = true; + if (!unicam_all_nodes_streaming(dev)) { + unicam_dbg(3, dev, "Not all nodes are streaming yet."); + return 0; + } + + dev->sequence = 0; + ret = unicam_runtime_get(dev); + if (ret < 0) { + unicam_dbg(3, dev, "unicam_runtime_get failed\n"); + goto err_streaming; + } + + /* + * TODO: Retrieve the number of active data lanes from the connected + * subdevice. + */ + dev->active_data_lanes = dev->max_data_lanes; + + ret = clk_set_rate(dev->clock, 100 * 1000 * 1000); + if (ret) { + unicam_err(dev, "failed to set up clock\n"); + goto err_pm_put; + } + + ret = clk_prepare_enable(dev->clock); + if (ret) { + unicam_err(dev, "Failed to enable CSI clock: %d\n", ret); + goto err_pm_put; + } + + for (i = 0; i < ARRAY_SIZE(dev->node); i++) { + struct unicam_buffer *buf; + + if (!dev->node[i].streaming) + continue; + + spin_lock_irqsave(&dev->node[i].dma_queue_lock, flags); + buf = list_first_entry(&dev->node[i].dma_queue, + struct unicam_buffer, list); + dev->node[i].cur_frm = buf; + dev->node[i].next_frm = buf; + list_del(&buf->list); + spin_unlock_irqrestore(&dev->node[i].dma_queue_lock, flags); + + buffer_addr[i] = + vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); + } + + unicam_start_rx(dev, buffer_addr); + + ret = v4l2_subdev_call(dev->sensor, video, s_stream, 1); + if (ret < 0) { + unicam_err(dev, "stream on failed in subdev\n"); + goto err_disable_unicam; + } + + return 0; + +err_disable_unicam: + unicam_disable(dev); + clk_disable_unprepare(dev->clock); +err_pm_put: + unicam_runtime_put(dev); +err_streaming: + unicam_return_buffers(node); + node->streaming = false; + + return ret; +} + +static void unicam_stop_streaming(struct vb2_queue *vq) +{ + struct unicam_node *node = vb2_get_drv_priv(vq); + struct unicam_device *dev = node->dev; + + node->streaming = false; + + if (node->pad_id == IMAGE_PAD) { + /* + * Stop streaming the sensor and disable the peripheral. + * We cannot continue streaming embedded data with the + * image pad disabled. + */ + if (v4l2_subdev_call(dev->sensor, video, s_stream, 0) < 0) + unicam_err(dev, "stream off failed in subdev\n"); + + unicam_disable(dev); + clk_disable_unprepare(dev->clock); + unicam_runtime_put(dev); + + } else if (node->pad_id == METADATA_PAD) { + /* + * Allow the hardware to spin in the dummy buffer. + * This is only really needed if the embedded data pad is + * disabled before the image pad. + */ + unicam_wr_dma_addr(dev, node->dummy_buf_dma_addr, + DUMMY_BUF_SIZE, METADATA_PAD); + } + + /* Clear all queued buffers for the node */ + unicam_return_buffers(node); +} + +static int unicam_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; + + if (inp->index != 0) + return -EINVAL; + + inp->type = V4L2_INPUT_TYPE_CAMERA; + if (v4l2_subdev_has_op(dev->sensor, video, s_dv_timings)) { + inp->capabilities = V4L2_IN_CAP_DV_TIMINGS; + inp->std = 0; + } else if (v4l2_subdev_has_op(dev->sensor, video, s_std)) { + inp->capabilities = V4L2_IN_CAP_STD; + if (v4l2_subdev_call(dev->sensor, video, g_tvnorms, &inp->std) + < 0) + inp->std = V4L2_STD_ALL; + } else { + inp->capabilities = 0; + inp->std = 0; + } + sprintf(inp->name, "Camera 0"); + return 0; +} + +static int unicam_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + + return 0; +} + +static int unicam_s_input(struct file *file, void *priv, unsigned int i) +{ + /* + * FIXME: Ideally we would like to be able to query the source + * subdevice for information over the input connectors it supports, + * and map that through in to a call to video_ops->s_routing. + * There is no infrastructure support for defining that within + * devicetree at present. Until that is implemented we can't + * map a user physical connector number to s_routing input number. + */ + if (i > 0) + return -EINVAL; + + return 0; +} + +static int unicam_querystd(struct file *file, void *priv, + v4l2_std_id *std) +{ + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; + + return v4l2_subdev_call(dev->sensor, video, querystd, std); +} + +static int unicam_g_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; + + return v4l2_subdev_call(dev->sensor, video, g_std, std); +} + +static int unicam_s_std(struct file *file, void *priv, v4l2_std_id std) +{ + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; + int ret; + v4l2_std_id current_std; + + ret = v4l2_subdev_call(dev->sensor, video, g_std, ¤t_std); + if (ret) + return ret; + + if (std == current_std) + return 0; + + if (vb2_is_busy(&node->buffer_queue)) + return -EBUSY; + + ret = v4l2_subdev_call(dev->sensor, video, s_std, std); + + /* Force recomputation of bytesperline */ + node->v_fmt.fmt.pix.bytesperline = 0; + + unicam_reset_format(node); + + return ret; +} + +static int unicam_s_edid(struct file *file, void *priv, struct v4l2_edid *edid) +{ + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; + + return v4l2_subdev_call(dev->sensor, pad, set_edid, edid); +} + +static int unicam_g_edid(struct file *file, void *priv, struct v4l2_edid *edid) +{ + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; + + return v4l2_subdev_call(dev->sensor, pad, get_edid, edid); +} + +static int unicam_s_selection(struct file *file, void *priv, + struct v4l2_selection *sel) +{ + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; + struct v4l2_subdev_selection sdsel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = sel->target, + .flags = sel->flags, + .r = sel->r, + }; + + return v4l2_subdev_call(dev->sensor, pad, set_selection, NULL, &sdsel); +} + +static int unicam_g_selection(struct file *file, void *priv, + struct v4l2_selection *sel) +{ + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; + struct v4l2_subdev_selection sdsel = { + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + .target = sel->target, + }; + int ret; + + ret = v4l2_subdev_call(dev->sensor, pad, get_selection, NULL, &sdsel); + if (!ret) + sel->r = sdsel.r; + + return ret; +} + +static int unicam_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; + const struct unicam_fmt *fmt; + struct v4l2_subdev_frame_size_enum fse; + int ret; + + /* check for valid format */ + fmt = find_format_by_pix(dev, fsize->pixel_format); + if (!fmt) { + unicam_dbg(3, dev, "Invalid pixel code: %x\n", + fsize->pixel_format); + return -EINVAL; + } + fse.code = fmt->code; + + fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fse.index = fsize->index; + fse.pad = node->pad_id; + + ret = v4l2_subdev_call(dev->sensor, pad, enum_frame_size, NULL, &fse); + if (ret) + return ret; + + unicam_dbg(1, dev, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n", + __func__, fse.index, fse.code, fse.min_width, fse.max_width, + fse.min_height, fse.max_height); + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = fse.max_width; + fsize->discrete.height = fse.max_height; + + return 0; +} + +static int unicam_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fival) +{ + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; + const struct unicam_fmt *fmt; + struct v4l2_subdev_frame_interval_enum fie = { + .index = fival->index, + .width = fival->width, + .height = fival->height, + .which = V4L2_SUBDEV_FORMAT_ACTIVE, + }; + int ret; + + fmt = find_format_by_pix(dev, fival->pixel_format); + if (!fmt) + return -EINVAL; + + fie.code = fmt->code; + ret = v4l2_subdev_call(dev->sensor, pad, enum_frame_interval, + NULL, &fie); + if (ret) + return ret; + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete = fie.interval; + + return 0; +} + +static int unicam_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; + + return v4l2_g_parm_cap(video_devdata(file), dev->sensor, a); +} + +static int unicam_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) +{ + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; + + return v4l2_s_parm_cap(video_devdata(file), dev->sensor, a); +} + +static int unicam_g_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) +{ + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; + + return v4l2_subdev_call(dev->sensor, video, g_dv_timings, timings); +} + +static int unicam_s_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) +{ + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; + struct v4l2_dv_timings current_timings; + int ret; + + ret = v4l2_subdev_call(dev->sensor, video, g_dv_timings, + ¤t_timings); + + if (v4l2_match_dv_timings(timings, ¤t_timings, 0, false)) + return 0; + + if (vb2_is_busy(&node->buffer_queue)) + return -EBUSY; + + ret = v4l2_subdev_call(dev->sensor, video, s_dv_timings, timings); + + /* Force recomputation of bytesperline */ + node->v_fmt.fmt.pix.bytesperline = 0; + + unicam_reset_format(node); + + return ret; +} + +static int unicam_query_dv_timings(struct file *file, void *priv, + struct v4l2_dv_timings *timings) +{ + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; + + return v4l2_subdev_call(dev->sensor, video, query_dv_timings, timings); +} + +static int unicam_enum_dv_timings(struct file *file, void *priv, + struct v4l2_enum_dv_timings *timings) +{ + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; + + return v4l2_subdev_call(dev->sensor, pad, enum_dv_timings, timings); +} + +static int unicam_dv_timings_cap(struct file *file, void *priv, + struct v4l2_dv_timings_cap *cap) +{ + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; + + return v4l2_subdev_call(dev->sensor, pad, dv_timings_cap, cap); +} + +static int unicam_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *sub) +{ + switch (sub->type) { + case V4L2_EVENT_FRAME_SYNC: + return v4l2_event_subscribe(fh, sub, 2, NULL); + case V4L2_EVENT_SOURCE_CHANGE: + return v4l2_event_subscribe(fh, sub, 4, NULL); + } + + return v4l2_ctrl_subscribe_event(fh, sub); +} + +static int unicam_log_status(struct file *file, void *fh) +{ + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; + u32 reg; + + /* status for sub devices */ + v4l2_device_call_all(&dev->v4l2_dev, 0, core, log_status); + + unicam_info(dev, "-----Receiver status-----\n"); + unicam_info(dev, "V4L2 width/height: %ux%u\n", + node->v_fmt.fmt.pix.width, node->v_fmt.fmt.pix.height); + unicam_info(dev, "Mediabus format: %08x\n", node->fmt->code); + unicam_info(dev, "V4L2 format: %08x\n", + node->v_fmt.fmt.pix.pixelformat); + reg = reg_read(dev, UNICAM_IPIPE); + unicam_info(dev, "Unpacking/packing: %u / %u\n", + get_field(reg, UNICAM_PUM_MASK), + get_field(reg, UNICAM_PPM_MASK)); + unicam_info(dev, "----Live data----\n"); + unicam_info(dev, "Programmed stride: %4u\n", + reg_read(dev, UNICAM_IBLS)); + unicam_info(dev, "Detected resolution: %ux%u\n", + reg_read(dev, UNICAM_IHSTA), + reg_read(dev, UNICAM_IVSTA)); + unicam_info(dev, "Write pointer: %08x\n", + reg_read(dev, UNICAM_IBWP)); + + return 0; +} + +static void unicam_notify(struct v4l2_subdev *sd, + unsigned int notification, void *arg) +{ + struct unicam_device *dev = to_unicam_device(sd->v4l2_dev); + + switch (notification) { + case V4L2_DEVICE_NOTIFY_EVENT: + v4l2_event_queue(&dev->node[IMAGE_PAD].video_dev, arg); + break; + default: + break; + } +} + +static const struct vb2_ops unicam_video_qops = { + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, + .queue_setup = unicam_queue_setup, + .buf_prepare = unicam_buffer_prepare, + .buf_queue = unicam_buffer_queue, + .start_streaming = unicam_start_streaming, + .stop_streaming = unicam_stop_streaming, +}; + +/* + * unicam_v4l2_open : This function is based on the v4l2_fh_open helper + * function. It has been augmented to handle sensor subdevice power management, + */ +static int unicam_v4l2_open(struct file *file) +{ + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; + int ret; + + mutex_lock(&node->lock); + + ret = v4l2_fh_open(file); + if (ret) { + unicam_err(dev, "v4l2_fh_open failed\n"); + goto unlock; + } + + node->open++; + + if (!v4l2_fh_is_singular_file(file)) + goto unlock; + + ret = v4l2_subdev_call(dev->sensor, core, s_power, 1); + if (ret < 0 && ret != -ENOIOCTLCMD) { + v4l2_fh_release(file); + node->open--; + goto unlock; + } + + ret = 0; + +unlock: + mutex_unlock(&node->lock); + return ret; +} + +static int unicam_v4l2_release(struct file *file) +{ + struct unicam_node *node = video_drvdata(file); + struct unicam_device *dev = node->dev; + struct v4l2_subdev *sd = dev->sensor; + bool fh_singular; + int ret; + + mutex_lock(&node->lock); + + fh_singular = v4l2_fh_is_singular_file(file); + + ret = _vb2_fop_release(file, NULL); + + if (fh_singular) + v4l2_subdev_call(sd, core, s_power, 0); + + node->open--; + mutex_unlock(&node->lock); + + return ret; +} + +/* unicam capture driver file operations */ +static const struct v4l2_file_operations unicam_fops = { + .owner = THIS_MODULE, + .open = unicam_v4l2_open, + .release = unicam_v4l2_release, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap, +}; + +/* unicam capture ioctl operations */ +static const struct v4l2_ioctl_ops unicam_ioctl_ops = { + .vidioc_querycap = unicam_querycap, + .vidioc_enum_fmt_vid_cap = unicam_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = unicam_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = unicam_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = unicam_try_fmt_vid_cap, + + .vidioc_enum_fmt_meta_cap = unicam_enum_fmt_meta_cap, + .vidioc_g_fmt_meta_cap = unicam_g_fmt_meta_cap, + .vidioc_s_fmt_meta_cap = unicam_g_fmt_meta_cap, + .vidioc_try_fmt_meta_cap = unicam_g_fmt_meta_cap, + + .vidioc_enum_input = unicam_enum_input, + .vidioc_g_input = unicam_g_input, + .vidioc_s_input = unicam_s_input, + + .vidioc_querystd = unicam_querystd, + .vidioc_s_std = unicam_s_std, + .vidioc_g_std = unicam_g_std, + + .vidioc_g_edid = unicam_g_edid, + .vidioc_s_edid = unicam_s_edid, + + .vidioc_enum_framesizes = unicam_enum_framesizes, + .vidioc_enum_frameintervals = unicam_enum_frameintervals, + + .vidioc_g_selection = unicam_g_selection, + .vidioc_s_selection = unicam_s_selection, + + .vidioc_g_parm = unicam_g_parm, + .vidioc_s_parm = unicam_s_parm, + + .vidioc_s_dv_timings = unicam_s_dv_timings, + .vidioc_g_dv_timings = unicam_g_dv_timings, + .vidioc_query_dv_timings = unicam_query_dv_timings, + .vidioc_enum_dv_timings = unicam_enum_dv_timings, + .vidioc_dv_timings_cap = unicam_dv_timings_cap, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_log_status = unicam_log_status, + .vidioc_subscribe_event = unicam_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static int +unicam_async_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) +{ + struct unicam_device *unicam = to_unicam_device(notifier->v4l2_dev); + + if (unicam->sensor) { + unicam_info(unicam, "Rejecting subdev %s (Already set!!)", + subdev->name); + return 0; + } + + unicam->sensor = subdev; + unicam_dbg(1, unicam, "Using sensor %s for capture\n", subdev->name); + + return 0; +} + +static void unicam_release(struct kref *kref) +{ + struct unicam_device *unicam = + container_of(kref, struct unicam_device, kref); + + v4l2_ctrl_handler_free(&unicam->ctrl_handler); + media_device_cleanup(&unicam->mdev); + + if (unicam->sensor_config) + v4l2_subdev_free_pad_config(unicam->sensor_config); + + kfree(unicam); +} + +static void unicam_put(struct unicam_device *unicam) +{ + kref_put(&unicam->kref, unicam_release); +} + +static void unicam_get(struct unicam_device *unicam) +{ + kref_get(&unicam->kref); +} + +static void unicam_node_release(struct video_device *vdev) +{ + struct unicam_node *node = video_get_drvdata(vdev); + + unicam_put(node->dev); +} + +static int register_node(struct unicam_device *unicam, struct unicam_node *node, + enum v4l2_buf_type type, int pad_id) +{ + struct video_device *vdev; + struct vb2_queue *q; + struct v4l2_mbus_framefmt mbus_fmt = {0}; + const struct unicam_fmt *fmt; + int ret; + + if (pad_id == IMAGE_PAD) { + ret = __subdev_get_format(unicam, &mbus_fmt, pad_id); + if (ret) { + unicam_err(unicam, "Failed to get_format - ret %d\n", + ret); + return ret; + } + + fmt = find_format_by_code(mbus_fmt.code); + if (!fmt) { + /* + * Find the first format that the sensor and unicam both + * support + */ + fmt = get_first_supported_format(unicam); + + if (!fmt) + /* No compatible formats */ + return -EINVAL; + + mbus_fmt.code = fmt->code; + ret = __subdev_set_format(unicam, &mbus_fmt, pad_id); + if (ret) + return -EINVAL; + } + if (mbus_fmt.field != V4L2_FIELD_NONE) { + /* Interlaced not supported - disable it now. */ + mbus_fmt.field = V4L2_FIELD_NONE; + ret = __subdev_set_format(unicam, &mbus_fmt, pad_id); + if (ret) + return -EINVAL; + } + + node->v_fmt.fmt.pix.pixelformat = fmt->fourcc ? fmt->fourcc + : fmt->repacked_fourcc; + } else { + /* Fix this node format as embedded data. */ + fmt = find_format_by_code(MEDIA_BUS_FMT_SENSOR_DATA); + node->v_fmt.fmt.meta.dataformat = fmt->fourcc; + } + + node->dev = unicam; + node->pad_id = pad_id; + node->fmt = fmt; + + /* Read current subdev format */ + unicam_reset_format(node); + + if (v4l2_subdev_has_op(unicam->sensor, video, s_std)) { + v4l2_std_id tvnorms; + + if (WARN_ON(!v4l2_subdev_has_op(unicam->sensor, video, + g_tvnorms))) + /* + * Subdevice should not advertise s_std but not + * g_tvnorms + */ + return -EINVAL; + + ret = v4l2_subdev_call(unicam->sensor, video, + g_tvnorms, &tvnorms); + if (WARN_ON(ret)) + return -EINVAL; + node->video_dev.tvnorms |= tvnorms; + } + + spin_lock_init(&node->dma_queue_lock); + mutex_init(&node->lock); + + vdev = &node->video_dev; + if (pad_id == IMAGE_PAD) { + /* Add controls from the subdevice */ + ret = v4l2_ctrl_add_handler(&unicam->ctrl_handler, + unicam->sensor->ctrl_handler, NULL, + true); + if (ret < 0) + return ret; + + /* + * If the sensor subdevice has any controls, associate the node + * with the ctrl handler to allow access from userland. + */ + if (!list_empty(&unicam->ctrl_handler.ctrls)) + vdev->ctrl_handler = &unicam->ctrl_handler; + } + + q = &node->buffer_queue; + q->type = type; + q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; + q->drv_priv = node; + q->ops = &unicam_video_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct unicam_buffer); + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->lock = &node->lock; + q->min_buffers_needed = 2; + q->dev = &unicam->pdev->dev; + + ret = vb2_queue_init(q); + if (ret) { + unicam_err(unicam, "vb2_queue_init() failed\n"); + return ret; + } + + INIT_LIST_HEAD(&node->dma_queue); + + vdev->release = unicam_node_release; + vdev->fops = &unicam_fops; + vdev->ioctl_ops = &unicam_ioctl_ops; + vdev->v4l2_dev = &unicam->v4l2_dev; + vdev->vfl_dir = VFL_DIR_RX; + vdev->queue = q; + vdev->lock = &node->lock; + vdev->device_caps = (pad_id == IMAGE_PAD) ? + (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING) : + (V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING); + + /* Define the device names */ + snprintf(vdev->name, sizeof(vdev->name), "%s-%s", UNICAM_MODULE_NAME, + pad_id == IMAGE_PAD ? "image" : "embedded"); + + video_set_drvdata(vdev, node); + if (pad_id == IMAGE_PAD) + vdev->entity.flags |= MEDIA_ENT_FL_DEFAULT; + node->pad.flags = MEDIA_PAD_FL_SINK; + media_entity_pads_init(&vdev->entity, 1, &node->pad); + + node->dummy_buf_cpu_addr = dma_alloc_coherent(&unicam->pdev->dev, + DUMMY_BUF_SIZE, + &node->dummy_buf_dma_addr, + GFP_KERNEL); + if (!node->dummy_buf_cpu_addr) { + unicam_err(unicam, "Unable to allocate dummy buffer.\n"); + return -ENOMEM; + } + + if (pad_id == METADATA_PAD) { + v4l2_disable_ioctl(vdev, VIDIOC_DQEVENT); + v4l2_disable_ioctl(vdev, VIDIOC_SUBSCRIBE_EVENT); + v4l2_disable_ioctl(vdev, VIDIOC_UNSUBSCRIBE_EVENT); + } + if (pad_id == METADATA_PAD || + !v4l2_subdev_has_op(unicam->sensor, video, s_std)) { + v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_STD); + v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_STD); + v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUMSTD); + } + if (pad_id == METADATA_PAD || + !v4l2_subdev_has_op(unicam->sensor, video, querystd)) + v4l2_disable_ioctl(&node->video_dev, VIDIOC_QUERYSTD); + if (pad_id == METADATA_PAD || + !v4l2_subdev_has_op(unicam->sensor, video, s_dv_timings)) { + v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_EDID); + v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_EDID); + v4l2_disable_ioctl(&node->video_dev, VIDIOC_DV_TIMINGS_CAP); + v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_DV_TIMINGS); + v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_DV_TIMINGS); + v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUM_DV_TIMINGS); + v4l2_disable_ioctl(&node->video_dev, VIDIOC_QUERY_DV_TIMINGS); + } + if (pad_id == METADATA_PAD || + !v4l2_subdev_has_op(unicam->sensor, pad, enum_frame_interval)) + v4l2_disable_ioctl(&node->video_dev, + VIDIOC_ENUM_FRAMEINTERVALS); + if (pad_id == METADATA_PAD || + !v4l2_subdev_has_op(unicam->sensor, video, g_frame_interval)) + v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_PARM); + if (pad_id == METADATA_PAD || + !v4l2_subdev_has_op(unicam->sensor, video, s_frame_interval)) + v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_PARM); + + if (pad_id == METADATA_PAD || + !v4l2_subdev_has_op(unicam->sensor, pad, enum_frame_size)) + v4l2_disable_ioctl(&node->video_dev, VIDIOC_ENUM_FRAMESIZES); + + if (node->pad_id == METADATA_PAD || + !v4l2_subdev_has_op(unicam->sensor, pad, set_selection)) + v4l2_disable_ioctl(&node->video_dev, VIDIOC_S_SELECTION); + + if (node->pad_id == METADATA_PAD || + !v4l2_subdev_has_op(unicam->sensor, pad, get_selection)) + v4l2_disable_ioctl(&node->video_dev, VIDIOC_G_SELECTION); + + ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); + if (ret) { + unicam_err(unicam, "Unable to register video device %s\n", + vdev->name); + return ret; + } + + /* + * Acquire a reference to unicam, which will be released when the video + * device will be unregistered and userspace will have closed all open + * file handles. + */ + unicam_get(unicam); + node->registered = true; + + if (pad_id != METADATA_PAD || unicam->sensor_embedded_data) { + ret = media_create_pad_link(&unicam->sensor->entity, pad_id, + &node->video_dev.entity, 0, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (ret) + unicam_err(unicam, "Unable to create pad link for %s\n", + vdev->name); + } + + return ret; +} + +static void unregister_nodes(struct unicam_device *unicam) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(unicam->node); i++) { + struct unicam_node *node = &unicam->node[i]; + + if (node->dummy_buf_cpu_addr) { + dma_free_coherent(&unicam->pdev->dev, DUMMY_BUF_SIZE, + node->dummy_buf_cpu_addr, + node->dummy_buf_dma_addr); + } + + if (node->registered) { + node->registered = false; + video_unregister_device(&node->video_dev); + } + } +} + +static int unicam_probe_complete(struct unicam_device *unicam) +{ + int ret; + + unicam->v4l2_dev.notify = unicam_notify; + + unicam->sensor_config = v4l2_subdev_alloc_pad_config(unicam->sensor); + if (!unicam->sensor_config) + return -ENOMEM; + + unicam->sensor_embedded_data = (unicam->sensor->entity.num_pads >= 2); + + ret = register_node(unicam, &unicam->node[IMAGE_PAD], + V4L2_BUF_TYPE_VIDEO_CAPTURE, IMAGE_PAD); + if (ret) { + unicam_err(unicam, "Unable to register image video device.\n"); + goto unregister; + } + + ret = register_node(unicam, &unicam->node[METADATA_PAD], + V4L2_BUF_TYPE_META_CAPTURE, METADATA_PAD); + if (ret) { + unicam_err(unicam, "Unable to register metadata video device.\n"); + goto unregister; + } + + ret = v4l2_device_register_ro_subdev_nodes(&unicam->v4l2_dev); + if (ret) { + unicam_err(unicam, "Unable to register subdev nodes.\n"); + goto unregister; + } + + /* + * Release the initial reference, all references are now owned by the + * video devices. + */ + unicam_put(unicam); + return 0; + +unregister: + unregister_nodes(unicam); + unicam_put(unicam); + + return ret; +} + +static int unicam_async_complete(struct v4l2_async_notifier *notifier) +{ + struct unicam_device *unicam = to_unicam_device(notifier->v4l2_dev); + + return unicam_probe_complete(unicam); +} + +static const struct v4l2_async_notifier_operations unicam_async_ops = { + .bound = unicam_async_bound, + .complete = unicam_async_complete, +}; + +static int of_unicam_connect_subdevs(struct unicam_device *dev) +{ + struct platform_device *pdev = dev->pdev; + struct v4l2_fwnode_endpoint ep = { 0 }; + struct device_node *ep_node; + struct device_node *sensor_node; + unsigned int lane; + int ret = -EINVAL; + + if (of_property_read_u32(pdev->dev.of_node, "brcm,num-data-lanes", + &dev->max_data_lanes) < 0) { + unicam_err(dev, "number of data lanes not set\n"); + return -EINVAL; + } + + /* Get the local endpoint and remote device. */ + ep_node = of_graph_get_next_endpoint(pdev->dev.of_node, NULL); + if (!ep_node) { + unicam_dbg(3, dev, "can't get next endpoint\n"); + return -EINVAL; + } + + unicam_dbg(3, dev, "ep_node is %pOF\n", ep_node); + + sensor_node = of_graph_get_remote_port_parent(ep_node); + if (!sensor_node) { + unicam_dbg(3, dev, "can't get remote parent\n"); + goto cleanup_exit; + } + + unicam_dbg(1, dev, "found subdevice %pOF\n", sensor_node); + + /* Parse the local endpoint and validate its configuration. */ + v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), &ep); + + unicam_dbg(3, dev, "parsed local endpoint, bus_type %u\n", + ep.bus_type); + + dev->bus_type = ep.bus_type; + + switch (ep.bus_type) { + case V4L2_MBUS_CSI2_DPHY: + switch (ep.bus.mipi_csi2.num_data_lanes) { + case 1: + case 2: + case 4: + break; + + default: + unicam_err(dev, "subdevice %pOF: %u data lanes not supported\n", + sensor_node, + ep.bus.mipi_csi2.num_data_lanes); + goto cleanup_exit; + } + + for (lane = 0; lane < ep.bus.mipi_csi2.num_data_lanes; lane++) { + if (ep.bus.mipi_csi2.data_lanes[lane] != lane + 1) { + unicam_err(dev, "subdevice %pOF: data lanes reordering not supported\n", + sensor_node); + goto cleanup_exit; + } + } + + if (ep.bus.mipi_csi2.num_data_lanes > dev->max_data_lanes) { + unicam_err(dev, "subdevice requires %u data lanes when %u are supported\n", + ep.bus.mipi_csi2.num_data_lanes, + dev->max_data_lanes); + } + + dev->max_data_lanes = ep.bus.mipi_csi2.num_data_lanes; + dev->bus_flags = ep.bus.mipi_csi2.flags; + + break; + + case V4L2_MBUS_CCP2: + if (ep.bus.mipi_csi1.clock_lane != 0 || + ep.bus.mipi_csi1.data_lane != 1) { + unicam_err(dev, "subdevice %pOF: unsupported lanes configuration\n", + sensor_node); + goto cleanup_exit; + } + + dev->max_data_lanes = 1; + dev->bus_flags = ep.bus.mipi_csi1.strobe; + break; + + default: + /* Unsupported bus type */ + unicam_err(dev, "subdevice %pOF: unsupported bus type %u\n", + sensor_node, ep.bus_type); + goto cleanup_exit; + } + + unicam_dbg(3, dev, "subdevice %pOF: %s bus, %u data lanes, flags=0x%08x\n", + sensor_node, + dev->bus_type == V4L2_MBUS_CSI2_DPHY ? "CSI-2" : "CCP2", + dev->max_data_lanes, dev->bus_flags); + + /* Initialize and register the async notifier. */ + v4l2_async_notifier_init(&dev->notifier); + dev->notifier.ops = &unicam_async_ops; + + dev->asd.match_type = V4L2_ASYNC_MATCH_FWNODE; + dev->asd.match.fwnode = of_fwnode_handle(sensor_node); + ret = v4l2_async_notifier_add_subdev(&dev->notifier, &dev->asd); + if (ret) { + unicam_err(dev, "Error adding subdevice: %d\n", ret); + goto cleanup_exit; + } + + ret = v4l2_async_notifier_register(&dev->v4l2_dev, &dev->notifier); + if (ret) { + unicam_err(dev, "Error registering async notifier: %d\n", ret); + ret = -EINVAL; + } + +cleanup_exit: + of_node_put(sensor_node); + of_node_put(ep_node); + + return ret; +} + +static int unicam_probe(struct platform_device *pdev) +{ + struct unicam_device *unicam; + int ret; + + unicam = kzalloc(sizeof(*unicam), GFP_KERNEL); + if (!unicam) + return -ENOMEM; + + kref_init(&unicam->kref); + unicam->pdev = pdev; + + unicam->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(unicam->base)) { + unicam_err(unicam, "Failed to get main io block\n"); + ret = PTR_ERR(unicam->base); + goto err_unicam_put; + } + + unicam->clk_gate_base = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(unicam->clk_gate_base)) { + unicam_err(unicam, "Failed to get 2nd io block\n"); + ret = PTR_ERR(unicam->clk_gate_base); + goto err_unicam_put; + } + + unicam->clock = devm_clk_get(&pdev->dev, "lp"); + if (IS_ERR(unicam->clock)) { + unicam_err(unicam, "Failed to get clock\n"); + ret = PTR_ERR(unicam->clock); + goto err_unicam_put; + } + + ret = platform_get_irq(pdev, 0); + if (ret <= 0) { + dev_err(&pdev->dev, "No IRQ resource\n"); + ret = -EINVAL; + goto err_unicam_put; + } + + ret = devm_request_irq(&pdev->dev, ret, unicam_isr, 0, + "unicam_capture0", unicam); + if (ret) { + dev_err(&pdev->dev, "Unable to request interrupt\n"); + ret = -EINVAL; + goto err_unicam_put; + } + + unicam->mdev.dev = &pdev->dev; + strscpy(unicam->mdev.model, UNICAM_MODULE_NAME, + sizeof(unicam->mdev.model)); + strscpy(unicam->mdev.serial, "", sizeof(unicam->mdev.serial)); + snprintf(unicam->mdev.bus_info, sizeof(unicam->mdev.bus_info), + "platform:%s", dev_name(&pdev->dev)); + unicam->mdev.hw_revision = 0; + + media_device_init(&unicam->mdev); + + unicam->v4l2_dev.mdev = &unicam->mdev; + + ret = v4l2_device_register(&pdev->dev, &unicam->v4l2_dev); + if (ret) { + unicam_err(unicam, + "Unable to register v4l2 device.\n"); + goto err_unicam_put; + } + + ret = media_device_register(&unicam->mdev); + if (ret < 0) { + unicam_err(unicam, + "Unable to register media-controller device.\n"); + goto err_v4l2_unregister; + } + + /* Reserve space for the controls */ + ret = v4l2_ctrl_handler_init(&unicam->ctrl_handler, 16); + if (ret < 0) + goto err_media_unregister; + + /* set the driver data in platform device */ + platform_set_drvdata(pdev, unicam); + + ret = of_unicam_connect_subdevs(unicam); + if (ret) { + dev_err(&pdev->dev, "Failed to connect subdevs\n"); + goto err_media_unregister; + } + + /* Enable the block power domain */ + pm_runtime_enable(&pdev->dev); + + return 0; + +err_media_unregister: + media_device_unregister(&unicam->mdev); +err_v4l2_unregister: + v4l2_device_unregister(&unicam->v4l2_dev); +err_unicam_put: + unicam_put(unicam); + + return ret; +} + +static int unicam_remove(struct platform_device *pdev) +{ + struct unicam_device *unicam = platform_get_drvdata(pdev); + + unicam_dbg(2, unicam, "%s\n", __func__); + + v4l2_async_notifier_unregister(&unicam->notifier); + v4l2_device_unregister(&unicam->v4l2_dev); + media_device_unregister(&unicam->mdev); + unregister_nodes(unicam); + + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static const struct of_device_id unicam_of_match[] = { + { .compatible = "brcm,bcm2835-unicam", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, unicam_of_match); + +static struct platform_driver unicam_driver = { + .probe = unicam_probe, + .remove = unicam_remove, + .driver = { + .name = UNICAM_MODULE_NAME, + .of_match_table = of_match_ptr(unicam_of_match), + }, +}; + +module_platform_driver(unicam_driver); + +MODULE_AUTHOR("Dave Stevenson "); +MODULE_DESCRIPTION("BCM2835 Unicam driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(UNICAM_VERSION); diff --git a/drivers/media/platform/bcm2835/vc4-regs-unicam.h b/drivers/media/platform/bcm2835/vc4-regs-unicam.h new file mode 100644 index 000000000000..ae059a171d0f --- /dev/null +++ b/drivers/media/platform/bcm2835/vc4-regs-unicam.h @@ -0,0 +1,253 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/* + * Copyright (C) 2017-2020 Raspberry Pi Trading. + * Dave Stevenson + */ + +#ifndef VC4_REGS_UNICAM_H +#define VC4_REGS_UNICAM_H + +/* + * The following values are taken from files found within the code drop + * made by Broadcom for the BCM21553 Graphics Driver, predominantly in + * brcm_usrlib/dag/vmcsx/vcinclude/hardware_vc4.h. + * They have been modified to be only the register offset. + */ +#define UNICAM_CTRL 0x000 +#define UNICAM_STA 0x004 +#define UNICAM_ANA 0x008 +#define UNICAM_PRI 0x00c +#define UNICAM_CLK 0x010 +#define UNICAM_CLT 0x014 +#define UNICAM_DAT0 0x018 +#define UNICAM_DAT1 0x01c +#define UNICAM_DAT2 0x020 +#define UNICAM_DAT3 0x024 +#define UNICAM_DLT 0x028 +#define UNICAM_CMP0 0x02c +#define UNICAM_CMP1 0x030 +#define UNICAM_CAP0 0x034 +#define UNICAM_CAP1 0x038 +#define UNICAM_ICTL 0x100 +#define UNICAM_ISTA 0x104 +#define UNICAM_IDI0 0x108 +#define UNICAM_IPIPE 0x10c +#define UNICAM_IBSA0 0x110 +#define UNICAM_IBEA0 0x114 +#define UNICAM_IBLS 0x118 +#define UNICAM_IBWP 0x11c +#define UNICAM_IHWIN 0x120 +#define UNICAM_IHSTA 0x124 +#define UNICAM_IVWIN 0x128 +#define UNICAM_IVSTA 0x12c +#define UNICAM_ICC 0x130 +#define UNICAM_ICS 0x134 +#define UNICAM_IDC 0x138 +#define UNICAM_IDPO 0x13c +#define UNICAM_IDCA 0x140 +#define UNICAM_IDCD 0x144 +#define UNICAM_IDS 0x148 +#define UNICAM_DCS 0x200 +#define UNICAM_DBSA0 0x204 +#define UNICAM_DBEA0 0x208 +#define UNICAM_DBWP 0x20c +#define UNICAM_DBCTL 0x300 +#define UNICAM_IBSA1 0x304 +#define UNICAM_IBEA1 0x308 +#define UNICAM_IDI1 0x30c +#define UNICAM_DBSA1 0x310 +#define UNICAM_DBEA1 0x314 +#define UNICAM_MISC 0x400 + +/* + * The following bitmasks are from the kernel released by Broadcom + * for Android - https://android.googlesource.com/kernel/bcm/ + * The Rhea, Hawaii, and Java chips all contain the same VideoCore4 + * Unicam block as BCM2835, as defined in eg + * arch/arm/mach-rhea/include/mach/rdb_A0/brcm_rdb_cam.h and similar. + * Values reworked to use the kernel BIT and GENMASK macros. + * + * Some of the bit mnenomics have been amended to match the datasheet. + */ +/* UNICAM_CTRL Register */ +#define UNICAM_CPE BIT(0) +#define UNICAM_MEM BIT(1) +#define UNICAM_CPR BIT(2) +#define UNICAM_CPM_MASK GENMASK(3, 3) +#define UNICAM_CPM_CSI2 0 +#define UNICAM_CPM_CCP2 1 +#define UNICAM_SOE BIT(4) +#define UNICAM_DCM_MASK GENMASK(5, 5) +#define UNICAM_DCM_STROBE 0 +#define UNICAM_DCM_DATA 1 +#define UNICAM_SLS BIT(6) +#define UNICAM_PFT_MASK GENMASK(11, 8) +#define UNICAM_OET_MASK GENMASK(20, 12) + +/* UNICAM_STA Register */ +#define UNICAM_SYN BIT(0) +#define UNICAM_CS BIT(1) +#define UNICAM_SBE BIT(2) +#define UNICAM_PBE BIT(3) +#define UNICAM_HOE BIT(4) +#define UNICAM_PLE BIT(5) +#define UNICAM_SSC BIT(6) +#define UNICAM_CRCE BIT(7) +#define UNICAM_OES BIT(8) +#define UNICAM_IFO BIT(9) +#define UNICAM_OFO BIT(10) +#define UNICAM_BFO BIT(11) +#define UNICAM_DL BIT(12) +#define UNICAM_PS BIT(13) +#define UNICAM_IS BIT(14) +#define UNICAM_PI0 BIT(15) +#define UNICAM_PI1 BIT(16) +#define UNICAM_FSI_S BIT(17) +#define UNICAM_FEI_S BIT(18) +#define UNICAM_LCI_S BIT(19) +#define UNICAM_BUF0_RDY BIT(20) +#define UNICAM_BUF0_NO BIT(21) +#define UNICAM_BUF1_RDY BIT(22) +#define UNICAM_BUF1_NO BIT(23) +#define UNICAM_DI BIT(24) + +#define UNICAM_STA_MASK_ALL \ + (UNICAM_DL + \ + UNICAM_SBE + \ + UNICAM_PBE + \ + UNICAM_HOE + \ + UNICAM_PLE + \ + UNICAM_SSC + \ + UNICAM_CRCE + \ + UNICAM_IFO + \ + UNICAM_OFO + \ + UNICAM_PS + \ + UNICAM_PI0 + \ + UNICAM_PI1) + +/* UNICAM_ANA Register */ +#define UNICAM_APD BIT(0) +#define UNICAM_BPD BIT(1) +#define UNICAM_AR BIT(2) +#define UNICAM_DDL BIT(3) +#define UNICAM_CTATADJ_MASK GENMASK(7, 4) +#define UNICAM_PTATADJ_MASK GENMASK(11, 8) + +/* UNICAM_PRI Register */ +#define UNICAM_PE BIT(0) +#define UNICAM_PT_MASK GENMASK(2, 1) +#define UNICAM_NP_MASK GENMASK(7, 4) +#define UNICAM_PP_MASK GENMASK(11, 8) +#define UNICAM_BS_MASK GENMASK(15, 12) +#define UNICAM_BL_MASK GENMASK(17, 16) + +/* UNICAM_CLK Register */ +#define UNICAM_CLE BIT(0) +#define UNICAM_CLPD BIT(1) +#define UNICAM_CLLPE BIT(2) +#define UNICAM_CLHSE BIT(3) +#define UNICAM_CLTRE BIT(4) +#define UNICAM_CLAC_MASK GENMASK(8, 5) +#define UNICAM_CLSTE BIT(29) + +/* UNICAM_CLT Register */ +#define UNICAM_CLT1_MASK GENMASK(7, 0) +#define UNICAM_CLT2_MASK GENMASK(15, 8) + +/* UNICAM_DATn Registers */ +#define UNICAM_DLE BIT(0) +#define UNICAM_DLPD BIT(1) +#define UNICAM_DLLPE BIT(2) +#define UNICAM_DLHSE BIT(3) +#define UNICAM_DLTRE BIT(4) +#define UNICAM_DLSM BIT(5) +#define UNICAM_DLFO BIT(28) +#define UNICAM_DLSTE BIT(29) + +#define UNICAM_DAT_MASK_ALL (UNICAM_DLSTE + UNICAM_DLFO) + +/* UNICAM_DLT Register */ +#define UNICAM_DLT1_MASK GENMASK(7, 0) +#define UNICAM_DLT2_MASK GENMASK(15, 8) +#define UNICAM_DLT3_MASK GENMASK(23, 16) + +/* UNICAM_ICTL Register */ +#define UNICAM_FSIE BIT(0) +#define UNICAM_FEIE BIT(1) +#define UNICAM_IBOB BIT(2) +#define UNICAM_FCM BIT(3) +#define UNICAM_TFC BIT(4) +#define UNICAM_LIP_MASK GENMASK(6, 5) +#define UNICAM_LCIE_MASK GENMASK(28, 16) + +/* UNICAM_IDI0/1 Register */ +#define UNICAM_ID0_MASK GENMASK(7, 0) +#define UNICAM_ID1_MASK GENMASK(15, 8) +#define UNICAM_ID2_MASK GENMASK(23, 16) +#define UNICAM_ID3_MASK GENMASK(31, 24) + +/* UNICAM_ISTA Register */ +#define UNICAM_FSI BIT(0) +#define UNICAM_FEI BIT(1) +#define UNICAM_LCI BIT(2) + +#define UNICAM_ISTA_MASK_ALL (UNICAM_FSI + UNICAM_FEI + UNICAM_LCI) + +/* UNICAM_IPIPE Register */ +#define UNICAM_PUM_MASK GENMASK(2, 0) + /* Unpacking modes */ + #define UNICAM_PUM_NONE 0 + #define UNICAM_PUM_UNPACK6 1 + #define UNICAM_PUM_UNPACK7 2 + #define UNICAM_PUM_UNPACK8 3 + #define UNICAM_PUM_UNPACK10 4 + #define UNICAM_PUM_UNPACK12 5 + #define UNICAM_PUM_UNPACK14 6 + #define UNICAM_PUM_UNPACK16 7 +#define UNICAM_DDM_MASK GENMASK(6, 3) +#define UNICAM_PPM_MASK GENMASK(9, 7) + /* Packing modes */ + #define UNICAM_PPM_NONE 0 + #define UNICAM_PPM_PACK8 1 + #define UNICAM_PPM_PACK10 2 + #define UNICAM_PPM_PACK12 3 + #define UNICAM_PPM_PACK14 4 + #define UNICAM_PPM_PACK16 5 +#define UNICAM_DEM_MASK GENMASK(11, 10) +#define UNICAM_DEBL_MASK GENMASK(14, 12) +#define UNICAM_ICM_MASK GENMASK(16, 15) +#define UNICAM_IDM_MASK GENMASK(17, 17) + +/* UNICAM_ICC Register */ +#define UNICAM_ICFL_MASK GENMASK(4, 0) +#define UNICAM_ICFH_MASK GENMASK(9, 5) +#define UNICAM_ICST_MASK GENMASK(12, 10) +#define UNICAM_ICLT_MASK GENMASK(15, 13) +#define UNICAM_ICLL_MASK GENMASK(31, 16) + +/* UNICAM_DCS Register */ +#define UNICAM_DIE BIT(0) +#define UNICAM_DIM BIT(1) +#define UNICAM_DBOB BIT(3) +#define UNICAM_FDE BIT(4) +#define UNICAM_LDP BIT(5) +#define UNICAM_EDL_MASK GENMASK(15, 8) + +/* UNICAM_DBCTL Register */ +#define UNICAM_DBEN BIT(0) +#define UNICAM_BUF0_IE BIT(1) +#define UNICAM_BUF1_IE BIT(2) + +/* UNICAM_CMP[0,1] register */ +#define UNICAM_PCE BIT(31) +#define UNICAM_GI BIT(9) +#define UNICAM_CPH BIT(8) +#define UNICAM_PCVC_MASK GENMASK(7, 6) +#define UNICAM_PCDT_MASK GENMASK(5, 0) + +/* UNICAM_MISC register */ +#define UNICAM_FL0 BIT(6) +#define UNICAM_FL1 BIT(9) + +#endif From patchwork Mon May 4 09:25:42 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525259 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id CD6D5139A for ; Mon, 4 May 2020 09:26:31 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id ABE9A20757 for ; Mon, 4 May 2020 09:26:31 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Rreg+TFq" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728353AbgEDJ0a (ORCPT ); Mon, 4 May 2020 05:26:30 -0400 Received: from perceval.ideasonboard.com ([213.167.242.64]:56720 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728335AbgEDJ03 (ORCPT ); Mon, 4 May 2020 05:26:29 -0400 Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 5B68D304; Mon, 4 May 2020 11:26:27 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584387; bh=RKPAmP7XWHNpx3GJUpasLkGICwp8lvQ7BhqDmWnWedw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Rreg+TFq33wtlSn4DpZBUxKz8//9qkaGSfpwSf/Cz8ZkbeWqMNMqMi9MA5UGuKm16 6GyTHry4F0jqCFC3DwuC1FLCfeINCcUt8v7bUxSxFkGUnfE+hT9jiuINbfIU/CpOp0 BV6UWo9Mfmqhgq3sqa6FpNUR26g6+oc1Mi0NmGBM= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson Subject: [PATCH v2 05/34] ARM: dts: bcm2711: Add Unicam DT nodes Date: Mon, 4 May 2020 12:25:42 +0300 Message-Id: <20200504092611.9798-6-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org The Unicam IP cores are CSI-2/CCP2 receivers. They are part of VC4, but can be controlled directly from the operating system without going through the VPU firmware. Exposes them as DT nodes. Signed-off-by: Laurent Pinchart --- arch/arm/boot/dts/bcm2711.dtsi | 43 ++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/arch/arm/boot/dts/bcm2711.dtsi b/arch/arm/boot/dts/bcm2711.dtsi index a91cf68e3c4c..0a4d75a64e7c 100644 --- a/arch/arm/boot/dts/bcm2711.dtsi +++ b/arch/arm/boot/dts/bcm2711.dtsi @@ -2,6 +2,7 @@ #include "bcm283x.dtsi" #include +#include #include / { @@ -244,6 +245,48 @@ pwm1: pwm@7e20c800 { hvs@7e400000 { interrupts = ; }; + + csi0: csi@7e800000 { + compatible = "brcm,bcm2835-unicam"; + reg = <0x7e800000 0x800>, + <0x7e802000 0x4>; + interrupts = ; + clocks = <&clocks BCM2835_CLOCK_CAM0>; + clock-names = "lp"; + power-domains = <&power RPI_POWER_DOMAIN_UNICAM0>; + brcm,num-data-lanes = <2>; + #address-cells = <1>; + #size-cells = <0>; + #clock-cells = <1>; + status = "disabled"; + + port { + endpoint { + data-lanes = <1 2>; + }; + }; + }; + + csi1: csi@7e801000 { + compatible = "brcm,bcm2835-unicam"; + reg = <0x7e801000 0x800>, + <0x7e802004 0x4>; + interrupts = ; + clocks = <&clocks BCM2835_CLOCK_CAM1>; + clock-names = "lp"; + power-domains = <&power RPI_POWER_DOMAIN_UNICAM1>; + brcm,num-data-lanes = <4>; + #address-cells = <1>; + #size-cells = <0>; + #clock-cells = <1>; + status = "disabled"; + + port { + endpoint { + data-lanes = <1 2 3 4>; + }; + }; + }; }; /* From patchwork Mon May 4 09:25:43 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525261 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 6AAF617EF for ; Mon, 4 May 2020 09:26:32 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 446E420735 for ; Mon, 4 May 2020 09:26:32 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="NCrayBmW" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728357AbgEDJ0b (ORCPT ); Mon, 4 May 2020 05:26:31 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33142 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-FAIL-OK-FAIL) by vger.kernel.org with ESMTP id S1728335AbgEDJ0a (ORCPT ); Mon, 4 May 2020 05:26:30 -0400 Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5D644C061A0E for ; Mon, 4 May 2020 02:26:30 -0700 (PDT) Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id E7A0311F9; Mon, 4 May 2020 11:26:27 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584388; bh=4wxSh//eIwohyfUW+HT4R3mNFJyqW79vutVZ6KGvSf8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=NCrayBmWDRXAXWn9R+bUzGWWx+7H3QSe8xNJO7gTztL2lN3q250To0fSecHDCZblX EpLaGmmrXcWD4ZtJ2xcdBvX3AzkuIumFy3uwG3SvizkvPII39Ki6T+ipHbk/LcGq4Y 7TUqNyxJTMfpZcaILBEtlKwurrwJ/LZHSrtczOwk= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson , Dave Stevenson Subject: [PATCH v2 06/34] staging: vc04_services: Add new vc-sm-cma driver Date: Mon, 4 May 2020 12:25:43 +0300 Message-Id: <20200504092611.9798-7-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Dave Stevenson Add Broadcom VideoCore Shared Memory support. This new driver allows contiguous memory blocks to be imported into the VideoCore VPU memory map, and manages the lifetime of those objects, only releasing the source dmabuf once the VPU has confirmed it has finished with it. Driver upported from the RaspberryPi BSP at revision: 890691d1c996 ("staging: vc04_services: Fix vcsm overflow bug when counting transactions") forward ported to recent mainline kernel version. Signed-off-by: Naushir Patuck Signed-off-by: Dave Stevenson Signed-off-by: Jacopo Mondi Reported-by: kbuild test robot Reported-by: kbuild test robot Reported-by: kbuild test robot --- drivers/staging/vc04_services/Kconfig | 2 + drivers/staging/vc04_services/Makefile | 1 + .../include/linux/broadcom/vc_sm_cma_ioctl.h | 114 ++ .../staging/vc04_services/vc-sm-cma/Kconfig | 10 + .../staging/vc04_services/vc-sm-cma/Makefile | 13 + drivers/staging/vc04_services/vc-sm-cma/TODO | 1 + .../staging/vc04_services/vc-sm-cma/vc_sm.c | 1732 +++++++++++++++++ .../staging/vc04_services/vc-sm-cma/vc_sm.h | 84 + .../vc04_services/vc-sm-cma/vc_sm_cma_vchi.c | 505 +++++ .../vc04_services/vc-sm-cma/vc_sm_cma_vchi.h | 63 + .../vc04_services/vc-sm-cma/vc_sm_defs.h | 300 +++ .../vc04_services/vc-sm-cma/vc_sm_knl.h | 28 + 12 files changed, 2853 insertions(+) create mode 100644 drivers/staging/vc04_services/include/linux/broadcom/vc_sm_cma_ioctl.h create mode 100644 drivers/staging/vc04_services/vc-sm-cma/Kconfig create mode 100644 drivers/staging/vc04_services/vc-sm-cma/Makefile create mode 100644 drivers/staging/vc04_services/vc-sm-cma/TODO create mode 100644 drivers/staging/vc04_services/vc-sm-cma/vc_sm.c create mode 100644 drivers/staging/vc04_services/vc-sm-cma/vc_sm.h create mode 100644 drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.c create mode 100644 drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.h create mode 100644 drivers/staging/vc04_services/vc-sm-cma/vc_sm_defs.h create mode 100644 drivers/staging/vc04_services/vc-sm-cma/vc_sm_knl.h diff --git a/drivers/staging/vc04_services/Kconfig b/drivers/staging/vc04_services/Kconfig index 6baf9dd57f1f..b9f1f019ebd8 100644 --- a/drivers/staging/vc04_services/Kconfig +++ b/drivers/staging/vc04_services/Kconfig @@ -23,5 +23,7 @@ source "drivers/staging/vc04_services/bcm2835-audio/Kconfig" source "drivers/staging/vc04_services/bcm2835-camera/Kconfig" +source "drivers/staging/vc04_services/vc-sm-cma/Kconfig" + endif diff --git a/drivers/staging/vc04_services/Makefile b/drivers/staging/vc04_services/Makefile index 54d9e2f31916..6e1abf494c1a 100644 --- a/drivers/staging/vc04_services/Makefile +++ b/drivers/staging/vc04_services/Makefile @@ -12,6 +12,7 @@ vchiq-objs := \ obj-$(CONFIG_SND_BCM2835) += bcm2835-audio/ obj-$(CONFIG_VIDEO_BCM2835) += bcm2835-camera/ +obj-$(CONFIG_BCM_VC_SM_CMA) += vc-sm-cma/ ccflags-y += -D__VCCOREVER__=0x04000000 diff --git a/drivers/staging/vc04_services/include/linux/broadcom/vc_sm_cma_ioctl.h b/drivers/staging/vc04_services/include/linux/broadcom/vc_sm_cma_ioctl.h new file mode 100644 index 000000000000..107460ad1be3 --- /dev/null +++ b/drivers/staging/vc04_services/include/linux/broadcom/vc_sm_cma_ioctl.h @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * Copyright 2019 Raspberry Pi (Trading) Ltd. All rights reserved. + * + * Based on vmcs_sm_ioctl.h Copyright Broadcom Corporation. + */ + +#ifndef __VC_SM_CMA_IOCTL_H +#define __VC_SM_CMA_IOCTL_H + +/* ---- Include Files ---------------------------------------------------- */ + +#if defined(__KERNEL__) +#include /* Needed for standard types */ +#else +#include +#endif + +#include + +/* ---- Constants and Types ---------------------------------------------- */ + +#define VC_SM_CMA_RESOURCE_NAME 32 +#define VC_SM_CMA_RESOURCE_NAME_DEFAULT "sm-host-resource" + +/* Type define used to create unique IOCTL number */ +#define VC_SM_CMA_MAGIC_TYPE 'J' + +/* IOCTL commands on /dev/vc-sm-cma */ +enum vc_sm_cma_cmd_e { + VC_SM_CMA_CMD_ALLOC = 0x5A, /* Start at 0x5A arbitrarily */ + + VC_SM_CMA_CMD_IMPORT_DMABUF, + + VC_SM_CMA_CMD_CLEAN_INVALID2, + + VC_SM_CMA_CMD_LAST /* Do not delete */ +}; + +/* Cache type supported, conveniently matches the user space definition in + * user-vcsm.h. + */ +enum vc_sm_cma_cache_e { + VC_SM_CMA_CACHE_NONE, + VC_SM_CMA_CACHE_HOST, + VC_SM_CMA_CACHE_VC, + VC_SM_CMA_CACHE_BOTH, +}; + +/* IOCTL Data structures */ +struct vc_sm_cma_ioctl_alloc { + /* user -> kernel */ + __u32 size; + __u32 num; + __u32 cached; /* enum vc_sm_cma_cache_e */ + __u32 pad; + __u8 name[VC_SM_CMA_RESOURCE_NAME]; + + /* kernel -> user */ + __s32 handle; + __u32 vc_handle; + __u64 dma_addr; +}; + +struct vc_sm_cma_ioctl_import_dmabuf { + /* user -> kernel */ + __s32 dmabuf_fd; + __u32 cached; /* enum vc_sm_cma_cache_e */ + __u8 name[VC_SM_CMA_RESOURCE_NAME]; + + /* kernel -> user */ + __s32 handle; + __u32 vc_handle; + __u32 size; + __u32 pad; + __u64 dma_addr; +}; + +/* + * Cache functions to be set to struct vc_sm_cma_ioctl_clean_invalid2 + * invalidate_mode. + */ +#define VC_SM_CACHE_OP_NOP 0x00 +#define VC_SM_CACHE_OP_INV 0x01 +#define VC_SM_CACHE_OP_CLEAN 0x02 +#define VC_SM_CACHE_OP_FLUSH 0x03 + +struct vc_sm_cma_ioctl_clean_invalid2 { + __u32 op_count; + __u32 pad; + struct vc_sm_cma_ioctl_clean_invalid_block { + __u32 invalidate_mode; + __u32 block_count; + void * __user start_address; + __u32 block_size; + __u32 inter_block_stride; + } s[0]; +}; + +/* IOCTL numbers */ +#define VC_SM_CMA_IOCTL_MEM_ALLOC\ + _IOR(VC_SM_CMA_MAGIC_TYPE, VC_SM_CMA_CMD_ALLOC,\ + struct vc_sm_cma_ioctl_alloc) + +#define VC_SM_CMA_IOCTL_MEM_IMPORT_DMABUF\ + _IOR(VC_SM_CMA_MAGIC_TYPE, VC_SM_CMA_CMD_IMPORT_DMABUF,\ + struct vc_sm_cma_ioctl_import_dmabuf) + +#define VC_SM_CMA_IOCTL_MEM_CLEAN_INVALID2\ + _IOR(VC_SM_CMA_MAGIC_TYPE, VC_SM_CMA_CMD_CLEAN_INVALID2,\ + struct vc_sm_cma_ioctl_clean_invalid2) + +#endif /* __VC_SM_CMA_IOCTL_H */ diff --git a/drivers/staging/vc04_services/vc-sm-cma/Kconfig b/drivers/staging/vc04_services/vc-sm-cma/Kconfig new file mode 100644 index 000000000000..5ac115da6b49 --- /dev/null +++ b/drivers/staging/vc04_services/vc-sm-cma/Kconfig @@ -0,0 +1,10 @@ +config BCM_VC_SM_CMA + bool "VideoCore Shared Memory (CMA) driver" + select BCM2835_VCHIQ + select RBTREE + select DMA_SHARED_BUFFER + help + Say Y here to enable the shared memory interface that + supports sharing dmabufs with VideoCore. + This operates over the VCHIQ interface to a service + running on VideoCore. diff --git a/drivers/staging/vc04_services/vc-sm-cma/Makefile b/drivers/staging/vc04_services/vc-sm-cma/Makefile new file mode 100644 index 000000000000..77d173694fbf --- /dev/null +++ b/drivers/staging/vc04_services/vc-sm-cma/Makefile @@ -0,0 +1,13 @@ +ccflags-y += \ + -I$(srctree)/$(src)/../ \ + -I$(srctree)/$(src)/../interface/vchi \ + -I$(srctree)/$(src)/../interface/vchiq_arm\ + -I$(srctree)/$(src)/../include + +ccflags-y += \ + -D__VCCOREVER__=0 + +vc-sm-cma-$(CONFIG_BCM_VC_SM_CMA) := \ + vc_sm.o vc_sm_cma_vchi.o + +obj-$(CONFIG_BCM_VC_SM_CMA) += vc-sm-cma.o diff --git a/drivers/staging/vc04_services/vc-sm-cma/TODO b/drivers/staging/vc04_services/vc-sm-cma/TODO new file mode 100644 index 000000000000..ac9b5f8a7389 --- /dev/null +++ b/drivers/staging/vc04_services/vc-sm-cma/TODO @@ -0,0 +1 @@ +No currently outstanding tasks except some clean-up. diff --git a/drivers/staging/vc04_services/vc-sm-cma/vc_sm.c b/drivers/staging/vc04_services/vc-sm-cma/vc_sm.c new file mode 100644 index 000000000000..cd5fb561debb --- /dev/null +++ b/drivers/staging/vc04_services/vc-sm-cma/vc_sm.c @@ -0,0 +1,1732 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * VideoCore Shared Memory driver using CMA. + * + * Copyright: 2018, Raspberry Pi (Trading) Ltd + * Dave Stevenson + * + * Based on vmcs_sm driver from Broadcom Corporation for some API, + * and taking some code for buffer allocation and dmabuf handling from + * videobuf2. + * + * + * This driver has 3 main uses: + * 1) Allocating buffers for the kernel or userspace that can be shared with the + * VPU. + * 2) Importing dmabufs from elsewhere for sharing with the VPU. + * 3) Allocating buffers for use by the VPU. + * + * In the first and second cases the native handle is a dmabuf. Releasing the + * resource inherently comes from releasing the dmabuf, and this will trigger + * unmapping on the VPU. The underlying allocation and our buffer structure are + * retained until the VPU has confirmed that it has finished with it. + * + * For the VPU allocations the VPU is responsible for triggering the release, + * and therefore the released message decrements the dma_buf refcount (with the + * VPU mapping having already been marked as released). + */ + +/* ---- Include Files ----------------------------------------------------- */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vchiq_connected.h" +#include "vc_sm_cma_vchi.h" + +#include "vc_sm.h" +#include "vc_sm_knl.h" +#include + +/* ---- Private Constants and Types --------------------------------------- */ + +#define DEVICE_NAME "vcsm-cma" +#define DEVICE_MINOR 0 + +#define VC_SM_RESOURCE_NAME_DEFAULT "sm-host-resource" + +#define VC_SM_DIR_ROOT_NAME "vcsm-cma" +#define VC_SM_STATE "state" + +/* Private file data associated with each opened device. */ +struct vc_sm_privdata_t { + pid_t pid; /* PID of creator. */ + + int restart_sys; /* Tracks restart on interrupt. */ + enum vc_sm_msg_type int_action; /* Interrupted action. */ + u32 int_trans_id; /* Interrupted transaction. */ +}; + +typedef int (*VC_SM_SHOW) (struct seq_file *s, void *v); +struct sm_pde_t { + VC_SM_SHOW show; /* Debug fs function hookup. */ + struct dentry *dir_entry; /* Debug fs directory entry. */ + void *priv_data; /* Private data */ +}; + +/* Global state information. */ +struct sm_state_t { + struct platform_device *pdev; + + struct miscdevice misc_dev; + + struct sm_instance *sm_handle; /* Handle for videocore service. */ + + spinlock_t kernelid_map_lock; /* Spinlock protecting kernelid_map */ + struct idr kernelid_map; + + struct mutex map_lock; /* Global map lock. */ + struct list_head buffer_list; /* List of buffer. */ + + struct vc_sm_privdata_t *data_knl; /* Kernel internal data tracking. */ + struct vc_sm_privdata_t *vpu_allocs; /* All allocations from the VPU */ + struct dentry *dir_root; /* Debug fs entries root. */ + struct sm_pde_t dir_state; /* Debug fs entries state sub-tree. */ + + bool require_released_callback; /* VPU will send a released msg when it + * has finished with a resource. + */ + u32 int_trans_id; /* Interrupted transaction. */ +}; + +struct vc_sm_dma_buf_attachment { + struct device *dev; + struct sg_table sg_table; + struct list_head list; + enum dma_data_direction dma_dir; +}; + +/* ---- Private Variables ----------------------------------------------- */ + +static struct sm_state_t *sm_state; +static int sm_inited; + +/* ---- Private Function Prototypes -------------------------------------- */ + +/* ---- Private Functions ------------------------------------------------ */ + +static int get_kernel_id(struct vc_sm_buffer *buffer) +{ + int handle; + + spin_lock(&sm_state->kernelid_map_lock); + handle = idr_alloc(&sm_state->kernelid_map, buffer, 0, 0, GFP_KERNEL); + spin_unlock(&sm_state->kernelid_map_lock); + + return handle; +} + +static struct vc_sm_buffer *lookup_kernel_id(int handle) +{ + return idr_find(&sm_state->kernelid_map, handle); +} + +static void free_kernel_id(int handle) +{ + spin_lock(&sm_state->kernelid_map_lock); + idr_remove(&sm_state->kernelid_map, handle); + spin_unlock(&sm_state->kernelid_map_lock); +} + +static int vc_sm_cma_seq_file_show(struct seq_file *s, void *v) +{ + struct sm_pde_t *sm_pde; + + sm_pde = (struct sm_pde_t *)(s->private); + + if (sm_pde && sm_pde->show) + sm_pde->show(s, v); + + return 0; +} + +static int vc_sm_cma_single_open(struct inode *inode, struct file *file) +{ + return single_open(file, vc_sm_cma_seq_file_show, inode->i_private); +} + +static const struct file_operations vc_sm_cma_debug_fs_fops = { + .open = vc_sm_cma_single_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int vc_sm_cma_global_state_show(struct seq_file *s, void *v) +{ + struct vc_sm_buffer *resource = NULL; + int resource_count = 0; + + if (!sm_state) + return 0; + + seq_printf(s, "\nVC-ServiceHandle %p\n", sm_state->sm_handle); + + /* Log all applicable mapping(s). */ + + mutex_lock(&sm_state->map_lock); + seq_puts(s, "\nResources\n"); + if (!list_empty(&sm_state->buffer_list)) { + list_for_each_entry(resource, &sm_state->buffer_list, + global_buffer_list) { + resource_count++; + + seq_printf(s, "\nResource %p\n", + resource); + seq_printf(s, " NAME %s\n", + resource->name); + seq_printf(s, " SIZE %zu\n", + resource->size); + seq_printf(s, " DMABUF %p\n", + resource->dma_buf); + if (resource->imported) { + seq_printf(s, " ATTACH %p\n", + resource->import.attach); + seq_printf(s, " SGT %p\n", + resource->import.sgt); + } else { + seq_printf(s, " SGT %p\n", + resource->alloc.sg_table); + } + seq_printf(s, " DMA_ADDR %pad\n", + &resource->dma_addr); + seq_printf(s, " VC_HANDLE %08x\n", + resource->vc_handle); + seq_printf(s, " VC_MAPPING %d\n", + resource->vpu_state); + } + } + seq_printf(s, "\n\nTotal resource count: %d\n\n", resource_count); + + mutex_unlock(&sm_state->map_lock); + + return 0; +} + +/* + * Adds a buffer to the private data list which tracks all the allocated + * data. + */ +static void vc_sm_add_resource(struct vc_sm_privdata_t *privdata, + struct vc_sm_buffer *buffer) +{ + mutex_lock(&sm_state->map_lock); + list_add(&buffer->global_buffer_list, &sm_state->buffer_list); + mutex_unlock(&sm_state->map_lock); + + pr_debug("[%s]: added buffer %p (name %s, size %zu)\n", + __func__, buffer, buffer->name, buffer->size); +} + +/* + * Cleans up imported dmabuf. + */ +static void vc_sm_clean_up_dmabuf(struct vc_sm_buffer *buffer) +{ + if (!buffer->imported) + return; + + /* Handle cleaning up imported dmabufs */ + mutex_lock(&buffer->lock); + if (buffer->import.sgt) { + dma_buf_unmap_attachment(buffer->import.attach, + buffer->import.sgt, + DMA_BIDIRECTIONAL); + buffer->import.sgt = NULL; + } + if (buffer->import.attach) { + dma_buf_detach(buffer->dma_buf, buffer->import.attach); + buffer->import.attach = NULL; + } + mutex_unlock(&buffer->lock); +} + +/* + * Instructs VPU to decrement the refcount on a buffer. + */ +static void vc_sm_vpu_free(struct vc_sm_buffer *buffer) +{ + if (buffer->vc_handle && buffer->vpu_state == VPU_MAPPED) { + struct vc_sm_free_t free = { buffer->vc_handle, 0 }; + int status = vc_sm_cma_vchi_free(sm_state->sm_handle, &free, + &sm_state->int_trans_id); + if (status != 0 && status != -EINTR) { + pr_err("[%s]: failed to free memory on videocore (status: %u, trans_id: %u)\n", + __func__, status, sm_state->int_trans_id); + } + + if (sm_state->require_released_callback) { + /* Need to wait for the VPU to confirm the free. */ + + /* Retain a reference on this until the VPU has + * released it + */ + buffer->vpu_state = VPU_UNMAPPING; + } else { + buffer->vpu_state = VPU_NOT_MAPPED; + buffer->vc_handle = 0; + } + } +} + +/* + * Release an allocation. + * All refcounting is done via the dma buf object. + * + * Must be called with the mutex held. The function will either release the + * mutex (if defering the release) or destroy it. The caller must therefore not + * reuse the buffer on return. + */ +static void vc_sm_release_resource(struct vc_sm_buffer *buffer) +{ + pr_debug("[%s]: buffer %p (name %s, size %zu), imported %u\n", + __func__, buffer, buffer->name, buffer->size, + buffer->imported); + + if (buffer->vc_handle) { + /* We've sent the unmap request but not had the response. */ + pr_debug("[%s]: Waiting for VPU unmap response on %p\n", + __func__, buffer); + goto defer; + } + if (buffer->in_use) { + /* dmabuf still in use - we await the release */ + pr_debug("[%s]: buffer %p is still in use\n", __func__, buffer); + goto defer; + } + + /* Release the allocation (whether imported dmabuf or CMA allocation) */ + if (buffer->imported) { + if (buffer->import.dma_buf) + dma_buf_put(buffer->import.dma_buf); + else + pr_err("%s: Imported dmabuf already been put for buf %p\n", + __func__, buffer); + buffer->import.dma_buf = NULL; + } else { + dma_free_coherent(&sm_state->pdev->dev, buffer->size, + buffer->cookie, buffer->dma_addr); + } + + /* Free our buffer. Start by removing it from the list */ + mutex_lock(&sm_state->map_lock); + list_del(&buffer->global_buffer_list); + mutex_unlock(&sm_state->map_lock); + + pr_debug("%s: Release our allocation - done\n", __func__); + mutex_unlock(&buffer->lock); + + mutex_destroy(&buffer->lock); + + kfree(buffer); + return; + +defer: + mutex_unlock(&buffer->lock); +} + +/* Create support for private data tracking. */ +static struct vc_sm_privdata_t *vc_sm_cma_create_priv_data(pid_t id) +{ + char alloc_name[32]; + struct vc_sm_privdata_t *file_data = NULL; + + /* Allocate private structure. */ + file_data = kzalloc(sizeof(*file_data), GFP_KERNEL); + + if (!file_data) + return NULL; + + snprintf(alloc_name, sizeof(alloc_name), "%d", id); + + file_data->pid = id; + + return file_data; +} + +/* Dma buf operations for use with our own allocations */ + +static int vc_sm_dma_buf_attach(struct dma_buf *dmabuf, + struct dma_buf_attachment *attachment) + +{ + struct vc_sm_dma_buf_attachment *a; + struct sg_table *sgt; + struct vc_sm_buffer *buf = dmabuf->priv; + struct scatterlist *rd, *wr; + int ret, i; + + a = kzalloc(sizeof(*a), GFP_KERNEL); + if (!a) + return -ENOMEM; + + pr_debug("%s dmabuf %p attachment %p\n", __func__, dmabuf, attachment); + + mutex_lock(&buf->lock); + + INIT_LIST_HEAD(&a->list); + + sgt = &a->sg_table; + + /* Copy the buf->base_sgt scatter list to the attachment, as we can't + * map the same scatter list to multiple attachments at the same time. + */ + ret = sg_alloc_table(sgt, buf->alloc.sg_table->orig_nents, GFP_KERNEL); + if (ret) { + kfree(a); + return -ENOMEM; + } + + rd = buf->alloc.sg_table->sgl; + wr = sgt->sgl; + for (i = 0; i < sgt->orig_nents; ++i) { + sg_set_page(wr, sg_page(rd), rd->length, rd->offset); + rd = sg_next(rd); + wr = sg_next(wr); + } + + a->dma_dir = DMA_NONE; + attachment->priv = a; + + list_add(&a->list, &buf->attachments); + mutex_unlock(&buf->lock); + + return 0; +} + +static void vc_sm_dma_buf_detach(struct dma_buf *dmabuf, + struct dma_buf_attachment *attachment) +{ + struct vc_sm_dma_buf_attachment *a = attachment->priv; + struct vc_sm_buffer *buf = dmabuf->priv; + struct sg_table *sgt; + + pr_debug("%s dmabuf %p attachment %p\n", __func__, dmabuf, attachment); + if (!a) + return; + + sgt = &a->sg_table; + + /* release the scatterlist cache */ + if (a->dma_dir != DMA_NONE) + dma_unmap_sg(attachment->dev, sgt->sgl, sgt->orig_nents, + a->dma_dir); + sg_free_table(sgt); + + mutex_lock(&buf->lock); + list_del(&a->list); + mutex_unlock(&buf->lock); + + kfree(a); +} + +static struct sg_table *vc_sm_map_dma_buf(struct dma_buf_attachment *attachment, + enum dma_data_direction direction) +{ + struct vc_sm_dma_buf_attachment *a = attachment->priv; + /* stealing dmabuf mutex to serialize map/unmap operations */ + struct mutex *lock = &attachment->dmabuf->lock; + struct sg_table *table; + + mutex_lock(lock); + pr_debug("%s attachment %p\n", __func__, attachment); + table = &a->sg_table; + + /* return previously mapped sg table */ + if (a->dma_dir == direction) { + mutex_unlock(lock); + return table; + } + + /* release any previous cache */ + if (a->dma_dir != DMA_NONE) { + dma_unmap_sg(attachment->dev, table->sgl, table->orig_nents, + a->dma_dir); + a->dma_dir = DMA_NONE; + } + + /* mapping to the client with new direction */ + table->nents = dma_map_sg(attachment->dev, table->sgl, + table->orig_nents, direction); + if (!table->nents) { + pr_err("failed to map scatterlist\n"); + mutex_unlock(lock); + return ERR_PTR(-EIO); + } + + a->dma_dir = direction; + mutex_unlock(lock); + + pr_debug("%s attachment %p\n", __func__, attachment); + return table; +} + +static void vc_sm_unmap_dma_buf(struct dma_buf_attachment *attachment, + struct sg_table *table, + enum dma_data_direction direction) +{ + pr_debug("%s attachment %p\n", __func__, attachment); + dma_unmap_sg(attachment->dev, table->sgl, table->nents, direction); +} + +static int vc_sm_dmabuf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) +{ + struct vc_sm_buffer *buf = dmabuf->priv; + int ret; + + pr_debug("%s dmabuf %p, buf %p, vm_start %08lX\n", __func__, dmabuf, + buf, vma->vm_start); + + mutex_lock(&buf->lock); + + /* now map it to userspace */ + vma->vm_pgoff = 0; + + ret = dma_mmap_coherent(&sm_state->pdev->dev, vma, buf->cookie, + buf->dma_addr, buf->size); + + if (ret) { + pr_err("Remapping memory failed, error: %d\n", ret); + return ret; + } + + vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP; + + mutex_unlock(&buf->lock); + + if (ret) + pr_err("%s: failure mapping buffer to userspace\n", + __func__); + + return ret; +} + +static void vc_sm_dma_buf_release(struct dma_buf *dmabuf) +{ + struct vc_sm_buffer *buffer; + + if (!dmabuf) + return; + + buffer = (struct vc_sm_buffer *)dmabuf->priv; + + mutex_lock(&buffer->lock); + + pr_debug("%s dmabuf %p, buffer %p\n", __func__, dmabuf, buffer); + + buffer->in_use = 0; + + /* Unmap on the VPU */ + vc_sm_vpu_free(buffer); + pr_debug("%s vpu_free done\n", __func__); + + /* Unmap our dma_buf object (the vc_sm_buffer remains until released + * on the VPU). + */ + vc_sm_clean_up_dmabuf(buffer); + pr_debug("%s clean_up dmabuf done\n", __func__); + + /* buffer->lock will be destroyed by vc_sm_release_resource if finished + * with, otherwise unlocked. Do NOT unlock here. + */ + vc_sm_release_resource(buffer); + pr_debug("%s done\n", __func__); +} + +static int vc_sm_dma_buf_begin_cpu_access(struct dma_buf *dmabuf, + enum dma_data_direction direction) +{ + struct vc_sm_buffer *buf; + struct vc_sm_dma_buf_attachment *a; + + if (!dmabuf) + return -EFAULT; + + buf = dmabuf->priv; + if (!buf) + return -EFAULT; + + mutex_lock(&buf->lock); + + list_for_each_entry(a, &buf->attachments, list) { + dma_sync_sg_for_cpu(a->dev, a->sg_table.sgl, + a->sg_table.nents, direction); + } + mutex_unlock(&buf->lock); + + return 0; +} + +static int vc_sm_dma_buf_end_cpu_access(struct dma_buf *dmabuf, + enum dma_data_direction direction) +{ + struct vc_sm_buffer *buf; + struct vc_sm_dma_buf_attachment *a; + + if (!dmabuf) + return -EFAULT; + buf = dmabuf->priv; + if (!buf) + return -EFAULT; + + mutex_lock(&buf->lock); + + list_for_each_entry(a, &buf->attachments, list) { + dma_sync_sg_for_device(a->dev, a->sg_table.sgl, + a->sg_table.nents, direction); + } + mutex_unlock(&buf->lock); + + return 0; +} + +static const struct dma_buf_ops dma_buf_ops = { + .map_dma_buf = vc_sm_map_dma_buf, + .unmap_dma_buf = vc_sm_unmap_dma_buf, + .mmap = vc_sm_dmabuf_mmap, + .release = vc_sm_dma_buf_release, + .attach = vc_sm_dma_buf_attach, + .detach = vc_sm_dma_buf_detach, + .begin_cpu_access = vc_sm_dma_buf_begin_cpu_access, + .end_cpu_access = vc_sm_dma_buf_end_cpu_access, +}; + +/* Dma_buf operations for chaining through to an imported dma_buf */ + +static +int vc_sm_import_dma_buf_attach(struct dma_buf *dmabuf, + struct dma_buf_attachment *attachment) +{ + struct vc_sm_buffer *buf = dmabuf->priv; + + if (!buf->imported) + return -EINVAL; + return buf->import.dma_buf->ops->attach(buf->import.dma_buf, + attachment); +} + +static +void vc_sm_import_dma_buf_detatch(struct dma_buf *dmabuf, + struct dma_buf_attachment *attachment) +{ + struct vc_sm_buffer *buf = dmabuf->priv; + + if (!buf->imported) + return; + buf->import.dma_buf->ops->detach(buf->import.dma_buf, attachment); +} + +static +struct sg_table *vc_sm_import_map_dma_buf(struct dma_buf_attachment *attachment, + enum dma_data_direction direction) +{ + struct vc_sm_buffer *buf = attachment->dmabuf->priv; + + if (!buf->imported) + return NULL; + return buf->import.dma_buf->ops->map_dma_buf(attachment, + direction); +} + +static +void vc_sm_import_unmap_dma_buf(struct dma_buf_attachment *attachment, + struct sg_table *table, + enum dma_data_direction direction) +{ + struct vc_sm_buffer *buf = attachment->dmabuf->priv; + + if (!buf->imported) + return; + buf->import.dma_buf->ops->unmap_dma_buf(attachment, table, direction); +} + +static +int vc_sm_import_dmabuf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma) +{ + struct vc_sm_buffer *buf = dmabuf->priv; + + pr_debug("%s: mmap dma_buf %p, buf %p, imported db %p\n", __func__, + dmabuf, buf, buf->import.dma_buf); + if (!buf->imported) { + pr_err("%s: mmap dma_buf %p- not an imported buffer\n", + __func__, dmabuf); + return -EINVAL; + } + return buf->import.dma_buf->ops->mmap(buf->import.dma_buf, vma); +} + +static +void vc_sm_import_dma_buf_release(struct dma_buf *dmabuf) +{ + struct vc_sm_buffer *buf = dmabuf->priv; + + pr_debug("%s: Relasing dma_buf %p\n", __func__, dmabuf); + mutex_lock(&buf->lock); + if (!buf->imported) + return; + + buf->in_use = 0; + + vc_sm_vpu_free(buf); + + vc_sm_release_resource(buf); +} + +static +int vc_sm_import_dma_buf_begin_cpu_access(struct dma_buf *dmabuf, + enum dma_data_direction direction) +{ + struct vc_sm_buffer *buf = dmabuf->priv; + + if (!buf->imported) + return -EINVAL; + return buf->import.dma_buf->ops->begin_cpu_access(buf->import.dma_buf, + direction); +} + +static +int vc_sm_import_dma_buf_end_cpu_access(struct dma_buf *dmabuf, + enum dma_data_direction direction) +{ + struct vc_sm_buffer *buf = dmabuf->priv; + + if (!buf->imported) + return -EINVAL; + return buf->import.dma_buf->ops->end_cpu_access(buf->import.dma_buf, + direction); +} + +static const struct dma_buf_ops dma_buf_import_ops = { + .map_dma_buf = vc_sm_import_map_dma_buf, + .unmap_dma_buf = vc_sm_import_unmap_dma_buf, + .mmap = vc_sm_import_dmabuf_mmap, + .release = vc_sm_import_dma_buf_release, + .attach = vc_sm_import_dma_buf_attach, + .detach = vc_sm_import_dma_buf_detatch, + .begin_cpu_access = vc_sm_import_dma_buf_begin_cpu_access, + .end_cpu_access = vc_sm_import_dma_buf_end_cpu_access, +}; + +/* Import a dma_buf to be shared with VC. */ +int +vc_sm_cma_import_dmabuf_internal(struct vc_sm_privdata_t *private, + struct dma_buf *dma_buf, + int fd, + struct dma_buf **imported_buf) +{ + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + struct vc_sm_buffer *buffer = NULL; + struct vc_sm_import import = { }; + struct vc_sm_import_result result = { }; + struct dma_buf_attachment *attach = NULL; + struct sg_table *sgt = NULL; + dma_addr_t dma_addr; + int ret = 0; + int status; + + /* Setup our allocation parameters */ + pr_debug("%s: importing dma_buf %p/fd %d\n", __func__, dma_buf, fd); + + if (fd < 0) + get_dma_buf(dma_buf); + else + dma_buf = dma_buf_get(fd); + + if (!dma_buf) + return -EINVAL; + + attach = dma_buf_attach(dma_buf, &sm_state->pdev->dev); + if (IS_ERR(attach)) { + ret = PTR_ERR(attach); + goto error; + } + + sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); + if (IS_ERR(sgt)) { + ret = PTR_ERR(sgt); + goto error; + } + + /* Verify that the address block is contiguous */ + if (sgt->nents != 1) { + ret = -ENOMEM; + goto error; + } + + /* Allocate local buffer to track this allocation. */ + buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); + if (!buffer) { + ret = -ENOMEM; + goto error; + } + + import.type = VC_SM_ALLOC_NON_CACHED; + dma_addr = sg_dma_address(sgt->sgl); + import.addr = (u32)dma_addr; + if ((import.addr & 0xC0000000) != 0xC0000000) { + pr_err("%s: Expecting an uncached alias for dma_addr %pad\n", + __func__, &dma_addr); + import.addr |= 0xC0000000; + } + import.size = sg_dma_len(sgt->sgl); + import.allocator = current->tgid; + import.kernel_id = get_kernel_id(buffer); + + memcpy(import.name, VC_SM_RESOURCE_NAME_DEFAULT, + sizeof(VC_SM_RESOURCE_NAME_DEFAULT)); + + pr_debug("[%s]: attempt to import \"%s\" data - type %u, addr %pad, size %u.\n", + __func__, import.name, import.type, &dma_addr, import.size); + + /* Allocate the videocore buffer. */ + status = vc_sm_cma_vchi_import(sm_state->sm_handle, &import, &result, + &sm_state->int_trans_id); + if (status == -EINTR) { + pr_debug("[%s]: requesting import memory action restart (trans_id: %u)\n", + __func__, sm_state->int_trans_id); + ret = -ERESTARTSYS; + private->restart_sys = -EINTR; + private->int_action = VC_SM_MSG_TYPE_IMPORT; + goto error; + } else if (status || !result.res_handle) { + pr_debug("[%s]: failed to import memory on videocore (status: %u, trans_id: %u)\n", + __func__, status, sm_state->int_trans_id); + ret = -ENOMEM; + goto error; + } + + mutex_init(&buffer->lock); + INIT_LIST_HEAD(&buffer->attachments); + memcpy(buffer->name, import.name, + min(sizeof(buffer->name), sizeof(import.name) - 1)); + + /* Keep track of the buffer we created. */ + buffer->private = private; + buffer->vc_handle = result.res_handle; + buffer->size = import.size; + buffer->vpu_state = VPU_MAPPED; + + buffer->imported = 1; + buffer->import.dma_buf = dma_buf; + + buffer->import.attach = attach; + buffer->import.sgt = sgt; + buffer->dma_addr = dma_addr; + buffer->in_use = 1; + buffer->kernel_id = import.kernel_id; + + /* + * We're done - we need to export a new dmabuf chaining through most + * functions, but enabling us to release our own internal references + * here. + */ + exp_info.ops = &dma_buf_import_ops; + exp_info.size = import.size; + exp_info.flags = O_RDWR; + exp_info.priv = buffer; + + buffer->dma_buf = dma_buf_export(&exp_info); + if (IS_ERR(buffer->dma_buf)) { + ret = PTR_ERR(buffer->dma_buf); + goto error; + } + + vc_sm_add_resource(private, buffer); + + *imported_buf = buffer->dma_buf; + + return 0; + +error: + if (result.res_handle) { + struct vc_sm_free_t free = { result.res_handle, 0 }; + + vc_sm_cma_vchi_free(sm_state->sm_handle, &free, + &sm_state->int_trans_id); + } + free_kernel_id(import.kernel_id); + kfree(buffer); + if (sgt) + dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL); + if (attach) + dma_buf_detach(dma_buf, attach); + dma_buf_put(dma_buf); + return ret; +} + +static int vc_sm_cma_vpu_alloc(u32 size, u32 align, const char *name, + u32 mem_handle, struct vc_sm_buffer **ret_buffer) +{ + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + struct vc_sm_buffer *buffer = NULL; + struct sg_table *sgt; + int aligned_size; + int ret = 0; + + /* Align to the user requested align */ + aligned_size = ALIGN(size, align); + /* and then to a page boundary */ + aligned_size = PAGE_ALIGN(aligned_size); + + if (!aligned_size) + return -EINVAL; + + /* Allocate local buffer to track this allocation. */ + buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + mutex_init(&buffer->lock); + /* Acquire the mutex as vc_sm_release_resource will release it in the + * error path. + */ + mutex_lock(&buffer->lock); + + buffer->cookie = dma_alloc_coherent(&sm_state->pdev->dev, + aligned_size, &buffer->dma_addr, + GFP_KERNEL); + if (!buffer->cookie) { + pr_err("[%s]: dma_alloc_coherent alloc of %d bytes failed\n", + __func__, aligned_size); + ret = -ENOMEM; + goto error; + } + + pr_debug("[%s]: alloc of %d bytes success\n", + __func__, aligned_size); + + sgt = kmalloc(sizeof(*sgt), GFP_KERNEL); + if (!sgt) { + ret = -ENOMEM; + goto error; + } + + ret = dma_get_sgtable(&sm_state->pdev->dev, sgt, buffer->cookie, + buffer->dma_addr, buffer->size); + if (ret < 0) { + pr_err("failed to get scatterlist from DMA API\n"); + kfree(sgt); + ret = -ENOMEM; + goto error; + } + buffer->alloc.sg_table = sgt; + + INIT_LIST_HEAD(&buffer->attachments); + + memcpy(buffer->name, name, + min(sizeof(buffer->name), strlen(name))); + + exp_info.ops = &dma_buf_ops; + exp_info.size = aligned_size; + exp_info.flags = O_RDWR; + exp_info.priv = buffer; + + buffer->dma_buf = dma_buf_export(&exp_info); + if (IS_ERR(buffer->dma_buf)) { + ret = PTR_ERR(buffer->dma_buf); + goto error; + } + buffer->dma_addr = (u32)sg_dma_address(buffer->alloc.sg_table->sgl); + if ((buffer->dma_addr & 0xC0000000) != 0xC0000000) { + pr_warn_once("%s: Expecting an uncached alias for dma_addr %pad\n", + __func__, &buffer->dma_addr); + buffer->dma_addr |= 0xC0000000; + } + buffer->private = sm_state->vpu_allocs; + + buffer->vc_handle = mem_handle; + buffer->vpu_state = VPU_MAPPED; + buffer->vpu_allocated = 1; + buffer->size = size; + /* + * Create an ID that will be passed along with our message so + * that when we service the release reply, we can look up which + * resource is being released. + */ + buffer->kernel_id = get_kernel_id(buffer); + + vc_sm_add_resource(sm_state->vpu_allocs, buffer); + + mutex_unlock(&buffer->lock); + + *ret_buffer = buffer; + return 0; +error: + if (buffer) + vc_sm_release_resource(buffer); + return ret; +} + +static void +vc_sm_vpu_event(struct sm_instance *instance, struct vc_sm_result_t *reply, + int reply_len) +{ + switch (reply->trans_id & ~0x80000000) { + case VC_SM_MSG_TYPE_CLIENT_VERSION: + { + /* Acknowledge that the firmware supports the version command */ + pr_debug("%s: firmware acked version msg. Require release cb\n", + __func__); + sm_state->require_released_callback = true; + } + break; + case VC_SM_MSG_TYPE_RELEASED: + { + struct vc_sm_released *release = (struct vc_sm_released *)reply; + struct vc_sm_buffer *buffer = + lookup_kernel_id(release->kernel_id); + if (!buffer) { + pr_err("%s: VC released a buffer that is already released, kernel_id %d\n", + __func__, release->kernel_id); + break; + } + mutex_lock(&buffer->lock); + + pr_debug("%s: Released addr %08x, size %u, id %08x, mem_handle %08x\n", + __func__, release->addr, release->size, + release->kernel_id, release->vc_handle); + + buffer->vc_handle = 0; + buffer->vpu_state = VPU_NOT_MAPPED; + free_kernel_id(release->kernel_id); + + if (buffer->vpu_allocated) { + /* VPU allocation, so release the dmabuf which will + * trigger the clean up. + */ + mutex_unlock(&buffer->lock); + dma_buf_put(buffer->dma_buf); + } else { + vc_sm_release_resource(buffer); + } + } + break; + case VC_SM_MSG_TYPE_VC_MEM_REQUEST: + { + struct vc_sm_buffer *buffer = NULL; + struct vc_sm_vc_mem_request *req = + (struct vc_sm_vc_mem_request *)reply; + struct vc_sm_vc_mem_request_result reply; + int ret; + + pr_debug("%s: Request %u bytes of memory, align %d name %s, trans_id %08x\n", + __func__, req->size, req->align, req->name, + req->trans_id); + ret = vc_sm_cma_vpu_alloc(req->size, req->align, req->name, + req->vc_handle, &buffer); + + reply.trans_id = req->trans_id; + if (!ret) { + reply.addr = buffer->dma_addr; + reply.kernel_id = buffer->kernel_id; + pr_debug("%s: Allocated resource buffer %p, addr %pad\n", + __func__, buffer, &buffer->dma_addr); + } else { + pr_err("%s: Allocation failed size %u, name %s, vc_handle %u\n", + __func__, req->size, req->name, req->vc_handle); + reply.addr = 0; + reply.kernel_id = 0; + } + vc_sm_vchi_client_vc_mem_req_reply(sm_state->sm_handle, &reply, + &sm_state->int_trans_id); + break; + } + break; + default: + pr_err("%s: Unknown vpu cmd %x\n", __func__, reply->trans_id); + break; + } +} + +/* Userspace handling */ +/* + * Open the device. Creates a private state to help track all allocation + * associated with this device. + */ +static int vc_sm_cma_open(struct inode *inode, struct file *file) +{ + /* Make sure the device was started properly. */ + if (!sm_state) { + pr_err("[%s]: invalid device\n", __func__); + return -EPERM; + } + + file->private_data = vc_sm_cma_create_priv_data(current->tgid); + if (!file->private_data) { + pr_err("[%s]: failed to create data tracker\n", __func__); + + return -ENOMEM; + } + + return 0; +} + +/* + * Close the vcsm-cma device. + * All allocations are file descriptors to the dmabuf objects, so we will get + * the clean up request on those as those are cleaned up. + */ +static int vc_sm_cma_release(struct inode *inode, struct file *file) +{ + struct vc_sm_privdata_t *file_data = + (struct vc_sm_privdata_t *)file->private_data; + int ret = 0; + + /* Make sure the device was started properly. */ + if (!sm_state || !file_data) { + pr_err("[%s]: invalid device\n", __func__); + ret = -EPERM; + goto out; + } + + pr_debug("[%s]: using private data %p\n", __func__, file_data); + + /* Terminate the private data. */ + kfree(file_data); + +out: + return ret; +} + +/* + * Allocate a shared memory handle and block. + * Allocation is from CMA, and then imported into the VPU mappings. + */ +int vc_sm_cma_ioctl_alloc(struct vc_sm_privdata_t *private, + struct vc_sm_cma_ioctl_alloc *ioparam) +{ + DEFINE_DMA_BUF_EXPORT_INFO(exp_info); + struct vc_sm_buffer *buffer = NULL; + struct vc_sm_import import = { 0 }; + struct vc_sm_import_result result = { 0 }; + struct dma_buf *dmabuf = NULL; + struct sg_table *sgt; + int aligned_size; + int ret = 0; + int status; + int fd = -1; + + aligned_size = PAGE_ALIGN(ioparam->size); + + if (!aligned_size) + return -EINVAL; + + /* Allocate local buffer to track this allocation. */ + buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); + if (!buffer) { + ret = -ENOMEM; + goto error; + } + + buffer->cookie = dma_alloc_coherent(&sm_state->pdev->dev, + aligned_size, + &buffer->dma_addr, + GFP_KERNEL); + if (!buffer->cookie) { + pr_err("[%s]: dma_alloc_coherent alloc of %d bytes failed\n", + __func__, aligned_size); + ret = -ENOMEM; + goto error; + } + + import.type = VC_SM_ALLOC_NON_CACHED; + import.allocator = current->tgid; + + if (*ioparam->name) + memcpy(import.name, ioparam->name, sizeof(import.name) - 1); + else + memcpy(import.name, VC_SM_RESOURCE_NAME_DEFAULT, + sizeof(VC_SM_RESOURCE_NAME_DEFAULT)); + + mutex_init(&buffer->lock); + INIT_LIST_HEAD(&buffer->attachments); + memcpy(buffer->name, import.name, + min(sizeof(buffer->name), sizeof(import.name) - 1)); + + exp_info.ops = &dma_buf_ops; + exp_info.size = aligned_size; + exp_info.flags = O_RDWR; + exp_info.priv = buffer; + + dmabuf = dma_buf_export(&exp_info); + if (IS_ERR(dmabuf)) { + ret = PTR_ERR(dmabuf); + goto error; + } + buffer->dma_buf = dmabuf; + + import.addr = buffer->dma_addr; + import.size = aligned_size; + import.kernel_id = get_kernel_id(buffer); + + /* Wrap it into a videocore buffer. */ + status = vc_sm_cma_vchi_import(sm_state->sm_handle, &import, &result, + &sm_state->int_trans_id); + if (status == -EINTR) { + pr_debug("[%s]: requesting import memory action restart (trans_id: %u)\n", + __func__, sm_state->int_trans_id); + ret = -ERESTARTSYS; + private->restart_sys = -EINTR; + private->int_action = VC_SM_MSG_TYPE_IMPORT; + goto error; + } else if (status || !result.res_handle) { + pr_err("[%s]: failed to import memory on videocore (status: %u, trans_id: %u)\n", + __func__, status, sm_state->int_trans_id); + ret = -ENOMEM; + goto error; + } + + /* Keep track of the buffer we created. */ + buffer->private = private; + buffer->vc_handle = result.res_handle; + buffer->size = import.size; + buffer->vpu_state = VPU_MAPPED; + buffer->kernel_id = import.kernel_id; + + sgt = kmalloc(sizeof(*sgt), GFP_KERNEL); + if (!sgt) { + ret = -ENOMEM; + goto error; + } + + ret = dma_get_sgtable(&sm_state->pdev->dev, sgt, buffer->cookie, + buffer->dma_addr, buffer->size); + if (ret < 0) { + /* FIXME: error handling */ + pr_err("failed to get scatterlist from DMA API\n"); + kfree(sgt); + ret = -ENOMEM; + goto error; + } + buffer->alloc.sg_table = sgt; + + fd = dma_buf_fd(dmabuf, O_CLOEXEC); + if (fd < 0) + goto error; + + vc_sm_add_resource(private, buffer); + + pr_debug("[%s]: Added resource as fd %d, buffer %p, private %p, dma_addr %pad\n", + __func__, fd, buffer, private, &buffer->dma_addr); + + /* We're done */ + ioparam->handle = fd; + ioparam->vc_handle = buffer->vc_handle; + ioparam->dma_addr = buffer->dma_addr; + return 0; + +error: + pr_err("[%s]: something failed - cleanup. ret %d\n", __func__, ret); + + if (dmabuf) { + /* dmabuf has been exported, therefore allow dmabuf cleanup to + * deal with this + */ + dma_buf_put(dmabuf); + } else { + /* No dmabuf, therefore just free the buffer here */ + if (buffer->cookie) + dma_free_coherent(&sm_state->pdev->dev, buffer->size, + buffer->cookie, buffer->dma_addr); + kfree(buffer); + } + return ret; +} + +#ifndef CONFIG_ARM64 +/* Converts VCSM_CACHE_OP_* to an operating function. */ +static void (*cache_op_to_func(const unsigned int cache_op)) + (const void*, const void*) +{ + switch (cache_op) { + case VC_SM_CACHE_OP_NOP: + return NULL; + + case VC_SM_CACHE_OP_INV: + case VC_SM_CACHE_OP_CLEAN: + case VC_SM_CACHE_OP_FLUSH: + return dmac_flush_range; + + default: + pr_err("[%s]: Invalid cache_op: 0x%08x\n", __func__, cache_op); + return NULL; + } +} + +/* + * Clean/invalid/flush cache of which buffer is already pinned (i.e. accessed). + */ +static int clean_invalid_contig_2d(const void __user *addr, + const size_t block_count, + const size_t block_size, + const size_t stride, + const unsigned int cache_op) +{ + size_t i; + void (*op_fn)(const void *start, const void *end); + + if (!block_size) { + pr_err("[%s]: size cannot be 0\n", __func__); + return -EINVAL; + } + + op_fn = cache_op_to_func(cache_op); + if (!op_fn) + return -EINVAL; + + for (i = 0; i < block_count; i ++, addr += stride) + op_fn(addr, addr + block_size); + + return 0; +} + +static int vc_sm_cma_clean_invalid2(unsigned int cmdnr, unsigned long arg) +{ + struct vc_sm_cma_ioctl_clean_invalid2 ioparam; + struct vc_sm_cma_ioctl_clean_invalid_block *block = NULL; + int i, ret = 0; + + /* Get parameter data. */ + if (copy_from_user(&ioparam, (void *)arg, sizeof(ioparam))) { + pr_err("[%s]: failed to copy-from-user header for cmd %x\n", + __func__, cmdnr); + return -EFAULT; + } + block = kmalloc(ioparam.op_count * sizeof(*block), GFP_KERNEL); + if (!block) + return -EFAULT; + + if (copy_from_user(block, (void *)(arg + sizeof(ioparam)), + ioparam.op_count * sizeof(*block)) != 0) { + pr_err("[%s]: failed to copy-from-user payload for cmd %x\n", + __func__, cmdnr); + ret = -EFAULT; + goto out; + } + + for (i = 0; i < ioparam.op_count; i++) { + const struct vc_sm_cma_ioctl_clean_invalid_block * const op = + block + i; + + if (op->invalidate_mode == VC_SM_CACHE_OP_NOP) + continue; + + ret = clean_invalid_contig_2d((void __user *)op->start_address, + op->block_count, op->block_size, + op->inter_block_stride, + op->invalidate_mode); + if (ret) + break; + } +out: + kfree(block); + + return ret; +} +#endif + +static long vc_sm_cma_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret = 0; + unsigned int cmdnr = _IOC_NR(cmd); + struct vc_sm_privdata_t *file_data = + (struct vc_sm_privdata_t *)file->private_data; + + /* Validate we can work with this device. */ + if (!sm_state || !file_data) { + pr_err("[%s]: invalid device\n", __func__); + return -EPERM; + } + + /* Action is a re-post of a previously interrupted action? */ + if (file_data->restart_sys == -EINTR) { + struct vc_sm_action_clean_t action_clean; + + pr_debug("[%s]: clean up of action %u (trans_id: %u) following EINTR\n", + __func__, file_data->int_action, + file_data->int_trans_id); + + action_clean.res_action = file_data->int_action; + action_clean.action_trans_id = file_data->int_trans_id; + + file_data->restart_sys = 0; + } + + /* Now process the command. */ + switch (cmdnr) { + /* New memory allocation. + */ + case VC_SM_CMA_CMD_ALLOC: + { + struct vc_sm_cma_ioctl_alloc ioparam; + + /* Get the parameter data. */ + if (copy_from_user + (&ioparam, (void *)arg, sizeof(ioparam)) != 0) { + pr_err("[%s]: failed to copy-from-user for cmd %x\n", + __func__, cmdnr); + ret = -EFAULT; + break; + } + + ret = vc_sm_cma_ioctl_alloc(file_data, &ioparam); + if (!ret && + (copy_to_user((void *)arg, &ioparam, + sizeof(ioparam)) != 0)) { + /* FIXME: Release allocation */ + pr_err("[%s]: failed to copy-to-user for cmd %x\n", + __func__, cmdnr); + ret = -EFAULT; + } + break; + } + + case VC_SM_CMA_CMD_IMPORT_DMABUF: + { + struct vc_sm_cma_ioctl_import_dmabuf ioparam; + struct dma_buf *new_dmabuf; + + /* Get the parameter data. */ + if (copy_from_user + (&ioparam, (void *)arg, sizeof(ioparam)) != 0) { + pr_err("[%s]: failed to copy-from-user for cmd %x\n", + __func__, cmdnr); + ret = -EFAULT; + break; + } + + ret = vc_sm_cma_import_dmabuf_internal(file_data, + NULL, + ioparam.dmabuf_fd, + &new_dmabuf); + + if (!ret) { + struct vc_sm_buffer *buf = new_dmabuf->priv; + + ioparam.size = buf->size; + ioparam.handle = dma_buf_fd(new_dmabuf, + O_CLOEXEC); + ioparam.vc_handle = buf->vc_handle; + ioparam.dma_addr = buf->dma_addr; + + if (ioparam.handle < 0 || + (copy_to_user((void *)arg, &ioparam, + sizeof(ioparam)) != 0)) { + dma_buf_put(new_dmabuf); + /* FIXME: Release allocation */ + ret = -EFAULT; + } + } + break; + } + +#ifndef CONFIG_ARM64 + /* + * Flush/Invalidate the cache for a given mapping. + * Blocks must be pinned (i.e. accessed) before this call. + */ + case VC_SM_CMA_CMD_CLEAN_INVALID2: + ret = vc_sm_cma_clean_invalid2(cmdnr, arg); + break; +#endif + + default: + pr_debug("[%s]: cmd %x tgid %u, owner %u\n", __func__, cmdnr, + current->tgid, file_data->pid); + + ret = -EINVAL; + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +struct vc_sm_cma_ioctl_clean_invalid2_32 { + u32 op_count; + struct vc_sm_cma_ioctl_clean_invalid_block_32 { + u16 invalidate_mode; + u16 block_count; + compat_uptr_t start_address; + u32 block_size; + u32 inter_block_stride; + } s[0]; +}; + +#define VC_SM_CMA_CMD_CLEAN_INVALID2_32\ + _IOR(VC_SM_CMA_MAGIC_TYPE, VC_SM_CMA_CMD_CLEAN_INVALID2,\ + struct vc_sm_cma_ioctl_clean_invalid2_32) + +static long vc_sm_cma_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + switch (cmd) { + case VC_SM_CMA_CMD_CLEAN_INVALID2_32: + /* FIXME */ + return -EINVAL; + + default: + return vc_sm_cma_ioctl(file, cmd, arg); + } +} +#endif + +/* Device operations that we managed in this driver. */ +static const struct file_operations vc_sm_ops = { + .owner = THIS_MODULE, + .unlocked_ioctl = vc_sm_cma_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = vc_sm_cma_compat_ioctl, +#endif + .open = vc_sm_cma_open, + .release = vc_sm_cma_release, +}; + +/* Driver load/unload functions */ +/* Videocore connected. */ +static void vc_sm_connected_init(void) +{ + int ret; + struct vchi_instance_handle *vchi_instance; + struct vc_sm_version version; + struct vc_sm_result_t version_result; + + pr_info("[%s]: start\n", __func__); + + /* + * Initialize and create a VCHI connection for the shared memory service + * running on videocore. + */ + ret = vchi_initialise(&vchi_instance); + if (ret) { + pr_err("[%s]: failed to initialise VCHI instance (ret=%d)\n", + __func__, ret); + + return; + } + + ret = vchi_connect(vchi_instance); + if (ret) { + pr_err("[%s]: failed to connect VCHI instance (ret=%d)\n", + __func__, ret); + + return; + } + + /* Initialize an instance of the shared memory service. */ + sm_state->sm_handle = vc_sm_cma_vchi_init(vchi_instance, 1, + vc_sm_vpu_event); + if (!sm_state->sm_handle) { + pr_err("[%s]: failed to initialize shared memory service\n", + __func__); + + return; + } + + /* Create a debug fs directory entry (root). */ + sm_state->dir_root = debugfs_create_dir(VC_SM_DIR_ROOT_NAME, NULL); + + sm_state->dir_state.show = &vc_sm_cma_global_state_show; + sm_state->dir_state.dir_entry = + debugfs_create_file(VC_SM_STATE, 0444, sm_state->dir_root, + &sm_state->dir_state, + &vc_sm_cma_debug_fs_fops); + + INIT_LIST_HEAD(&sm_state->buffer_list); + + /* Create a shared memory device. */ + sm_state->misc_dev.minor = MISC_DYNAMIC_MINOR; + sm_state->misc_dev.name = DEVICE_NAME; + sm_state->misc_dev.fops = &vc_sm_ops; + sm_state->misc_dev.parent = NULL; + /* Temporarily set as 666 until udev rules have been sorted */ + sm_state->misc_dev.mode = 0666; + ret = misc_register(&sm_state->misc_dev); + if (ret) { + pr_err("vcsm-cma: failed to register misc device.\n"); + goto err_remove_debugfs; + } + + sm_state->data_knl = vc_sm_cma_create_priv_data(0); + if (!sm_state->data_knl) { + pr_err("[%s]: failed to create kernel private data tracker\n", + __func__); + goto err_remove_misc_dev; + } + + version.version = 2; + ret = vc_sm_cma_vchi_client_version(sm_state->sm_handle, &version, + &version_result, + &sm_state->int_trans_id); + if (ret) { + pr_err("[%s]: Failed to send version request %d\n", __func__, + ret); + } + + /* Done! */ + sm_inited = 1; + pr_info("[%s]: installed successfully\n", __func__); + return; + +err_remove_misc_dev: + misc_deregister(&sm_state->misc_dev); +err_remove_debugfs: + debugfs_remove_recursive(sm_state->dir_root); + vc_sm_cma_vchi_stop(&sm_state->sm_handle); +} + +/* Driver loading. */ +static int bcm2835_vc_sm_cma_probe(struct platform_device *pdev) +{ + pr_info("%s: Videocore shared memory driver\n", __func__); + + sm_state = devm_kzalloc(&pdev->dev, sizeof(*sm_state), GFP_KERNEL); + if (!sm_state) + return -ENOMEM; + sm_state->pdev = pdev; + mutex_init(&sm_state->map_lock); + + spin_lock_init(&sm_state->kernelid_map_lock); + idr_init_base(&sm_state->kernelid_map, 1); + + pdev->dev.dma_parms = devm_kzalloc(&pdev->dev, + sizeof(*pdev->dev.dma_parms), + GFP_KERNEL); + /* dma_set_max_seg_size checks if dma_parms is NULL. */ + dma_set_max_seg_size(&pdev->dev, 0x3FFFFFFF); + + vchiq_add_connected_callback(vc_sm_connected_init); + return 0; +} + +/* Driver unloading. */ +static int bcm2835_vc_sm_cma_remove(struct platform_device *pdev) +{ + pr_debug("[%s]: start\n", __func__); + if (sm_inited) { + misc_deregister(&sm_state->misc_dev); + + /* Remove all proc entries. */ + debugfs_remove_recursive(sm_state->dir_root); + + /* Stop the videocore shared memory service. */ + vc_sm_cma_vchi_stop(&sm_state->sm_handle); + } + + if (sm_state) { + idr_destroy(&sm_state->kernelid_map); + + /* Free the memory for the state structure. */ + mutex_destroy(&sm_state->map_lock); + } + + pr_debug("[%s]: end\n", __func__); + return 0; +} + +/* Kernel API calls */ +/* Get an internal resource handle mapped from the external one. */ +int vc_sm_cma_int_handle(void *handle) +{ + struct dma_buf *dma_buf = (struct dma_buf *)handle; + struct vc_sm_buffer *buf; + + /* Validate we can work with this device. */ + if (!sm_state || !handle) { + pr_err("[%s]: invalid input\n", __func__); + return 0; + } + + buf = (struct vc_sm_buffer *)dma_buf->priv; + return buf->vc_handle; +} +EXPORT_SYMBOL_GPL(vc_sm_cma_int_handle); + +/* Free a previously allocated shared memory handle and block. */ +int vc_sm_cma_free(void *handle) +{ + struct dma_buf *dma_buf = (struct dma_buf *)handle; + + /* Validate we can work with this device. */ + if (!sm_state || !handle) { + pr_err("[%s]: invalid input\n", __func__); + return -EPERM; + } + + pr_debug("%s: handle %p/dmabuf %p\n", __func__, handle, dma_buf); + + dma_buf_put(dma_buf); + + return 0; +} +EXPORT_SYMBOL_GPL(vc_sm_cma_free); + +/* Import a dmabuf to be shared with VC. */ +int vc_sm_cma_import_dmabuf(struct dma_buf *src_dmabuf, void **handle) +{ + struct dma_buf *new_dma_buf; + struct vc_sm_buffer *buf; + int ret; + + /* Validate we can work with this device. */ + if (!sm_state || !src_dmabuf || !handle) { + pr_err("[%s]: invalid input\n", __func__); + return -EPERM; + } + + ret = vc_sm_cma_import_dmabuf_internal(sm_state->data_knl, src_dmabuf, + -1, &new_dma_buf); + + if (!ret) { + pr_debug("%s: imported to ptr %p\n", __func__, new_dma_buf); + buf = (struct vc_sm_buffer *)new_dma_buf->priv; + + /* Assign valid handle at this time.*/ + *handle = new_dma_buf; + } else { + /* + * succeeded in importing the dma_buf, but then + * failed to look it up again. How? + * Release the fd again. + */ + pr_err("%s: imported vc_sm_cma_get_buffer failed %d\n", + __func__, ret); + } + + return ret; +} +EXPORT_SYMBOL_GPL(vc_sm_cma_import_dmabuf); + +static struct platform_driver bcm2835_vcsm_cma_driver = { + .probe = bcm2835_vc_sm_cma_probe, + .remove = bcm2835_vc_sm_cma_remove, + .driver = { + .name = DEVICE_NAME, + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(bcm2835_vcsm_cma_driver); + +MODULE_AUTHOR("Dave Stevenson"); +MODULE_DESCRIPTION("VideoCore CMA Shared Memory Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:vcsm-cma"); diff --git a/drivers/staging/vc04_services/vc-sm-cma/vc_sm.h b/drivers/staging/vc04_services/vc-sm-cma/vc_sm.h new file mode 100644 index 000000000000..f1c7b95b14ce --- /dev/null +++ b/drivers/staging/vc04_services/vc-sm-cma/vc_sm.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * VideoCore Shared Memory driver using CMA. + * + * Copyright: 2018, Raspberry Pi (Trading) Ltd + * + */ + +#ifndef VC_SM_H +#define VC_SM_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VC_SM_MAX_NAME_LEN 32 + +enum vc_sm_vpu_mapping_state { + VPU_NOT_MAPPED, + VPU_MAPPED, + VPU_UNMAPPING +}; + +struct vc_sm_alloc_data { + unsigned long num_pages; + void *priv_virt; + struct sg_table *sg_table; +}; + +struct vc_sm_imported { + struct dma_buf *dma_buf; + struct dma_buf_attachment *attach; + struct sg_table *sgt; +}; + +struct vc_sm_buffer { + struct list_head global_buffer_list; /* Global list of buffers. */ + + /* Index in the kernel_id idr so that we can find the + * mmal_msg_context again when servicing the VCHI reply. + */ + int kernel_id; + + size_t size; + + /* Lock over all the following state for this buffer */ + struct mutex lock; + struct list_head attachments; + + char name[VC_SM_MAX_NAME_LEN]; + + int in_use:1; /* Kernel is still using this resource */ + int imported:1; /* Imported dmabuf */ + + enum vc_sm_vpu_mapping_state vpu_state; + u32 vc_handle; /* VideoCore handle for this buffer */ + int vpu_allocated; /* + * The VPU made this allocation. Release the + * local dma_buf when the VPU releases the + * resource. + */ + + /* DMABUF related fields */ + struct dma_buf *dma_buf; + dma_addr_t dma_addr; + void *cookie; + + struct vc_sm_privdata_t *private; + + union { + struct vc_sm_alloc_data alloc; + struct vc_sm_imported import; + }; +}; + +#endif diff --git a/drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.c b/drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.c new file mode 100644 index 000000000000..6a203c60bf7f --- /dev/null +++ b/drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.c @@ -0,0 +1,505 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * VideoCore Shared Memory CMA allocator + * + * Copyright: 2018, Raspberry Pi (Trading) Ltd + * Copyright 2011-2012 Broadcom Corporation. All rights reserved. + * + * Based on vmcs_sm driver from Broadcom Corporation. + * + */ + +/* ---- Include Files ----------------------------------------------------- */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "vc_sm_cma_vchi.h" + +#define VC_SM_VER 1 +#define VC_SM_MIN_VER 0 + +/* ---- Private Constants and Types -------------------------------------- */ + +/* Command blocks come from a pool */ +#define SM_MAX_NUM_CMD_RSP_BLKS 32 + +struct sm_cmd_rsp_blk { + struct list_head head; /* To create lists */ + /* To be signaled when the response is there */ + struct completion cmplt; + + u32 id; + u16 length; + + u8 msg[VC_SM_MAX_MSG_LEN]; + + uint32_t wait:1; + uint32_t sent:1; + uint32_t alloc:1; + +}; + +struct sm_instance { + u32 num_connections; + struct vchi_service_handle *vchi_handle[VCHI_MAX_NUM_CONNECTIONS]; + struct task_struct *io_thread; + struct completion io_cmplt; + + vpu_event_cb vpu_event; + + /* Mutex over the following lists */ + struct mutex lock; + u32 trans_id; + struct list_head cmd_list; + struct list_head rsp_list; + struct list_head dead_list; + + struct sm_cmd_rsp_blk free_blk[SM_MAX_NUM_CMD_RSP_BLKS]; + + /* Mutex over the free_list */ + struct mutex free_lock; + struct list_head free_list; + + struct semaphore free_sema; + +}; + +/* ---- Private Variables ------------------------------------------------ */ + +/* ---- Private Function Prototypes -------------------------------------- */ + +/* ---- Private Functions ------------------------------------------------ */ +static int +bcm2835_vchi_msg_queue(struct vchi_service_handle *handle, + void *data, + unsigned int size) +{ + return vchi_queue_kernel_message(handle, + data, + size); +} + +static struct +sm_cmd_rsp_blk *vc_vchi_cmd_create(struct sm_instance *instance, + enum vc_sm_msg_type id, void *msg, + u32 size, int wait) +{ + struct sm_cmd_rsp_blk *blk; + struct vc_sm_msg_hdr_t *hdr; + + if (down_interruptible(&instance->free_sema)) { + blk = kmalloc(sizeof(*blk), GFP_KERNEL); + if (!blk) + return NULL; + + blk->alloc = 1; + init_completion(&blk->cmplt); + } else { + mutex_lock(&instance->free_lock); + blk = + list_first_entry(&instance->free_list, + struct sm_cmd_rsp_blk, head); + list_del(&blk->head); + mutex_unlock(&instance->free_lock); + } + + blk->sent = 0; + blk->wait = wait; + blk->length = sizeof(*hdr) + size; + + hdr = (struct vc_sm_msg_hdr_t *)blk->msg; + hdr->type = id; + mutex_lock(&instance->lock); + instance->trans_id++; + /* + * Retain the top bit for identifying asynchronous events, or VPU cmds. + */ + instance->trans_id &= ~0x80000000; + hdr->trans_id = instance->trans_id; + blk->id = instance->trans_id; + mutex_unlock(&instance->lock); + + if (size) + memcpy(hdr->body, msg, size); + + return blk; +} + +static void +vc_vchi_cmd_delete(struct sm_instance *instance, struct sm_cmd_rsp_blk *blk) +{ + if (blk->alloc) { + kfree(blk); + return; + } + + mutex_lock(&instance->free_lock); + list_add(&blk->head, &instance->free_list); + mutex_unlock(&instance->free_lock); + up(&instance->free_sema); +} + +static void vc_sm_cma_vchi_rx_ack(struct sm_instance *instance, + struct sm_cmd_rsp_blk *cmd, + struct vc_sm_result_t *reply, + u32 reply_len) +{ + mutex_lock(&instance->lock); + list_for_each_entry(cmd, + &instance->rsp_list, + head) { + if (cmd->id == reply->trans_id) + break; + } + mutex_unlock(&instance->lock); + + if (&cmd->head == &instance->rsp_list) { + //pr_debug("%s: received response %u, throw away...", + pr_err("%s: received response %u, throw away...", + __func__, + reply->trans_id); + } else if (reply_len > sizeof(cmd->msg)) { + pr_err("%s: reply too big (%u) %u, throw away...", + __func__, reply_len, + reply->trans_id); + } else { + memcpy(cmd->msg, reply, + reply_len); + complete(&cmd->cmplt); + } +} + +static int vc_sm_cma_vchi_videocore_io(void *arg) +{ + struct sm_instance *instance = arg; + struct sm_cmd_rsp_blk *cmd = NULL, *cmd_tmp; + struct vc_sm_result_t *reply; + u32 reply_len; + s32 status; + int svc_use = 1; + + while (1) { + if (svc_use) + vchi_service_release(instance->vchi_handle[0]); + svc_use = 0; + + if (wait_for_completion_interruptible(&instance->io_cmplt)) + continue; + + vchi_service_use(instance->vchi_handle[0]); + svc_use = 1; + + do { + /* + * Get new command and move it to response list + */ + mutex_lock(&instance->lock); + if (list_empty(&instance->cmd_list)) { + /* no more commands to process */ + mutex_unlock(&instance->lock); + break; + } + cmd = list_first_entry(&instance->cmd_list, + struct sm_cmd_rsp_blk, head); + list_move(&cmd->head, &instance->rsp_list); + cmd->sent = 1; + mutex_unlock(&instance->lock); + + /* Send the command */ + status = + bcm2835_vchi_msg_queue(instance->vchi_handle[0], + cmd->msg, cmd->length); + if (status) { + pr_err("%s: failed to queue message (%d)", + __func__, status); + } + + /* If no reply is needed then we're done */ + if (!cmd->wait) { + mutex_lock(&instance->lock); + list_del(&cmd->head); + mutex_unlock(&instance->lock); + vc_vchi_cmd_delete(instance, cmd); + continue; + } + + if (status) { + complete(&cmd->cmplt); + continue; + } + + } while (1); + + while (!vchi_msg_peek(instance->vchi_handle[0], (void **)&reply, + &reply_len, VCHI_FLAGS_NONE)) { + if (reply->trans_id & 0x80000000) { + /* Async event or cmd from the VPU */ + if (instance->vpu_event) + instance->vpu_event(instance, reply, + reply_len); + } else { + vc_sm_cma_vchi_rx_ack(instance, cmd, reply, + reply_len); + } + + vchi_msg_remove(instance->vchi_handle[0]); + } + + /* Go through the dead list and free them */ + mutex_lock(&instance->lock); + list_for_each_entry_safe(cmd, cmd_tmp, &instance->dead_list, + head) { + list_del(&cmd->head); + vc_vchi_cmd_delete(instance, cmd); + } + mutex_unlock(&instance->lock); + } + + return 0; +} + +static void vc_sm_cma_vchi_callback(void *param, + const enum vchi_callback_reason reason, + void *msg_handle) +{ + struct sm_instance *instance = param; + + (void)msg_handle; + + switch (reason) { + case VCHI_CALLBACK_MSG_AVAILABLE: + complete(&instance->io_cmplt); + break; + + case VCHI_CALLBACK_SERVICE_CLOSED: + pr_info("%s: service CLOSED!!", __func__); + default: + break; + } +} + +struct sm_instance *vc_sm_cma_vchi_init(struct vchi_instance_handle *vchi_instance, + unsigned int num_connections, + vpu_event_cb vpu_event) +{ + u32 i; + struct sm_instance *instance; + int status; + + pr_debug("%s: start", __func__); + + if (num_connections > VCHI_MAX_NUM_CONNECTIONS) { + pr_err("%s: unsupported number of connections %u (max=%u)", + __func__, num_connections, VCHI_MAX_NUM_CONNECTIONS); + + goto err_null; + } + /* Allocate memory for this instance */ + instance = kzalloc(sizeof(*instance), GFP_KERNEL); + + /* Misc initialisations */ + mutex_init(&instance->lock); + init_completion(&instance->io_cmplt); + INIT_LIST_HEAD(&instance->cmd_list); + INIT_LIST_HEAD(&instance->rsp_list); + INIT_LIST_HEAD(&instance->dead_list); + INIT_LIST_HEAD(&instance->free_list); + sema_init(&instance->free_sema, SM_MAX_NUM_CMD_RSP_BLKS); + mutex_init(&instance->free_lock); + for (i = 0; i < SM_MAX_NUM_CMD_RSP_BLKS; i++) { + init_completion(&instance->free_blk[i].cmplt); + list_add(&instance->free_blk[i].head, &instance->free_list); + } + + /* Open the VCHI service connections */ + instance->num_connections = num_connections; + for (i = 0; i < num_connections; i++) { + struct service_creation params = { + .version = VCHI_VERSION_EX(VC_SM_VER, VC_SM_MIN_VER), + .service_id = VC_SM_SERVER_NAME, + .callback = vc_sm_cma_vchi_callback, + .callback_param = instance, + }; + + status = vchi_service_open(vchi_instance, + ¶ms, &instance->vchi_handle[i]); + if (status) { + pr_err("%s: failed to open VCHI service (%d)", + __func__, status); + + goto err_close_services; + } + } + + /* Create the thread which takes care of all io to/from videoocore. */ + instance->io_thread = kthread_create(&vc_sm_cma_vchi_videocore_io, + (void *)instance, "SMIO"); + if (!instance->io_thread) { + pr_err("%s: failed to create SMIO thread", __func__); + + goto err_close_services; + } + instance->vpu_event = vpu_event; + set_user_nice(instance->io_thread, -10); + wake_up_process(instance->io_thread); + + pr_debug("%s: success - instance %p", __func__, instance); + return instance; + +err_close_services: + for (i = 0; i < instance->num_connections; i++) { + if (instance->vchi_handle[i]) + vchi_service_close(instance->vchi_handle[i]); + } + kfree(instance); +err_null: + pr_debug("%s: FAILED", __func__); + return NULL; +} + +int vc_sm_cma_vchi_stop(struct sm_instance **handle) +{ + struct sm_instance *instance; + u32 i; + + if (!handle) { + pr_err("%s: invalid pointer to handle %p", __func__, handle); + goto lock; + } + + if (!*handle) { + pr_err("%s: invalid handle %p", __func__, *handle); + goto lock; + } + + instance = *handle; + + /* Close all VCHI service connections */ + for (i = 0; i < instance->num_connections; i++) { + s32 success; + + vchi_service_use(instance->vchi_handle[i]); + + success = vchi_service_close(instance->vchi_handle[i]); + } + + kfree(instance); + + *handle = NULL; + return 0; + +lock: + return -EINVAL; +} + +static int vc_sm_cma_vchi_send_msg(struct sm_instance *handle, + enum vc_sm_msg_type msg_id, void *msg, + u32 msg_size, void *result, u32 result_size, + u32 *cur_trans_id, u8 wait_reply) +{ + int status = 0; + struct sm_instance *instance = handle; + struct sm_cmd_rsp_blk *cmd_blk; + + if (!handle) { + pr_err("%s: invalid handle", __func__); + return -EINVAL; + } + if (!msg) { + pr_err("%s: invalid msg pointer", __func__); + return -EINVAL; + } + + cmd_blk = + vc_vchi_cmd_create(instance, msg_id, msg, msg_size, wait_reply); + if (!cmd_blk) { + pr_err("[%s]: failed to allocate global tracking resource", + __func__); + return -ENOMEM; + } + + if (cur_trans_id) + *cur_trans_id = cmd_blk->id; + + mutex_lock(&instance->lock); + list_add_tail(&cmd_blk->head, &instance->cmd_list); + mutex_unlock(&instance->lock); + complete(&instance->io_cmplt); + + if (!wait_reply) + /* We're done */ + return 0; + + /* Wait for the response */ + if (wait_for_completion_interruptible(&cmd_blk->cmplt)) { + mutex_lock(&instance->lock); + if (!cmd_blk->sent) { + list_del(&cmd_blk->head); + mutex_unlock(&instance->lock); + vc_vchi_cmd_delete(instance, cmd_blk); + return -ENXIO; + } + + list_move(&cmd_blk->head, &instance->dead_list); + mutex_unlock(&instance->lock); + complete(&instance->io_cmplt); + return -EINTR; /* We're done */ + } + + if (result && result_size) { + memcpy(result, cmd_blk->msg, result_size); + } else { + struct vc_sm_result_t *res = + (struct vc_sm_result_t *)cmd_blk->msg; + status = (res->success == 0) ? 0 : -ENXIO; + } + + mutex_lock(&instance->lock); + list_del(&cmd_blk->head); + mutex_unlock(&instance->lock); + vc_vchi_cmd_delete(instance, cmd_blk); + return status; +} + +int vc_sm_cma_vchi_free(struct sm_instance *handle, struct vc_sm_free_t *msg, + u32 *cur_trans_id) +{ + return vc_sm_cma_vchi_send_msg(handle, VC_SM_MSG_TYPE_FREE, + msg, sizeof(*msg), 0, 0, cur_trans_id, 0); +} + +int vc_sm_cma_vchi_import(struct sm_instance *handle, struct vc_sm_import *msg, + struct vc_sm_import_result *result, u32 *cur_trans_id) +{ + return vc_sm_cma_vchi_send_msg(handle, VC_SM_MSG_TYPE_IMPORT, + msg, sizeof(*msg), result, sizeof(*result), + cur_trans_id, 1); +} + +int vc_sm_cma_vchi_client_version(struct sm_instance *handle, + struct vc_sm_version *msg, + struct vc_sm_result_t *result, + u32 *cur_trans_id) +{ + return vc_sm_cma_vchi_send_msg(handle, VC_SM_MSG_TYPE_CLIENT_VERSION, + //msg, sizeof(*msg), result, sizeof(*result), + //cur_trans_id, 1); + msg, sizeof(*msg), NULL, 0, + cur_trans_id, 0); +} + +int vc_sm_vchi_client_vc_mem_req_reply(struct sm_instance *handle, + struct vc_sm_vc_mem_request_result *msg, + uint32_t *cur_trans_id) +{ + return vc_sm_cma_vchi_send_msg(handle, + VC_SM_MSG_TYPE_VC_MEM_REQUEST_REPLY, + msg, sizeof(*msg), 0, 0, cur_trans_id, + 0); +} diff --git a/drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.h b/drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.h new file mode 100644 index 000000000000..e8db34bd1e91 --- /dev/null +++ b/drivers/staging/vc04_services/vc-sm-cma/vc_sm_cma_vchi.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * VideoCore Shared Memory CMA allocator + * + * Copyright: 2018, Raspberry Pi (Trading) Ltd + * Copyright 2011-2012 Broadcom Corporation. All rights reserved. + * + * Based on vmcs_sm driver from Broadcom Corporation. + * + */ + +#ifndef __VC_SM_CMA_VCHI_H__INCLUDED__ +#define __VC_SM_CMA_VCHI_H__INCLUDED__ + +#include "interface/vchi/vchi.h" + +#include "vc_sm_defs.h" + +/* + * Forward declare. + */ +struct sm_instance; + +typedef void (*vpu_event_cb)(struct sm_instance *instance, + struct vc_sm_result_t *reply, int reply_len); + +/* + * Initialize the shared memory service, opens up vchi connection to talk to it. + */ +struct sm_instance *vc_sm_cma_vchi_init(struct vchi_instance_handle *vchi_instance, + unsigned int num_connections, + vpu_event_cb vpu_event); + +/* + * Terminates the shared memory service. + */ +int vc_sm_cma_vchi_stop(struct sm_instance **handle); + +/* + * Ask the shared memory service to free up some memory that was previously + * allocated by the vc_sm_cma_vchi_alloc function call. + */ +int vc_sm_cma_vchi_free(struct sm_instance *handle, struct vc_sm_free_t *msg, + u32 *cur_trans_id); + +/* + * Import a contiguous block of memory and wrap it in a GPU MEM_HANDLE_T. + */ +int vc_sm_cma_vchi_import(struct sm_instance *handle, struct vc_sm_import *msg, + struct vc_sm_import_result *result, + u32 *cur_trans_id); + +int vc_sm_cma_vchi_client_version(struct sm_instance *handle, + struct vc_sm_version *msg, + struct vc_sm_result_t *result, + u32 *cur_trans_id); + +int vc_sm_vchi_client_vc_mem_req_reply(struct sm_instance *handle, + struct vc_sm_vc_mem_request_result *msg, + uint32_t *cur_trans_id); + +#endif /* __VC_SM_CMA_VCHI_H__INCLUDED__ */ diff --git a/drivers/staging/vc04_services/vc-sm-cma/vc_sm_defs.h b/drivers/staging/vc04_services/vc-sm-cma/vc_sm_defs.h new file mode 100644 index 000000000000..8a0d1f6dbfe8 --- /dev/null +++ b/drivers/staging/vc04_services/vc-sm-cma/vc_sm_defs.h @@ -0,0 +1,300 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * VideoCore Shared Memory CMA allocator + * + * Copyright: 2018, Raspberry Pi (Trading) Ltd + * + * Based on vc_sm_defs.h from the vmcs_sm driver Copyright Broadcom Corporation. + * All IPC messages are copied across to this file, even if the vc-sm-cma + * driver is not currently using them. + * + **************************************************************************** + */ + +#ifndef __VC_SM_DEFS_H__INCLUDED__ +#define __VC_SM_DEFS_H__INCLUDED__ + +/* FourCC code used for VCHI connection */ +#define VC_SM_SERVER_NAME MAKE_FOURCC("SMEM") + +/* Maximum message length */ +#define VC_SM_MAX_MSG_LEN (sizeof(union vc_sm_msg_union_t) + \ + sizeof(struct vc_sm_msg_hdr_t)) +#define VC_SM_MAX_RSP_LEN (sizeof(union vc_sm_msg_union_t)) + +/* Resource name maximum size */ +#define VC_SM_RESOURCE_NAME 32 + +/* + * Version to be reported to the VPU + * VPU assumes 0 (aka 1) which does not require the released callback, nor + * expect the client to handle VC_MEM_REQUESTS. + * Version 2 requires the released callback, and must support VC_MEM_REQUESTS. + */ +#define VC_SM_PROTOCOL_VERSION 2 + +enum vc_sm_msg_type { + /* Message types supported for HOST->VC direction */ + + /* Allocate shared memory block */ + VC_SM_MSG_TYPE_ALLOC, + /* Lock allocated shared memory block */ + VC_SM_MSG_TYPE_LOCK, + /* Unlock allocated shared memory block */ + VC_SM_MSG_TYPE_UNLOCK, + /* Unlock allocated shared memory block, do not answer command */ + VC_SM_MSG_TYPE_UNLOCK_NOANS, + /* Free shared memory block */ + VC_SM_MSG_TYPE_FREE, + /* Resize a shared memory block */ + VC_SM_MSG_TYPE_RESIZE, + /* Walk the allocated shared memory block(s) */ + VC_SM_MSG_TYPE_WALK_ALLOC, + + /* A previously applied action will need to be reverted */ + VC_SM_MSG_TYPE_ACTION_CLEAN, + + /* + * Import a physical address and wrap into a MEM_HANDLE_T. + * Release with VC_SM_MSG_TYPE_FREE. + */ + VC_SM_MSG_TYPE_IMPORT, + /* + *Tells VC the protocol version supported by this client. + * 2 supports the async/cmd messages from the VPU for final release + * of memory, and for VC allocations. + */ + VC_SM_MSG_TYPE_CLIENT_VERSION, + /* Response to VC request for memory */ + VC_SM_MSG_TYPE_VC_MEM_REQUEST_REPLY, + + /* + * Asynchronous/cmd messages supported for VC->HOST direction. + * Signalled by setting the top bit in vc_sm_result_t trans_id. + */ + + /* + * VC has finished with an imported memory allocation. + * Release any Linux reference counts on the underlying block. + */ + VC_SM_MSG_TYPE_RELEASED, + /* VC request for memory */ + VC_SM_MSG_TYPE_VC_MEM_REQUEST, + + VC_SM_MSG_TYPE_MAX +}; + +/* Type of memory to be allocated */ +enum vc_sm_alloc_type_t { + VC_SM_ALLOC_CACHED, + VC_SM_ALLOC_NON_CACHED, +}; + +/* Message header for all messages in HOST->VC direction */ +struct vc_sm_msg_hdr_t { + u32 type; + u32 trans_id; + u8 body[0]; + +}; + +/* Request to allocate memory (HOST->VC) */ +struct vc_sm_alloc_t { + /* type of memory to allocate */ + enum vc_sm_alloc_type_t type; + /* byte amount of data to allocate per unit */ + u32 base_unit; + /* number of unit to allocate */ + u32 num_unit; + /* alignment to be applied on allocation */ + u32 alignment; + /* identity of who allocated this block */ + u32 allocator; + /* resource name (for easier tracking on vc side) */ + char name[VC_SM_RESOURCE_NAME]; + +}; + +/* Result of a requested memory allocation (VC->HOST) */ +struct vc_sm_alloc_result_t { + /* Transaction identifier */ + u32 trans_id; + + /* Resource handle */ + u32 res_handle; + /* Pointer to resource buffer */ + u32 res_mem; + /* Resource base size (bytes) */ + u32 res_base_size; + /* Resource number */ + u32 res_num; + +}; + +/* Request to free a previously allocated memory (HOST->VC) */ +struct vc_sm_free_t { + /* Resource handle (returned from alloc) */ + u32 res_handle; + /* Resource buffer (returned from alloc) */ + u32 res_mem; + +}; + +/* Request to lock a previously allocated memory (HOST->VC) */ +struct vc_sm_lock_unlock_t { + /* Resource handle (returned from alloc) */ + u32 res_handle; + /* Resource buffer (returned from alloc) */ + u32 res_mem; + +}; + +/* Request to resize a previously allocated memory (HOST->VC) */ +struct vc_sm_resize_t { + /* Resource handle (returned from alloc) */ + u32 res_handle; + /* Resource buffer (returned from alloc) */ + u32 res_mem; + /* Resource *new* size requested (bytes) */ + u32 res_new_size; + +}; + +/* Result of a requested memory lock (VC->HOST) */ +struct vc_sm_lock_result_t { + /* Transaction identifier */ + u32 trans_id; + + /* Resource handle */ + u32 res_handle; + /* Pointer to resource buffer */ + u32 res_mem; + /* + * Pointer to former resource buffer if the memory + * was reallocated + */ + u32 res_old_mem; + +}; + +/* Generic result for a request (VC->HOST) */ +struct vc_sm_result_t { + /* Transaction identifier */ + u32 trans_id; + + s32 success; + +}; + +/* Request to revert a previously applied action (HOST->VC) */ +struct vc_sm_action_clean_t { + /* Action of interest */ + enum vc_sm_msg_type res_action; + /* Transaction identifier for the action of interest */ + u32 action_trans_id; + +}; + +/* Request to remove all data associated with a given allocator (HOST->VC) */ +struct vc_sm_free_all_t { + /* Allocator identifier */ + u32 allocator; +}; + +/* Request to import memory (HOST->VC) */ +struct vc_sm_import { + /* type of memory to allocate */ + enum vc_sm_alloc_type_t type; + /* pointer to the VC (ie physical) address of the allocated memory */ + u32 addr; + /* size of buffer */ + u32 size; + /* opaque handle returned in RELEASED messages */ + u32 kernel_id; + /* Allocator identifier */ + u32 allocator; + /* resource name (for easier tracking on vc side) */ + char name[VC_SM_RESOURCE_NAME]; +}; + +/* Result of a requested memory import (VC->HOST) */ +struct vc_sm_import_result { + /* Transaction identifier */ + u32 trans_id; + + /* Resource handle */ + u32 res_handle; +}; + +/* Notification that VC has finished with an allocation (VC->HOST) */ +struct vc_sm_released { + /* cmd type / trans_id */ + u32 cmd; + + /* pointer to the VC (ie physical) address of the allocated memory */ + u32 addr; + /* size of buffer */ + u32 size; + /* opaque handle returned in RELEASED messages */ + u32 kernel_id; + u32 vc_handle; +}; + +/* + * Client informing VC as to the protocol version it supports. + * >=2 requires the released callback, and supports VC asking for memory. + * Failure means that the firmware doesn't support this call, and therefore the + * client should either fail, or NOT rely on getting the released callback. + */ +struct vc_sm_version { + u32 version; +}; + +/* Request FROM VideoCore for some memory */ +struct vc_sm_vc_mem_request { + /* cmd type */ + u32 cmd; + + /* trans_id (from VPU) */ + u32 trans_id; + /* size of buffer */ + u32 size; + /* alignment of buffer */ + u32 align; + /* resource name (for easier tracking) */ + char name[VC_SM_RESOURCE_NAME]; + /* VPU handle for the resource */ + u32 vc_handle; +}; + +/* Response from the kernel to provide the VPU with some memory */ +struct vc_sm_vc_mem_request_result { + /* Transaction identifier for the VPU */ + u32 trans_id; + /* pointer to the physical address of the allocated memory */ + u32 addr; + /* opaque handle returned in RELEASED messages */ + u32 kernel_id; +}; + +/* Union of ALL messages */ +union vc_sm_msg_union_t { + struct vc_sm_alloc_t alloc; + struct vc_sm_alloc_result_t alloc_result; + struct vc_sm_free_t free; + struct vc_sm_lock_unlock_t lock_unlock; + struct vc_sm_action_clean_t action_clean; + struct vc_sm_resize_t resize; + struct vc_sm_lock_result_t lock_result; + struct vc_sm_result_t result; + struct vc_sm_free_all_t free_all; + struct vc_sm_import import; + struct vc_sm_import_result import_result; + struct vc_sm_version version; + struct vc_sm_released released; + struct vc_sm_vc_mem_request vc_request; + struct vc_sm_vc_mem_request_result vc_request_result; +}; + +#endif /* __VC_SM_DEFS_H__INCLUDED__ */ diff --git a/drivers/staging/vc04_services/vc-sm-cma/vc_sm_knl.h b/drivers/staging/vc04_services/vc-sm-cma/vc_sm_knl.h new file mode 100644 index 000000000000..988fdd967922 --- /dev/null +++ b/drivers/staging/vc04_services/vc-sm-cma/vc_sm_knl.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * VideoCore Shared Memory CMA allocator + * + * Copyright: 2018, Raspberry Pi (Trading) Ltd + * + * Based on vc_sm_defs.h from the vmcs_sm driver Copyright Broadcom Corporation. + * + */ + +#ifndef __VC_SM_KNL_H__INCLUDED__ +#define __VC_SM_KNL_H__INCLUDED__ + +#if !defined(__KERNEL__) +#error "This interface is for kernel use only..." +#endif + +/* Free a previously allocated or imported shared memory handle and block. */ +int vc_sm_cma_free(void *handle); + +/* Get an internal resource handle mapped from the external one. */ +int vc_sm_cma_int_handle(void *handle); + +/* Import a block of memory into the GPU space. */ +int vc_sm_cma_import_dmabuf(struct dma_buf *dmabuf, void **handle); + +#endif /* __VC_SM_KNL_H__INCLUDED__ */ From patchwork Mon May 4 09:25:44 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525265 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id DBC9417EF for ; Mon, 4 May 2020 09:26:34 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id BE3BB20735 for ; Mon, 4 May 2020 09:26:34 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="eH2orPfI" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728373AbgEDJ0e (ORCPT ); Mon, 4 May 2020 05:26:34 -0400 Received: from perceval.ideasonboard.com ([213.167.242.64]:56720 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728349AbgEDJ0d (ORCPT ); Mon, 4 May 2020 05:26:33 -0400 Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id C30DF11FC; Mon, 4 May 2020 11:26:28 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584389; bh=CyOI4WTR4plAUuSDgiW+zcvB3c4nvfTrbn2gr+jBzLo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=eH2orPfInAW/oSMDLsD/zefRcPac48EbC/B9OnhpNdkM0tFIZssWWFa63SWX2RT+5 3PEDls/we330pNn++Fiq+ZZ/zvq880tRO24xWnzUri905pqM/MQ0mSBJijbbqMCUrO MlkX5PAPGppAKxFt3yNT1ByUbCfWxNVH9rO7poWg= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson Subject: [PATCH v2 07/34] staging: bcm2835: Break MMAL support out from camera Date: Mon, 4 May 2020 12:25:44 +0300 Message-Id: <20200504092611.9798-8-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Jacopo Mondi The BCM2835 camera host is currently the only component that uses the VCHIQ MMAL interface. This will soon change with the upporting of BCM2835 ISP, which make use of the same interface. Break VCHIQ MMAL interface support out from camera host directory to make it possible for the ISP driver to use it as well. The only modification to the existing mmal code is the introduction of EXPORT_SYMBOL() for symbols required by bcm2835-camera and the addition of the module author and licenses. Signed-off-by: Jacopo Mondi --- drivers/staging/vc04_services/Kconfig | 2 ++ drivers/staging/vc04_services/Makefile | 7 +++--- .../vc04_services/bcm2835-camera/Kconfig | 1 + .../vc04_services/bcm2835-camera/Makefile | 4 ++-- .../staging/vc04_services/vchiq-mmal/Kconfig | 8 +++++++ .../staging/vc04_services/vchiq-mmal/Makefile | 8 +++++++ .../mmal-common.h | 0 .../mmal-encodings.h | 0 .../mmal-msg-common.h | 0 .../mmal-msg-format.h | 0 .../mmal-msg-port.h | 0 .../{bcm2835-camera => vchiq-mmal}/mmal-msg.h | 0 .../mmal-parameters.h | 0 .../mmal-vchiq.c | 22 +++++++++++++++++++ .../mmal-vchiq.h | 0 15 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 drivers/staging/vc04_services/vchiq-mmal/Kconfig create mode 100644 drivers/staging/vc04_services/vchiq-mmal/Makefile rename drivers/staging/vc04_services/{bcm2835-camera => vchiq-mmal}/mmal-common.h (100%) rename drivers/staging/vc04_services/{bcm2835-camera => vchiq-mmal}/mmal-encodings.h (100%) rename drivers/staging/vc04_services/{bcm2835-camera => vchiq-mmal}/mmal-msg-common.h (100%) rename drivers/staging/vc04_services/{bcm2835-camera => vchiq-mmal}/mmal-msg-format.h (100%) rename drivers/staging/vc04_services/{bcm2835-camera => vchiq-mmal}/mmal-msg-port.h (100%) rename drivers/staging/vc04_services/{bcm2835-camera => vchiq-mmal}/mmal-msg.h (100%) rename drivers/staging/vc04_services/{bcm2835-camera => vchiq-mmal}/mmal-parameters.h (100%) rename drivers/staging/vc04_services/{bcm2835-camera => vchiq-mmal}/mmal-vchiq.c (98%) rename drivers/staging/vc04_services/{bcm2835-camera => vchiq-mmal}/mmal-vchiq.h (100%) diff --git a/drivers/staging/vc04_services/Kconfig b/drivers/staging/vc04_services/Kconfig index b9f1f019ebd8..7ae21d7f359b 100644 --- a/drivers/staging/vc04_services/Kconfig +++ b/drivers/staging/vc04_services/Kconfig @@ -25,5 +25,7 @@ source "drivers/staging/vc04_services/bcm2835-camera/Kconfig" source "drivers/staging/vc04_services/vc-sm-cma/Kconfig" +source "drivers/staging/vc04_services/vchiq-mmal/Kconfig" + endif diff --git a/drivers/staging/vc04_services/Makefile b/drivers/staging/vc04_services/Makefile index 6e1abf494c1a..296aff05b9ab 100644 --- a/drivers/staging/vc04_services/Makefile +++ b/drivers/staging/vc04_services/Makefile @@ -10,9 +10,10 @@ vchiq-objs := \ interface/vchiq_arm/vchiq_util.o \ interface/vchiq_arm/vchiq_connected.o \ -obj-$(CONFIG_SND_BCM2835) += bcm2835-audio/ -obj-$(CONFIG_VIDEO_BCM2835) += bcm2835-camera/ -obj-$(CONFIG_BCM_VC_SM_CMA) += vc-sm-cma/ +obj-$(CONFIG_SND_BCM2835) += bcm2835-audio/ +obj-$(CONFIG_VIDEO_BCM2835) += bcm2835-camera/ +obj-$(CONFIG_BCM_VC_SM_CMA) += vc-sm-cma/ +obj-$(CONFIG_BCM2835_VCHIQ_MMAL) += vchiq-mmal/ ccflags-y += -D__VCCOREVER__=0x04000000 diff --git a/drivers/staging/vc04_services/bcm2835-camera/Kconfig b/drivers/staging/vc04_services/bcm2835-camera/Kconfig index c81baf2c111e..d0653d1ed3c7 100644 --- a/drivers/staging/vc04_services/bcm2835-camera/Kconfig +++ b/drivers/staging/vc04_services/bcm2835-camera/Kconfig @@ -4,6 +4,7 @@ config VIDEO_BCM2835 depends on MEDIA_SUPPORT depends on VIDEO_V4L2 && (ARCH_BCM2835 || COMPILE_TEST) select BCM2835_VCHIQ + select BCM2835_VCHIQ_MMAL select VIDEOBUF2_VMALLOC select BTREE help diff --git a/drivers/staging/vc04_services/bcm2835-camera/Makefile b/drivers/staging/vc04_services/bcm2835-camera/Makefile index 472f21e1f2a1..3a76d6ade428 100644 --- a/drivers/staging/vc04_services/bcm2835-camera/Makefile +++ b/drivers/staging/vc04_services/bcm2835-camera/Makefile @@ -1,11 +1,11 @@ # SPDX-License-Identifier: GPL-2.0 bcm2835-v4l2-$(CONFIG_VIDEO_BCM2835) := \ bcm2835-camera.o \ - controls.o \ - mmal-vchiq.o + controls.o obj-$(CONFIG_VIDEO_BCM2835) += bcm2835-v4l2.o ccflags-y += \ -I $(srctree)/$(src)/.. \ + -I $(srctree)/$(src)/../vchiq-mmal/ \ -D__VCCOREVER__=0x04000000 diff --git a/drivers/staging/vc04_services/vchiq-mmal/Kconfig b/drivers/staging/vc04_services/vchiq-mmal/Kconfig new file mode 100644 index 000000000000..106f71e709df --- /dev/null +++ b/drivers/staging/vc04_services/vchiq-mmal/Kconfig @@ -0,0 +1,8 @@ +config BCM2835_VCHIQ_MMAL + tristate "BCM2835 MMAL VCHIQ service" + depends on (ARCH_BCM2835 || COMPILE_TEST) + select BCM_VC_SM_CMA + help + Enables the MMAL API over VCHIQ interface as used for the + majority of the multimedia services on VideoCore. + Defaults to Y when the Broadcomd BCM2835 camera host is selected. diff --git a/drivers/staging/vc04_services/vchiq-mmal/Makefile b/drivers/staging/vc04_services/vchiq-mmal/Makefile new file mode 100644 index 000000000000..f8164c33aec3 --- /dev/null +++ b/drivers/staging/vc04_services/vchiq-mmal/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 +bcm2835-mmal-vchiq-objs := mmal-vchiq.o + +obj-$(CONFIG_BCM2835_VCHIQ_MMAL) += bcm2835-mmal-vchiq.o + +ccflags-y += \ + -I$(srctree)/$(src)/.. \ + -D__VCCOREVER__=0x04000000 diff --git a/drivers/staging/vc04_services/bcm2835-camera/mmal-common.h b/drivers/staging/vc04_services/vchiq-mmal/mmal-common.h similarity index 100% rename from drivers/staging/vc04_services/bcm2835-camera/mmal-common.h rename to drivers/staging/vc04_services/vchiq-mmal/mmal-common.h diff --git a/drivers/staging/vc04_services/bcm2835-camera/mmal-encodings.h b/drivers/staging/vc04_services/vchiq-mmal/mmal-encodings.h similarity index 100% rename from drivers/staging/vc04_services/bcm2835-camera/mmal-encodings.h rename to drivers/staging/vc04_services/vchiq-mmal/mmal-encodings.h diff --git a/drivers/staging/vc04_services/bcm2835-camera/mmal-msg-common.h b/drivers/staging/vc04_services/vchiq-mmal/mmal-msg-common.h similarity index 100% rename from drivers/staging/vc04_services/bcm2835-camera/mmal-msg-common.h rename to drivers/staging/vc04_services/vchiq-mmal/mmal-msg-common.h diff --git a/drivers/staging/vc04_services/bcm2835-camera/mmal-msg-format.h b/drivers/staging/vc04_services/vchiq-mmal/mmal-msg-format.h similarity index 100% rename from drivers/staging/vc04_services/bcm2835-camera/mmal-msg-format.h rename to drivers/staging/vc04_services/vchiq-mmal/mmal-msg-format.h diff --git a/drivers/staging/vc04_services/bcm2835-camera/mmal-msg-port.h b/drivers/staging/vc04_services/vchiq-mmal/mmal-msg-port.h similarity index 100% rename from drivers/staging/vc04_services/bcm2835-camera/mmal-msg-port.h rename to drivers/staging/vc04_services/vchiq-mmal/mmal-msg-port.h diff --git a/drivers/staging/vc04_services/bcm2835-camera/mmal-msg.h b/drivers/staging/vc04_services/vchiq-mmal/mmal-msg.h similarity index 100% rename from drivers/staging/vc04_services/bcm2835-camera/mmal-msg.h rename to drivers/staging/vc04_services/vchiq-mmal/mmal-msg.h diff --git a/drivers/staging/vc04_services/bcm2835-camera/mmal-parameters.h b/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h similarity index 100% rename from drivers/staging/vc04_services/bcm2835-camera/mmal-parameters.h rename to drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h diff --git a/drivers/staging/vc04_services/bcm2835-camera/mmal-vchiq.c b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c similarity index 98% rename from drivers/staging/vc04_services/bcm2835-camera/mmal-vchiq.c rename to drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c index de03b90021a8..cb3ffebb9ace 100644 --- a/drivers/staging/vc04_services/bcm2835-camera/mmal-vchiq.c +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -1396,6 +1397,7 @@ int vchiq_mmal_port_set_format(struct vchiq_mmal_instance *instance, return ret; } +EXPORT_SYMBOL_GPL(vchiq_mmal_port_set_format); int vchiq_mmal_port_parameter_set(struct vchiq_mmal_instance *instance, struct vchiq_mmal_port *port, @@ -1412,6 +1414,7 @@ int vchiq_mmal_port_parameter_set(struct vchiq_mmal_instance *instance, return ret; } +EXPORT_SYMBOL_GPL(vchiq_mmal_port_parameter_set); int vchiq_mmal_port_parameter_get(struct vchiq_mmal_instance *instance, struct vchiq_mmal_port *port, @@ -1428,6 +1431,7 @@ int vchiq_mmal_port_parameter_get(struct vchiq_mmal_instance *instance, return ret; } +EXPORT_SYMBOL_GPL(vchiq_mmal_port_parameter_get); /* enable a port * @@ -1458,6 +1462,7 @@ int vchiq_mmal_port_enable(struct vchiq_mmal_instance *instance, return ret; } +EXPORT_SYMBOL_GPL(vchiq_mmal_port_enable); int vchiq_mmal_port_disable(struct vchiq_mmal_instance *instance, struct vchiq_mmal_port *port) @@ -1478,6 +1483,7 @@ int vchiq_mmal_port_disable(struct vchiq_mmal_instance *instance, return ret; } +EXPORT_SYMBOL_GPL(vchiq_mmal_port_disable); /* ports will be connected in a tunneled manner so data buffers * are not handled by client. @@ -1565,6 +1571,7 @@ int vchiq_mmal_port_connect_tunnel(struct vchiq_mmal_instance *instance, return ret; } +EXPORT_SYMBOL_GPL(vchiq_mmal_port_connect_tunnel); int vchiq_mmal_submit_buffer(struct vchiq_mmal_instance *instance, struct vchiq_mmal_port *port, @@ -1583,6 +1590,7 @@ int vchiq_mmal_submit_buffer(struct vchiq_mmal_instance *instance, return 0; } +EXPORT_SYMBOL_GPL(vchiq_mmal_submit_buffer); int mmal_vchi_buffer_init(struct vchiq_mmal_instance *instance, struct mmal_buffer *buf) @@ -1595,6 +1603,7 @@ int mmal_vchi_buffer_init(struct vchiq_mmal_instance *instance, buf->msg_context = msg_context; return 0; } +EXPORT_SYMBOL_GPL(mmal_vchi_buffer_init); int mmal_vchi_buffer_cleanup(struct mmal_buffer *buf) { @@ -1606,6 +1615,7 @@ int mmal_vchi_buffer_cleanup(struct mmal_buffer *buf) return 0; } +EXPORT_SYMBOL_GPL(mmal_vchi_buffer_cleanup); /* Initialise a mmal component and its ports * @@ -1693,6 +1703,7 @@ int vchiq_mmal_component_init(struct vchiq_mmal_instance *instance, return ret; } +EXPORT_SYMBOL_GPL(vchiq_mmal_component_init); /* * cause a mmal component to be destroyed @@ -1714,6 +1725,7 @@ int vchiq_mmal_component_finalise(struct vchiq_mmal_instance *instance, return ret; } +EXPORT_SYMBOL_GPL(vchiq_mmal_component_finalise); /* * cause a mmal component to be enabled @@ -1739,6 +1751,7 @@ int vchiq_mmal_component_enable(struct vchiq_mmal_instance *instance, return ret; } +EXPORT_SYMBOL_GPL(vchiq_mmal_component_enable); /* * cause a mmal component to be enabled @@ -1764,6 +1777,7 @@ int vchiq_mmal_component_disable(struct vchiq_mmal_instance *instance, return ret; } +EXPORT_SYMBOL_GPL(vchiq_mmal_component_disable); int vchiq_mmal_version(struct vchiq_mmal_instance *instance, u32 *major_out, u32 *minor_out) @@ -1779,6 +1793,7 @@ int vchiq_mmal_version(struct vchiq_mmal_instance *instance, return ret; } +EXPORT_SYMBOL_GPL(vchiq_mmal_version); int vchiq_mmal_finalise(struct vchiq_mmal_instance *instance) { @@ -1809,6 +1824,7 @@ int vchiq_mmal_finalise(struct vchiq_mmal_instance *instance) return status; } +EXPORT_SYMBOL_GPL(vchiq_mmal_finalise); int vchiq_mmal_init(struct vchiq_mmal_instance **out_instance) { @@ -1889,3 +1905,9 @@ int vchiq_mmal_init(struct vchiq_mmal_instance **out_instance) kfree(instance); return -ENODEV; } +EXPORT_SYMBOL_GPL(vchiq_mmal_init); + +MODULE_DESCRIPTION("BCM2835 MMAL VCHIQ interface"); +MODULE_AUTHOR("Dave Stevenson, "); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.0.1"); diff --git a/drivers/staging/vc04_services/bcm2835-camera/mmal-vchiq.h b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h similarity index 100% rename from drivers/staging/vc04_services/bcm2835-camera/mmal-vchiq.h rename to drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h From patchwork Mon May 4 09:25:45 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525263 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 7F068139A for ; Mon, 4 May 2020 09:26:34 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 67EB620757 for ; Mon, 4 May 2020 09:26:34 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Lo/MRgkl" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728360AbgEDJ0d (ORCPT ); Mon, 4 May 2020 05:26:33 -0400 Received: from perceval.ideasonboard.com ([213.167.242.64]:56776 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728354AbgEDJ0d (ORCPT ); Mon, 4 May 2020 05:26:33 -0400 Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id DEC324F7; Mon, 4 May 2020 11:26:29 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584390; bh=5y8gL6TF9WoQOmxoBPKOcac7F/nYWm/EzcVyDIQHKHU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Lo/MRgklnRHvFeP+4cmfFElTFGSDqXamzG62txfatvdQUzRCkl+uGxCvZXO/+oruM ZvSL/czx3P6kAvET88VfseYdzuAcjws5u9Vysu/08eyTuAmqHZNiZtRKc4Uebc1+ca /V+lMtIFU17vSS9pt7pG+UKcDho4lAWm+FzVEbcA= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson , Dave Stevenson Subject: [PATCH v2 08/34] staging: mmal-vchiq: Allocate and free components as required Date: Mon, 4 May 2020 12:25:45 +0300 Message-Id: <20200504092611.9798-9-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Dave Stevenson The existing code assumed that there would only ever be 4 components, and never freed the entries once used. Allow arbitrary creation and destruction of components. Signed-off-by: Dave Stevenson Signed-off-by: Jacopo Mondi --- .../vc04_services/vchiq-mmal/mmal-vchiq.c | 29 ++++++++++++------- .../vc04_services/vchiq-mmal/mmal-vchiq.h | 1 + 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c index cb3ffebb9ace..cf38ec78f77f 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c @@ -32,8 +32,11 @@ #define USE_VCHIQ_ARM #include "interface/vchi/vchi.h" -/* maximum number of components supported */ -#define VCHIQ_MMAL_MAX_COMPONENTS 4 +/* + * maximum number of components supported. + * This matches the maximum permitted by default on the VPU + */ +#define VCHIQ_MMAL_MAX_COMPONENTS 64 /*#define FULL_MSG_DUMP 1*/ @@ -168,8 +171,6 @@ struct vchiq_mmal_instance { /* protect accesses to context_map */ struct mutex context_map_lock; - /* component to use next */ - int component_idx; struct vchiq_mmal_component component[VCHIQ_MMAL_MAX_COMPONENTS]; /* ordered workqueue to process all bulk operations */ @@ -1626,18 +1627,24 @@ int vchiq_mmal_component_init(struct vchiq_mmal_instance *instance, { int ret; int idx; /* port index */ - struct vchiq_mmal_component *component; + struct vchiq_mmal_component *component = NULL; if (mutex_lock_interruptible(&instance->vchiq_mutex)) return -EINTR; - if (instance->component_idx == VCHIQ_MMAL_MAX_COMPONENTS) { + for (idx = 0; idx < VCHIQ_MMAL_MAX_COMPONENTS; idx++) { + if (!instance->component[idx].in_use) { + component = &instance->component[idx]; + component->in_use = 1; + break; + } + } + + if (!component) { ret = -EINVAL; /* todo is this correct error? */ goto unlock; } - component = &instance->component[instance->component_idx]; - ret = create_component(instance, component, name); if (ret < 0) { pr_err("%s: failed to create component %d (Not enough GPU mem?)\n", @@ -1688,8 +1695,6 @@ int vchiq_mmal_component_init(struct vchiq_mmal_instance *instance, goto release_component; } - instance->component_idx++; - *component_out = component; mutex_unlock(&instance->vchiq_mutex); @@ -1699,6 +1704,8 @@ int vchiq_mmal_component_init(struct vchiq_mmal_instance *instance, release_component: destroy_component(instance, component); unlock: + if (component) + component->in_use = 0; mutex_unlock(&instance->vchiq_mutex); return ret; @@ -1721,6 +1728,8 @@ int vchiq_mmal_component_finalise(struct vchiq_mmal_instance *instance, ret = destroy_component(instance, component); + component->in_use = 0; + mutex_unlock(&instance->vchiq_mutex); return ret; diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h index 47897e81ec58..4e34728d87e5 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h @@ -82,6 +82,7 @@ struct vchiq_mmal_port { }; struct vchiq_mmal_component { + u32 in_use:1; u32 enabled:1; u32 handle; /* VideoCore handle for component */ u32 inputs; /* Number of input ports */ From patchwork Mon May 4 09:25:46 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525267 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 353B51668 for ; Mon, 4 May 2020 09:26:36 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 1194C20746 for ; Mon, 4 May 2020 09:26:36 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="g6dhque0" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728377AbgEDJ0f (ORCPT ); Mon, 4 May 2020 05:26:35 -0400 Received: from perceval.ideasonboard.com ([213.167.242.64]:56776 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728366AbgEDJ0e (ORCPT ); Mon, 4 May 2020 05:26:34 -0400 Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 7DF47304; Mon, 4 May 2020 11:26:30 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584390; bh=7xUZQpxVOmB9fSAG9xYpU/sM2nx14zLbVch455+y+34=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=g6dhque0tAGFH/rWZ629wwOyiIP4noerTIK+Tbuk4q4nDGTPgicdlrExzyD5Z0wPs DQG+txr8U54+DNi57zZfSmK2D1ryooqeVGMFTShMYjH6OwJ+u0sO+r+ieKeyRgWZYn w0BkzWhqskudZ07JqQVhiVMfl/7B2TlZCwWPN06k= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson , Dave Stevenson Subject: [PATCH v2 09/34] staging: mmal-vchiq: Avoid use of bool in structures Date: Mon, 4 May 2020 12:25:46 +0300 Message-Id: <20200504092611.9798-10-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Dave Stevenson Fixes up a checkpatch error "Avoid using bool structure members because of possible alignment issues". Signed-off-by: Dave Stevenson Signed-off-by: Jacopo Mondi --- drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c index cf38ec78f77f..1ac27755df51 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c @@ -1754,7 +1754,7 @@ int vchiq_mmal_component_enable(struct vchiq_mmal_instance *instance, ret = enable_component(instance, component); if (ret == 0) - component->enabled = true; + component->enabled = 1; mutex_unlock(&instance->vchiq_mutex); From patchwork Mon May 4 09:25:47 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525269 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id CC19417EF for ; Mon, 4 May 2020 09:26:36 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id AA08120746 for ; Mon, 4 May 2020 09:26:36 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="ShPm053E" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728379AbgEDJ0g (ORCPT ); Mon, 4 May 2020 05:26:36 -0400 Received: from perceval.ideasonboard.com ([213.167.242.64]:56720 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728370AbgEDJ0e (ORCPT ); Mon, 4 May 2020 05:26:34 -0400 Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 22BD811F9; Mon, 4 May 2020 11:26:31 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584391; bh=ntdxwDQmZU4bWN8xfDwY3ZlZ286reJx4q9xY8xWN940=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ShPm053EI3WHSM8jS8gxaDssxlTIWemm0l3WNzV3GWycEh3F089lZ0KYPBIOXyB0/ 2I53ENGYP20rW/ETz8FphJMvIKwysDUjogryiIv6WVTUjFfb9CCTjhteBE83OF6PF7 xGDjpdETQShzD1iSOFxPA0Sy/RYwc0UEgI6bQL5Q= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson , Dave Stevenson Subject: [PATCH v2 10/34] staging: mmal-vchiq: Make timeout a defined parameter Date: Mon, 4 May 2020 12:25:47 +0300 Message-Id: <20200504092611.9798-11-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Dave Stevenson The timeout period for VPU communications is a useful thing to extend when debugging. Set it via a define, rather than a magic number buried in the code. Signed-off-by: Dave Stevenson Signed-off-by: Jacopo Mondi --- drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c index 1ac27755df51..12c2f86b02ad 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c @@ -38,6 +38,12 @@ */ #define VCHIQ_MMAL_MAX_COMPONENTS 64 +/* + * Timeout for synchronous msg responses in seconds. + * Helpful to increase this if stopping in the VPU debugger. + */ +#define SYNC_MSG_TIMEOUT 3 + /*#define FULL_MSG_DUMP 1*/ #ifdef DEBUG @@ -686,7 +692,7 @@ static int send_synchronous_mmal_msg(struct vchiq_mmal_instance *instance, } timeout = wait_for_completion_timeout(&msg_context->u.sync.cmplt, - 3 * HZ); + SYNC_MSG_TIMEOUT * HZ); if (timeout == 0) { pr_err("timed out waiting for sync completion\n"); ret = -ETIME; From patchwork Mon May 4 09:25:48 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525273 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 03B42139A for ; Mon, 4 May 2020 09:26:39 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id DD2D720746 for ; Mon, 4 May 2020 09:26:38 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="NyRVDVcO" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728383AbgEDJ0i (ORCPT ); Mon, 4 May 2020 05:26:38 -0400 Received: from perceval.ideasonboard.com ([213.167.242.64]:56720 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728374AbgEDJ0h (ORCPT ); Mon, 4 May 2020 05:26:37 -0400 Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id BB316121D; Mon, 4 May 2020 11:26:31 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584392; bh=GB/QVnQ+X3MsXZLCXgD+8w80zFPAIP95tWKCTKSwSE0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=NyRVDVcOJyMcty8xut1QQUqjk2+VtVrTvTc9zDEd9DMt/2FnlIQn9IofKIZX2bhwH 8yRFUXt9/TWvYEJjF7hUKqbGZINAh0FwPrfC8OkjGL30P5w6D5ilJJ+FSEqpj+VXwe mEKt4doVAjUOCp321t7vIC2yXiDfaTlxi647U27s= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson , Dave Stevenson Subject: [PATCH v2 11/34] staging: mmal-vchiq: Make a mmal_buf struct for passing parameters Date: Mon, 4 May 2020 12:25:48 +0300 Message-Id: <20200504092611.9798-12-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Dave Stevenson The callback from vchi_mmal to the client was growing lots of extra parameters. Consolidate them into a single struct instead of growing the list further. The struct is associated with the client buffer, therefore there are various changes to setup various containers for the struct, and pass the appropriate members. Signed-off-by: Dave Stevenson Signed-off-by: Jacopo Mondi --- .../bcm2835-camera/bcm2835-camera.c | 62 ++++++++++++------- .../vc04_services/vchiq-mmal/mmal-common.h | 5 ++ .../vc04_services/vchiq-mmal/mmal-vchiq.c | 29 ++++++--- .../vc04_services/vchiq-mmal/mmal-vchiq.h | 3 +- 4 files changed, 64 insertions(+), 35 deletions(-) diff --git a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c index 597acef35d0b..68a2260d88f5 100644 --- a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c +++ b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c @@ -75,6 +75,12 @@ static const struct v4l2_fract tpf_max = {.numerator = 1, .denominator = FPS_MIN}, tpf_default = {.numerator = 1000, .denominator = 30000}; +/* Container for MMAL and VB2 buffers*/ +struct vb2_mmal_buffer { + struct vb2_v4l2_buffer vb; + struct mmal_buffer mmal; +}; + /* video formats */ static struct mmal_fmt formats[] = { { @@ -261,14 +267,15 @@ static int buffer_init(struct vb2_buffer *vb) { struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vb->vb2_queue); struct vb2_v4l2_buffer *vb2 = to_vb2_v4l2_buffer(vb); - struct mmal_buffer *buf = container_of(vb2, struct mmal_buffer, vb); + struct vb2_mmal_buffer *buf = + container_of(vb2, struct vb2_mmal_buffer, vb); v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s: dev:%p, vb %p\n", __func__, dev, vb); - buf->buffer = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); - buf->buffer_size = vb2_plane_size(&buf->vb.vb2_buf, 0); + buf->mmal.buffer = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); + buf->mmal.buffer_size = vb2_plane_size(&buf->vb.vb2_buf, 0); - return mmal_vchi_buffer_init(dev->instance, buf); + return mmal_vchi_buffer_init(dev->instance, &buf->mmal); } static int buffer_prepare(struct vb2_buffer *vb) @@ -297,11 +304,13 @@ static void buffer_cleanup(struct vb2_buffer *vb) { struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vb->vb2_queue); struct vb2_v4l2_buffer *vb2 = to_vb2_v4l2_buffer(vb); - struct mmal_buffer *buf = container_of(vb2, struct mmal_buffer, vb); + struct vb2_mmal_buffer *buf = + container_of(vb2, struct vb2_mmal_buffer, vb); v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s: dev:%p, vb %p\n", __func__, dev, vb); - mmal_vchi_buffer_cleanup(buf); + + mmal_vchi_buffer_cleanup(&buf->mmal); } static inline bool is_capturing(struct bm2835_mmal_dev *dev) @@ -313,14 +322,16 @@ static inline bool is_capturing(struct bm2835_mmal_dev *dev) static void buffer_cb(struct vchiq_mmal_instance *instance, struct vchiq_mmal_port *port, int status, - struct mmal_buffer *buf, - unsigned long length, u32 mmal_flags, s64 dts, s64 pts) + struct mmal_buffer *mmal_buf) { struct bm2835_mmal_dev *dev = port->cb_ctx; + struct vb2_mmal_buffer *buf = + container_of(mmal_buf, struct vb2_mmal_buffer, mmal); v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s: status:%d, buf:%p, length:%lu, flags %u, pts %lld\n", - __func__, status, buf, length, mmal_flags, pts); + __func__, status, buf, mmal_buf->length, mmal_buf->mmal_flags, + mmal_buf->pts); if (status) { /* error in transfer */ @@ -331,7 +342,7 @@ static void buffer_cb(struct vchiq_mmal_instance *instance, return; } - if (length == 0) { + if (mmal_buf->length == 0) { /* stream ended */ if (dev->capture.frame_count) { /* empty buffer whilst capturing - expected to be an @@ -347,7 +358,8 @@ static void buffer_cb(struct vchiq_mmal_instance *instance, &dev->capture.frame_count, sizeof(dev->capture.frame_count)); } - if (vchiq_mmal_submit_buffer(instance, port, buf)) + if (vchiq_mmal_submit_buffer(instance, port, + &buf->mmal)) v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "Failed to return EOS buffer"); } else { @@ -367,16 +379,16 @@ static void buffer_cb(struct vchiq_mmal_instance *instance, return; } - if (dev->capture.vc_start_timestamp != -1 && pts) { + if (dev->capture.vc_start_timestamp != -1 && mmal_buf->pts) { ktime_t timestamp; - s64 runtime_us = pts - + s64 runtime_us = mmal_buf->pts - dev->capture.vc_start_timestamp; timestamp = ktime_add_us(dev->capture.kernel_start_ts, runtime_us); v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "Convert start time %llu and %llu with offset %llu to %llu\n", ktime_to_ns(dev->capture.kernel_start_ts), - dev->capture.vc_start_timestamp, pts, + dev->capture.vc_start_timestamp, mmal_buf->pts, ktime_to_ns(timestamp)); buf->vb.vb2_buf.timestamp = ktime_to_ns(timestamp); } else { @@ -385,13 +397,13 @@ static void buffer_cb(struct vchiq_mmal_instance *instance, buf->vb.sequence = dev->capture.sequence++; buf->vb.field = V4L2_FIELD_NONE; - vb2_set_plane_payload(&buf->vb.vb2_buf, 0, length); - if (mmal_flags & MMAL_BUFFER_HEADER_FLAG_KEYFRAME) + vb2_set_plane_payload(&buf->vb.vb2_buf, 0, mmal_buf->length); + if (mmal_buf->mmal_flags & MMAL_BUFFER_HEADER_FLAG_KEYFRAME) buf->vb.flags |= V4L2_BUF_FLAG_KEYFRAME; vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); - if (mmal_flags & MMAL_BUFFER_HEADER_FLAG_EOS && + if (mmal_buf->mmal_flags & MMAL_BUFFER_HEADER_FLAG_EOS && is_capturing(dev)) { v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "Grab another frame as buffer has EOS"); @@ -475,14 +487,16 @@ static void buffer_queue(struct vb2_buffer *vb) { struct bm2835_mmal_dev *dev = vb2_get_drv_priv(vb->vb2_queue); struct vb2_v4l2_buffer *vb2 = to_vb2_v4l2_buffer(vb); - struct mmal_buffer *buf = container_of(vb2, struct mmal_buffer, vb); + struct vb2_mmal_buffer *buf = + container_of(vb2, struct vb2_mmal_buffer, vb); int ret; v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "%s: dev:%p buf:%p, idx %u\n", __func__, dev, buf, vb2->vb2_buf.index); - ret = vchiq_mmal_submit_buffer(dev->instance, dev->capture.port, buf); + ret = vchiq_mmal_submit_buffer(dev->instance, dev->capture.port, + &buf->mmal); if (ret < 0) v4l2_err(&dev->v4l2_dev, "%s: error submitting buffer\n", __func__); @@ -595,7 +609,7 @@ static void stop_streaming(struct vb2_queue *vq) dev->capture.frame_count = 0; /* ensure a format has actually been set */ - if (!dev->capture.port) { + if (!port) { v4l2_err(&dev->v4l2_dev, "no capture port - stream not started?\n"); return; @@ -615,11 +629,11 @@ static void stop_streaming(struct vb2_queue *vq) /* disable the connection from camera to encoder */ ret = vchiq_mmal_port_disable(dev->instance, dev->capture.camera_port); - if (!ret && dev->capture.camera_port != dev->capture.port) { + if (!ret && dev->capture.camera_port != port) { v4l2_dbg(1, bcm2835_v4l2_debug, &dev->v4l2_dev, "disabling port\n"); - ret = vchiq_mmal_port_disable(dev->instance, dev->capture.port); - } else if (dev->capture.camera_port != dev->capture.port) { + ret = vchiq_mmal_port_disable(dev->instance, port); + } else if (dev->capture.camera_port != port) { v4l2_err(&dev->v4l2_dev, "port_disable failed, error %d\n", ret); } @@ -1921,7 +1935,7 @@ static int bcm2835_mmal_probe(struct platform_device *pdev) q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; q->io_modes = VB2_MMAP | VB2_USERPTR | VB2_READ; q->drv_priv = dev; - q->buf_struct_size = sizeof(struct mmal_buffer); + q->buf_struct_size = sizeof(struct vb2_mmal_buffer); q->ops = &bm2835_mmal_video_qops; q->mem_ops = &vb2_vmalloc_memops; q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-common.h b/drivers/staging/vc04_services/vchiq-mmal/mmal-common.h index ff5398737b4a..a635389208fc 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-common.h +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-common.h @@ -49,6 +49,11 @@ struct mmal_buffer { unsigned long buffer_size; /* size of allocated buffer */ struct mmal_msg_context *msg_context; + + unsigned long length; + u32 mmal_flags; + s64 dts; + s64 pts; }; /* */ diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c index 12c2f86b02ad..9af964422e42 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c @@ -253,17 +253,25 @@ static void buffer_work_cb(struct work_struct *work) { struct mmal_msg_context *msg_context = container_of(work, struct mmal_msg_context, u.bulk.work); + struct mmal_buffer *buffer = msg_context->u.bulk.buffer; + + if (!buffer) { + pr_err("%s: ctx: %p, No mmal buffer to pass details\n", + __func__, msg_context); + return; + } + + buffer->length = msg_context->u.bulk.buffer_used; + buffer->mmal_flags = msg_context->u.bulk.mmal_flags; + buffer->dts = msg_context->u.bulk.dts; + buffer->pts = msg_context->u.bulk.pts; atomic_dec(&msg_context->u.bulk.port->buffers_with_vpu); msg_context->u.bulk.port->buffer_cb(msg_context->u.bulk.instance, msg_context->u.bulk.port, msg_context->u.bulk.status, - msg_context->u.bulk.buffer, - msg_context->u.bulk.buffer_used, - msg_context->u.bulk.mmal_flags, - msg_context->u.bulk.dts, - msg_context->u.bulk.pts); + msg_context->u.bulk.buffer); } /* workqueue scheduled callback to handle receiving buffers @@ -1321,11 +1329,14 @@ static int port_disable(struct vchiq_mmal_instance *instance, mmalbuf = list_entry(buf_head, struct mmal_buffer, list); list_del(buf_head); - if (port->buffer_cb) + if (port->buffer_cb) { + mmalbuf->length = 0; + mmalbuf->mmal_flags = 0; + mmalbuf->dts = MMAL_TIME_UNKNOWN; + mmalbuf->pts = MMAL_TIME_UNKNOWN; port->buffer_cb(instance, - port, 0, mmalbuf, 0, 0, - MMAL_TIME_UNKNOWN, - MMAL_TIME_UNKNOWN); + port, 0, mmalbuf); + } } spin_unlock_irqrestore(&port->slock, flags); diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h index 4e34728d87e5..cca7289761c2 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h @@ -44,8 +44,7 @@ struct vchiq_mmal_port; typedef void (*vchiq_mmal_buffer_cb)( struct vchiq_mmal_instance *instance, struct vchiq_mmal_port *port, - int status, struct mmal_buffer *buffer, - unsigned long length, u32 mmal_flags, s64 dts, s64 pts); + int status, struct mmal_buffer *buffer); struct vchiq_mmal_port { u32 enabled:1; From patchwork Mon May 4 09:25:49 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525271 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id BD379139A for ; Mon, 4 May 2020 09:26:37 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A143220746 for ; Mon, 4 May 2020 09:26:37 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="kdvK7+e+" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728381AbgEDJ0h (ORCPT ); Mon, 4 May 2020 05:26:37 -0400 Received: from perceval.ideasonboard.com ([213.167.242.64]:56776 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728375AbgEDJ0g (ORCPT ); Mon, 4 May 2020 05:26:36 -0400 Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 583B1122D; Mon, 4 May 2020 11:26:32 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584392; bh=bcrBk5pjmC1dZXsZyCSNkxIOOdWR3MzWn3YpkXbieIw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=kdvK7+e+IrOXCAdbKZUyZUAqhJ/7HXHIbLbesqRU0YZl1BgpLet+ZC2S6LoF1yNdn 6la3Odcu4LwHn72OhskjuhArfeLUghcRhrcZwe6+GLOdKcPUV6paFkmmIzz41Zpn4Q RQgGYI1iddtMhHdVTkZpg9fY7APX2AFLUEwj8p+A= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson , Dave Stevenson Subject: [PATCH v2 12/34] staging: mmal-vchiq: Add support for event callbacks. Date: Mon, 4 May 2020 12:25:49 +0300 Message-Id: <20200504092611.9798-13-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Dave Stevenson (Preparation for the codec driver). The codec uses the event mechanism to report things such as resolution changes. It is signalled by the cmd field of the buffer being non-zero. Add support for passing this information out to the client. Signed-off-by: Dave Stevenson Signed-off-by: Jacopo Mondi --- .../vc04_services/vchiq-mmal/mmal-common.h | 1 + .../vc04_services/vchiq-mmal/mmal-msg.h | 35 ++++ .../vc04_services/vchiq-mmal/mmal-vchiq.c | 170 ++++++++++++++++-- .../vc04_services/vchiq-mmal/mmal-vchiq.h | 4 + 4 files changed, 196 insertions(+), 14 deletions(-) diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-common.h b/drivers/staging/vc04_services/vchiq-mmal/mmal-common.h index a635389208fc..b78c180e0c79 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-common.h +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-common.h @@ -50,6 +50,7 @@ struct mmal_buffer { struct mmal_msg_context *msg_context; + u32 cmd; /* MMAL command. 0=data. */ unsigned long length; u32 mmal_flags; s64 dts; diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-msg.h b/drivers/staging/vc04_services/vchiq-mmal/mmal-msg.h index 43cc59316f90..607ea384f715 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-msg.h +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-msg.h @@ -346,6 +346,41 @@ struct mmal_msg_port_parameter_get_reply { /* event messages */ #define MMAL_WORKER_EVENT_SPACE 256 +/* Four CC's for events */ +#define MMAL_FOURCC(a, b, c, d) ((a) | (b << 8) | (c << 16) | (d << 24)) + +#define MMAL_EVENT_ERROR MMAL_FOURCC('E', 'R', 'R', 'O') +#define MMAL_EVENT_EOS MMAL_FOURCC('E', 'E', 'O', 'S') +#define MMAL_EVENT_FORMAT_CHANGED MMAL_FOURCC('E', 'F', 'C', 'H') +#define MMAL_EVENT_PARAMETER_CHANGED MMAL_FOURCC('E', 'P', 'C', 'H') + +/* Structs for each of the event message payloads */ +struct mmal_msg_event_eos { + u32 port_type; /**< Type of port that received the end of stream */ + u32 port_index; /**< Index of port that received the end of stream */ +}; + +/** Format changed event data. */ +struct mmal_msg_event_format_changed { + /* Minimum size of buffers the port requires */ + u32 buffer_size_min; + /* Minimum number of buffers the port requires */ + u32 buffer_num_min; + /* Size of buffers the port recommends for optimal performance. + * A value of zero means no special recommendation. + */ + u32 buffer_size_recommended; + /* Number of buffers the port recommends for optimal + * performance. A value of zero means no special recommendation. + */ + u32 buffer_num_recommended; + + u32 es_ptr; + struct mmal_es_format format; + union mmal_es_specific_format es; + u8 extradata[MMAL_FORMAT_EXTRADATA_MAX_SIZE]; +}; + struct mmal_msg_event_to_host { u32 client_component; /* component context */ diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c index 9af964422e42..8358e25851c1 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c @@ -145,6 +145,8 @@ struct mmal_msg_context { /* Presentation and Decode timestamps */ s64 pts; s64 dts; + /* MMAL buffer command flag */ + u32 cmd; int status; /* context status */ @@ -232,18 +234,6 @@ release_msg_context(struct mmal_msg_context *msg_context) kfree(msg_context); } -/* deals with receipt of event to host message */ -static void event_to_host_cb(struct vchiq_mmal_instance *instance, - struct mmal_msg *msg, u32 msg_len) -{ - pr_debug("unhandled event\n"); - pr_debug("component:%u port type:%d num:%d cmd:0x%x length:%d\n", - msg->u.event_to_host.client_component, - msg->u.event_to_host.port_type, - msg->u.event_to_host.port_num, - msg->u.event_to_host.cmd, msg->u.event_to_host.length); -} - /* workqueue scheduled callback * * we do this because it is important we do not call any other vchiq @@ -265,13 +255,18 @@ static void buffer_work_cb(struct work_struct *work) buffer->mmal_flags = msg_context->u.bulk.mmal_flags; buffer->dts = msg_context->u.bulk.dts; buffer->pts = msg_context->u.bulk.pts; + buffer->cmd = msg_context->u.bulk.cmd; - atomic_dec(&msg_context->u.bulk.port->buffers_with_vpu); + if (!buffer->cmd) + atomic_dec(&msg_context->u.bulk.port->buffers_with_vpu); msg_context->u.bulk.port->buffer_cb(msg_context->u.bulk.instance, msg_context->u.bulk.port, msg_context->u.bulk.status, msg_context->u.bulk.buffer); + + if (buffer->cmd) + mutex_unlock(&msg_context->u.bulk.port->event_context_mutex); } /* workqueue scheduled callback to handle receiving buffers @@ -350,6 +345,7 @@ static int bulk_receive(struct vchiq_mmal_instance *instance, msg_context->u.bulk.buffer_used = rd_len; msg_context->u.bulk.dts = msg->u.buffer_from_host.buffer_header.dts; msg_context->u.bulk.pts = msg->u.buffer_from_host.buffer_header.pts; + msg_context->u.bulk.cmd = msg->u.buffer_from_host.buffer_header.cmd; queue_work(msg_context->instance->bulk_wq, &msg_context->u.bulk.buffer_to_host_work); @@ -451,6 +447,103 @@ buffer_from_host(struct vchiq_mmal_instance *instance, return ret; } +/* deals with receipt of event to host message */ +static void event_to_host_cb(struct vchiq_mmal_instance *instance, + struct mmal_msg *msg, u32 msg_len) +{ + /* FIXME: Not going to work on 64 bit */ + struct vchiq_mmal_component *component = + (struct vchiq_mmal_component *)msg->u.event_to_host.client_component; + struct vchiq_mmal_port *port = NULL; + struct mmal_msg_context *msg_context; + u32 port_num = msg->u.event_to_host.port_num; + + if (msg->u.buffer_from_host.drvbuf.magic == MMAL_MAGIC) { + pr_err("%s: MMAL_MSG_TYPE_BUFFER_TO_HOST with bad magic\n", + __func__); + return; + } + + switch (msg->u.event_to_host.port_type) { + case MMAL_PORT_TYPE_CONTROL: + if (port_num) { + pr_err("%s: port_num of %u >= number of ports 1", + __func__, port_num); + return; + } + port = &component->control; + break; + case MMAL_PORT_TYPE_INPUT: + if (port_num >= component->inputs) { + pr_err("%s: port_num of %u >= number of ports %u", + __func__, port_num, + port_num >= component->inputs); + return; + } + port = &component->input[port_num]; + break; + case MMAL_PORT_TYPE_OUTPUT: + if (port_num >= component->outputs) { + pr_err("%s: port_num of %u >= number of ports %u", + __func__, port_num, + port_num >= component->outputs); + return; + } + port = &component->output[port_num]; + break; + case MMAL_PORT_TYPE_CLOCK: + if (port_num >= component->clocks) { + pr_err("%s: port_num of %u >= number of ports %u", + __func__, port_num, + port_num >= component->clocks); + return; + } + port = &component->clock[port_num]; + break; + default: + break; + } + + if (!mutex_trylock(&port->event_context_mutex)) { + pr_err("dropping event 0x%x\n", msg->u.event_to_host.cmd); + return; + } + msg_context = port->event_context; + + if (msg->h.status != MMAL_MSG_STATUS_SUCCESS) { + /* message reception had an error */ + //pr_warn + pr_err("%s: error %d in reply\n", __func__, msg->h.status); + + msg_context->u.bulk.status = msg->h.status; + } else if (msg->u.event_to_host.length > MMAL_WORKER_EVENT_SPACE) { + /* data is not in message, queue a bulk receive */ + pr_err("%s: payload not in message - bulk receive??! NOT SUPPORTED\n", + __func__); + msg_context->u.bulk.status = -1; + } else { + memcpy(msg_context->u.bulk.buffer->buffer, + msg->u.event_to_host.data, + msg->u.event_to_host.length); + + msg_context->u.bulk.buffer_used = + msg->u.event_to_host.length; + + msg_context->u.bulk.mmal_flags = 0; + msg_context->u.bulk.dts = MMAL_TIME_UNKNOWN; + msg_context->u.bulk.pts = MMAL_TIME_UNKNOWN; + msg_context->u.bulk.cmd = msg->u.event_to_host.cmd; + + pr_debug("event component:%u port type:%d num:%d cmd:0x%x length:%d\n", + msg->u.event_to_host.client_component, + msg->u.event_to_host.port_type, + msg->u.event_to_host.port_num, + msg->u.event_to_host.cmd, msg->u.event_to_host.length); + } + + schedule_work(&msg_context->u.bulk.work); +} + /* deals with receipt of buffer to host message */ static void buffer_to_host_cb(struct vchiq_mmal_instance *instance, struct mmal_msg *msg, u32 msg_len) @@ -1334,6 +1427,7 @@ static int port_disable(struct vchiq_mmal_instance *instance, mmalbuf->mmal_flags = 0; mmalbuf->dts = MMAL_TIME_UNKNOWN; mmalbuf->pts = MMAL_TIME_UNKNOWN; + mmalbuf->cmd = 0; port->buffer_cb(instance, port, 0, mmalbuf); } @@ -1635,6 +1729,43 @@ int mmal_vchi_buffer_cleanup(struct mmal_buffer *buf) } EXPORT_SYMBOL_GPL(mmal_vchi_buffer_cleanup); +static void init_event_context(struct vchiq_mmal_instance *instance, + struct vchiq_mmal_port *port) +{ + struct mmal_msg_context *ctx = get_msg_context(instance); + + mutex_init(&port->event_context_mutex); + + port->event_context = ctx; + ctx->u.bulk.instance = instance; + ctx->u.bulk.port = port; + ctx->u.bulk.buffer = + kzalloc(sizeof(*ctx->u.bulk.buffer), GFP_KERNEL); + if (!ctx->u.bulk.buffer) + goto release_msg_context; + ctx->u.bulk.buffer->buffer = kzalloc(MMAL_WORKER_EVENT_SPACE, + GFP_KERNEL); + if (!ctx->u.bulk.buffer->buffer) + goto release_buffer; + + INIT_WORK(&ctx->u.bulk.work, buffer_work_cb); + return; + +release_buffer: + kfree(ctx->u.bulk.buffer); +release_msg_context: + release_msg_context(ctx); +} + +static void free_event_context(struct vchiq_mmal_port *port) +{ + struct mmal_msg_context *ctx = port->event_context; + + kfree(ctx->u.bulk.buffer->buffer); + kfree(ctx->u.bulk.buffer); + release_msg_context(ctx); +} + /* Initialise a mmal component and its ports * */ @@ -1678,6 +1809,7 @@ int vchiq_mmal_component_init(struct vchiq_mmal_instance *instance, ret = port_info_get(instance, &component->control); if (ret < 0) goto release_component; + init_event_context(instance, &component->control); for (idx = 0; idx < component->inputs; idx++) { component->input[idx].type = MMAL_PORT_TYPE_INPUT; @@ -1688,6 +1820,7 @@ int vchiq_mmal_component_init(struct vchiq_mmal_instance *instance, ret = port_info_get(instance, &component->input[idx]); if (ret < 0) goto release_component; + init_event_context(instance, &component->input[idx]); } for (idx = 0; idx < component->outputs; idx++) { @@ -1699,6 +1832,7 @@ int vchiq_mmal_component_init(struct vchiq_mmal_instance *instance, ret = port_info_get(instance, &component->output[idx]); if (ret < 0) goto release_component; + init_event_context(instance, &component->output[idx]); } for (idx = 0; idx < component->clocks; idx++) { @@ -1710,6 +1844,7 @@ int vchiq_mmal_component_init(struct vchiq_mmal_instance *instance, ret = port_info_get(instance, &component->clock[idx]); if (ret < 0) goto release_component; + init_event_context(instance, &component->clock[idx]); } *component_out = component; @@ -1735,7 +1870,7 @@ EXPORT_SYMBOL_GPL(vchiq_mmal_component_init); int vchiq_mmal_component_finalise(struct vchiq_mmal_instance *instance, struct vchiq_mmal_component *component) { - int ret; + int ret, idx; if (mutex_lock_interruptible(&instance->vchiq_mutex)) return -EINTR; @@ -1747,6 +1882,13 @@ int vchiq_mmal_component_finalise(struct vchiq_mmal_instance *instance, component->in_use = 0; + for (idx = 0; idx < component->inputs; idx++) + free_event_context(&component->input[idx]); + for (idx = 0; idx < component->outputs; idx++) + free_event_context(&component->output[idx]); + for (idx = 0; idx < component->clocks; idx++) + free_event_context(&component->clock[idx]); + mutex_unlock(&instance->vchiq_mutex); return ret; diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h index cca7289761c2..eeef5ee9467a 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h @@ -78,6 +78,10 @@ struct vchiq_mmal_port { vchiq_mmal_buffer_cb buffer_cb; /* callback context */ void *cb_ctx; + + /* ensure serialised use of the one event context structure */ + struct mutex event_context_mutex; + struct mmal_msg_context *event_context; }; struct vchiq_mmal_component { From patchwork Mon May 4 09:25:50 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525275 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 636DA17EF for ; Mon, 4 May 2020 09:26:39 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 4BDF520735 for ; Mon, 4 May 2020 09:26:39 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="UijvZyFB" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728387AbgEDJ0i (ORCPT ); Mon, 4 May 2020 05:26:38 -0400 Received: from perceval.ideasonboard.com ([213.167.242.64]:56776 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728369AbgEDJ0i (ORCPT ); Mon, 4 May 2020 05:26:38 -0400 Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id E7D3E18B4; Mon, 4 May 2020 11:26:32 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584393; bh=fdUn+2I6xsfvP9tQOWzLWMsJD0N8ZbkCbnhRD98iPe8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=UijvZyFBccniQwc/fPo6qgnZXvhsIe4J/vKNfe09bhsJ4qXFe1z79hY1rIeFZaII9 GYIuaXB/GLqxoscVgOh2/4yJTQiwhPMMhZ87m5ZaXcDTyW+WVzFdWgX9SjDj5Kczru 3Wihoe4rO8EoqJRWa2x1hWE0JUEhVH7bUqst+kGs= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson , Dave Stevenson Subject: [PATCH v2 13/34] staging: mmal-vchiq: Support sending data to MMAL ports Date: Mon, 4 May 2020 12:25:50 +0300 Message-Id: <20200504092611.9798-14-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Dave Stevenson Add the ability to send data to ports. This only supports zero copy mode as the required bulk transfer setup calls are not done. Signed-off-by: Dave Stevenson Signed-off-by: Jacopo Mondi --- .../vc04_services/vchiq-mmal/mmal-vchiq.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c index 8358e25851c1..63a46818184f 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c @@ -422,11 +422,19 @@ buffer_from_host(struct vchiq_mmal_instance *instance, m.u.buffer_from_host.buffer_header.data = (u32)(unsigned long)buf->buffer; m.u.buffer_from_host.buffer_header.alloc_size = buf->buffer_size; - m.u.buffer_from_host.buffer_header.length = 0; /* nothing used yet */ - m.u.buffer_from_host.buffer_header.offset = 0; /* no offset */ - m.u.buffer_from_host.buffer_header.flags = 0; /* no flags */ - m.u.buffer_from_host.buffer_header.pts = MMAL_TIME_UNKNOWN; - m.u.buffer_from_host.buffer_header.dts = MMAL_TIME_UNKNOWN; + if (port->type == MMAL_PORT_TYPE_OUTPUT) { + m.u.buffer_from_host.buffer_header.length = 0; + m.u.buffer_from_host.buffer_header.offset = 0; + m.u.buffer_from_host.buffer_header.flags = 0; + m.u.buffer_from_host.buffer_header.pts = MMAL_TIME_UNKNOWN; + m.u.buffer_from_host.buffer_header.dts = MMAL_TIME_UNKNOWN; + } else { + m.u.buffer_from_host.buffer_header.length = buf->length; + m.u.buffer_from_host.buffer_header.offset = 0; + m.u.buffer_from_host.buffer_header.flags = buf->mmal_flags; + m.u.buffer_from_host.buffer_header.pts = buf->pts; + m.u.buffer_from_host.buffer_header.dts = buf->dts; + } /* clear buffer type sepecific data */ memset(&m.u.buffer_from_host.buffer_header_type_specific, 0, From patchwork Mon May 4 09:25:51 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525277 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 25997139A for ; Mon, 4 May 2020 09:26:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 04BEE20757 for ; Mon, 4 May 2020 09:26:40 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="rlX6xa3q" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728389AbgEDJ0j (ORCPT ); Mon, 4 May 2020 05:26:39 -0400 Received: from perceval.ideasonboard.com ([213.167.242.64]:56720 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727930AbgEDJ0i (ORCPT ); Mon, 4 May 2020 05:26:38 -0400 Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 83E5B4F7; Mon, 4 May 2020 11:26:33 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584393; bh=uoB7RWfsDI1tUtPfy6NsvS2LrOneETgjCM1h0RmOJrs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=rlX6xa3qAb3dMi6OFHejESTCwsxp3zBFNU88UC/MKPqrWM/sC3uiftRTdMd4mYrx7 ++xEe4lm/1aoTEtIT2gqPQjLbx2Dnadbb07v9KspoC6zr+bNVc6+88kZdYZZTMdVgp M7UmNNCtbOBpPVvKIIb9oVROqzV19EoklabEhf3o= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson , Dave Stevenson Subject: [PATCH v2 14/34] staging: mmal-vchiq: Fixup vchiq-mmal include ordering Date: Mon, 4 May 2020 12:25:51 +0300 Message-Id: <20200504092611.9798-15-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Dave Stevenson There were dependencies on including the headers in the correct order. Fix up the headers so that they include the other headers that they depend on themselves. Signed-off-by: Dave Stevenson Signed-off-by: Jacopo Mondi --- drivers/staging/vc04_services/vchiq-mmal/mmal-msg.h | 1 + drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-msg.h b/drivers/staging/vc04_services/vchiq-mmal/mmal-msg.h index 607ea384f715..bd83159403e5 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-msg.h +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-msg.h @@ -38,6 +38,7 @@ #include "mmal-msg-common.h" #include "mmal-msg-format.h" #include "mmal-msg-port.h" +#include "mmal-vchiq.h" enum mmal_msg_type { MMAL_MSG_TYPE_QUIT = 1, diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h index eeef5ee9467a..7a490f29737a 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h @@ -16,6 +16,7 @@ #ifndef MMAL_VCHIQ_H #define MMAL_VCHIQ_H +#include "mmal-common.h" #include "mmal-msg-format.h" #define MAX_PORT_COUNT 4 From patchwork Mon May 4 09:25:52 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525279 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 40DF0139A for ; Mon, 4 May 2020 09:26:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 28EAE20746 for ; Mon, 4 May 2020 09:26:41 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="MTZH1uky" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728345AbgEDJ0k (ORCPT ); Mon, 4 May 2020 05:26:40 -0400 Received: from perceval.ideasonboard.com ([213.167.242.64]:56776 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728374AbgEDJ0j (ORCPT ); Mon, 4 May 2020 05:26:39 -0400 Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 2DE2111FC; Mon, 4 May 2020 11:26:34 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584394; bh=vtGkog12vd6FBp8Cm5HUcfVOwismuLj2ST1JROgCHfA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=MTZH1ukyCkVMJ6duBMLSdothgkSsH96UPsMGf2WEdzMzt0gIHTn/Qx1fnjKEgPysK jiNAJPymRqQKnYCXs6lBDgPt12X+4kBypJF9fy63BNYvmPj9mJj2DbNY/GRiMOwagR 547fvFwU2lc5mAwfbzToS88WtL0upvLNk/LDQ2ZQ= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson , Dave Stevenson Subject: [PATCH v2 15/34] staging: mmal-vchiq: Use vc-sm-cma to support zero copy Date: Mon, 4 May 2020 12:25:52 +0300 Message-Id: <20200504092611.9798-16-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Dave Stevenson With the vc-sm-cma driver we can support zero copy of buffers between the kernel and VPU. Add this support to vchiq-mmal. Signed-off-by: Dave Stevenson staging: vc-sm-cma: Use a void* pointer as the handle within the kernel The driver was using an unsigned int as the handle to the outside world, and doing a nasty cast to the struct dmabuf when handed it back. This breaks badly with a 64 bit kernel where the pointer doesn't fit in an unsigned int. Switch to using a void* within the kernel. Reality is that it is a struct dma_buf*, but advertising it as such to other drivers seems to encourage the use of it as such, and I'm not sure on the implications of that. Signed-off-by: Dave Stevenson Signed-off-by: Jacopo Mondi Reported-by: kbuild test robot --- .../vc04_services/vchiq-mmal/mmal-common.h | 4 ++ .../vc04_services/vchiq-mmal/mmal-vchiq.c | 66 ++++++++++++++++++- .../vc04_services/vchiq-mmal/mmal-vchiq.h | 1 + 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-common.h b/drivers/staging/vc04_services/vchiq-mmal/mmal-common.h index b78c180e0c79..3893a280fcfa 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-common.h +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-common.h @@ -50,6 +50,10 @@ struct mmal_buffer { struct mmal_msg_context *msg_context; + struct dma_buf *dma_buf;/* Exported dmabuf fd from videobuf2 */ + void *vcsm_handle; /* VCSM handle having imported the dmabuf */ + u32 vc_handle; /* VC handle to that dmabuf */ + u32 cmd; /* MMAL command. 0=data. */ unsigned long length; u32 mmal_flags; diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c index 63a46818184f..c65c262cffbb 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c @@ -26,9 +26,12 @@ #include #include "mmal-common.h" +#include "mmal-parameters.h" #include "mmal-vchiq.h" #include "mmal-msg.h" +#include "vc-sm-cma/vc_sm_knl.h" + #define USE_VCHIQ_ARM #include "interface/vchi/vchi.h" @@ -419,8 +422,13 @@ buffer_from_host(struct vchiq_mmal_instance *instance, /* buffer header */ m.u.buffer_from_host.buffer_header.cmd = 0; - m.u.buffer_from_host.buffer_header.data = - (u32)(unsigned long)buf->buffer; + if (port->zero_copy) { + m.u.buffer_from_host.buffer_header.data = buf->vc_handle; + } else { + m.u.buffer_from_host.buffer_header.data = + (u32)(unsigned long)buf->buffer; + } + m.u.buffer_from_host.buffer_header.alloc_size = buf->buffer_size; if (port->type == MMAL_PORT_TYPE_OUTPUT) { m.u.buffer_from_host.buffer_header.length = 0; @@ -585,6 +593,22 @@ static void buffer_to_host_cb(struct vchiq_mmal_instance *instance, msg_context->u.bulk.status = msg->h.status; + } else if (msg->u.buffer_from_host.is_zero_copy) { + /* + * Zero copy buffer, so nothing to do. + * Copy buffer info and make callback. + */ + msg_context->u.bulk.buffer_used = + msg->u.buffer_from_host.buffer_header.length; + msg_context->u.bulk.mmal_flags = + msg->u.buffer_from_host.buffer_header.flags; + msg_context->u.bulk.dts = + msg->u.buffer_from_host.buffer_header.dts; + msg_context->u.bulk.pts = + msg->u.buffer_from_host.buffer_header.pts; + msg_context->u.bulk.cmd = + msg->u.buffer_from_host.buffer_header.cmd; + } else if (msg->u.buffer_from_host.buffer_header.length == 0) { /* empty buffer */ if (msg->u.buffer_from_host.buffer_header.flags & @@ -1532,6 +1556,9 @@ int vchiq_mmal_port_parameter_set(struct vchiq_mmal_instance *instance, mutex_unlock(&instance->vchiq_mutex); + if (parameter == MMAL_PARAMETER_ZERO_COPY && !ret) + port->zero_copy = !!(*(bool *)value); + return ret; } EXPORT_SYMBOL_GPL(vchiq_mmal_port_parameter_set); @@ -1700,6 +1727,31 @@ int vchiq_mmal_submit_buffer(struct vchiq_mmal_instance *instance, unsigned long flags = 0; int ret; + /* + * We really want to do this in mmal_vchi_buffer_init but can't as + * videobuf2 won't let us have the dmabuf there. + */ + if (port->zero_copy && buffer->dma_buf && !buffer->vcsm_handle) { + pr_debug("%s: import dmabuf %p\n", __func__, buffer->dma_buf); + ret = vc_sm_cma_import_dmabuf(buffer->dma_buf, + &buffer->vcsm_handle); + if (ret) { + pr_err("%s: vc_sm_import_dmabuf_fd failed, ret %d\n", + __func__, ret); + return ret; + } + + buffer->vc_handle = vc_sm_cma_int_handle(buffer->vcsm_handle); + if (!buffer->vc_handle) { + pr_err("%s: vc_sm_int_handle failed %d\n", + __func__, ret); + vc_sm_cma_free(buffer->vcsm_handle); + return ret; + } + pr_debug("%s: import dmabuf %p - got vc handle %08X\n", + __func__, buffer->dma_buf, buffer->vc_handle); + } + ret = buffer_from_host(instance, port, buffer); if (ret == -EINVAL) { /* Port is disabled. Queue for when it is enabled. */ @@ -1733,6 +1785,16 @@ int mmal_vchi_buffer_cleanup(struct mmal_buffer *buf) release_msg_context(msg_context); buf->msg_context = NULL; + if (buf->vcsm_handle) { + int ret; + + pr_debug("%s: vc_sm_cma_free on handle %p\n", __func__, + buf->vcsm_handle); + ret = vc_sm_cma_free(buf->vcsm_handle); + if (ret) + pr_err("%s: vcsm_free failed, ret %d\n", __func__, ret); + buf->vcsm_handle = 0; + } return 0; } EXPORT_SYMBOL_GPL(mmal_vchi_buffer_cleanup); diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h index 7a490f29737a..ae6c69ba16ee 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h @@ -49,6 +49,7 @@ typedef void (*vchiq_mmal_buffer_cb)( struct vchiq_mmal_port { u32 enabled:1; + u32 zero_copy:1; u32 handle; u32 type; /* port type, cached to use on port info set */ u32 index; /* port index, cached to use on port info set */ From patchwork Mon May 4 09:25:53 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525281 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id CCE441862 for ; Mon, 4 May 2020 09:26:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id B5FA420735 for ; Mon, 4 May 2020 09:26:41 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="e5iyAC44" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728392AbgEDJ0l (ORCPT ); Mon, 4 May 2020 05:26:41 -0400 Received: from perceval.ideasonboard.com ([213.167.242.64]:56720 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728388AbgEDJ0k (ORCPT ); Mon, 4 May 2020 05:26:40 -0400 Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id C4E891209; Mon, 4 May 2020 11:26:34 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584395; bh=E9H01W765i5eEsfdtVh7yWH+dX2/Uxq53FSkgDcRFoA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=e5iyAC44YKufoFCdEUa1rb5vpv6zumV7c7bjJW5C0GnjmEBM1ObDtbXC7HYveMMzw rsxBF/ogDmgOmj8JP3t5c6sIU92hwooBi4dh+wi2WzMd+5LFeOOsbkRlcaH+s8PG+q LCVlyiiJSpTwqFuxCR5L7qmR4gU8TSOUzjpX858Q= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson , Dave Stevenson Subject: [PATCH v2 16/34] staging: mmal-vchiq: Fix client_component for 64 bit kernel Date: Mon, 4 May 2020 12:25:53 +0300 Message-Id: <20200504092611.9798-17-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Dave Stevenson The MMAL client_component field is used with the event mechanism to allow the client to identify the component for which the event is generated. The field is only 32bits in size, therefore we can't use a pointer to the component in a 64 bit kernel. Component handles are already held in an array per VCHI instance, so use the array index as the client_component handle to avoid having to create a new IDR for this purpose. Signed-off-by: Dave Stevenson Signed-off-by: Jacopo Mondi --- .../staging/vc04_services/vchiq-mmal/mmal-vchiq.c | 12 +++++++++--- .../staging/vc04_services/vchiq-mmal/mmal-vchiq.h | 1 + 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c index c65c262cffbb..5872690b404d 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c @@ -467,9 +467,9 @@ buffer_from_host(struct vchiq_mmal_instance *instance, static void event_to_host_cb(struct vchiq_mmal_instance *instance, struct mmal_msg *msg, u32 msg_len) { - /* FIXME: Not going to work on 64 bit */ + int comp_idx = msg->u.event_to_host.client_component; struct vchiq_mmal_component *component = - (struct vchiq_mmal_component *)msg->u.event_to_host.client_component; + &instance->component[comp_idx]; struct vchiq_mmal_port *port = NULL; struct mmal_msg_context *msg_context; u32 port_num = msg->u.event_to_host.port_num; @@ -1068,7 +1068,7 @@ static int create_component(struct vchiq_mmal_instance *instance, /* build component create message */ m.h.type = MMAL_MSG_TYPE_COMPONENT_CREATE; - m.u.component_create.client_component = (u32)(unsigned long)component; + m.u.component_create.client_component = component->client_component; strncpy(m.u.component_create.name, name, sizeof(m.u.component_create.name)); @@ -1863,6 +1863,12 @@ int vchiq_mmal_component_init(struct vchiq_mmal_instance *instance, goto unlock; } + /* We need a handle to reference back to our component structure. + * Use the array index in instance->component rather than rolling + * another IDR. + */ + component->client_component = idx; + ret = create_component(instance, component, name); if (ret < 0) { pr_err("%s: failed to create component %d (Not enough GPU mem?)\n", diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h index ae6c69ba16ee..247521fbcc1d 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h @@ -97,6 +97,7 @@ struct vchiq_mmal_component { struct vchiq_mmal_port input[MAX_PORT_COUNT]; /* input ports */ struct vchiq_mmal_port output[MAX_PORT_COUNT]; /* output ports */ struct vchiq_mmal_port clock[MAX_PORT_COUNT]; /* clock ports */ + u32 client_component; /* Used to ref back to client struct */ }; int vchiq_mmal_init(struct vchiq_mmal_instance **out_instance); From patchwork Mon May 4 09:25:54 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525283 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 7B0BF139A for ; Mon, 4 May 2020 09:26:42 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 650B720735 for ; Mon, 4 May 2020 09:26:42 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="Chq5+uQz" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728394AbgEDJ0l (ORCPT ); Mon, 4 May 2020 05:26:41 -0400 Received: from perceval.ideasonboard.com ([213.167.242.64]:56776 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728390AbgEDJ0l (ORCPT ); Mon, 4 May 2020 05:26:41 -0400 Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 62233304; Mon, 4 May 2020 11:26:35 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584395; bh=rqbJGSFwaRZMySQiWI435LMr1RxSgIqobMsNzXt1TCI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Chq5+uQzHf358DzPmcUtLcmvZpzrOVxw/55Yw6c3QtHIDAhPq/xp9oPlZwRRsxf/8 4izmbIRRM60hBtlz2A40qo7S5d4ZhfeL3Hxd46KDeS5Y/4M1EypKfYvdCc4dVqZeap gMonCU/NFqqB/MU/QqRP2yEccfzCkZEn21KnDRdM= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson , Dave Stevenson Subject: [PATCH v2 17/34] staging: mmal_vchiq: Add in the Bayer encoding formats Date: Mon, 4 May 2020 12:25:54 +0300 Message-Id: <20200504092611.9798-18-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Dave Stevenson The list of formats was copied before Bayer support was added. The ISP supports Bayer and is being supported by the bcm2835_codec driver, so add in the encodings for them. Signed-off-by: Dave Stevenson Signed-off-by: Jacopo Mondi --- .../vc04_services/vchiq-mmal/mmal-encodings.h | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-encodings.h b/drivers/staging/vc04_services/vchiq-mmal/mmal-encodings.h index 2be9941a1f30..44ba91aa6d47 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-encodings.h +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-encodings.h @@ -69,6 +69,33 @@ */ #define MMAL_ENCODING_OPAQUE MMAL_FOURCC('O', 'P', 'Q', 'V') +/* Bayer formats + * FourCC values copied from V4L2 where defined. + */ +/* 8 bit per pixel Bayer formats. */ +#define MMAL_ENCODING_BAYER_SBGGR8 MMAL_FOURCC('B', 'A', '8', '1') +#define MMAL_ENCODING_BAYER_SGBRG8 MMAL_FOURCC('G', 'B', 'R', 'G') +#define MMAL_ENCODING_BAYER_SGRBG8 MMAL_FOURCC('G', 'R', 'B', 'G') +#define MMAL_ENCODING_BAYER_SRGGB8 MMAL_FOURCC('R', 'G', 'G', 'B') + +/* 10 bit per pixel packed Bayer formats. */ +#define MMAL_ENCODING_BAYER_SBGGR10P MMAL_FOURCC('p', 'B', 'A', 'A') +#define MMAL_ENCODING_BAYER_SGRBG10P MMAL_FOURCC('p', 'g', 'A', 'A') +#define MMAL_ENCODING_BAYER_SGBRG10P MMAL_FOURCC('p', 'G', 'A', 'A') +#define MMAL_ENCODING_BAYER_SRGGB10P MMAL_FOURCC('p', 'R', 'A', 'A') + +/* 12 bit per pixel packed Bayer formats. */ +#define MMAL_ENCODING_BAYER_SBGGR12P MMAL_FOURCC('p', 'B', '1', '2') +#define MMAL_ENCODING_BAYER_SGRBG12P MMAL_FOURCC('p', 'g', '1', '2') +#define MMAL_ENCODING_BAYER_SGBRG12P MMAL_FOURCC('p', 'G', '1', '2') +#define MMAL_ENCODING_BAYER_SRGGB12P MMAL_FOURCC('p', 'R', '1', '2') + +/* 16 bit per pixel Bayer formats. */ +#define MMAL_ENCODING_BAYER_SBGGR16 MMAL_FOURCC('B', 'G', '1', '6') +#define MMAL_ENCODING_BAYER_SGBRG16 MMAL_FOURCC('G', 'B', '1', '6') +#define MMAL_ENCODING_BAYER_SGRBG16 MMAL_FOURCC('G', 'R', '1', '6') +#define MMAL_ENCODING_BAYER_SRGGB16 MMAL_FOURCC('R', 'G', '1', '6') + /** An EGL image handle */ #define MMAL_ENCODING_EGL_IMAGE MMAL_FOURCC('E', 'G', 'L', 'I') From patchwork Mon May 4 09:25:55 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525285 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 94D49139A for ; Mon, 4 May 2020 09:26:43 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 743D020735 for ; Mon, 4 May 2020 09:26:43 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="LQmywnNu" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728397AbgEDJ0m (ORCPT ); Mon, 4 May 2020 05:26:42 -0400 Received: from perceval.ideasonboard.com ([213.167.242.64]:56720 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728391AbgEDJ0m (ORCPT ); Mon, 4 May 2020 05:26:42 -0400 Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 2788211F9; Mon, 4 May 2020 11:26:36 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584396; bh=2dJL4ojmUStJrhldPp5h5wFsjYl4Yt2L1vo8/xiB8UM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=LQmywnNuNtJ0JUfW5mnA1a5t3sRySmWmAeIuh//OYZLyBM8eSN5458lrr+ESavR2F OsYoaI9vDF1lOddoDEFdhqZEiEUc6qjN9K5nyxa0l/wXxWL9maVUSX7SJo5ezewNig LVdN0LsOvGBkzxV7WQqpDwZks6c1ToLzwVgJzfOM= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson , Dave Stevenson Subject: [PATCH v2 18/34] staging: mmal-vchiq: Always return the param size from param_get Date: Mon, 4 May 2020 12:25:55 +0300 Message-Id: <20200504092611.9798-19-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Dave Stevenson mmal-vchiq is a reimplementation of the userland library for MMAL. When getting a parameter, the client provides the storage and the size of the storage. The VPU then returns the size of the parameter that it wished to return, and as much as possible of that parameter is returned to the client. The implementation previously only returned the size provided by the VPU should it exceed the buffer size. So for parameters such as the supported encodings list the client had no idea how much of the provided storage had been populated. Signed-off-by: Dave Stevenson Signed-off-by: Jacopo Mondi --- drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c index 5872690b404d..5e7d4263e39b 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c @@ -1407,11 +1407,12 @@ static int port_parameter_get(struct vchiq_mmal_instance *instance, */ memcpy(value, &rmsg->u.port_parameter_get_reply.value, *value_size); - *value_size = rmsg->u.port_parameter_get_reply.size; } else { memcpy(value, &rmsg->u.port_parameter_get_reply.value, rmsg->u.port_parameter_get_reply.size); } + /* Always report the size of the returned parameter to the caller */ + *value_size = rmsg->u.port_parameter_get_reply.size; pr_debug("%s:result:%d component:0x%x port:%d parameter:%d\n", __func__, ret, port->component->handle, port->handle, parameter_id); From patchwork Mon May 4 09:25:56 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525287 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 5E00C139A for ; Mon, 4 May 2020 09:26:44 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 46BD820746 for ; Mon, 4 May 2020 09:26:44 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="ltVQBUsk" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728399AbgEDJ0n (ORCPT ); Mon, 4 May 2020 05:26:43 -0400 Received: from perceval.ideasonboard.com ([213.167.242.64]:56776 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728395AbgEDJ0n (ORCPT ); Mon, 4 May 2020 05:26:43 -0400 Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id C0AED122D; Mon, 4 May 2020 11:26:36 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584397; bh=pcrrfsQlmR7SGkG1+Y+oygWKbVvk+y5ojBGVU0ZYFJA=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ltVQBUsknkLvRmLx5zTmapkNYE/b1XdY8hnrSL+sZGpo8B4cj79f1VnwGqxIzrzsm 0yC4wK+HKoEIo/ZVUeMrFz9EzqYjECMIoK6yW48kv9oOLhMlW5ReSmRlVGTLTOvBPE Xt8AyXvpOavgKPNRdVv8vMrmobXFdUaQ/cWPeI0c= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson , Dave Stevenson Subject: [PATCH v2 19/34] staging: mmal-vchiq: If the VPU returns an error, don't negate it Date: Mon, 4 May 2020 12:25:56 +0300 Message-Id: <20200504092611.9798-20-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Dave Stevenson There is an enum for the errors that the VPU can return. port_parameter_get was negating that value, but also using -EINVAL from the Linux error codes. Pass the VPU error code as positive values. Should the function need to pass a Linux failure, then return that as negative. Signed-off-by: Dave Stevenson Signed-off-by: Jacopo Mondi --- drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c index 5e7d4263e39b..4e559f88d828 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c @@ -1395,7 +1395,8 @@ static int port_parameter_get(struct vchiq_mmal_instance *instance, goto release_msg; } - ret = -rmsg->u.port_parameter_get_reply.status; + ret = rmsg->u.port_parameter_get_reply.status; + /* port_parameter_get_reply.size includes the header, * whilst *value_size doesn't. */ From patchwork Mon May 4 09:25:57 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525289 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id B8A1C1668 for ; Mon, 4 May 2020 09:26:45 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A367F20746 for ; Mon, 4 May 2020 09:26:45 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="V9lef/1m" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728404AbgEDJ0p (ORCPT ); Mon, 4 May 2020 05:26:45 -0400 Received: from perceval.ideasonboard.com ([213.167.242.64]:56720 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728390AbgEDJ0n (ORCPT ); Mon, 4 May 2020 05:26:43 -0400 Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id B6B8F121D; Mon, 4 May 2020 11:26:37 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584398; bh=+brpwA1vXJHBDGowRYRV7Rb1SchHJJF7/qPiIvA6OVM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=V9lef/1mAFk2Dd/QWwTecbXV+vUcn99+EETHDTawj6VzAKu3NqeTIusk1M4R908Fs zF3BjSA6+4NSSGAAqYh4Jx/yTfZTAkXN0//R1CTa0HIbRibYnlPfJIDHMKG9OTu/HE NvuySW4SrH5ickFiCV3owI/MiMWHevSsfXYcbos0= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson , Dave Stevenson Subject: [PATCH v2 20/34] staging: mmal-vchiq: Fix handling of VB2_MEMORY_DMABUF buffers Date: Mon, 4 May 2020 12:25:57 +0300 Message-Id: <20200504092611.9798-21-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Dave Stevenson If the queue is configured as VB2_MEMORY_DMABUF then vb2_core_expbuf fails as it ensures the queue is defined as VB2_MEMORY_MMAP. Correct the handling so that we unmap the buffer from vcsm and the VPU on cleanup, and then correctly get the dma buf of the new buffer. Signed-off-by: Dave Stevenson Signed-off-by: Jacopo Mondi --- .../vc04_services/vchiq-mmal/mmal-vchiq.c | 21 +++++++++++++------ .../vc04_services/vchiq-mmal/mmal-vchiq.h | 2 ++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c index 4e559f88d828..9fc1a29f9f1a 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c @@ -1779,13 +1779,9 @@ int mmal_vchi_buffer_init(struct vchiq_mmal_instance *instance, } EXPORT_SYMBOL_GPL(mmal_vchi_buffer_init); -int mmal_vchi_buffer_cleanup(struct mmal_buffer *buf) +int mmal_vchi_buffer_unmap(struct mmal_buffer *buf) { - struct mmal_msg_context *msg_context = buf->msg_context; - - if (msg_context) - release_msg_context(msg_context); - buf->msg_context = NULL; + int ret = 0; if (buf->vcsm_handle) { int ret; @@ -1797,6 +1793,19 @@ int mmal_vchi_buffer_cleanup(struct mmal_buffer *buf) pr_err("%s: vcsm_free failed, ret %d\n", __func__, ret); buf->vcsm_handle = 0; } + return ret; +} +EXPORT_SYMBOL_GPL(mmal_vchi_buffer_unmap); + +int mmal_vchi_buffer_cleanup(struct mmal_buffer *buf) +{ + struct mmal_msg_context *msg_context = buf->msg_context; + + if (msg_context) + release_msg_context(msg_context); + buf->msg_context = NULL; + + mmal_vchi_buffer_unmap(buf); return 0; } EXPORT_SYMBOL_GPL(mmal_vchi_buffer_cleanup); diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h index 247521fbcc1d..0a75c96f6d58 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.h @@ -167,6 +167,8 @@ int vchiq_mmal_submit_buffer(struct vchiq_mmal_instance *instance, struct vchiq_mmal_port *port, struct mmal_buffer *buf); +int mmal_vchi_buffer_unmap(struct mmal_buffer *buf); + int mmal_vchi_buffer_init(struct vchiq_mmal_instance *instance, struct mmal_buffer *buf); int mmal_vchi_buffer_cleanup(struct mmal_buffer *buf); From patchwork Mon May 4 09:25:58 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525293 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 57B7917EF for ; Mon, 4 May 2020 09:26:47 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 3765620746 for ; Mon, 4 May 2020 09:26:47 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="ajzgpJ1O" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728411AbgEDJ0q (ORCPT ); Mon, 4 May 2020 05:26:46 -0400 Received: from perceval.ideasonboard.com ([213.167.242.64]:56776 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728401AbgEDJ0o (ORCPT ); Mon, 4 May 2020 05:26:44 -0400 Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id A3D0A18B4; Mon, 4 May 2020 11:26:38 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584399; bh=RTswVnVvkrphvGZQrtudfR0gweDjJJ+wd4T7jlg4Oh0=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ajzgpJ1Ovu5pMLJwEds+7+ghgJUFKjFW3pdB+F+2+9n0fo71WlqSxfzB1ZX7fy57D gkqDG3nOoXpzl6BFmexhufrATjaGt5529v7uy+/volmsvC4NS9qI1jo2/X60ptjWVz tktJsTBw4UI8asPW5beweV+SbAuLJhXKg+wn9kP0= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson , Dave Stevenson Subject: [PATCH v2 21/34] staging: mmal-vchiq: Update mmal_parameters.h with recently defined params Date: Mon, 4 May 2020 12:25:58 +0300 Message-Id: <20200504092611.9798-22-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Dave Stevenson mmal_parameters.h hasn't been updated to reflect additions made over the last few years. Update it to reflect the currently supported parameters. Signed-off-by: Dave Stevenson Signed-off-by: Jacopo Mondi --- .../vchiq-mmal/mmal-parameters.h | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h b/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h index 80a99128f5f3..926392d754c7 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h @@ -580,7 +580,37 @@ enum mmal_parameter_video_type { MMAL_PARAMETER_VIDEO_ENCODE_H264_LOW_DELAY_HRD_FLAG, /**< @ref MMAL_PARAMETER_BOOLEAN_T */ - MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER + MMAL_PARAMETER_VIDEO_ENCODE_INLINE_HEADER, + + /**< Take a @ref MMAL_PARAMETER_BOOLEAN_T. */ + MMAL_PARAMETER_VIDEO_ENCODE_SEI_ENABLE, + + /**< Take a @ref MMAL_PARAMETER_BOOLEAN_T. */ + MMAL_PARAMETER_VIDEO_ENCODE_INLINE_VECTORS, + + /**< Take a @ref MMAL_PARAMETER_VIDEO_RENDER_STATS_T. */ + MMAL_PARAMETER_VIDEO_RENDER_STATS, + + /**< Take a @ref MMAL_PARAMETER_VIDEO_INTERLACE_TYPE_T. */ + MMAL_PARAMETER_VIDEO_INTERLACE_TYPE, + + /**< Takes a @ref MMAL_PARAMETER_BOOLEAN_T */ + MMAL_PARAMETER_VIDEO_INTERPOLATE_TIMESTAMPS, + + /**< Takes a @ref MMAL_PARAMETER_BOOLEAN_T */ + MMAL_PARAMETER_VIDEO_ENCODE_SPS_TIMING, + + /**< Takes a @ref MMAL_PARAMETER_UINT32_T */ + MMAL_PARAMETER_VIDEO_MAX_NUM_CALLBACKS, + + /**< Takes a @ref MMAL_PARAMETER_SOURCE_PATTERN_T */ + MMAL_PARAMETER_VIDEO_SOURCE_PATTERN, + + /**< Takes a @ref MMAL_PARAMETER_BOOLEAN_T */ + MMAL_PARAMETER_VIDEO_ENCODE_SEPARATE_NAL_BUFS, + + /**< Takes a @ref MMAL_PARAMETER_UINT32_T */ + MMAL_PARAMETER_VIDEO_DROPPABLE_PFRAME_LENGTH, }; /** Valid mirror modes */ From patchwork Mon May 4 09:25:59 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525291 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id DAD19139A for ; Mon, 4 May 2020 09:26:46 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id BB51620746 for ; Mon, 4 May 2020 09:26:46 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="F+C7RpeD" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728335AbgEDJ0p (ORCPT ); Mon, 4 May 2020 05:26:45 -0400 Received: from perceval.ideasonboard.com ([213.167.242.64]:56720 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728395AbgEDJ0p (ORCPT ); Mon, 4 May 2020 05:26:45 -0400 Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 140D94F7; Mon, 4 May 2020 11:26:39 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584401; bh=LDVliL/PHYd5kyS/hZzNxXnIFRr/vN4pjrGIE4byH18=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=F+C7RpeD3Gv11vmgfuqprB6zM5suvET9/PUzPmp7kTFmnV2AUUvKlhBpuirRbFLa+ UOeoCrRjs8/3RO7yjK4WtQVD1oiVCbjsrdv2i9gVxK1ATARJUtdSfOI6N/pXtxl9X1 NspMn8sSl2hZnex8Ikw3L+uQZ+Kzg03EnGkITNu8= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson , Dave Stevenson Subject: [PATCH v2 22/34] staging: mmal-vchiq: Free the event context for control ports Date: Mon, 4 May 2020 12:25:59 +0300 Message-Id: <20200504092611.9798-23-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Dave Stevenson vchiq_mmal_component_init calls init_event_context for the control port, but vchiq_mmal_component_finalise didn't free it, causing a memory leak.. Add the free call. Signed-off-by: Dave Stevenson Signed-off-by: Jacopo Mondi --- drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c index 9fc1a29f9f1a..95437d9c519c 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c @@ -1976,6 +1976,8 @@ int vchiq_mmal_component_finalise(struct vchiq_mmal_instance *instance, for (idx = 0; idx < component->clocks; idx++) free_event_context(&component->clock[idx]); + free_event_context(&component->control); + mutex_unlock(&instance->vchiq_mutex); return ret; From patchwork Mon May 4 09:26:00 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525295 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 0356B1668 for ; Mon, 4 May 2020 09:26:48 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E038020735 for ; Mon, 4 May 2020 09:26:47 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="NwY7Hf+q" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728413AbgEDJ0r (ORCPT ); Mon, 4 May 2020 05:26:47 -0400 Received: from perceval.ideasonboard.com ([213.167.242.64]:56776 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728390AbgEDJ0q (ORCPT ); Mon, 4 May 2020 05:26:46 -0400 Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id E02D4304; Mon, 4 May 2020 11:26:41 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584403; bh=/RaRFa8mDihTo1e3nxmOzsrX/C1zqtEXblGZKnlGzS4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=NwY7Hf+qDg6lVICUFmLSTU085x6kIuz+ZSGDC3e4DtcbtbEpkfS2LRuP4X6gI2+cR I3V5PCkep6ucc5Y6zaBdTrENOQKAIpeXpWGRI0lMwcwdE3X1LQ6BFCSU7YuNegVeLq xbhE6T5WpT3vlddTlZIBujogqh+sTuVzOkpQC+0A= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson , Dave Stevenson Subject: [PATCH v2 23/34] staging: mmal-vchiq: Fix memory leak in error path Date: Mon, 4 May 2020 12:26:00 +0300 Message-Id: <20200504092611.9798-24-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Dave Stevenson On error, vchiq_mmal_component_init could leave the event context allocated for ports. Clean them up in the error path. Signed-off-by: Dave Stevenson Signed-off-by: Jacopo Mondi --- .../vc04_services/vchiq-mmal/mmal-vchiq.c | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c index 95437d9c519c..4c89bc0bec15 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c @@ -1842,9 +1842,26 @@ static void free_event_context(struct vchiq_mmal_port *port) { struct mmal_msg_context *ctx = port->event_context; + if (!ctx) + return; + kfree(ctx->u.bulk.buffer->buffer); kfree(ctx->u.bulk.buffer); release_msg_context(ctx); + port->event_context = NULL; +} + +static void release_all_event_contexts(struct vchiq_mmal_component *component) +{ + int idx; + + for (idx = 0; idx < component->inputs; idx++) + free_event_context(&component->input[idx]); + for (idx = 0; idx < component->outputs; idx++) + free_event_context(&component->output[idx]); + for (idx = 0; idx < component->clocks; idx++) + free_event_context(&component->clock[idx]); + free_event_context(&component->control); } /* Initialise a mmal component and its ports @@ -1942,6 +1959,7 @@ int vchiq_mmal_component_init(struct vchiq_mmal_instance *instance, release_component: destroy_component(instance, component); + release_all_event_contexts(component); unlock: if (component) component->in_use = 0; @@ -1957,7 +1975,7 @@ EXPORT_SYMBOL_GPL(vchiq_mmal_component_init); int vchiq_mmal_component_finalise(struct vchiq_mmal_instance *instance, struct vchiq_mmal_component *component) { - int ret, idx; + int ret; if (mutex_lock_interruptible(&instance->vchiq_mutex)) return -EINTR; @@ -1969,14 +1987,7 @@ int vchiq_mmal_component_finalise(struct vchiq_mmal_instance *instance, component->in_use = 0; - for (idx = 0; idx < component->inputs; idx++) - free_event_context(&component->input[idx]); - for (idx = 0; idx < component->outputs; idx++) - free_event_context(&component->output[idx]); - for (idx = 0; idx < component->clocks; idx++) - free_event_context(&component->clock[idx]); - - free_event_context(&component->control); + release_all_event_contexts(component); mutex_unlock(&instance->vchiq_mutex); From patchwork Mon May 4 09:26:01 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525297 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id D6F22139A for ; Mon, 4 May 2020 09:26:48 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C014C20746 for ; Mon, 4 May 2020 09:26:48 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="caTj2A15" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728421AbgEDJ0r (ORCPT ); Mon, 4 May 2020 05:26:47 -0400 Received: from perceval.ideasonboard.com ([213.167.242.64]:56720 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728408AbgEDJ0r (ORCPT ); Mon, 4 May 2020 05:26:47 -0400 Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 89C9011F9; Mon, 4 May 2020 11:26:43 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584404; bh=HiWSCz2oAiDjpfm5bLqBamBEm9oEEJGcTPzVplhtRb8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=caTj2A15H/IlIeusnwoIwpnAchkMh6a4wYCw0dCN3tBberBUdfLzzEWAzMiWGDZnn rSHnw6cd2Q+SaohOswW3CqEweNbiFdQo/R2UjztEqeJzcl9CHdZwPDWU9XwCG6Wdjm +GczpdrXofxC2wjFuq7KjeBvBPEvn0UWO+WCrZyg= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson Subject: [PATCH v2 24/34] staging: mmal-vchiq: Fix formatting errors in mmal_parameters.h Date: Mon, 4 May 2020 12:26:01 +0300 Message-Id: <20200504092611.9798-25-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Naushir Patuck No functional changes in this commit. - Remove erroneous whitespace. - Remove _t postfix label on structs and enums. Signed-off-by: Naushir Patuck Signed-off-by: Jacopo Mondi --- .../bcm2835-camera/bcm2835-camera.c | 2 +- .../vchiq-mmal/mmal-parameters.h | 46 +++++++++---------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c index 68a2260d88f5..613b04683dca 100644 --- a/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c +++ b/drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.c @@ -1497,7 +1497,7 @@ static int get_num_cameras(struct vchiq_mmal_instance *instance, { int ret; struct vchiq_mmal_component *cam_info_component; - struct mmal_parameter_camera_info_t cam_info = {0}; + struct mmal_parameter_camera_info cam_info = {0}; u32 param_size = sizeof(cam_info); int i; diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h b/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h index 926392d754c7..1793103b18fd 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h @@ -23,21 +23,21 @@ #define MMAL_PARAMETERS_H /** Common parameter ID group, used with many types of component. */ -#define MMAL_PARAMETER_GROUP_COMMON (0 << 16) +#define MMAL_PARAMETER_GROUP_COMMON (0 << 16) /** Camera-specific parameter ID group. */ -#define MMAL_PARAMETER_GROUP_CAMERA (1 << 16) +#define MMAL_PARAMETER_GROUP_CAMERA (1 << 16) /** Video-specific parameter ID group. */ -#define MMAL_PARAMETER_GROUP_VIDEO (2 << 16) +#define MMAL_PARAMETER_GROUP_VIDEO (2 << 16) /** Audio-specific parameter ID group. */ -#define MMAL_PARAMETER_GROUP_AUDIO (3 << 16) +#define MMAL_PARAMETER_GROUP_AUDIO (3 << 16) /** Clock-specific parameter ID group. */ -#define MMAL_PARAMETER_GROUP_CLOCK (4 << 16) +#define MMAL_PARAMETER_GROUP_CLOCK (4 << 16) /** Miracast-specific parameter ID group. */ -#define MMAL_PARAMETER_GROUP_MIRACAST (5 << 16) +#define MMAL_PARAMETER_GROUP_MIRACAST (5 << 16) /* Common parameters */ enum mmal_parameter_common_type { - /**< Never a valid parameter ID */ + /**< Never a valid parameter ID */ MMAL_PARAMETER_UNUSED = MMAL_PARAMETER_GROUP_COMMON, /**< MMAL_PARAMETER_ENCODING_T */ @@ -341,7 +341,7 @@ enum mmal_parameter_imagefx { MMAL_PARAM_IMAGEFX_CARTOON, }; -enum MMAL_PARAM_FLICKERAVOID_T { +enum MMAL_PARAM_FLICKERAVOID { MMAL_PARAM_FLICKERAVOID_OFF, MMAL_PARAM_FLICKERAVOID_AUTO, MMAL_PARAM_FLICKERAVOID_50HZ, @@ -753,15 +753,15 @@ struct mmal_parameter_imagefx_parameters { #define MMAL_PARAMETER_CAMERA_INFO_MAX_FLASHES 2 #define MMAL_PARAMETER_CAMERA_INFO_MAX_STR_LEN 16 -struct mmal_parameter_camera_info_camera_t { - u32 port_id; - u32 max_width; - u32 max_height; - u32 lens_present; - u8 camera_name[MMAL_PARAMETER_CAMERA_INFO_MAX_STR_LEN]; +struct mmal_parameter_camera_info_camera { + u32 port_id; + u32 max_width; + u32 max_height; + u32 lens_present; + u8 camera_name[MMAL_PARAMETER_CAMERA_INFO_MAX_STR_LEN]; }; -enum mmal_parameter_camera_info_flash_type_t { +enum mmal_parameter_camera_info_flash_type { /* Make values explicit to ensure they match values in config ini */ MMAL_PARAMETER_CAMERA_INFO_FLASH_TYPE_XENON = 0, MMAL_PARAMETER_CAMERA_INFO_FLASH_TYPE_LED = 1, @@ -769,16 +769,16 @@ enum mmal_parameter_camera_info_flash_type_t { MMAL_PARAMETER_CAMERA_INFO_FLASH_TYPE_MAX = 0x7FFFFFFF }; -struct mmal_parameter_camera_info_flash_t { - enum mmal_parameter_camera_info_flash_type_t flash_type; +struct mmal_parameter_camera_info_flash { + enum mmal_parameter_camera_info_flash_type flash_type; }; -struct mmal_parameter_camera_info_t { - u32 num_cameras; - u32 num_flashes; - struct mmal_parameter_camera_info_camera_t - cameras[MMAL_PARAMETER_CAMERA_INFO_MAX_CAMERAS]; - struct mmal_parameter_camera_info_flash_t +struct mmal_parameter_camera_info { + u32 num_cameras; + u32 num_flashes; + struct mmal_parameter_camera_info_camera + cameras[MMAL_PARAMETER_CAMERA_INFO_MAX_CAMERAS]; + struct mmal_parameter_camera_info_flash flashes[MMAL_PARAMETER_CAMERA_INFO_MAX_FLASHES]; }; From patchwork Mon May 4 09:26:02 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525301 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 9961B139A for ; Mon, 4 May 2020 09:26:50 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 82D0B215A4 for ; Mon, 4 May 2020 09:26:50 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="DefwXQjO" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728428AbgEDJ0t (ORCPT ); Mon, 4 May 2020 05:26:49 -0400 Received: from perceval.ideasonboard.com ([213.167.242.64]:56776 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728401AbgEDJ0s (ORCPT ); Mon, 4 May 2020 05:26:48 -0400 Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 412D211FC; Mon, 4 May 2020 11:26:44 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584404; bh=9NtgkeLVgWE7v9BqrI8Tx5NtA2yHZs41cJqWjcNliZs=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=DefwXQjOtmVh6vfkS7DLbKnvfvZiAgzZtRvm4tdIEcgggTzwr3W93Qfo3lWb5zGvK NaE7mBAnZI69poY59J23l0P1oLOMz1NHUSr/6tthUn0fuZPn2tqmdTEIVc0FWcrZSP Benf1Dt/ib0zThm6c+/vC5aqE/DPXzM3462tST7o= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson Subject: [PATCH v2 25/34] staging: vchiq_arm: Register vcsm-cma as a platform driver Date: Mon, 4 May 2020 12:26:02 +0300 Message-Id: <20200504092611.9798-26-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Dave Stevenson Following the same pattern as bcm2835-camera and bcm2835-audio, register the vcsm-cma driver as a platform driver Signed-off-by: Dave Stevenson Signed-off-by: Jacopo Mondi --- drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index a1ea9777a444..c5c7af28c1c8 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -107,6 +107,7 @@ static struct class *vchiq_class; static DEFINE_SPINLOCK(msg_queue_spinlock); static struct platform_device *bcm2835_camera; static struct platform_device *bcm2835_audio; +static struct platform_device *vcsm_cma; static struct vchiq_drvdata bcm2835_drvdata = { .cache_line_size = 32, @@ -2789,6 +2790,7 @@ static int vchiq_probe(struct platform_device *pdev) VCHIQ_VERSION, VCHIQ_VERSION_MIN, MAJOR(vchiq_devid), MINOR(vchiq_devid)); + vcsm_cma = vchiq_register_child(pdev, "vcsm-cma"); bcm2835_camera = vchiq_register_child(pdev, "bcm2835-camera"); bcm2835_audio = vchiq_register_child(pdev, "bcm2835_audio"); @@ -2804,6 +2806,7 @@ static int vchiq_probe(struct platform_device *pdev) static int vchiq_remove(struct platform_device *pdev) { platform_device_unregister(bcm2835_camera); + platform_device_unregister(vcsm_cma); vchiq_debugfs_deinit(); device_destroy(vchiq_class, vchiq_devid); cdev_del(&vchiq_cdev); From patchwork Mon May 4 09:26:03 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525299 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id DA4B7139A for ; Mon, 4 May 2020 09:26:49 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C34FD21582 for ; Mon, 4 May 2020 09:26:49 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="X9EzMyaZ" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728427AbgEDJ0t (ORCPT ); Mon, 4 May 2020 05:26:49 -0400 Received: from perceval.ideasonboard.com ([213.167.242.64]:56720 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728419AbgEDJ0s (ORCPT ); Mon, 4 May 2020 05:26:48 -0400 Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id E38701209; Mon, 4 May 2020 11:26:44 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584405; bh=ZDaZRct1RVy7xs8SPv417jYteulvJrltZo++xeluApo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=X9EzMyaZO9E1/CyaameloyaYAVfGKnxD4o5bp0clvEkdOkMU4e976ZR30+MlQox3t IObbuh2xmUOpXAhG3WxQhCdNZjxhGGLU//u8eowobqZxHQHfEQYLaL1+5ix/CQKQwm ZeCe0Q99ngZ1JOCt4CL2o5PHcHQLYbZ8MSBDpiiw= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson , Dave Stevenson Subject: [PATCH v2 26/34] staging: vchiq_arm: Set up dma ranges on child devices Date: Mon, 4 May 2020 12:26:03 +0300 Message-Id: <20200504092611.9798-27-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Dave Stevenson The VCHIQ driver now loads the audio, camera, codec, and vc-sm drivers as platform drivers. However they were not being given the correct DMA configuration. Call of_dma_configure with the parent (VCHIQ) parameters to be inherited by the child. Signed-off-by: Dave Stevenson Signed-off-by: Jacopo Mondi --- .../staging/vc04_services/interface/vchiq_arm/vchiq_arm.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index c5c7af28c1c8..15ccd624aaab 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -2733,6 +2733,12 @@ vchiq_register_child(struct platform_device *pdev, const char *name) child = NULL; } + /* + * We want the dma-ranges etc to be copied from the parent VCHIQ device + * to be passed on to the children too. + */ + of_dma_configure(&new_dev->dev, pdev->dev.of_node, true); + return child; } From patchwork Mon May 4 09:26:04 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525305 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 4087417EF for ; Mon, 4 May 2020 09:26:52 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 24BA824955 for ; Mon, 4 May 2020 09:26:52 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="JwSyiJmV" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728436AbgEDJ0v (ORCPT ); Mon, 4 May 2020 05:26:51 -0400 Received: from perceval.ideasonboard.com ([213.167.242.64]:56720 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728425AbgEDJ0u (ORCPT ); Mon, 4 May 2020 05:26:50 -0400 Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 7DFC5121D; Mon, 4 May 2020 11:26:45 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584405; bh=V5vv4x5wKJ6hQdH/FUQfwW3D36Z91fOws36otLzl6f4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=JwSyiJmVRFA5RcwUsB4NyOVsMs8GegaAgfcq3rOgvK9sa2EQCYWNlEVs8tOtpQUKQ nxcUOFErAwWwuMAwK7RGbrNJ4e7YR8XCAxYgmQqubj7XU4f3WPeyCxvNYuJFR7VFdr Go+HRTKdwq2IlkdF1T7ReAfM8mGNS0BQGAR/IhTQ= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson , Dave Stevenson Subject: [PATCH v2 27/34] staging: vchiq: Use the old dma controller for OF config on platform devices Date: Mon, 4 May 2020 12:26:04 +0300 Message-Id: <20200504092611.9798-28-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Dave Stevenson vchiq on Pi4 is no longer under the soc node, therefore it doesn't get the dma-ranges for the VPU. Switch to using the configuration of the old dma controller as that will set the dma-ranges correctly. Signed-off-by: Dave Stevenson Signed-off-by: Jacopo Mondi --- .../interface/vchiq_arm/vchiq_arm.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index 15ccd624aaab..d1a556f16499 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -2719,6 +2719,7 @@ vchiq_register_child(struct platform_device *pdev, const char *name) { struct platform_device_info pdevinfo; struct platform_device *child; + struct device_node *np; memset(&pdevinfo, 0, sizeof(pdevinfo)); @@ -2734,10 +2735,20 @@ vchiq_register_child(struct platform_device *pdev, const char *name) } /* - * We want the dma-ranges etc to be copied from the parent VCHIQ device - * to be passed on to the children too. + * We want the dma-ranges etc to be copied from a device with the + * correct dma-ranges for the VPU. + * VCHIQ on Pi4 is now under scb which doesn't get those dma-ranges. + * Take the "dma" node as going to be suitable as it sees the world + * through the same eyes as the VPU. */ - of_dma_configure(&new_dev->dev, pdev->dev.of_node, true); + np = of_find_node_by_path("dma"); + if (!np) + np = pdev->dev.of_node; + + of_dma_configure(&child->dev, np, true); + + if (np != pdev->dev.of_node) + of_node_put(np); return child; } From patchwork Mon May 4 09:26:05 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525303 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id B0EAD1668 for ; Mon, 4 May 2020 09:26:51 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 9934121974 for ; Mon, 4 May 2020 09:26:51 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="BZrUWm0I" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728434AbgEDJ0v (ORCPT ); Mon, 4 May 2020 05:26:51 -0400 Received: from perceval.ideasonboard.com ([213.167.242.64]:56776 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728422AbgEDJ0u (ORCPT ); Mon, 4 May 2020 05:26:50 -0400 Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 1F40D4F7; Mon, 4 May 2020 11:26:46 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584406; bh=h1f4LLJwwL0fBSoFl0xi5O2F/8Lszy9bphrat8ky49o=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=BZrUWm0IeMGoJ1sKI5Z9OTvAHnF8bWGP6a2HY2vfYisU3rr1HpCXX0U4dxrEx2Dux e9ljkZOGexON6HKVREzNFucVYCUfX01voAM/EOCzbpAeDOp11bpADdHokXFJB8UBqM lR2TvxSZ+BYdY8x6BuMKJfqM9Svwxs79wnvZow5s= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson , Oliver Gjoneski Subject: [PATCH v2 28/34] staging: vchiq_2835_arm: Implement a DMA pool for small bulk transfers Date: Mon, 4 May 2020 12:26:05 +0300 Message-Id: <20200504092611.9798-29-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Oliver Gjoneski During a bulk transfer we request a DMA allocation to hold the scatter-gather list. Most of the time, this allocation is small (<< PAGE_SIZE), however it can be requested at a high enough frequency to cause fragmentation and/or stress the CMA allocator (think time spent in compaction here, or during allocations elsewhere). Implement a pool to serve up small DMA allocations, falling back to a coherent allocation if the request is greater than VCHIQ_DMA_POOL_SIZE. Signed-off-by: Oliver Gjoneski Signed-off-by: Jacopo Mondi --- .../interface/vchiq_arm/vchiq_2835_arm.c | 38 ++++++++++++++++--- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c index c18c6ca0b6c0..3e422a7eb3f1 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,8 @@ #define BELL0 0x00 #define BELL2 0x08 +#define VCHIQ_DMA_POOL_SIZE PAGE_SIZE + struct vchiq_2835_state { int inited; struct vchiq_arm_state arm_state; @@ -37,6 +40,7 @@ struct vchiq_pagelist_info { struct pagelist *pagelist; size_t pagelist_buffer_size; dma_addr_t dma_addr; + bool is_from_pool; enum dma_data_direction dma_dir; unsigned int num_pages; unsigned int pages_need_release; @@ -57,6 +61,7 @@ static void __iomem *g_regs; * of 32. */ static unsigned int g_cache_line_size = 32; +static struct dma_pool *g_dma_pool; static unsigned int g_fragments_size; static char *g_fragments_base; static char *g_free_fragments; @@ -159,6 +164,14 @@ int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state *state) } g_dev = dev; + g_dma_pool = dmam_pool_create("vchiq_scatter_pool", dev, + VCHIQ_DMA_POOL_SIZE, g_cache_line_size, + 0); + if (!g_dma_pool) { + dev_err(dev, "failed to create dma pool"); + return -ENOMEM; + } + vchiq_log_info(vchiq_arm_log_level, "vchiq_init - done (slots %pK, phys %pad)", vchiq_slot_zero, &slot_phys); @@ -293,9 +306,14 @@ cleanup_pagelistinfo(struct vchiq_pagelist_info *pagelistinfo) for (i = 0; i < pagelistinfo->num_pages; i++) put_page(pagelistinfo->pages[i]); } - - dma_free_coherent(g_dev, pagelistinfo->pagelist_buffer_size, - pagelistinfo->pagelist, pagelistinfo->dma_addr); + if (pagelistinfo->is_from_pool) { + dma_pool_free(g_dma_pool, pagelistinfo->pagelist, + pagelistinfo->dma_addr); + } else { + dma_free_coherent(g_dev, pagelistinfo->pagelist_buffer_size, + pagelistinfo->pagelist, + pagelistinfo->dma_addr); + } } /* There is a potential problem with partial cache lines (pages?) @@ -315,6 +333,7 @@ create_pagelist(char __user *buf, size_t count, unsigned short type) u32 *addrs; unsigned int num_pages, offset, i, k; int actual_pages; + bool is_from_pool; size_t pagelist_size; struct scatterlist *scatterlist, *sg; int dma_buffers; @@ -341,8 +360,16 @@ create_pagelist(char __user *buf, size_t count, unsigned short type) /* Allocate enough storage to hold the page pointers and the page * list */ - pagelist = dma_alloc_coherent(g_dev, pagelist_size, &dma_addr, - GFP_KERNEL); + if (pagelist_size > VCHIQ_DMA_POOL_SIZE) { + pagelist = dma_alloc_coherent(g_dev, + pagelist_size, + &dma_addr, + GFP_KERNEL); + is_from_pool = false; + } else { + pagelist = dma_pool_alloc(g_dma_pool, GFP_KERNEL, &dma_addr); + is_from_pool = true; + } vchiq_log_trace(vchiq_arm_log_level, "%s - %pK", __func__, pagelist); @@ -363,6 +390,7 @@ create_pagelist(char __user *buf, size_t count, unsigned short type) pagelistinfo->pagelist = pagelist; pagelistinfo->pagelist_buffer_size = pagelist_size; pagelistinfo->dma_addr = dma_addr; + pagelistinfo->is_from_pool = is_from_pool; pagelistinfo->dma_dir = (type == PAGELIST_WRITE) ? DMA_TO_DEVICE : DMA_FROM_DEVICE; pagelistinfo->num_pages = num_pages; From patchwork Mon May 4 09:26:06 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525309 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 321D617EF for ; Mon, 4 May 2020 09:26:54 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 1B4A721BE5 for ; Mon, 4 May 2020 09:26:54 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="snjCtUc5" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728441AbgEDJ0x (ORCPT ); Mon, 4 May 2020 05:26:53 -0400 Received: from perceval.ideasonboard.com ([213.167.242.64]:56720 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728430AbgEDJ0w (ORCPT ); Mon, 4 May 2020 05:26:52 -0400 Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id B05D2122D; Mon, 4 May 2020 11:26:46 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584407; bh=Xc+03rJOLzY9YeBMxi1IzWdzC5nCF0ClGsyZ+jt95bc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=snjCtUc5V3qf/NRAus8cbTeVhNAvr/BVrH+QKmHaKkpaLa6olzbuZA+T6iI8S0VDk P15jrJIb+3dXgyfW/PmjqBvjyFTHtG6Hl7orucoltiUsj9dqgEQvR5I48vo5XaOaco hdqhsy7imx7+0B/iSdoUjd0SPxj02+ZpbgJ83jW0= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson , Phil Elwell Subject: [PATCH v2 29/34] staging: vchiq: Add 36-bit address support Date: Mon, 4 May 2020 12:26:06 +0300 Message-Id: <20200504092611.9798-30-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Phil Elwell Conditional on a new compatible string, change the pagelist encoding such that the top 24 bits are the pfn, leaving 8 bits for run length (-1). Signed-off-by: Phil Elwell Signed-off-by: Jacopo Mondi Reported-by: kbuild test robot --- .../interface/vchiq_arm/vchiq_2835_arm.c | 88 ++++++++++++++----- .../interface/vchiq_arm/vchiq_arm.c | 6 ++ .../interface/vchiq_arm/vchiq_arm.h | 1 + 3 files changed, 74 insertions(+), 21 deletions(-) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c index 3e422a7eb3f1..ecec84ad4345 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c @@ -16,6 +16,8 @@ #include #define TOTAL_SLOTS (VCHIQ_SLOT_ZERO_SLOTS + 2 * 32) +#define VC_SAFE(x) (g_use_36bit_addrs ? ((u32)(x) | 0xc0000000) : (u32)(x)) +#define IS_VC_SAFE(x) (g_use_36bit_addrs ? !((x) & ~0x3fffffffull) : 1) #include "vchiq_arm.h" #include "vchiq_connected.h" @@ -62,6 +64,7 @@ static void __iomem *g_regs; */ static unsigned int g_cache_line_size = 32; static struct dma_pool *g_dma_pool; +static unsigned int g_use_36bit_addrs = 0; static unsigned int g_fragments_size; static char *g_fragments_base; static char *g_free_fragments; @@ -104,6 +107,8 @@ int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state *state) g_cache_line_size = drvdata->cache_line_size; g_fragments_size = 2 * g_cache_line_size; + g_use_36bit_addrs = (dev->dma_pfn_offset == 0); + /* Allocate space for the channels in coherent memory */ slot_mem_size = PAGE_ALIGN(TOTAL_SLOTS * VCHIQ_SLOT_SIZE); frag_mem_size = PAGE_ALIGN(g_fragments_size * MAX_FRAGMENTS); @@ -115,14 +120,21 @@ int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state *state) return -ENOMEM; } + if (!IS_VC_SAFE(slot_phys)) { + dev_err(dev, "allocated DMA memory %pad is not VC-safe\n", + &slot_phys); + return -ENOMEM; + } + WARN_ON(((unsigned long)slot_mem & (PAGE_SIZE - 1)) != 0); + channelbase = VC_SAFE(slot_phys); vchiq_slot_zero = vchiq_init_slots(slot_mem, slot_mem_size); if (!vchiq_slot_zero) return -EINVAL; vchiq_slot_zero->platform_data[VCHIQ_PLATFORM_FRAGMENTS_OFFSET_IDX] = - (int)slot_phys + slot_mem_size; + channelbase + slot_mem_size; vchiq_slot_zero->platform_data[VCHIQ_PLATFORM_FRAGMENTS_COUNT_IDX] = MAX_FRAGMENTS; @@ -155,7 +167,6 @@ int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state *state) } /* Send the base address of the slots to VideoCore */ - channelbase = slot_phys; err = rpi_firmware_property(fw, RPI_FIRMWARE_VCHIQ_INIT, &channelbase, sizeof(channelbase)); if (err || channelbase) { @@ -241,7 +252,7 @@ vchiq_prepare_bulk_data(struct vchiq_bulk *bulk, void *offset, int size, if (!pagelistinfo) return VCHIQ_ERROR; - bulk->data = (void *)(unsigned long)pagelistinfo->dma_addr; + bulk->data = (void *)VC_SAFE(pagelistinfo->dma_addr); /* * Store the pagelistinfo address in remote_data, @@ -475,25 +486,60 @@ create_pagelist(char __user *buf, size_t count, unsigned short type) /* Combine adjacent blocks for performance */ k = 0; - for_each_sg(scatterlist, sg, dma_buffers, i) { - u32 len = sg_dma_len(sg); - u32 addr = sg_dma_address(sg); + if (g_use_36bit_addrs) { + for_each_sg(scatterlist, sg, dma_buffers, i) { + u32 len = sg_dma_len(sg); + u64 addr = sg_dma_address(sg); + u32 page_id = (u32)((addr >> 4) & ~0xff); + u32 sg_pages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; - /* Note: addrs is the address + page_count - 1 - * The firmware expects blocks after the first to be page- - * aligned and a multiple of the page size - */ - WARN_ON(len == 0); - WARN_ON(i && (i != (dma_buffers - 1)) && (len & ~PAGE_MASK)); - WARN_ON(i && (addr & ~PAGE_MASK)); - if (k > 0 && - ((addrs[k - 1] & PAGE_MASK) + - (((addrs[k - 1] & ~PAGE_MASK) + 1) << PAGE_SHIFT)) - == (addr & PAGE_MASK)) - addrs[k - 1] += ((len + PAGE_SIZE - 1) >> PAGE_SHIFT); - else - addrs[k++] = (addr & PAGE_MASK) | - (((len + PAGE_SIZE - 1) >> PAGE_SHIFT) - 1); + /* Note: addrs is the address + page_count - 1 + * The firmware expects blocks after the first to be page- + * aligned and a multiple of the page size + */ + WARN_ON(len == 0); + WARN_ON(i && + (i != (dma_buffers - 1)) && (len & ~PAGE_MASK)); + WARN_ON(i && (addr & ~PAGE_MASK)); + WARN_ON(upper_32_bits(addr) > 0xf); + if (k > 0 && + ((addrs[k - 1] & ~0xff) + + (((addrs[k - 1] & 0xff) + 1) << 8) + == page_id)) { + u32 inc_pages = min(sg_pages, + 0xff - (addrs[k - 1] & 0xff)); + addrs[k - 1] += inc_pages; + page_id += inc_pages << 8; + sg_pages -= inc_pages; + } + while (sg_pages) { + u32 inc_pages = min(sg_pages, 0x100u); + addrs[k++] = page_id | (inc_pages - 1); + page_id += inc_pages << 8; + sg_pages -= inc_pages; + } + } + } else { + for_each_sg(scatterlist, sg, dma_buffers, i) { + u32 len = sg_dma_len(sg); + u32 addr = VC_SAFE(sg_dma_address(sg)); + u32 new_pages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; + + /* Note: addrs is the address + page_count - 1 + * The firmware expects blocks after the first to be page- + * aligned and a multiple of the page size + */ + WARN_ON(len == 0); + WARN_ON(i && (i != (dma_buffers - 1)) && (len & ~PAGE_MASK)); + WARN_ON(i && (addr & ~PAGE_MASK)); + if (k > 0 && + ((addrs[k - 1] & PAGE_MASK) + + (((addrs[k - 1] & ~PAGE_MASK) + 1) << PAGE_SHIFT)) + == (addr & PAGE_MASK)) + addrs[k - 1] += new_pages; + else + addrs[k++] = (addr & PAGE_MASK) | (new_pages - 1); + } } /* Partial cache lines (fragments) require special measures */ diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index d1a556f16499..dd3c8f829daa 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -117,6 +117,11 @@ static struct vchiq_drvdata bcm2836_drvdata = { .cache_line_size = 64, }; +static struct vchiq_drvdata bcm2711_drvdata = { + .cache_line_size = 64, + .use_36bit_addrs = true, +}; + static const char *const ioctl_names[] = { "CONNECT", "SHUTDOWN", @@ -2710,6 +2715,7 @@ void vchiq_platform_conn_state_changed(struct vchiq_state *state, static const struct of_device_id vchiq_of_match[] = { { .compatible = "brcm,bcm2835-vchiq", .data = &bcm2835_drvdata }, { .compatible = "brcm,bcm2836-vchiq", .data = &bcm2836_drvdata }, + { .compatible = "brcm,bcm2711-vchiq", .data = &bcm2711_drvdata }, {}, }; MODULE_DEVICE_TABLE(of, vchiq_of_match); diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h index 0784c5002417..f8b1c005af62 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h @@ -52,6 +52,7 @@ struct vchiq_arm_state { struct vchiq_drvdata { const unsigned int cache_line_size; + const bool use_36bit_addrs; struct rpi_firmware *fw; }; From patchwork Mon May 4 09:26:07 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525307 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id EC90C139A for ; Mon, 4 May 2020 09:26:53 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id CB35221775 for ; Mon, 4 May 2020 09:26:53 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="YF1cIwjY" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728439AbgEDJ0x (ORCPT ); Mon, 4 May 2020 05:26:53 -0400 Received: from perceval.ideasonboard.com ([213.167.242.64]:56776 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728401AbgEDJ0v (ORCPT ); Mon, 4 May 2020 05:26:51 -0400 Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 4947F304; Mon, 4 May 2020 11:26:47 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584407; bh=kA6YRPSNeFZEAYiNKAMVdXCXB8iBJh1FSsBXbLabxA8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=YF1cIwjY386T5teD5tz/eZo/fhmr0WIwlSLS22a9kQ2ANKWax3ASPFkk/KjCtKtTs 5oYR7xbpS42NtMRshv3wQ50xnFzVixudHsh3BxOBZgF69aTIOMdVxQxcc9zR1JwfHa r2ObMFn2U7bEEpdy0bbaVh4jxYM3Z66U/FxPvkAA= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson , Phil Elwell Subject: [PATCH v2 30/34] staging: vchiq_arm: Give vchiq children DT nodes Date: Mon, 4 May 2020 12:26:07 +0300 Message-Id: <20200504092611.9798-31-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Phil Elwell vchiq kernel clients are now instantiated as platform drivers rather than using DT, but the children of the vchiq interface may still benefit from access to DT properties. Give them the option of a a sub-node of the vchiq parent for configuration and to allow them to be disabled. Signed-off-by: Phil Elwell Signed-off-by: Jacopo Mondi --- .../staging/vc04_services/interface/vchiq_arm/vchiq_arm.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index dd3c8f829daa..2325ab825941 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -2734,12 +2734,20 @@ vchiq_register_child(struct platform_device *pdev, const char *name) pdevinfo.id = PLATFORM_DEVID_NONE; pdevinfo.dma_mask = DMA_BIT_MASK(32); + np = of_get_child_by_name(pdev->dev.of_node, name); + + /* Skip the child if it is explicitly disabled */ + if (np && !of_device_is_available(np)) + return NULL; + child = platform_device_register_full(&pdevinfo); if (IS_ERR(child)) { dev_warn(&pdev->dev, "%s not registered\n", name); child = NULL; } + child->dev.of_node = np; + /* * We want the dma-ranges etc to be copied from a device with the * correct dma-ranges for the VPU. From patchwork Mon May 4 09:26:08 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525311 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id E24DC17EF for ; Mon, 4 May 2020 09:26:55 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id CA99821582 for ; Mon, 4 May 2020 09:26:55 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="f1MzPf+R" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728445AbgEDJ0y (ORCPT ); Mon, 4 May 2020 05:26:54 -0400 Received: from perceval.ideasonboard.com ([213.167.242.64]:56776 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728425AbgEDJ0x (ORCPT ); Mon, 4 May 2020 05:26:53 -0400 Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id DAD1B18B4; Mon, 4 May 2020 11:26:47 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584408; bh=6GoZWwU4kOyVM9O6/vpDFHhr6N6wCxtZ/qYWxG+TZC8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=f1MzPf+R43MXbXxAHbdI2DlMkHQeoxJ9rXX/9maVcmxMDE4tN/eXXGtyIDEu2jfP6 fJrJuT8QZhzX6sH33j5qekzbNNLbExKkxbAv1+rly+TVED1sXwd9UUaA6U08BofxzO x0Ui/LQkQCjbm8K6MgEkCZZsMUVEIhMcC5LawktE= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson , Phil Elwell Subject: [PATCH v2 31/34] staging: vchiq_arm: Add a matching unregister call Date: Mon, 4 May 2020 12:26:08 +0300 Message-Id: <20200504092611.9798-32-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Phil Elwell All the registered children of vchiq have a corresponding call to platform_device_unregister except bcm2835_audio. Fix that. Fixes: 25c7597af20d ("staging: vchiq_arm: Register a platform device for audio") Signed-off-by: Phil Elwell Signed-off-by: Jacopo Mondi --- drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index 2325ab825941..59ae2d479ef4 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -2836,6 +2836,7 @@ static int vchiq_probe(struct platform_device *pdev) static int vchiq_remove(struct platform_device *pdev) { + platform_device_unregister(bcm2835_audio); platform_device_unregister(bcm2835_camera); platform_device_unregister(vcsm_cma); vchiq_debugfs_deinit(); From patchwork Mon May 4 09:26:09 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525313 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 5C4811668 for ; Mon, 4 May 2020 09:26:56 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 45FD424957 for ; Mon, 4 May 2020 09:26:56 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="moi8DWm4" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728446AbgEDJ0z (ORCPT ); Mon, 4 May 2020 05:26:55 -0400 Received: from perceval.ideasonboard.com ([213.167.242.64]:56720 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728440AbgEDJ0y (ORCPT ); Mon, 4 May 2020 05:26:54 -0400 Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 7E1D411F9; Mon, 4 May 2020 11:26:48 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584408; bh=3QXUY59gY+oX6owWIq+l7VUGMxvbiSyVbSxU6sug5GY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=moi8DWm4vwd+MgoboKmfvyaokUMLenrGF+lkPPBJ/7esjLfuAPApv68knsJCbyrau 7ct3+E022WIaBfqbtmlafhOEIKAFHvK2yZpanesY/92ief1xEFG7H79LkgRmkcNWnO AKVNrZNhnfHu7rArhBS9ckcYKlHtxQoUzIsqT5I4= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson , Dave Stevenson Subject: [PATCH v2 32/34] media: videobuf2: Allow exporting of a struct dmabuf Date: Mon, 4 May 2020 12:26:09 +0300 Message-Id: <20200504092611.9798-33-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Dave Stevenson videobuf2 only allowed exporting a dmabuf as a file descriptor, but there are instances where having the struct dma_buf is useful within the kernel. Split the current implementation into two, one step which exports a struct dma_buf, and the second which converts that into an fd. Signed-off-by: Dave Stevenson --- .../media/common/videobuf2/videobuf2-core.c | 21 ++++++++++++++++--- include/media/videobuf2-core.h | 15 +++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/drivers/media/common/videobuf2/videobuf2-core.c b/drivers/media/common/videobuf2/videobuf2-core.c index 44d65f5be845..befdc89983e2 100644 --- a/drivers/media/common/videobuf2/videobuf2-core.c +++ b/drivers/media/common/videobuf2/videobuf2-core.c @@ -2073,12 +2073,12 @@ static int __find_plane_by_offset(struct vb2_queue *q, unsigned long off, return -EINVAL; } -int vb2_core_expbuf(struct vb2_queue *q, int *fd, unsigned int type, - unsigned int index, unsigned int plane, unsigned int flags) +int vb2_core_expbuf_dmabuf(struct vb2_queue *q, unsigned int type, + unsigned int index, unsigned int plane, + unsigned int flags, struct dma_buf **dmabuf) { struct vb2_buffer *vb = NULL; struct vb2_plane *vb_plane; - int ret; struct dma_buf *dbuf; if (q->memory != VB2_MEMORY_MMAP) { @@ -2128,6 +2128,21 @@ int vb2_core_expbuf(struct vb2_queue *q, int *fd, unsigned int type, return -EINVAL; } + *dmabuf = dbuf; + return 0; +} +EXPORT_SYMBOL_GPL(vb2_core_expbuf_dmabuf); + +int vb2_core_expbuf(struct vb2_queue *q, int *fd, unsigned int type, + unsigned int index, unsigned int plane, unsigned int flags) +{ + struct dma_buf *dbuf; + int ret; + + ret = vb2_core_expbuf_dmabuf(q, type, index, plane, flags, &dbuf); + if (ret) + return ret; + ret = dma_buf_fd(dbuf, flags & ~O_ACCMODE); if (ret < 0) { dprintk(3, "buffer %d, plane %d failed to export (%d)\n", diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index f11b96514cf7..86920d3264ab 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -875,6 +875,21 @@ int vb2_core_streamon(struct vb2_queue *q, unsigned int type); */ int vb2_core_streamoff(struct vb2_queue *q, unsigned int type); +/** + * vb2_core_expbuf_dmabuf() - Export a buffer as a dma_buf structure + * @q: videobuf2 queue + * @type: buffer type + * @index: id number of the buffer + * @plane: index of the plane to be exported, 0 for single plane queues + * @flags: flags for newly created file, currently only O_CLOEXEC is + * supported, refer to manual of open syscall for more details + * @dmabuf: Returns the dmabuf pointer + * + */ +int vb2_core_expbuf_dmabuf(struct vb2_queue *q, unsigned int type, + unsigned int index, unsigned int plane, + unsigned int flags, struct dma_buf **dmabuf); + /** * vb2_core_expbuf() - Export a buffer as a file descriptor. * @q: pointer to &struct vb2_queue with videobuf2 queue. From patchwork Mon May 4 09:26:10 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525317 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id CEB951668 for ; Mon, 4 May 2020 09:27:06 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 9EBCD21973 for ; Mon, 4 May 2020 09:27:06 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="dvn8Oqg9" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727824AbgEDJ1G (ORCPT ); Mon, 4 May 2020 05:27:06 -0400 Received: from perceval.ideasonboard.com ([213.167.242.64]:56776 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728430AbgEDJ1F (ORCPT ); Mon, 4 May 2020 05:27:05 -0400 Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 2087F11FC; Mon, 4 May 2020 11:26:49 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584409; bh=mAUmUF0C6cKXdNGi+hPFueIQZuPHkaYQoIBlen0hmLU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=dvn8Oqg9zUO1h69L4Z2WWc7hq+drBE+58Yo7MjLH/cUDPzm/jsQbSq+HpdxrVP5Zm q8H7B6C3fOieHsXpgkAUfj6u+as4LI1MHU/X89AtvJDgNiVZ1B+eWrXrSeY48Z7+VC 5RqMkpwg8Eag4F7ILqC52HAvf/48meVu7erXw6pE= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson Subject: [PATCH v2 33/34] staging: bcm2835-isp: Add support for BC2835 ISP Date: Mon, 4 May 2020 12:26:10 +0300 Message-Id: <20200504092611.9798-34-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Naushir Patuck Port the V4L2 compatible driver for the ISP unit found on Broadcom BCM2835 chips. The driver interfaces though the VideoCore unit using the VCHIQ MMAL interface. ISP driver upported from from RaspberryPi BSP at revision: 6c3505be6c3e ("staging: vc04_services: isp: Make all references to bcm2835_isp_fmt const") Signed-off-by: Dave Stevenson Signed-off-by: Naushir Patuck [Adapt to staging by moving all modifications that in the BSP are scattered in core components inside this directory] Signed-off-by: Jacopo Mondi --- .../v4l/pixfmt-meta-bcm2835-isp-stats.rst | 41 + drivers/staging/vc04_services/Kconfig | 2 + drivers/staging/vc04_services/Makefile | 1 + .../staging/vc04_services/bcm2835-isp/Kconfig | 14 + .../vc04_services/bcm2835-isp/Makefile | 10 + .../bcm2835-isp/bcm2835-v4l2-isp.c | 1632 +++++++++++++++++ .../bcm2835-isp/bcm2835_isp_ctrls.h | 67 + .../bcm2835-isp/bcm2835_isp_fmts.h | 301 +++ .../include/uapi/linux/bcm2835-isp.h | 333 ++++ .../staging/vc04_services/vchiq-mmal/Kconfig | 3 +- .../vc04_services/vchiq-mmal/mmal-encodings.h | 4 + .../vchiq-mmal/mmal-parameters.h | 153 +- 12 files changed, 2559 insertions(+), 2 deletions(-) create mode 100644 drivers/staging/vc04_services/Documentation/userspace-api/media/v4l/pixfmt-meta-bcm2835-isp-stats.rst create mode 100644 drivers/staging/vc04_services/bcm2835-isp/Kconfig create mode 100644 drivers/staging/vc04_services/bcm2835-isp/Makefile create mode 100644 drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c create mode 100644 drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_ctrls.h create mode 100644 drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_fmts.h create mode 100644 drivers/staging/vc04_services/include/uapi/linux/bcm2835-isp.h diff --git a/drivers/staging/vc04_services/Documentation/userspace-api/media/v4l/pixfmt-meta-bcm2835-isp-stats.rst b/drivers/staging/vc04_services/Documentation/userspace-api/media/v4l/pixfmt-meta-bcm2835-isp-stats.rst new file mode 100644 index 000000000000..f974774c8252 --- /dev/null +++ b/drivers/staging/vc04_services/Documentation/userspace-api/media/v4l/pixfmt-meta-bcm2835-isp-stats.rst @@ -0,0 +1,41 @@ +.. Permission is granted to copy, distribute and/or modify this +.. document under the terms of the GNU Free Documentation License, +.. Version 1.1 or any later version published by the Free Software +.. Foundation, with no Invariant Sections, no Front-Cover Texts +.. and no Back-Cover Texts. A copy of the license is included at +.. Documentation/media/uapi/fdl-appendix.rst. +.. +.. TODO: replace it to GFDL-1.1-or-later WITH no-invariant-sections + +.. _v4l2-meta-fmt-bcm2835-isp-stats: + +***************************************** +V4L2_META_FMT_BCM2835_ISP_STATS ('BSTA') +***************************************** + +BCM2835 ISP Statistics + +Description +=========== + +The BCM2835 ISP hardware calculate image statistics for an input Bayer frame. +These statistics are obtained from the "bcm2835-isp0-capture3" device node +using the :c:type:`v4l2_meta_format` interface. They are formatted as described +by the :c:type:`bcm2835_isp_stats` structure below. + +.. code-block:: c + + #define DEFAULT_AWB_REGIONS_X 16 + #define DEFAULT_AWB_REGIONS_Y 12 + + #define NUM_HISTOGRAMS 2 + #define NUM_HISTOGRAM_BINS 128 + #define AWB_REGIONS (DEFAULT_AWB_REGIONS_X * DEFAULT_AWB_REGIONS_Y) + #define FLOATING_REGIONS 16 + #define AGC_REGIONS 16 + #define FOCUS_REGIONS 12 + +.. kernel-doc:: include/uapi/linux/bcm2835-isp.h + :functions: bcm2835_isp_stats_hist bcm2835_isp_stats_region + bcm2835_isp_stats_focus bcm2835_isp_stats + diff --git a/drivers/staging/vc04_services/Kconfig b/drivers/staging/vc04_services/Kconfig index 7ae21d7f359b..f9fc88bd7343 100644 --- a/drivers/staging/vc04_services/Kconfig +++ b/drivers/staging/vc04_services/Kconfig @@ -23,6 +23,8 @@ source "drivers/staging/vc04_services/bcm2835-audio/Kconfig" source "drivers/staging/vc04_services/bcm2835-camera/Kconfig" +source "drivers/staging/vc04_services/bcm2835-isp/Kconfig" + source "drivers/staging/vc04_services/vc-sm-cma/Kconfig" source "drivers/staging/vc04_services/vchiq-mmal/Kconfig" diff --git a/drivers/staging/vc04_services/Makefile b/drivers/staging/vc04_services/Makefile index 296aff05b9ab..5fc3b65224a3 100644 --- a/drivers/staging/vc04_services/Makefile +++ b/drivers/staging/vc04_services/Makefile @@ -12,6 +12,7 @@ vchiq-objs := \ obj-$(CONFIG_SND_BCM2835) += bcm2835-audio/ obj-$(CONFIG_VIDEO_BCM2835) += bcm2835-camera/ +obj-$(CONFIG_VIDEO_ISP_BCM2835) += bcm2835-isp/ obj-$(CONFIG_BCM_VC_SM_CMA) += vc-sm-cma/ obj-$(CONFIG_BCM2835_VCHIQ_MMAL) += vchiq-mmal/ diff --git a/drivers/staging/vc04_services/bcm2835-isp/Kconfig b/drivers/staging/vc04_services/bcm2835-isp/Kconfig new file mode 100644 index 000000000000..71b14acc297e --- /dev/null +++ b/drivers/staging/vc04_services/bcm2835-isp/Kconfig @@ -0,0 +1,14 @@ +config VIDEO_ISP_BCM2835 + tristate "BCM2835 ISP support" + depends on MEDIA_SUPPORT + depends on VIDEO_V4L2 && (ARCH_BCM2835 || COMPILE_TEST) + depends on MEDIA_CONTROLLER + select BCM2835_VCHIQ_MMAL + select VIDEOBUF2_DMA_CONTIG + help + This is the V4L2 driver for the Broadcom BCM2835 ISP hardware. + This operates over the VCHIQ interface to a service running on + VideoCore. + + To compile this driver as a module, choose M here: the module + will be called bcm2835-isp. diff --git a/drivers/staging/vc04_services/bcm2835-isp/Makefile b/drivers/staging/vc04_services/bcm2835-isp/Makefile new file mode 100644 index 000000000000..0120c55d7a31 --- /dev/null +++ b/drivers/staging/vc04_services/bcm2835-isp/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 +bcm2835-isp-objs := bcm2835-v4l2-isp.o + +obj-$(CONFIG_VIDEO_ISP_BCM2835) += bcm2835-isp.o + +ccflags-y += \ + -I$(srctree)/$(src)/.. \ + -I$(srctree)/$(src)/../include/uapi/ \ + -I$(srctree)/$(src)/../vchiq-mmal/ \ + -D__VCCOREVER__=0x04000000 diff --git a/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c b/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c new file mode 100644 index 000000000000..a32faab4b8dc --- /dev/null +++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835-v4l2-isp.c @@ -0,0 +1,1632 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Broadcom BCM2835 ISP driver + * + * Copyright © 2019-2020 Raspberry Pi (Trading) Ltd. + * + * Author: Naushir Patuck (naush@raspberrypi.com) + * + */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "vchiq-mmal/mmal-msg.h" +#include "vchiq-mmal/mmal-parameters.h" +#include "vchiq-mmal/mmal-vchiq.h" + +#include "bcm2835_isp_ctrls.h" +#include "bcm2835_isp_fmts.h" + +static unsigned int debug; +module_param(debug, uint, 0644); +MODULE_PARM_DESC(debug, "activates debug info"); + +static unsigned int video_nr = 13; +module_param(video_nr, uint, 0644); +MODULE_PARM_DESC(video_nr, "base video device number"); + +#define BCM2835_ISP_NAME "bcm2835-isp" +#define BCM2835_ISP_ENTITY_NAME_LEN 32 + +#define BCM2835_ISP_NUM_OUTPUTS 1 +#define BCM2835_ISP_NUM_CAPTURES 2 +#define BCM2835_ISP_NUM_METADATA 1 + +#define BCM2835_ISP_NUM_NODES \ + (BCM2835_ISP_NUM_OUTPUTS + BCM2835_ISP_NUM_CAPTURES + \ + BCM2835_ISP_NUM_METADATA) + +/* Default frame dimension of 1280 pixels. */ +#define DEFAULT_DIM 1280U +/* + * Maximum frame dimension of 16384 pixels. Even though the ISP runs in tiles, + * have a sensible limit so that we do not create an excessive number of tiles + * to process. + */ +#define MAX_DIM 16384U +/* + * Minimum frame dimension of 64 pixels. Anything lower, and the tiling + * algorihtm may not be able to cope when applying filter context. + */ +#define MIN_DIM 64U + +/* Per-queue, driver-specific private data */ +struct bcm2835_isp_q_data { + /* + * These parameters should be treated as gospel, with everything else + * being determined from them. + */ + unsigned int bytesperline; + unsigned int width; + unsigned int height; + unsigned int sizeimage; + const struct bcm2835_isp_fmt *fmt; +}; + +/* + * Structure to describe a single node /dev/video which represents a single + * input or output queue to the ISP device. + */ +struct bcm2835_isp_node { + int vfl_dir; + unsigned int id; + const char *name; + struct video_device vfd; + struct media_pad pad; + struct media_intf_devnode *intf_devnode; + struct media_link *intf_link; + struct mutex lock; /* top level device node lock */ + struct mutex queue_lock; + + struct vb2_queue queue; + unsigned int sequence; + + /* The list of formats supported on the node. */ + struct bcm2835_isp_fmt_list supported_fmts; + + struct bcm2835_isp_q_data q_data; + + /* Parent device structure */ + struct bcm2835_isp_dev *dev; + + bool registered; + bool media_node_registered; + bool queue_init; +}; + +/* + * Structure representing the entire ISP device, comprising several input and + * output nodes /dev/video. + */ +struct bcm2835_isp_dev { + struct v4l2_device v4l2_dev; + struct device *dev; + struct v4l2_ctrl_handler ctrl_handler; + struct media_device mdev; + struct media_entity entity; + bool media_device_registered; + bool media_entity_registered; + struct vchiq_mmal_instance *mmal_instance; + struct vchiq_mmal_component *component; + struct completion frame_cmplt; + + struct bcm2835_isp_node node[BCM2835_ISP_NUM_NODES]; + struct media_pad pad[BCM2835_ISP_NUM_NODES]; + atomic_t num_streaming; + + /* Image pipeline controls. */ + int r_gain; + int b_gain; +}; + +struct bcm2835_isp_buffer { + struct vb2_v4l2_buffer vb; + struct mmal_buffer mmal; +}; + +static +inline struct bcm2835_isp_dev *node_get_dev(struct bcm2835_isp_node *node) +{ + return node->dev; +} + +static inline bool node_is_output(struct bcm2835_isp_node *node) +{ + return node->queue.type == V4L2_BUF_TYPE_VIDEO_OUTPUT; +} + +static inline bool node_is_capture(struct bcm2835_isp_node *node) +{ + return node->queue.type == V4L2_BUF_TYPE_VIDEO_CAPTURE; +} + +static inline bool node_is_stats(struct bcm2835_isp_node *node) +{ + return node->queue.type == V4L2_BUF_TYPE_META_CAPTURE; +} + +static inline enum v4l2_buf_type index_to_queue_type(int index) +{ + if (index < BCM2835_ISP_NUM_OUTPUTS) + return V4L2_BUF_TYPE_VIDEO_OUTPUT; + else if (index < BCM2835_ISP_NUM_OUTPUTS + BCM2835_ISP_NUM_CAPTURES) + return V4L2_BUF_TYPE_VIDEO_CAPTURE; + else + return V4L2_BUF_TYPE_META_CAPTURE; +} + +static struct vchiq_mmal_port *get_port_data(struct bcm2835_isp_node *node) +{ + struct bcm2835_isp_dev *dev = node_get_dev(node); + + if (!dev->component) + return NULL; + + switch (node->queue.type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + return &dev->component->input[node->id]; + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + case V4L2_BUF_TYPE_META_CAPTURE: + return &dev->component->output[node->id]; + default: + v4l2_err(&dev->v4l2_dev, "%s: Invalid queue type %u\n", + __func__, node->queue.type); + break; + } + return NULL; +} + +static int set_isp_param(struct bcm2835_isp_node *node, u32 parameter, + void *value, u32 value_size) +{ + struct vchiq_mmal_port *port = get_port_data(node); + struct bcm2835_isp_dev *dev = node_get_dev(node); + + return vchiq_mmal_port_parameter_set(dev->mmal_instance, port, + parameter, value, value_size); +} + +static int set_wb_gains(struct bcm2835_isp_node *node) +{ + struct bcm2835_isp_dev *dev = node_get_dev(node); + struct mmal_parameter_awbgains gains = { + .r_gain = { dev->r_gain, 1000 }, + .b_gain = { dev->b_gain, 1000 } + }; + + return set_isp_param(node, MMAL_PARAMETER_CUSTOM_AWB_GAINS, + &gains, sizeof(gains)); +} + +static int set_digital_gain(struct bcm2835_isp_node *node, uint32_t gain) +{ + struct mmal_parameter_rational digital_gain = { + .num = gain, + .den = 1000 + }; + + return set_isp_param(node, MMAL_PARAMETER_DIGITAL_GAIN, + &digital_gain, sizeof(digital_gain)); +} + +static const struct bcm2835_isp_fmt *get_fmt(u32 mmal_fmt) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(supported_formats); i++) { + if (supported_formats[i].mmal_fmt == mmal_fmt) + return &supported_formats[i]; + } + return NULL; +} + +static const +struct bcm2835_isp_fmt *find_format_by_fourcc(unsigned int fourcc, + struct bcm2835_isp_node *node) +{ + struct bcm2835_isp_fmt_list *fmts = &node->supported_fmts; + const struct bcm2835_isp_fmt *fmt; + unsigned int i; + + for (i = 0; i < fmts->num_entries; i++) { + fmt = fmts->list[i]; + if (fmt->fourcc == fourcc) + return fmt; + } + + return NULL; +} + +static const +struct bcm2835_isp_fmt *find_format(struct v4l2_format *f, + struct bcm2835_isp_node *node) +{ + return find_format_by_fourcc(node_is_stats(node) ? + f->fmt.meta.dataformat : + f->fmt.pix.pixelformat, + node); +} + +/* vb2_to_mmal_buffer() - converts vb2 buffer header to MMAL + * + * Copies all the required fields from a VB2 buffer to the MMAL buffer header, + * ready for sending to the VPU. + */ +static void vb2_to_mmal_buffer(struct mmal_buffer *buf, + struct vb2_v4l2_buffer *vb2) +{ + u64 pts; + + buf->mmal_flags = 0; + if (vb2->flags & V4L2_BUF_FLAG_KEYFRAME) + buf->mmal_flags |= MMAL_BUFFER_HEADER_FLAG_KEYFRAME; + + /* Data must be framed correctly as one frame per buffer. */ + buf->mmal_flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_END; + + buf->length = vb2->vb2_buf.planes[0].bytesused; + /* + * Minor ambiguity in the V4L2 spec as to whether passing in a 0 length + * buffer, or one with V4L2_BUF_FLAG_LAST set denotes end of stream. + * Handle either. + */ + if (!buf->length || vb2->flags & V4L2_BUF_FLAG_LAST) + buf->mmal_flags |= MMAL_BUFFER_HEADER_FLAG_EOS; + + /* vb2 timestamps in nsecs, mmal in usecs */ + pts = vb2->vb2_buf.timestamp; + do_div(pts, 1000); + buf->pts = pts; + buf->dts = MMAL_TIME_UNKNOWN; +} + +static void mmal_buffer_cb(struct vchiq_mmal_instance *instance, + struct vchiq_mmal_port *port, int status, + struct mmal_buffer *mmal_buf) +{ + struct bcm2835_isp_buffer *q_buf; + struct bcm2835_isp_node *node = port->cb_ctx; + struct bcm2835_isp_dev *dev = node_get_dev(node); + struct vb2_v4l2_buffer *vb2; + + q_buf = container_of(mmal_buf, struct bcm2835_isp_buffer, mmal); + vb2 = &q_buf->vb; + v4l2_dbg(2, debug, &dev->v4l2_dev, + "%s: port:%s[%d], status:%d, buf:%p, dmabuf:%p, length:%lu, flags %u, pts %lld\n", + __func__, node_is_output(node) ? "input" : "output", node->id, + status, mmal_buf, mmal_buf->dma_buf, mmal_buf->length, + mmal_buf->mmal_flags, mmal_buf->pts); + + if (mmal_buf->cmd) + v4l2_err(&dev->v4l2_dev, + "%s: Unexpected event on output callback - %08x\n", + __func__, mmal_buf->cmd); + + if (status) { + /* error in transfer */ + if (vb2) { + /* there was a buffer with the error so return it */ + vb2_buffer_done(&vb2->vb2_buf, VB2_BUF_STATE_ERROR); + } + return; + } + + /* vb2 timestamps in nsecs, mmal in usecs */ + vb2->vb2_buf.timestamp = mmal_buf->pts * 1000; + vb2->sequence = node->sequence++; + vb2_set_plane_payload(&vb2->vb2_buf, 0, mmal_buf->length); + vb2_buffer_done(&vb2->vb2_buf, VB2_BUF_STATE_DONE); + + if (!port->enabled) + complete(&dev->frame_cmplt); +} + +static void setup_mmal_port_format(struct bcm2835_isp_node *node, + struct vchiq_mmal_port *port) +{ + struct bcm2835_isp_q_data *q_data = &node->q_data; + + port->format.encoding = q_data->fmt->mmal_fmt; + /* Raw image format - set width/height */ + port->es.video.width = (q_data->bytesperline << 3) / q_data->fmt->depth; + port->es.video.height = q_data->height; + port->es.video.crop.width = q_data->width; + port->es.video.crop.height = q_data->height; + port->es.video.crop.x = 0; + port->es.video.crop.y = 0; +}; + +static int setup_mmal_port(struct bcm2835_isp_node *node) +{ + struct vchiq_mmal_port *port = get_port_data(node); + struct bcm2835_isp_dev *dev = node_get_dev(node); + unsigned int enable = 1; + int ret; + + v4l2_dbg(2, debug, &dev->v4l2_dev, "%s: setup %s[%d]\n", __func__, + node->name, node->id); + + vchiq_mmal_port_parameter_set(dev->mmal_instance, port, + MMAL_PARAMETER_ZERO_COPY, &enable, + sizeof(enable)); + setup_mmal_port_format(node, port); + ret = vchiq_mmal_port_set_format(dev->mmal_instance, port); + if (ret < 0) { + v4l2_dbg(1, debug, &dev->v4l2_dev, + "%s: vchiq_mmal_port_set_format failed\n", + __func__); + return ret; + } + + if (node->q_data.sizeimage < port->minimum_buffer.size) { + v4l2_err(&dev->v4l2_dev, + "buffer size mismatch sizeimage %u < min size %u\n", + node->q_data.sizeimage, port->minimum_buffer.size); + return -EINVAL; + } + + return 0; +} + +static int bcm2835_isp_mmal_buf_cleanup(struct mmal_buffer *mmal_buf) +{ + mmal_vchi_buffer_cleanup(mmal_buf); + + if (mmal_buf->dma_buf) { + dma_buf_put(mmal_buf->dma_buf); + mmal_buf->dma_buf = NULL; + } + + return 0; +} + +static int bcm2835_isp_node_queue_setup(struct vb2_queue *q, + unsigned int *nbuffers, + unsigned int *nplanes, + unsigned int sizes[], + struct device *alloc_devs[]) +{ + struct bcm2835_isp_node *node = vb2_get_drv_priv(q); + struct vchiq_mmal_port *port; + unsigned int size; + + if (setup_mmal_port(node)) + return -EINVAL; + + size = node->q_data.sizeimage; + if (size == 0) { + v4l2_info(&node_get_dev(node)->v4l2_dev, + "%s: Image size unset in queue_setup for node %s[%d]\n", + __func__, node->name, node->id); + return -EINVAL; + } + + if (*nplanes) + return sizes[0] < size ? -EINVAL : 0; + + *nplanes = 1; + sizes[0] = size; + + port = get_port_data(node); + port->current_buffer.size = size; + + if (*nbuffers < port->minimum_buffer.num) + *nbuffers = port->minimum_buffer.num; + + port->current_buffer.num = *nbuffers; + + v4l2_dbg(2, debug, &node_get_dev(node)->v4l2_dev, + "%s: Image size %u, nbuffers %u for node %s[%d]\n", + __func__, sizes[0], *nbuffers, node->name, node->id); + return 0; +} + +static int bcm2835_isp_buf_init(struct vb2_buffer *vb) +{ + struct bcm2835_isp_node *node = vb2_get_drv_priv(vb->vb2_queue); + struct bcm2835_isp_dev *dev = node_get_dev(node); + struct vb2_v4l2_buffer *vb2 = to_vb2_v4l2_buffer(vb); + struct bcm2835_isp_buffer *buf = + container_of(vb2, struct bcm2835_isp_buffer, vb); + + v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: vb %p\n", __func__, vb); + + buf->mmal.buffer = vb2_plane_vaddr(&buf->vb.vb2_buf, 0); + buf->mmal.buffer_size = vb2_plane_size(&buf->vb.vb2_buf, 0); + mmal_vchi_buffer_init(dev->mmal_instance, &buf->mmal); + return 0; +} + +static int bcm2835_isp_buf_prepare(struct vb2_buffer *vb) +{ + struct bcm2835_isp_node *node = vb2_get_drv_priv(vb->vb2_queue); + struct bcm2835_isp_dev *dev = node_get_dev(node); + struct vb2_v4l2_buffer *vb2 = to_vb2_v4l2_buffer(vb); + struct bcm2835_isp_buffer *buf = + container_of(vb2, struct bcm2835_isp_buffer, vb); + struct dma_buf *dma_buf; + int ret; + + v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: type: %d ptr %p\n", + __func__, vb->vb2_queue->type, vb); + + if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { + if (vb2->field == V4L2_FIELD_ANY) + vb2->field = V4L2_FIELD_NONE; + if (vb2->field != V4L2_FIELD_NONE) { + v4l2_err(&dev->v4l2_dev, + "%s field isn't supported\n", __func__); + return -EINVAL; + } + } + + if (vb2_plane_size(vb, 0) < node->q_data.sizeimage) { + v4l2_err(&dev->v4l2_dev, + "%s data will not fit into plane (%lu < %lu)\n", + __func__, vb2_plane_size(vb, 0), + (long)node->q_data.sizeimage); + return -EINVAL; + } + + if (!V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) + vb2_set_plane_payload(vb, 0, node->q_data.sizeimage); + + switch (vb->memory) { + case VB2_MEMORY_DMABUF: + dma_buf = dma_buf_get(vb->planes[0].m.fd); + + if (dma_buf != buf->mmal.dma_buf) { + /* + * dmabuf either hasn't already been mapped, or it has + * changed. + */ + if (buf->mmal.dma_buf) { + v4l2_err(&dev->v4l2_dev, + "%s Buffer changed - why did the core not call cleanup?\n", + __func__); + bcm2835_isp_mmal_buf_cleanup(&buf->mmal); + } + + buf->mmal.dma_buf = dma_buf; + } else { + /* + * Already have a reference to the buffer, so release it + * here. + */ + dma_buf_put(dma_buf); + } + ret = 0; + break; + case VB2_MEMORY_MMAP: + /* + * We want to do this at init, but vb2_core_expbuf checks that + * the index < q->num_buffers, and q->num_buffers only gets + * updated once all the buffers are allocated. + */ + if (!buf->mmal.dma_buf) { + ret = vb2_core_expbuf_dmabuf(vb->vb2_queue, + vb->vb2_queue->type, + vb->index, 0, O_CLOEXEC, + &buf->mmal.dma_buf); + v4l2_dbg(3, debug, &dev->v4l2_dev, + "%s: exporting ptr %p to dmabuf %p\n", + __func__, vb, buf->mmal.dma_buf); + if (ret) + v4l2_err(&dev->v4l2_dev, + "%s: Failed to expbuf idx %d, ret %d\n", + __func__, vb->index, ret); + } else { + ret = 0; + } + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static void bcm2835_isp_node_buffer_queue(struct vb2_buffer *buf) +{ + struct bcm2835_isp_node *node = vb2_get_drv_priv(buf->vb2_queue); + struct vb2_v4l2_buffer *vbuf = + container_of(buf, struct vb2_v4l2_buffer, vb2_buf); + struct bcm2835_isp_buffer *buffer = + container_of(vbuf, struct bcm2835_isp_buffer, vb); + struct bcm2835_isp_dev *dev = node_get_dev(node); + + v4l2_dbg(3, debug, &dev->v4l2_dev, "%s: node %s[%d], buffer %p\n", + __func__, node->name, node->id, buffer); + + vb2_to_mmal_buffer(&buffer->mmal, &buffer->vb); + v4l2_dbg(3, debug, &dev->v4l2_dev, + "%s: node %s[%d] - submitting mmal dmabuf %p\n", __func__, + node->name, node->id, buffer->mmal.dma_buf); + vchiq_mmal_submit_buffer(dev->mmal_instance, get_port_data(node), + &buffer->mmal); +} + +static void bcm2835_isp_buffer_cleanup(struct vb2_buffer *vb) +{ + struct vb2_v4l2_buffer *vb2 = to_vb2_v4l2_buffer(vb); + struct bcm2835_isp_buffer *buffer = + container_of(vb2, struct bcm2835_isp_buffer, vb); + + bcm2835_isp_mmal_buf_cleanup(&buffer->mmal); +} + +static int bcm2835_isp_node_start_streaming(struct vb2_queue *q, + unsigned int count) +{ + struct bcm2835_isp_node *node = vb2_get_drv_priv(q); + struct bcm2835_isp_dev *dev = node_get_dev(node); + struct vchiq_mmal_port *port = get_port_data(node); + int ret; + + v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: node %s[%d] (count %u)\n", + __func__, node->name, node->id, count); + + ret = vchiq_mmal_component_enable(dev->mmal_instance, dev->component); + if (ret) { + v4l2_err(&dev->v4l2_dev, "%s: Failed enabling component, ret %d\n", + __func__, ret); + return -EIO; + } + + node->sequence = 0; + port->cb_ctx = node; + ret = vchiq_mmal_port_enable(dev->mmal_instance, port, + mmal_buffer_cb); + if (!ret) + atomic_inc(&dev->num_streaming); + else + v4l2_err(&dev->v4l2_dev, + "%s: Failed enabling port, ret %d\n", __func__, ret); + + return ret; +} + +static void bcm2835_isp_node_stop_streaming(struct vb2_queue *q) +{ + struct bcm2835_isp_node *node = vb2_get_drv_priv(q); + struct bcm2835_isp_dev *dev = node_get_dev(node); + struct vchiq_mmal_port *port = get_port_data(node); + unsigned int i; + int ret; + + v4l2_dbg(1, debug, &dev->v4l2_dev, "%s: node %s[%d], mmal port %p\n", + __func__, node->name, node->id, port); + + init_completion(&dev->frame_cmplt); + + /* Disable MMAL port - this will flush buffers back */ + ret = vchiq_mmal_port_disable(dev->mmal_instance, port); + if (ret) + v4l2_err(&dev->v4l2_dev, + "%s: Failed disabling %s port, ret %d\n", __func__, + node_is_output(node) ? "i/p" : "o/p", + ret); + + while (atomic_read(&port->buffers_with_vpu)) { + v4l2_dbg(1, debug, &dev->v4l2_dev, + "%s: Waiting for buffers to be returned - %d outstanding\n", + __func__, atomic_read(&port->buffers_with_vpu)); + ret = wait_for_completion_timeout(&dev->frame_cmplt, HZ); + if (ret <= 0) { + v4l2_err(&dev->v4l2_dev, + "%s: Timeout waiting for buffers to be returned - %d outstanding\n", + __func__, + atomic_read(&port->buffers_with_vpu)); + break; + } + } + + /* Release the VCSM handle here to release the associated dmabuf */ + for (i = 0; i < q->num_buffers; i++) { + struct vb2_v4l2_buffer *vb2 = to_vb2_v4l2_buffer(q->bufs[i]); + struct bcm2835_isp_buffer *buf = + container_of(vb2, struct bcm2835_isp_buffer, vb); + bcm2835_isp_mmal_buf_cleanup(&buf->mmal); + } + + atomic_dec(&dev->num_streaming); + /* If all ports disabled, then disable the component */ + if (atomic_read(&dev->num_streaming) == 0) { + ret = vchiq_mmal_component_disable(dev->mmal_instance, + dev->component); + if (ret) { + v4l2_err(&dev->v4l2_dev, + "%s: Failed disabling component, ret %d\n", + __func__, ret); + } + } + + /* + * Simply wait for any vb2 buffers to finish. We could take steps to + * make them complete more quickly if we care, or even return them + * ourselves. + */ + vb2_wait_for_all_buffers(&node->queue); +} + +static const struct vb2_ops bcm2835_isp_node_queue_ops = { + .queue_setup = bcm2835_isp_node_queue_setup, + .buf_init = bcm2835_isp_buf_init, + .buf_prepare = bcm2835_isp_buf_prepare, + .buf_queue = bcm2835_isp_node_buffer_queue, + .buf_cleanup = bcm2835_isp_buffer_cleanup, + .start_streaming = bcm2835_isp_node_start_streaming, + .stop_streaming = bcm2835_isp_node_stop_streaming, +}; + +static const +struct bcm2835_isp_fmt *get_default_format(struct bcm2835_isp_node *node) +{ + return node->supported_fmts.list[0]; +} + +static inline unsigned int get_bytesperline(int width, + const struct bcm2835_isp_fmt *fmt) +{ + return ALIGN((width * fmt->depth) >> 3, fmt->bytesperline_align); +} + +static inline unsigned int get_sizeimage(int bpl, int width, int height, + const struct bcm2835_isp_fmt *fmt) +{ + return (bpl * height * fmt->size_multiplier_x2) >> 1; +} + +static int bcm2835_isp_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct bcm2835_isp_dev *dev = + container_of(ctrl->handler, struct bcm2835_isp_dev, ctrl_handler); + struct bcm2835_isp_node *node = &dev->node[0]; + int ret = 0; + + /* + * The ISP firmware driver will ensure these settings are applied on + * a frame boundary, so we are safe to write them as they come in. + * + * Note that the bcm2835_isp_* param structures are identical to the + * mmal-parameters.h definitions. This avoids the need for unnecessary + * field-by-field copying between structures. + */ + switch (ctrl->id) { + case V4L2_CID_RED_BALANCE: + dev->r_gain = ctrl->val; + ret = set_wb_gains(node); + break; + case V4L2_CID_BLUE_BALANCE: + dev->b_gain = ctrl->val; + ret = set_wb_gains(node); + break; + case V4L2_CID_DIGITAL_GAIN: + ret = set_digital_gain(node, ctrl->val); + break; + case V4L2_CID_USER_BCM2835_ISP_CC_MATRIX: + ret = set_isp_param(node, MMAL_PARAMETER_CUSTOM_CCM, + ctrl->p_new.p_u8, + sizeof(struct bcm2835_isp_custom_ccm)); + break; + case V4L2_CID_USER_BCM2835_ISP_LENS_SHADING: + ret = set_isp_param(node, MMAL_PARAMETER_LENS_SHADING_OVERRIDE, + ctrl->p_new.p_u8, + sizeof(struct bcm2835_isp_lens_shading)); + break; + case V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL: + ret = set_isp_param(node, MMAL_PARAMETER_BLACK_LEVEL, + ctrl->p_new.p_u8, + sizeof(struct bcm2835_isp_black_level)); + break; + case V4L2_CID_USER_BCM2835_ISP_GEQ: + ret = set_isp_param(node, MMAL_PARAMETER_GEQ, + ctrl->p_new.p_u8, + sizeof(struct bcm2835_isp_geq)); + break; + case V4L2_CID_USER_BCM2835_ISP_GAMMA: + ret = set_isp_param(node, MMAL_PARAMETER_GAMMA, + ctrl->p_new.p_u8, + sizeof(struct bcm2835_isp_gamma)); + break; + case V4L2_CID_USER_BCM2835_ISP_DENOISE: + ret = set_isp_param(node, MMAL_PARAMETER_DENOISE, + ctrl->p_new.p_u8, + sizeof(struct bcm2835_isp_denoise)); + break; + case V4L2_CID_USER_BCM2835_ISP_SHARPEN: + ret = set_isp_param(node, MMAL_PARAMETER_SHARPEN, + ctrl->p_new.p_u8, + sizeof(struct bcm2835_isp_sharpen)); + break; + case V4L2_CID_USER_BCM2835_ISP_DPC: + ret = set_isp_param(node, MMAL_PARAMETER_DPC, + ctrl->p_new.p_u8, + sizeof(struct bcm2835_isp_dpc)); + break; + default: + v4l2_info(&dev->v4l2_dev, "Unrecognised control\n"); + ret = -EINVAL; + } + + if (ret) { + v4l2_err(&dev->v4l2_dev, "%s: Failed setting ctrl \"%s\" (%08x), err %d\n", + __func__, ctrl->name, ctrl->id, ret); + ret = -EIO; + } + + return ret; +} + +static const struct v4l2_ctrl_ops bcm2835_isp_ctrl_ops = { + .s_ctrl = bcm2835_isp_s_ctrl, +}; + +static const struct v4l2_file_operations bcm2835_isp_fops = { + .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = vb2_fop_release, + .poll = vb2_fop_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = vb2_fop_mmap +}; + +static int populate_qdata_fmt(struct v4l2_format *f, + struct bcm2835_isp_node *node) +{ + struct bcm2835_isp_dev *dev = node_get_dev(node); + struct bcm2835_isp_q_data *q_data = &node->q_data; + struct vchiq_mmal_port *port; + int ret; + + if (!node_is_stats(node)) { + v4l2_dbg(1, debug, &dev->v4l2_dev, + "%s: Setting pix format for type %d, wxh: %ux%u, fmt: %08x, size %u\n", + __func__, f->type, f->fmt.pix.width, f->fmt.pix.height, + f->fmt.pix.pixelformat, f->fmt.pix.sizeimage); + + q_data->fmt = find_format(f, node); + q_data->width = f->fmt.pix.width; + q_data->height = f->fmt.pix.height; + q_data->height = f->fmt.pix.height; + + /* All parameters should have been set correctly by try_fmt */ + q_data->bytesperline = f->fmt.pix.bytesperline; + q_data->sizeimage = f->fmt.pix.sizeimage; + } else { + v4l2_dbg(1, debug, &dev->v4l2_dev, + "%s: Setting meta format for fmt: %08x, size %u\n", + __func__, f->fmt.meta.dataformat, + f->fmt.meta.buffersize); + + q_data->fmt = find_format(f, node); + q_data->width = 0; + q_data->height = 0; + q_data->bytesperline = 0; + q_data->sizeimage = f->fmt.meta.buffersize; + } + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "%s: Calculated bpl as %u, size %u\n", __func__, + q_data->bytesperline, q_data->sizeimage); + + /* If we have a component then setup the port as well */ + port = get_port_data(node); + setup_mmal_port_format(node, port); + ret = vchiq_mmal_port_set_format(dev->mmal_instance, port); + if (ret) { + v4l2_err(&dev->v4l2_dev, + "%s: Failed vchiq_mmal_port_set_format on port, ret %d\n", + __func__, ret); + ret = -EINVAL; + } + + if (q_data->sizeimage < port->minimum_buffer.size) { + v4l2_err(&dev->v4l2_dev, + "%s: Current buffer size of %u < min buf size %u - driver mismatch to MMAL\n", + __func__, + q_data->sizeimage, + port->minimum_buffer.size); + } + + v4l2_dbg(1, debug, &dev->v4l2_dev, + "%s: Set format for type %d, wxh: %dx%d, fmt: %08x, size %u\n", + __func__, f->type, q_data->width, q_data->height, + q_data->fmt->fourcc, q_data->sizeimage); + + return ret; +} + +static int bcm2835_isp_node_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strscpy(cap->driver, BCM2835_ISP_NAME, sizeof(cap->driver)); + strscpy(cap->card, BCM2835_ISP_NAME, sizeof(cap->card)); + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + BCM2835_ISP_NAME); + + return 0; +} + +static int bcm2835_isp_node_g_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct bcm2835_isp_node *node = video_drvdata(file); + + if (f->type != node->queue.type) + return -EINVAL; + + if (node_is_stats(node)) { + f->fmt.meta.dataformat = V4L2_META_FMT_BCM2835_ISP_STATS; + f->fmt.meta.buffersize = + get_port_data(node)->minimum_buffer.size; + } else { + struct bcm2835_isp_q_data *q_data = &node->q_data; + + f->fmt.pix.width = q_data->width; + f->fmt.pix.height = q_data->height; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.pixelformat = q_data->fmt->fourcc; + f->fmt.pix.bytesperline = q_data->bytesperline; + f->fmt.pix.sizeimage = q_data->sizeimage; + f->fmt.pix.colorspace = q_data->fmt->colorspace; + } + + return 0; +} + +static int bcm2835_isp_node_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct bcm2835_isp_node *node = video_drvdata(file); + struct bcm2835_isp_fmt_list *fmts = &node->supported_fmts; + + if (f->type != node->queue.type) + return -EINVAL; + + if (f->index < fmts->num_entries) { + /* Format found */ + f->pixelformat = fmts->list[f->index]->fourcc; + f->flags = fmts->list[f->index]->flags; + return 0; + } + + return -EINVAL; +} + +static int bcm2835_isp_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct bcm2835_isp_node *node = video_drvdata(file); + struct bcm2835_isp_dev *dev = node_get_dev(node); + const struct bcm2835_isp_fmt *fmt; + + if (node_is_stats(node) || fsize->index) + return -EINVAL; + + fmt = find_format_by_fourcc(fsize->pixel_format, node); + if (!fmt) { + v4l2_err(&dev->v4l2_dev, "Invalid pixel code: %x\n", + fsize->pixel_format); + return -EINVAL; + } + + fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; + fsize->stepwise.min_width = MIN_DIM; + fsize->stepwise.max_width = MAX_DIM; + fsize->stepwise.step_width = fmt->step_size; + + fsize->stepwise.min_height = MIN_DIM; + fsize->stepwise.max_height = MAX_DIM; + fsize->stepwise.step_height = fmt->step_size; + + return 0; +} + +static int bcm2835_isp_node_try_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct bcm2835_isp_node *node = video_drvdata(file); + const struct bcm2835_isp_fmt *fmt; + + if (f->type != node->queue.type) + return -EINVAL; + + fmt = find_format(f, node); + if (!fmt) + fmt = get_default_format(node); + + if (!node_is_stats(node)) { + f->fmt.pix.width = max(min(f->fmt.pix.width, MAX_DIM), + MIN_DIM); + f->fmt.pix.height = max(min(f->fmt.pix.height, MAX_DIM), + MIN_DIM); + + f->fmt.pix.pixelformat = fmt->fourcc; + f->fmt.pix.colorspace = fmt->colorspace; + f->fmt.pix.bytesperline = get_bytesperline(f->fmt.pix.width, + fmt); + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.sizeimage = + get_sizeimage(f->fmt.pix.bytesperline, f->fmt.pix.width, + f->fmt.pix.height, fmt); + } else { + f->fmt.meta.dataformat = fmt->fourcc; + f->fmt.meta.buffersize = + get_port_data(node)->minimum_buffer.size; + } + + return 0; +} + +static int bcm2835_isp_node_s_fmt(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct bcm2835_isp_node *node = video_drvdata(file); + int ret; + + if (f->type != node->queue.type) + return -EINVAL; + + ret = bcm2835_isp_node_try_fmt(file, priv, f); + if (ret) + return ret; + + v4l2_dbg(1, debug, &node_get_dev(node)->v4l2_dev, + "%s: Set format for node %s[%d]\n", + __func__, node->name, node->id); + + return populate_qdata_fmt(f, node); +} + +static int bcm2835_isp_node_s_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct mmal_parameter_crop crop; + struct bcm2835_isp_node *node = video_drvdata(file); + struct bcm2835_isp_dev *dev = node_get_dev(node); + struct vchiq_mmal_port *port = get_port_data(node); + + /* This return value is required fro V4L2 compliance. */ + if (node_is_stats(node)) + return -ENOTTY; + + if (!s->r.width || !s->r.height) + return -EINVAL; + + /* Adjust the crop window if goes outside the frame dimensions. */ + s->r.left = min((unsigned int)max(s->r.left, 0), + node->q_data.width - MIN_DIM); + s->r.top = min((unsigned int)max(s->r.top, 0), + node->q_data.height - MIN_DIM); + s->r.width = max(min(s->r.width, node->q_data.width - s->r.left), + MIN_DIM); + s->r.height = max(min(s->r.height, node->q_data.height - s->r.top), + MIN_DIM); + + crop.rect.x = s->r.left; + crop.rect.y = s->r.top; + crop.rect.width = s->r.width; + crop.rect.height = s->r.height; + + return vchiq_mmal_port_parameter_set(dev->mmal_instance, port, + MMAL_PARAMETER_CROP, + &crop, sizeof(crop)); +} + +static int bcm2835_isp_node_g_selection(struct file *file, void *fh, + struct v4l2_selection *s) +{ + struct bcm2835_isp_node *node = video_drvdata(file); + struct bcm2835_isp_dev *dev = node_get_dev(node); + struct vchiq_mmal_port *port = get_port_data(node); + struct mmal_parameter_crop crop; + u32 crop_size = sizeof(crop); + int ret; + + /* This return value is required for V4L2 compliance. */ + if (node_is_stats(node)) + return -ENOTTY; + + /* We can only return out an input crop. */ + if (s->target != V4L2_SEL_TGT_CROP) + return -EINVAL; + + ret = vchiq_mmal_port_parameter_get(dev->mmal_instance, port, + MMAL_PARAMETER_CROP, + &crop, &crop_size); + if (!ret) + return -EINVAL; + + s->r.left = crop.rect.x; + s->r.top = crop.rect.y; + s->r.width = crop.rect.width; + s->r.height = crop.rect.height; + + return 0; +} + +static int bcm3285_isp_subscribe_event(struct v4l2_fh *fh, + const struct v4l2_event_subscription *s) +{ + switch (s->type) { + /* Cannot change source parameters dynamically at runtime. */ + case V4L2_EVENT_SOURCE_CHANGE: + return -EINVAL; + case V4L2_EVENT_CTRL: + return v4l2_ctrl_subscribe_event(fh, s); + default: + return v4l2_event_subscribe(fh, s, 4, NULL); + } +} + +static const struct v4l2_ioctl_ops bcm2835_isp_node_ioctl_ops = { + .vidioc_querycap = bcm2835_isp_node_querycap, + .vidioc_g_fmt_vid_cap = bcm2835_isp_node_g_fmt, + .vidioc_g_fmt_vid_out = bcm2835_isp_node_g_fmt, + .vidioc_g_fmt_meta_cap = bcm2835_isp_node_g_fmt, + .vidioc_s_fmt_vid_cap = bcm2835_isp_node_s_fmt, + .vidioc_s_fmt_vid_out = bcm2835_isp_node_s_fmt, + .vidioc_s_fmt_meta_cap = bcm2835_isp_node_s_fmt, + .vidioc_try_fmt_vid_cap = bcm2835_isp_node_try_fmt, + .vidioc_try_fmt_vid_out = bcm2835_isp_node_try_fmt, + .vidioc_try_fmt_meta_cap = bcm2835_isp_node_try_fmt, + .vidioc_s_selection = bcm2835_isp_node_s_selection, + .vidioc_g_selection = bcm2835_isp_node_g_selection, + + .vidioc_enum_fmt_vid_cap = bcm2835_isp_node_enum_fmt, + .vidioc_enum_fmt_vid_out = bcm2835_isp_node_enum_fmt, + .vidioc_enum_fmt_meta_cap = bcm2835_isp_node_enum_fmt, + .vidioc_enum_framesizes = bcm2835_isp_enum_framesizes, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_expbuf = vb2_ioctl_expbuf, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, + + .vidioc_subscribe_event = bcm3285_isp_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +/* + * Size of the array to provide to the VPU when asking for the list of supported + * formats. + * + * The ISP component currently advertises 33 input formats, so add a small + * overhead on that. + */ +#define MAX_SUPPORTED_ENCODINGS 40 + +/* Populate node->supported_fmts with the formats supported by those ports. */ +static int bcm2835_isp_get_supported_fmts(struct bcm2835_isp_node *node) +{ + struct bcm2835_isp_dev *dev = node_get_dev(node); + struct bcm2835_isp_fmt const **list; + unsigned int i, j, num_encodings; + u32 fourccs[MAX_SUPPORTED_ENCODINGS]; + u32 param_size = sizeof(fourccs); + int ret; + + ret = vchiq_mmal_port_parameter_get(dev->mmal_instance, + get_port_data(node), + MMAL_PARAMETER_SUPPORTED_ENCODINGS, + &fourccs, ¶m_size); + + if (ret) { + if (ret == MMAL_MSG_STATUS_ENOSPC) { + v4l2_err(&dev->v4l2_dev, + "%s: port has more encoding than we provided space for. Some are dropped.\n", + __func__); + num_encodings = MAX_SUPPORTED_ENCODINGS; + } else { + v4l2_err(&dev->v4l2_dev, "%s: get_param ret %u.\n", + __func__, ret); + return -EINVAL; + } + } else { + num_encodings = param_size / sizeof(u32); + } + + /* + * Assume at this stage that all encodings will be supported in V4L2. + * Any that aren't supported will waste a very small amount of memory. + */ + list = devm_kzalloc(dev->dev, + sizeof(struct bcm2835_isp_fmt *) * num_encodings, + GFP_KERNEL); + if (!list) + return -ENOMEM; + node->supported_fmts.list = list; + + for (i = 0, j = 0; i < num_encodings; i++) { + const struct bcm2835_isp_fmt *fmt = get_fmt(fourccs[i]); + + if (fmt) { + list[j] = fmt; + j++; + } + } + node->supported_fmts.num_entries = j; + + return 0; +} + +/* + * Register a device node /dev/video to go along with one of the ISP's input + * or output nodes. + */ +static int register_node(struct bcm2835_isp_dev *dev, + struct bcm2835_isp_node *node, + int index) +{ + struct video_device *vfd; + struct vb2_queue *queue; + int ret; + + mutex_init(&node->lock); + + node->dev = dev; + vfd = &node->vfd; + queue = &node->queue; + queue->type = index_to_queue_type(index); + /* + * Setup the node type-specific params. + * + * Only the OUTPUT node can set controls and crop windows. However, + * we must allow the s/g_selection ioctl on the stats node as v4l2 + * compliance expects it to return a -ENOTTY, and the framework + * does not handle it if the ioctl is disabled. + */ + switch (queue->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + vfd->device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; + node->id = index; + node->vfl_dir = VFL_DIR_TX; + node->name = "output"; + break; + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + vfd->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + /* First Capture node starts at id 0, etc. */ + node->id = index - BCM2835_ISP_NUM_OUTPUTS; + node->vfl_dir = VFL_DIR_RX; + node->name = "capture"; + v4l2_disable_ioctl(&node->vfd, VIDIOC_S_CTRL); + v4l2_disable_ioctl(&node->vfd, VIDIOC_S_SELECTION); + v4l2_disable_ioctl(&node->vfd, VIDIOC_G_SELECTION); + break; + case V4L2_BUF_TYPE_META_CAPTURE: + vfd->device_caps = V4L2_CAP_META_CAPTURE | V4L2_CAP_STREAMING; + node->id = index - BCM2835_ISP_NUM_OUTPUTS; + node->vfl_dir = VFL_DIR_RX; + node->name = "stats"; + v4l2_disable_ioctl(&node->vfd, VIDIOC_S_CTRL); + break; + } + + /* We use the selection API instead of the old crop API. */ + v4l2_disable_ioctl(vfd, VIDIOC_CROPCAP); + v4l2_disable_ioctl(vfd, VIDIOC_G_CROP); + v4l2_disable_ioctl(vfd, VIDIOC_S_CROP); + + ret = bcm2835_isp_get_supported_fmts(node); + if (ret) + return ret; + + /* Initialise the the video node. */ + vfd->vfl_type = VFL_TYPE_VIDEO; + vfd->fops = &bcm2835_isp_fops, + vfd->ioctl_ops = &bcm2835_isp_node_ioctl_ops, + vfd->minor = -1, + vfd->release = video_device_release_empty, + vfd->queue = &node->queue; + vfd->lock = &node->lock; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->vfl_dir = node->vfl_dir; + + node->q_data.fmt = get_default_format(node); + node->q_data.width = DEFAULT_DIM; + node->q_data.height = DEFAULT_DIM; + node->q_data.bytesperline = + get_bytesperline(DEFAULT_DIM, node->q_data.fmt); + node->q_data.sizeimage = node_is_stats(node) ? + get_port_data(node)->recommended_buffer.size : + get_sizeimage(node->q_data.bytesperline, + node->q_data.width, + node->q_data.height, + node->q_data.fmt); + + queue->io_modes = VB2_MMAP | VB2_DMABUF; + queue->drv_priv = node; + queue->ops = &bcm2835_isp_node_queue_ops; + queue->mem_ops = &vb2_dma_contig_memops; + queue->buf_struct_size = sizeof(struct bcm2835_isp_buffer); + queue->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; + queue->dev = dev->dev; + queue->lock = &node->queue_lock; + + ret = vb2_queue_init(queue); + if (ret < 0) { + v4l2_info(&dev->v4l2_dev, "vb2_queue_init failed\n"); + return ret; + } + node->queue_init = true; + + /* Define the device names */ + snprintf(vfd->name, sizeof(node->vfd.name), "%s-%s%d", BCM2835_ISP_NAME, + node->name, node->id); + + ret = video_register_device(vfd, VFL_TYPE_VIDEO, video_nr + index); + if (ret) { + v4l2_err(&dev->v4l2_dev, + "Failed to register video %s[%d] device node\n", + node->name, node->id); + return ret; + } + + node->registered = true; + video_set_drvdata(vfd, node); + + /* Set some controls and defaults, but only on the VIDEO_OUTPUT node. */ + if (node_is_output(node)) { + unsigned int i; + + /* Use this ctrl template to assign all out ISP custom ctrls. */ + struct v4l2_ctrl_config ctrl_template = { + .ops = &bcm2835_isp_ctrl_ops, + .type = V4L2_CTRL_TYPE_U8, + .def = 0, + .min = 0x00, + .max = 0xff, + .step = 1, + }; + + v4l2_ctrl_handler_init(&dev->ctrl_handler, 4); + + dev->r_gain = 1000; + dev->b_gain = 1000; + + v4l2_ctrl_new_std(&dev->ctrl_handler, &bcm2835_isp_ctrl_ops, + V4L2_CID_RED_BALANCE, 1, 0xffff, 1, + dev->r_gain); + + v4l2_ctrl_new_std(&dev->ctrl_handler, &bcm2835_isp_ctrl_ops, + V4L2_CID_BLUE_BALANCE, 1, 0xffff, 1, + dev->b_gain); + + v4l2_ctrl_new_std(&dev->ctrl_handler, &bcm2835_isp_ctrl_ops, + V4L2_CID_DIGITAL_GAIN, 1, 0xffff, 1, 1000); + + for (i = 0; i < ARRAY_SIZE(custom_ctrls); i++) { + ctrl_template.name = custom_ctrls[i].name; + ctrl_template.id = custom_ctrls[i].id; + ctrl_template.dims[0] = custom_ctrls[i].size; + ctrl_template.flags = custom_ctrls[i].flags; + v4l2_ctrl_new_custom(&dev->ctrl_handler, + &ctrl_template, NULL); + } + + node->vfd.ctrl_handler = &dev->ctrl_handler; + } + + v4l2_info(&dev->v4l2_dev, + "Device node %s[%d] registered as /dev/video%d\n", + node->name, node->id, vfd->num); + + return 0; +} + +/* Unregister one of the /dev/video nodes associated with the ISP. */ +static void unregister_node(struct bcm2835_isp_node *node) +{ + struct bcm2835_isp_dev *dev = node_get_dev(node); + + v4l2_info(&dev->v4l2_dev, + "Unregistering node %s[%d] device node /dev/video%d\n", + node->name, node->id, node->vfd.num); + + if (node->queue_init) + vb2_queue_release(&node->queue); + + if (node->registered) { + video_unregister_device(&node->vfd); + if (node_is_output(node)) + v4l2_ctrl_handler_free(&dev->ctrl_handler); + } + + /* + * node->supported_fmts.list is free'd automatically + * as a managed resource. + */ + node->supported_fmts.list = NULL; + node->supported_fmts.num_entries = 0; + node->vfd.ctrl_handler = NULL; + node->registered = false; + node->queue_init = false; +} + +static void media_controller_unregister(struct bcm2835_isp_dev *dev) +{ + unsigned int i; + + v4l2_info(&dev->v4l2_dev, "Unregister from media controller\n"); + + if (dev->media_device_registered) { + media_device_unregister(&dev->mdev); + media_device_cleanup(&dev->mdev); + dev->media_device_registered = false; + } + + kfree(dev->entity.name); + dev->entity.name = NULL; + + if (dev->media_entity_registered) { + media_device_unregister_entity(&dev->entity); + dev->media_entity_registered = false; + } + + for (i = 0; i < BCM2835_ISP_NUM_NODES; i++) { + struct bcm2835_isp_node *node = &dev->node[i]; + + if (node->media_node_registered) { + media_remove_intf_links(node->intf_link->intf); + media_entity_remove_links(&dev->node[i].vfd.entity); + media_devnode_remove(node->intf_devnode); + media_device_unregister_entity(&node->vfd.entity); + kfree(node->vfd.entity.name); + } + node->media_node_registered = false; + } + + dev->v4l2_dev.mdev = NULL; +} + +static int media_controller_register_node(struct bcm2835_isp_dev *dev, int num) +{ + struct bcm2835_isp_node *node = &dev->node[num]; + struct media_entity *entity = &node->vfd.entity; + int output = node_is_output(node); + char *name; + int ret; + + v4l2_info(&dev->v4l2_dev, + "Register %s node %d with media controller\n", + output ? "output" : "capture", num); + entity->obj_type = MEDIA_ENTITY_TYPE_VIDEO_DEVICE; + entity->function = MEDIA_ENT_F_IO_V4L; + entity->info.dev.major = VIDEO_MAJOR; + entity->info.dev.minor = node->vfd.minor; + name = kmalloc(BCM2835_ISP_ENTITY_NAME_LEN, GFP_KERNEL); + if (!name) { + ret = -ENOMEM; + goto error_no_mem; + } + snprintf(name, BCM2835_ISP_ENTITY_NAME_LEN, "%s0-%s%d", + BCM2835_ISP_NAME, output ? "output" : "capture", num); + entity->name = name; + node->pad.flags = output ? MEDIA_PAD_FL_SOURCE : MEDIA_PAD_FL_SINK; + ret = media_entity_pads_init(entity, 1, &node->pad); + if (ret) + goto error_pads_init; + ret = media_device_register_entity(&dev->mdev, entity); + if (ret) + goto error_register_entity; + + node->intf_devnode = media_devnode_create(&dev->mdev, + MEDIA_INTF_T_V4L_VIDEO, 0, + VIDEO_MAJOR, node->vfd.minor); + if (!node->intf_devnode) { + ret = -ENOMEM; + goto error_devnode_create; + } + + node->intf_link = media_create_intf_link(entity, + &node->intf_devnode->intf, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (!node->intf_link) { + ret = -ENOMEM; + goto error_create_intf_link; + } + + if (output) + ret = media_create_pad_link(entity, 0, &dev->entity, num, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + else + ret = media_create_pad_link(&dev->entity, num, entity, 0, + MEDIA_LNK_FL_IMMUTABLE | + MEDIA_LNK_FL_ENABLED); + if (ret) + goto error_create_pad_link; + + dev->node[num].media_node_registered = true; + return 0; + +error_create_pad_link: + media_remove_intf_links(&node->intf_devnode->intf); +error_create_intf_link: + media_devnode_remove(node->intf_devnode); +error_devnode_create: + media_device_unregister_entity(&node->vfd.entity); +error_register_entity: +error_pads_init: + kfree(entity->name); + entity->name = NULL; +error_no_mem: + if (ret) + v4l2_info(&dev->v4l2_dev, "Error registering node\n"); + + return ret; +} + +static int media_controller_register(struct bcm2835_isp_dev *dev) +{ + char *name; + unsigned int i; + int ret; + + v4l2_dbg(2, debug, &dev->v4l2_dev, "Registering with media controller\n"); + dev->mdev.dev = dev->dev; + strscpy(dev->mdev.model, "bcm2835-isp", + sizeof(dev->mdev.model)); + strscpy(dev->mdev.bus_info, "platform:bcm2835-isp", + sizeof(dev->mdev.bus_info)); + media_device_init(&dev->mdev); + dev->v4l2_dev.mdev = &dev->mdev; + + v4l2_dbg(2, debug, &dev->v4l2_dev, "Register entity for nodes\n"); + + name = kmalloc(BCM2835_ISP_ENTITY_NAME_LEN, GFP_KERNEL); + if (!name) { + ret = -ENOMEM; + goto done; + } + snprintf(name, BCM2835_ISP_ENTITY_NAME_LEN, "bcm2835_isp0"); + dev->entity.name = name; + dev->entity.obj_type = MEDIA_ENTITY_TYPE_BASE; + dev->entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER; + + for (i = 0; i < BCM2835_ISP_NUM_NODES; i++) { + dev->pad[i].flags = node_is_output(&dev->node[i]) ? + MEDIA_PAD_FL_SINK : MEDIA_PAD_FL_SOURCE; + } + + ret = media_entity_pads_init(&dev->entity, BCM2835_ISP_NUM_NODES, + dev->pad); + if (ret) + goto done; + + ret = media_device_register_entity(&dev->mdev, &dev->entity); + if (ret) + goto done; + + dev->media_entity_registered = true; + for (i = 0; i < BCM2835_ISP_NUM_NODES; i++) { + ret = media_controller_register_node(dev, i); + if (ret) + goto done; + } + + ret = media_device_register(&dev->mdev); + if (!ret) + dev->media_device_registered = true; +done: + return ret; +} + +static int bcm2835_isp_remove(struct platform_device *pdev) +{ + struct bcm2835_isp_dev *dev = platform_get_drvdata(pdev); + unsigned int i; + + media_controller_unregister(dev); + + for (i = 0; i < BCM2835_ISP_NUM_NODES; i++) + unregister_node(&dev->node[i]); + + v4l2_device_unregister(&dev->v4l2_dev); + + if (dev->component) + vchiq_mmal_component_finalise(dev->mmal_instance, + dev->component); + + vchiq_mmal_finalise(dev->mmal_instance); + + return 0; +} + +static int bcm2835_isp_probe(struct platform_device *pdev) +{ + struct bcm2835_isp_dev *dev; + unsigned int i; + int ret; + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->dev = &pdev->dev; + + ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); + if (ret) + return ret; + + ret = vchiq_mmal_init(&dev->mmal_instance); + if (ret) { + v4l2_device_unregister(&dev->v4l2_dev); + return ret; + } + + ret = vchiq_mmal_component_init(dev->mmal_instance, "ril.isp", + &dev->component); + if (ret) { + v4l2_err(&dev->v4l2_dev, + "%s: failed to create ril.isp component\n", __func__); + goto error; + } + + if ((dev->component->inputs != BCM2835_ISP_NUM_OUTPUTS) || + (dev->component->outputs != BCM2835_ISP_NUM_CAPTURES + + BCM2835_ISP_NUM_METADATA)) { + v4l2_err(&dev->v4l2_dev, + "%s: ril.isp returned %d i/p (%d expected), %d o/p (%d expected) ports\n", + __func__, dev->component->inputs, + BCM2835_ISP_NUM_OUTPUTS, + dev->component->outputs, + BCM2835_ISP_NUM_CAPTURES + BCM2835_ISP_NUM_METADATA); + goto error; + } + + atomic_set(&dev->num_streaming, 0); + + for (i = 0; i < BCM2835_ISP_NUM_NODES; i++) { + struct bcm2835_isp_node *node = &dev->node[i]; + + ret = register_node(dev, node, i); + if (ret) + goto error; + } + + ret = media_controller_register(dev); + if (ret) + goto error; + + platform_set_drvdata(pdev, dev); + v4l2_info(&dev->v4l2_dev, "Loaded V4L2 %s\n", BCM2835_ISP_NAME); + return 0; + +error: + bcm2835_isp_remove(pdev); + + return ret; +} + +static struct platform_driver bcm2835_isp_pdrv = { + .probe = bcm2835_isp_probe, + .remove = bcm2835_isp_remove, + .driver = { + .name = BCM2835_ISP_NAME, + }, +}; + +module_platform_driver(bcm2835_isp_pdrv); + +MODULE_DESCRIPTION("BCM2835 ISP driver"); +MODULE_AUTHOR("Naushir Patuck "); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.0"); +MODULE_ALIAS("platform:bcm2835-isp"); diff --git a/drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_ctrls.h b/drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_ctrls.h new file mode 100644 index 000000000000..cfbb1063aad1 --- /dev/null +++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_ctrls.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Broadcom BCM2835 ISP driver + * + * Copyright © 2019-2020 Raspberry Pi (Trading) Ltd. + * + * Author: Naushir Patuck (naush@raspberrypi.com) + * + */ + +#ifndef BCM2835_ISP_CTRLS +#define BCM2835_ISP_CTRLS + +#include + +struct bcm2835_isp_custom_ctrl { + const char *name; + u32 id; + u32 size; + u32 flags; +}; + +static const struct bcm2835_isp_custom_ctrl custom_ctrls[] = { + { + .name = "Colour Correction Matrix", + .id = V4L2_CID_USER_BCM2835_ISP_CC_MATRIX, + .size = sizeof(struct bcm2835_isp_custom_ccm), + .flags = 0 + }, { + .name = "Lens Shading", + .id = V4L2_CID_USER_BCM2835_ISP_LENS_SHADING, + .size = sizeof(struct bcm2835_isp_lens_shading), + .flags = V4L2_CTRL_FLAG_EXECUTE_ON_WRITE + }, { + .name = "Black Level", + .id = V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL, + .size = sizeof(struct bcm2835_isp_black_level), + .flags = 0 + }, { + .name = "Green Equalisation", + .id = V4L2_CID_USER_BCM2835_ISP_GEQ, + .size = sizeof(struct bcm2835_isp_geq), + .flags = 0 + }, { + .name = "Gamma", + .id = V4L2_CID_USER_BCM2835_ISP_GAMMA, + .size = sizeof(struct bcm2835_isp_gamma), + .flags = 0 + }, { + .name = "Sharpen", + .id = V4L2_CID_USER_BCM2835_ISP_SHARPEN, + .size = sizeof(struct bcm2835_isp_sharpen), + .flags = 0 + }, { + .name = "Denoise", + .id = V4L2_CID_USER_BCM2835_ISP_DENOISE, + .size = sizeof(struct bcm2835_isp_denoise), + .flags = 0 + }, { + .name = "Defective Pixel Correction", + .id = V4L2_CID_USER_BCM2835_ISP_DPC, + .size = sizeof(struct bcm2835_isp_dpc), + .flags = 0 + } +}; + +#endif diff --git a/drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_fmts.h b/drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_fmts.h new file mode 100644 index 000000000000..af3bde152bb2 --- /dev/null +++ b/drivers/staging/vc04_services/bcm2835-isp/bcm2835_isp_fmts.h @@ -0,0 +1,301 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Broadcom BCM2835 ISP driver + * + * Copyright © 2019-2020 Raspberry Pi (Trading) Ltd. + * + * Author: Naushir Patuck (naush@raspberrypi.com) + * + */ + +#ifndef BCM2835_ISP_FMTS +#define BCM2835_ISP_FMTS + +#include +#include "vchiq-mmal/mmal-encodings.h" + +struct bcm2835_isp_fmt { + u32 fourcc; + int depth; + int bytesperline_align; + u32 flags; + u32 mmal_fmt; + int size_multiplier_x2; + enum v4l2_colorspace colorspace; + unsigned int step_size; +}; + +struct bcm2835_isp_fmt_list { + struct bcm2835_isp_fmt const **list; + unsigned int num_entries; +}; + +static const struct bcm2835_isp_fmt supported_formats[] = { + { + /* YUV formats */ + .fourcc = V4L2_PIX_FMT_YUV420, + .depth = 8, + .bytesperline_align = 32, + .flags = 0, + .mmal_fmt = MMAL_ENCODING_I420, + .size_multiplier_x2 = 3, + .colorspace = V4L2_COLORSPACE_SMPTE170M, + .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_YVU420, + .depth = 8, + .bytesperline_align = 32, + .flags = 0, + .mmal_fmt = MMAL_ENCODING_YV12, + .size_multiplier_x2 = 3, + .colorspace = V4L2_COLORSPACE_SMPTE170M, + .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_NV12, + .depth = 8, + .bytesperline_align = 32, + .flags = 0, + .mmal_fmt = MMAL_ENCODING_NV12, + .size_multiplier_x2 = 3, + .colorspace = V4L2_COLORSPACE_SMPTE170M, + .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_NV21, + .depth = 8, + .bytesperline_align = 32, + .flags = 0, + .mmal_fmt = MMAL_ENCODING_NV21, + .size_multiplier_x2 = 3, + .colorspace = V4L2_COLORSPACE_SMPTE170M, + .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, + .bytesperline_align = 32, + .flags = 0, + .mmal_fmt = MMAL_ENCODING_YUYV, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_SMPTE170M, + .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = 16, + .bytesperline_align = 32, + .flags = 0, + .mmal_fmt = MMAL_ENCODING_UYVY, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_SMPTE170M, + .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_YVYU, + .depth = 16, + .bytesperline_align = 32, + .flags = 0, + .mmal_fmt = MMAL_ENCODING_YVYU, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_SMPTE170M, + .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_VYUY, + .depth = 16, + .bytesperline_align = 32, + .flags = 0, + .mmal_fmt = MMAL_ENCODING_VYUY, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_SMPTE170M, + .step_size = 2, + }, { + /* RGB formats */ + .fourcc = V4L2_PIX_FMT_RGB24, + .depth = 24, + .bytesperline_align = 32, + .flags = 0, + .mmal_fmt = MMAL_ENCODING_RGB24, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .step_size = 1, + }, { + .fourcc = V4L2_PIX_FMT_RGB565, + .depth = 16, + .bytesperline_align = 32, + .flags = 0, + .mmal_fmt = MMAL_ENCODING_RGB16, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .step_size = 1, + }, { + .fourcc = V4L2_PIX_FMT_BGR24, + .depth = 24, + .bytesperline_align = 32, + .flags = 0, + .mmal_fmt = MMAL_ENCODING_BGR24, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .step_size = 1, + }, { + .fourcc = V4L2_PIX_FMT_ABGR32, + .depth = 32, + .bytesperline_align = 32, + .flags = 0, + .mmal_fmt = MMAL_ENCODING_BGRA, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .step_size = 1, + }, { + /* Bayer formats */ + /* 8 bit */ + .fourcc = V4L2_PIX_FMT_SRGGB8, + .depth = 8, + .bytesperline_align = 32, + .flags = 0, + .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB8, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, + .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR8, + .depth = 8, + .bytesperline_align = 32, + .flags = 0, + .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR8, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, + .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG8, + .depth = 8, + .bytesperline_align = 32, + .flags = 0, + .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG8, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, + .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG8, + .depth = 8, + .bytesperline_align = 32, + .flags = 0, + .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG8, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, + .step_size = 2, + }, { + /* 10 bit */ + .fourcc = V4L2_PIX_FMT_SRGGB10P, + .depth = 10, + .bytesperline_align = 32, + .flags = 0, + .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB10P, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, + .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR10P, + .depth = 10, + .bytesperline_align = 32, + .flags = 0, + .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR10P, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, + .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG10P, + .depth = 10, + .bytesperline_align = 32, + .flags = 0, + .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG10P, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, + .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG10P, + .depth = 10, + .bytesperline_align = 32, + .flags = 0, + .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG10P, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, + .step_size = 2, + }, { + /* 12 bit */ + .fourcc = V4L2_PIX_FMT_SRGGB12P, + .depth = 12, + .bytesperline_align = 32, + .flags = 0, + .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB12P, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, + .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR12P, + .depth = 12, + .bytesperline_align = 32, + .flags = 0, + .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR12P, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, + .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG12P, + .depth = 12, + .bytesperline_align = 32, + .flags = 0, + .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG12P, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, + .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG12P, + .depth = 12, + .bytesperline_align = 32, + .flags = 0, + .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG12P, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, + .step_size = 2, + }, { + /* 16 bit */ + .fourcc = V4L2_PIX_FMT_SRGGB16, + .depth = 16, + .bytesperline_align = 32, + .flags = 0, + .mmal_fmt = MMAL_ENCODING_BAYER_SRGGB16, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, + .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_SBGGR16, + .depth = 16, + .bytesperline_align = 32, + .flags = 0, + .mmal_fmt = MMAL_ENCODING_BAYER_SBGGR16, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, + .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_SGRBG16, + .depth = 16, + .bytesperline_align = 32, + .flags = 0, + .mmal_fmt = MMAL_ENCODING_BAYER_SGRBG16, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, + .step_size = 2, + }, { + .fourcc = V4L2_PIX_FMT_SGBRG16, + .depth = 16, + .bytesperline_align = 32, + .flags = 0, + .mmal_fmt = MMAL_ENCODING_BAYER_SGBRG16, + .size_multiplier_x2 = 2, + .colorspace = V4L2_COLORSPACE_RAW, + .step_size = 2, + }, { + /* ISP statistics format */ + .fourcc = V4L2_META_FMT_BCM2835_ISP_STATS, + .mmal_fmt = MMAL_ENCODING_BRCM_STATS, + /* The rest are not valid fields for stats. */ + } +}; + +#endif diff --git a/drivers/staging/vc04_services/include/uapi/linux/bcm2835-isp.h b/drivers/staging/vc04_services/include/uapi/linux/bcm2835-isp.h new file mode 100644 index 000000000000..edc452fa8318 --- /dev/null +++ b/drivers/staging/vc04_services/include/uapi/linux/bcm2835-isp.h @@ -0,0 +1,333 @@ +/* SPDX-License-Identifier: ((GPL-2.0+ WITH Linux-syscall-note) OR BSD-3-Clause) */ +/* + * bcm2835-isp.h + * + * BCM2835 ISP driver - user space header file. + * + * Copyright © 2019-2020 Raspberry Pi (Trading) Ltd. + * + * Author: Naushir Patuck (naush@raspberrypi.com) + * + */ + +#ifndef __BCM2835_ISP_H_ +#define __BCM2835_ISP_H_ + +#include + +/* TODO: move the control IDs definitions to v4l2-controls.h */ +#define V4L2_CID_USER_BCM2835_ISP_BASE (V4L2_CID_USER_BASE + 0x10c0) + +/* TODO: move the formats definitions to videodev2.h */ +/* 12 Y/CbCr 4:2:0 128 pixel wide column */ +#define V4L2_PIX_FMT_NV12_COL128 v4l2_fourcc('N', 'C', '1', '2') +/* Y/CbCr 4:2:0 10bpc, 3x10 packed as 4 bytes in a 128 bytes / 96 pixel wide column */ +#define V4L2_PIX_FMT_NV12_10_COL128 v4l2_fourcc('N', 'C', '3', '0') +/* Sensor Ancillary metadata */ +#define V4L2_META_FMT_SENSOR_DATA v4l2_fourcc('S', 'E', 'N', 'S') +/* BCM2835 ISP image statistics output */ +#define V4L2_META_FMT_BCM2835_ISP_STATS v4l2_fourcc('B', 'S', 'T', 'A') + +#define V4L2_CID_USER_BCM2835_ISP_CC_MATRIX \ + (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0001) +#define V4L2_CID_USER_BCM2835_ISP_LENS_SHADING \ + (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0002) +#define V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL \ + (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0003) +#define V4L2_CID_USER_BCM2835_ISP_GEQ \ + (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0004) +#define V4L2_CID_USER_BCM2835_ISP_GAMMA \ + (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0005) +#define V4L2_CID_USER_BCM2835_ISP_DENOISE \ + (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0006) +#define V4L2_CID_USER_BCM2835_ISP_SHARPEN \ + (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0007) +#define V4L2_CID_USER_BCM2835_ISP_DPC \ + (V4L2_CID_USER_BCM2835_ISP_BASE + 0x0008) + +/* + * All structs below are directly mapped onto the equivalent structs in + * drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h + * for convenience. + */ + +/** + * struct bcm2835_isp_rational - Rational value type. + * + * @num: Numerator. + * @den: Denominator. + */ +struct bcm2835_isp_rational { + __s32 num; + __s32 den; +}; + +/** + * struct bcm2835_isp_ccm - Colour correction matrix. + * + * @ccm: 3x3 correction matrix coefficients. + * @offsets: 1x3 correction offsets. + */ +struct bcm2835_isp_ccm { + struct bcm2835_isp_rational ccm[3][3]; + __s32 offsets[3]; +}; + +/** + * struct bcm2835_isp_custom_ccm - Custom CCM applied with the + * V4L2_CID_USER_BCM2835_ISP_CC_MATRIX ctrl. + * + * @enabled: Enable custom CCM. + * @ccm: Custom CCM coefficients and offsets. + */ +struct bcm2835_isp_custom_ccm { + __u32 enabled; + struct bcm2835_isp_ccm ccm; +}; + +/** + * enum bcm2835_isp_gain_format - format of the gains in the lens shading + * tables used with the + * V4L2_CID_USER_BCM2835_ISP_LENS_SHADING ctrl. + * + * @GAIN_FORMAT_U0P8_1: Gains are u0.8 format, starting at 1.0 + * @GAIN_FORMAT_U1P7_0: Gains are u1.7 format, starting at 0.0 + * @GAIN_FORMAT_U1P7_1: Gains are u1.7 format, starting at 1.0 + * @GAIN_FORMAT_U2P6_0: Gains are u2.6 format, starting at 0.0 + * @GAIN_FORMAT_U2P6_1: Gains are u2.6 format, starting at 1.0 + * @GAIN_FORMAT_U3P5_0: Gains are u3.5 format, starting at 0.0 + * @GAIN_FORMAT_U3P5_1: Gains are u3.5 format, starting at 1.0 + * @GAIN_FORMAT_U4P10: Gains are u4.10 format, starting at 0.0 + */ +enum bcm2835_isp_gain_format { + GAIN_FORMAT_U0P8_1 = 0, + GAIN_FORMAT_U1P7_0 = 1, + GAIN_FORMAT_U1P7_1 = 2, + GAIN_FORMAT_U2P6_0 = 3, + GAIN_FORMAT_U2P6_1 = 4, + GAIN_FORMAT_U3P5_0 = 5, + GAIN_FORMAT_U3P5_1 = 6, + GAIN_FORMAT_U4P10 = 7, +}; + +/** + * struct bcm2835_isp_lens_shading - Lens shading tables supplied with the + * V4L2_CID_USER_BCM2835_ISP_LENS_SHADING + * ctrl. + * + * @enabled: Enable lens shading. + * @grid_cell_size: Size of grid cells in samples (16, 32, 64, 128 or 256). + * @grid_width: Width of lens shading tables in grid cells. + * @grid_stride: Row to row distance (in grid cells) between grid cells + * in the same horizontal location. + * @grid_height: Height of lens shading tables in grid cells. + * @mem_handle_table: Memory handle to the tables. + * @ref_transform: Reference transform - unsupported, please pass zero. + * @corner_sampled: Whether the gains are sampled at the corner points + * of the grid cells or in the cell centres. + * @gain_format: Format of the gains (see enum &bcm2835_isp_gain_format). + */ +struct bcm2835_isp_lens_shading { + __u32 enabled; + __u32 grid_cell_size; + __u32 grid_width; + __u32 grid_stride; + __u32 grid_height; + __u32 mem_handle_table; + __u32 ref_transform; + __u32 corner_sampled; + __u32 gain_format; +}; + +/** + * struct bcm2835_isp_black_level - Sensor black level set with the + * V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL ctrl. + * + * @enabled: Enable black level. + * @black_level_r: Black level for red channel. + * @black_level_g: Black level for green channels. + * @black_level_b: Black level for blue channel. + */ +struct bcm2835_isp_black_level { + __u32 enabled; + __u16 black_level_r; + __u16 black_level_g; + __u16 black_level_b; + __u8 pad_[2]; /* Unused */ +}; + +/** + * struct bcm2835_isp_geq - Green equalisation parameters set with the + * V4L2_CID_USER_BCM2835_ISP_GEQ ctrl. + * + * @enabled: Enable green equalisation. + * @offset: Fixed offset of the green equalisation threshold. + * @slope: Slope of the green equalisation threshold. + */ +struct bcm2835_isp_geq { + __u32 enabled; + __u32 offset; + struct bcm2835_isp_rational slope; +}; + +#define BCM2835_NUM_GAMMA_PTS 33 + +/** + * struct bcm2835_isp_gamma - Gamma parameters set with the + * V4L2_CID_USER_BCM2835_ISP_GAMMA ctrl. + * + * @enabled: Enable gamma adjustment. + * @X: X values of the points defining the gamma curve. + * Values should be scaled to 16 bits. + * @Y: Y values of the points defining the gamma curve. + * Values should be scaled to 16 bits. + */ +struct bcm2835_isp_gamma { + __u32 enabled; + __u16 x[BCM2835_NUM_GAMMA_PTS]; + __u16 y[BCM2835_NUM_GAMMA_PTS]; +}; + +/** + * struct bcm2835_isp_denoise - Denoise parameters set with the + * V4L2_CID_USER_BCM2835_ISP_DENOISE ctrl. + * + * @enabled: Enable denoise. + * @constant: Fixed offset of the noise threshold. + * @slope: Slope of the noise threshold. + * @strength: Denoise strength between 0.0 (off) and 1.0 (maximum). + */ +struct bcm2835_isp_denoise { + __u32 enabled; + __u32 constant; + struct bcm2835_isp_rational slope; + struct bcm2835_isp_rational strength; +}; + +/** + * struct bcm2835_isp_sharpen - Sharpen parameters set with the + * V4L2_CID_USER_BCM2835_ISP_SHARPEN ctrl. + * + * @enabled: Enable sharpening. + * @threshold: Threshold at which to start sharpening pixels. + * @strength: Strength with which pixel sharpening increases. + * @limit: Limit to the amount of sharpening applied. + */ +struct bcm2835_isp_sharpen { + __u32 enabled; + struct bcm2835_isp_rational threshold; + struct bcm2835_isp_rational strength; + struct bcm2835_isp_rational limit; +}; + +/** + * enum bcm2835_isp_dpc_mode - defective pixel correction (DPC) strength. + * + * @DPC_MODE_OFF: No DPC. + * @DPC_MODE_NORMAL: Normal DPC. + * @DPC_MODE_STRONG: Strong DPC. + */ +enum bcm2835_isp_dpc_mode { + DPC_MODE_OFF = 0, + DPC_MODE_NORMAL = 1, + DPC_MODE_STRONG = 2, +}; + +/** + * struct bcm2835_isp_dpc - Defective pixel correction (DPC) parameters set + * with the V4L2_CID_USER_BCM2835_ISP_DPC ctrl. + * + * @enabled: Enable DPC. + * @strength: DPC strength (see enum &bcm2835_isp_dpc_mode). + */ +struct bcm2835_isp_dpc { + __u32 enabled; + __u32 strength; +}; + +/* + * ISP statistics structures. + * + * The bcm2835_isp_stats structure is generated at the output of the + * statistics node. Note that this does not directly map onto the statistics + * output of the ISP HW. Instead, the MMAL firmware code maps the HW statistics + * to the bcm2835_isp_stats structure. + */ +#define DEFAULT_AWB_REGIONS_X 16 +#define DEFAULT_AWB_REGIONS_Y 12 + +#define NUM_HISTOGRAMS 2 +#define NUM_HISTOGRAM_BINS 128 +#define AWB_REGIONS (DEFAULT_AWB_REGIONS_X * DEFAULT_AWB_REGIONS_Y) +#define FLOATING_REGIONS 16 +#define AGC_REGIONS 16 +#define FOCUS_REGIONS 12 + +/** + * struct bcm2835_isp_stats_hist - Histogram statistics + * + * @r_hist: Red channel histogram. + * @g_hist: Combined green channel histogram. + * @b_hist: Blue channel histogram. + */ +struct bcm2835_isp_stats_hist { + __u32 r_hist[NUM_HISTOGRAM_BINS]; + __u32 g_hist[NUM_HISTOGRAM_BINS]; + __u32 b_hist[NUM_HISTOGRAM_BINS]; +}; + +/** + * struct bcm2835_isp_stats_region - Region sums. + * + * @counted: The number of 2x2 bayer tiles accumulated. + * @notcounted: The number of 2x2 bayer tiles not accumulated. + * @r_sum: Total sum of counted pixels in the red channel for a region. + * @g_sum: Total sum of counted pixels in the green channel for a region. + * @b_sum: Total sum of counted pixels in the blue channel for a region. + */ +struct bcm2835_isp_stats_region { + __u32 counted; + __u32 notcounted; + __u64 r_sum; + __u64 g_sum; + __u64 b_sum; +}; + +/** + * struct bcm2835_isp_stats_focus - Focus statistics. + * + * @contrast_val: Focus measure - accumulated output of the focus filter. + * In the first dimension, index [0] counts pixels below a + * preset threshold, and index [1] counts pixels above the + * threshold. In the second dimension, index [0] uses the + * first predefined filter, and index [1] uses the second + * predefined filter. + * @contrast_val_num: The number of counted pixels in the above accumulation. + */ +struct bcm2835_isp_stats_focus { + __u64 contrast_val[2][2]; + __u32 contrast_val_num[2][2]; +}; + +/** + * struct bcm2835_isp_stats - ISP statistics. + * + * @version: Version of the bcm2835_isp_stats structure. + * @size: Size of the bcm2835_isp_stats structure. + * @hist: Histogram statistics for the entire image. + * @awb_stats: Statistics for the regions defined for AWB calculations. + * @floating_stats: Statistics for arbitrarily placed (floating) regions. + * @agc_stats: Statistics for the regions defined for AGC calculations. + * @focus_stats: Focus filter statistics for the focus regions. + */ +struct bcm2835_isp_stats { + __u32 version; + __u32 size; + struct bcm2835_isp_stats_hist hist[NUM_HISTOGRAMS]; + struct bcm2835_isp_stats_region awb_stats[AWB_REGIONS]; + struct bcm2835_isp_stats_region floating_stats[FLOATING_REGIONS]; + struct bcm2835_isp_stats_region agc_stats[AGC_REGIONS]; + struct bcm2835_isp_stats_focus focus_stats[FOCUS_REGIONS]; +}; + +#endif /* __BCM2835_ISP_H_ */ diff --git a/drivers/staging/vc04_services/vchiq-mmal/Kconfig b/drivers/staging/vc04_services/vchiq-mmal/Kconfig index 106f71e709df..072f3c755a68 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/Kconfig +++ b/drivers/staging/vc04_services/vchiq-mmal/Kconfig @@ -5,4 +5,5 @@ config BCM2835_VCHIQ_MMAL help Enables the MMAL API over VCHIQ interface as used for the majority of the multimedia services on VideoCore. - Defaults to Y when the Broadcomd BCM2835 camera host is selected. + Defaults to Y when the Broadcomd BCM2835 camera host or ISP are + selected. diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-encodings.h b/drivers/staging/vc04_services/vchiq-mmal/mmal-encodings.h index 44ba91aa6d47..8d904fcce388 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-encodings.h +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-encodings.h @@ -100,6 +100,10 @@ */ #define MMAL_ENCODING_EGL_IMAGE MMAL_FOURCC('E', 'G', 'L', 'I') +/** ISP image statistics format + */ +#define MMAL_ENCODING_BRCM_STATS MMAL_FOURCC('S', 'T', 'A', 'T') + /* }@ */ /** \name Pre-defined audio encodings */ diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h b/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h index 1793103b18fd..b3552af5cf8f 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-parameters.h @@ -221,6 +221,62 @@ enum mmal_parameter_camera_type { MMAL_PARAMETER_SHUTTER_SPEED, /**< Takes a @ref MMAL_PARAMETER_AWB_GAINS_T */ MMAL_PARAMETER_CUSTOM_AWB_GAINS, + /**< Takes a @ref MMAL_PARAMETER_CAMERA_SETTINGS_T */ + MMAL_PARAMETER_CAMERA_SETTINGS, + /**< Takes a @ref MMAL_PARAMETER_PRIVACY_INDICATOR_T */ + MMAL_PARAMETER_PRIVACY_INDICATOR, + /**< Takes a @ref MMAL_PARAMETER_BOOLEAN_T */ + MMAL_PARAMETER_VIDEO_DENOISE, + /**< Takes a @ref MMAL_PARAMETER_BOOLEAN_T */ + MMAL_PARAMETER_STILLS_DENOISE, + /**< Takes a @ref MMAL_PARAMETER_CAMERA_ANNOTATE_T */ + MMAL_PARAMETER_ANNOTATE, + /**< Takes a @ref MMAL_PARAMETER_STEREOSCOPIC_MODE_T */ + MMAL_PARAMETER_STEREOSCOPIC_MODE, + /**< Takes a @ref MMAL_PARAMETER_CAMERA_INTERFACE_T */ + MMAL_PARAMETER_CAMERA_INTERFACE, + /**< Takes a @ref MMAL_PARAMETER_CAMERA_CLOCKING_MODE_T */ + MMAL_PARAMETER_CAMERA_CLOCKING_MODE, + /**< Takes a @ref MMAL_PARAMETER_CAMERA_RX_CONFIG_T */ + MMAL_PARAMETER_CAMERA_RX_CONFIG, + /**< Takes a @ref MMAL_PARAMETER_CAMERA_RX_TIMING_T */ + MMAL_PARAMETER_CAMERA_RX_TIMING, + /**< Takes a @ref MMAL_PARAMETER_UINT32_T */ + MMAL_PARAMETER_DPF_CONFIG, + + /* 0x50 */ + /**< Takes a @ref MMAL_PARAMETER_UINT32_T */ + MMAL_PARAMETER_JPEG_RESTART_INTERVAL, + /**< Takes a @ref MMAL_PARAMETER_UINT32_T */ + MMAL_PARAMETER_CAMERA_ISP_BLOCK_OVERRIDE, + /**< Takes a @ref MMAL_PARAMETER_LENS_SHADING_T */ + MMAL_PARAMETER_LENS_SHADING_OVERRIDE, + /**< Takes a @ref MMAL_PARAMETER_UINT32_T */ + MMAL_PARAMETER_BLACK_LEVEL, + /**< Takes a @ref MMAL_PARAMETER_RESIZE_T */ + MMAL_PARAMETER_RESIZE_PARAMS, + /**< Takes a @ref MMAL_PARAMETER_CROP_T */ + MMAL_PARAMETER_CROP, + /**< Takes a @ref MMAL_PARAMETER_INT32_T */ + MMAL_PARAMETER_OUTPUT_SHIFT, + /**< Takes a @ref MMAL_PARAMETER_INT32_T */ + MMAL_PARAMETER_CCM_SHIFT, + /**< Takes a @ref MMAL_PARAMETER_CUSTOM_CCM_T */ + MMAL_PARAMETER_CUSTOM_CCM, + /**< Takes a @ref MMAL_PARAMETER_RATIONAL_T */ + MMAL_PARAMETER_ANALOG_GAIN, + /**< Takes a @ref MMAL_PARAMETER_RATIONAL_T */ + MMAL_PARAMETER_DIGITAL_GAIN, + /**< Takes a @ref MMAL_PARAMETER_DENOISE_T */ + MMAL_PARAMETER_DENOISE, + /**< Takes a @ref MMAL_PARAMETER_SHARPEN_T */ + MMAL_PARAMETER_SHARPEN, + /**< Takes a @ref MMAL_PARAMETER_GEQ_T */ + MMAL_PARAMETER_GEQ, + /**< Tales a @ref MMAP_PARAMETER_DPC_T */ + MMAL_PARAMETER_DPC, + /**< Tales a @ref MMAP_PARAMETER_GAMMA_T */ + MMAL_PARAMETER_GAMMA, }; struct mmal_parameter_rational { @@ -779,7 +835,102 @@ struct mmal_parameter_camera_info { struct mmal_parameter_camera_info_camera cameras[MMAL_PARAMETER_CAMERA_INFO_MAX_CAMERAS]; struct mmal_parameter_camera_info_flash - flashes[MMAL_PARAMETER_CAMERA_INFO_MAX_FLASHES]; + flashes[MMAL_PARAMETER_CAMERA_INFO_MAX_FLASHES]; +}; + +struct mmal_parameter_ccm { + struct mmal_parameter_rational ccm[3][3]; + s32 offsets[3]; +}; + +struct mmal_parameter_custom_ccm { + u32 enabled; /**< Enable the custom CCM. */ + struct mmal_parameter_ccm ccm; /**< CCM to be used. */ +}; + +struct mmal_parameter_lens_shading { + u32 enabled; + u32 grid_cell_size; + u32 grid_width; + u32 grid_stride; + u32 grid_height; + u32 mem_handle_table; + u32 ref_transform; +}; + +enum mmal_parameter_ls_gain_format_type { + MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U0P8_1 = 0, + MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U1P7_0 = 1, + MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U1P7_1 = 2, + MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U2P6_0 = 3, + MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U2P6_1 = 4, + MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U3P5_0 = 5, + MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U3P5_1 = 6, + MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_U4P10 = 7, + MMAL_PARAMETER_LS_GAIN_FORMAT_TYPE_DUMMY = 0x7FFFFFFF +}; + +struct mmal_parameter_lens_shading_v2 { + u32 enabled; + u32 grid_cell_size; + u32 grid_width; + u32 grid_stride; + u32 grid_height; + u32 mem_handle_table; + u32 ref_transform; + u32 corner_sampled; + enum mmal_parameter_ls_gain_format_type gain_format; +}; + +struct mmal_parameter_black_level { + u32 enabled; + u16 black_level_r; + u16 black_level_g; + u16 black_level_b; + u8 pad_[2]; /* Unused */ +}; + +struct mmal_parameter_geq { + u32 enabled; + u32 offset; + struct mmal_parameter_rational slope; +}; + +#define MMAL_NUM_GAMMA_PTS 33 +struct mmal_parameter_gamma { + u32 enabled; + u16 x[MMAL_NUM_GAMMA_PTS]; + u16 y[MMAL_NUM_GAMMA_PTS]; +}; + +struct mmal_parameter_denoise { + u32 enabled; + u32 constant; + struct mmal_parameter_rational slope; + struct mmal_parameter_rational strength; +}; + +struct mmal_parameter_sharpen { + u32 enabled; + struct mmal_parameter_rational threshold; + struct mmal_parameter_rational strength; + struct mmal_parameter_rational limit; +}; + +enum mmal_dpc_mode { + MMAL_DPC_MODE_OFF = 0, + MMAL_DPC_MODE_NORMAL = 1, + MMAL_DPC_MODE_STRONG = 2, + MMAL_DPC_MODE_MAX = 0x7FFFFFFF, +}; + +struct mmal_parameter_dpc { + u32 enabled; + u32 strength; +}; + +struct mmal_parameter_crop { + struct vchiq_mmal_rect rect; }; #endif From patchwork Mon May 4 09:26:11 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 11525315 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id E4A94139A for ; Mon, 4 May 2020 09:26:57 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C39DA2192A for ; Mon, 4 May 2020 09:26:57 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (1024-bit key) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="pqfwEcMV" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728447AbgEDJ05 (ORCPT ); Mon, 4 May 2020 05:26:57 -0400 Received: from perceval.ideasonboard.com ([213.167.242.64]:56720 "EHLO perceval.ideasonboard.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728425AbgEDJ04 (ORCPT ); Mon, 4 May 2020 05:26:56 -0400 Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id DACC91209; Mon, 4 May 2020 11:26:49 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1588584410; bh=uHRgbhwFDlNAhbKOzmn1UcejEtQyI6bnr3E0DowIxZw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=pqfwEcMVgGzpc9U1H7t/p0sihE5DqJ1CdCmJpqgj1JoUMeZbmr3RyUjgwp/w1fjnE UMztNobdPVy1De5EYsmPFARoUOSnE6JtcqTd72wsRcAyOkO1lwL2VO8Rb2MWCe8ZSB 3gTaWLmwJC6TuwQIbG9+78BXR7MB6+7ukaMUCgOU= From: Laurent Pinchart To: linux-media@vger.kernel.org Cc: Kieran Bingham , Jacopo Mondi , =?utf-8?q?Niklas_S=C3=B6derlund?= , Naushir Patuck , Dave Stevenson Subject: [PATCH v2 34/34] staging: vchiq: Load bcm2835_isp driver from vchiq Date: Mon, 4 May 2020 12:26:11 +0300 Message-Id: <20200504092611.9798-35-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> References: <20200504092611.9798-1-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org From: Naushir Patuck bcmn2835_isp is a platform driver dependent on vchiq, therefore add the load/unload functions for it to vchiq. Signed-off-by: Naushir Patuck Signed-off-by: Jacopo Mondi --- drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index 59ae2d479ef4..abc30e593b67 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -108,6 +108,7 @@ static DEFINE_SPINLOCK(msg_queue_spinlock); static struct platform_device *bcm2835_camera; static struct platform_device *bcm2835_audio; static struct platform_device *vcsm_cma; +static struct platform_device *bcm2835_isp; static struct vchiq_drvdata bcm2835_drvdata = { .cache_line_size = 32, @@ -2824,6 +2825,7 @@ static int vchiq_probe(struct platform_device *pdev) vcsm_cma = vchiq_register_child(pdev, "vcsm-cma"); bcm2835_camera = vchiq_register_child(pdev, "bcm2835-camera"); bcm2835_audio = vchiq_register_child(pdev, "bcm2835_audio"); + bcm2835_isp = vchiq_register_child(pdev, "bcm2835-isp"); return 0; @@ -2836,6 +2838,7 @@ static int vchiq_probe(struct platform_device *pdev) static int vchiq_remove(struct platform_device *pdev) { + platform_device_unregister(bcm2835_isp); platform_device_unregister(bcm2835_audio); platform_device_unregister(bcm2835_camera); platform_device_unregister(vcsm_cma);