mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-27 06:04:23 +08:00
media: raspberrypi: Add support for RP1-CFE
Add support for Raspberry Pi CFE. The CFE is a hardware block that contains: - MIPI D-PHY - MIPI CSI-2 receiver - Front End ISP (FE) The driver has been upported from the Raspberry Pi kernel commit 88a681df9623 ("ARM: dts: bcm2712-rpi: Add i2c<n>_pins labels"). Co-developed-by: Naushir Patuck <naush@raspberrypi.com> Signed-off-by: Naushir Patuck <naush@raspberrypi.com> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com> Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com> Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
This commit is contained in:
parent
1358bb5239
commit
6edb685abb
@ -19372,6 +19372,13 @@ F: Documentation/devicetree/bindings/media/raspberrypi,pispbe.yaml
|
||||
F: drivers/media/platform/raspberrypi/pisp_be/
|
||||
F: include/uapi/linux/media/raspberrypi/
|
||||
|
||||
RASPBERRY PI PISP CAMERA FRONT END
|
||||
M: Tomi Valkeinen <tomi.valkeinen@ideasonboard.com>
|
||||
M: Raspberry Pi Kernel Maintenance <kernel-list@raspberrypi.com>
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/media/raspberrypi,rp1-cfe.yaml
|
||||
F: drivers/media/platform/raspberrypi/rp1-cfe/
|
||||
|
||||
RC-CORE / LIRC FRAMEWORK
|
||||
M: Sean Young <sean@mess.org>
|
||||
L: linux-media@vger.kernel.org
|
||||
|
@ -3,3 +3,4 @@
|
||||
comment "Raspberry Pi media platform drivers"
|
||||
|
||||
source "drivers/media/platform/raspberrypi/pisp_be/Kconfig"
|
||||
source "drivers/media/platform/raspberrypi/rp1-cfe/Kconfig"
|
||||
|
@ -1,3 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-y += pisp_be/
|
||||
obj-y += rp1-cfe/
|
||||
|
15
drivers/media/platform/raspberrypi/rp1-cfe/Kconfig
Normal file
15
drivers/media/platform/raspberrypi/rp1-cfe/Kconfig
Normal file
@ -0,0 +1,15 @@
|
||||
# RP1 V4L2 camera support
|
||||
|
||||
config VIDEO_RP1_CFE
|
||||
tristate "Raspberry Pi RP1 Camera Front End (CFE) video capture driver"
|
||||
depends on VIDEO_DEV
|
||||
depends on PM
|
||||
select VIDEO_V4L2_SUBDEV_API
|
||||
select MEDIA_CONTROLLER
|
||||
select VIDEOBUF2_DMA_CONTIG
|
||||
select V4L2_FWNODE
|
||||
help
|
||||
Say Y here to enable support for the Raspberry Pi RP1 Camera Front End.
|
||||
|
||||
To compile this driver as a module, choose M here. The module will be
|
||||
called rp1-cfe.
|
6
drivers/media/platform/raspberrypi/rp1-cfe/Makefile
Normal file
6
drivers/media/platform/raspberrypi/rp1-cfe/Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Makefile for RP1 Camera Front End driver
|
||||
#
|
||||
rp1-cfe-objs := cfe.o csi2.o pisp-fe.o dphy.o
|
||||
obj-$(CONFIG_VIDEO_RP1_CFE) += rp1-cfe.o
|
332
drivers/media/platform/raspberrypi/rp1-cfe/cfe-fmts.h
Normal file
332
drivers/media/platform/raspberrypi/rp1-cfe/cfe-fmts.h
Normal file
@ -0,0 +1,332 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* RP1 Camera Front End formats definition
|
||||
*
|
||||
* Copyright (C) 2021-2024 - Raspberry Pi Ltd.
|
||||
*/
|
||||
#ifndef _CFE_FMTS_H_
|
||||
#define _CFE_FMTS_H_
|
||||
|
||||
#include "cfe.h"
|
||||
#include <media/mipi-csi2.h>
|
||||
|
||||
static const struct cfe_fmt formats[] = {
|
||||
/* YUV Formats */
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_YUYV,
|
||||
.code = MEDIA_BUS_FMT_YUYV8_1X16,
|
||||
.depth = 16,
|
||||
.csi_dt = MIPI_CSI2_DT_YUV422_8B,
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_UYVY,
|
||||
.code = MEDIA_BUS_FMT_UYVY8_1X16,
|
||||
.depth = 16,
|
||||
.csi_dt = MIPI_CSI2_DT_YUV422_8B,
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_YVYU,
|
||||
.code = MEDIA_BUS_FMT_YVYU8_1X16,
|
||||
.depth = 16,
|
||||
.csi_dt = MIPI_CSI2_DT_YUV422_8B,
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_VYUY,
|
||||
.code = MEDIA_BUS_FMT_VYUY8_1X16,
|
||||
.depth = 16,
|
||||
.csi_dt = MIPI_CSI2_DT_YUV422_8B,
|
||||
},
|
||||
{
|
||||
/* RGB Formats */
|
||||
.fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
|
||||
.code = MEDIA_BUS_FMT_RGB565_2X8_LE,
|
||||
.depth = 16,
|
||||
.csi_dt = MIPI_CSI2_DT_RGB565,
|
||||
},
|
||||
{ .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
|
||||
.code = MEDIA_BUS_FMT_RGB565_2X8_BE,
|
||||
.depth = 16,
|
||||
.csi_dt = MIPI_CSI2_DT_RGB565,
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */
|
||||
.code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE,
|
||||
.depth = 16,
|
||||
.csi_dt = MIPI_CSI2_DT_RGB555,
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */
|
||||
.code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE,
|
||||
.depth = 16,
|
||||
.csi_dt = MIPI_CSI2_DT_RGB555,
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_RGB24, /* rgb */
|
||||
.code = MEDIA_BUS_FMT_RGB888_1X24,
|
||||
.depth = 24,
|
||||
.csi_dt = MIPI_CSI2_DT_RGB888,
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_BGR24, /* bgr */
|
||||
.code = MEDIA_BUS_FMT_BGR888_1X24,
|
||||
.depth = 24,
|
||||
.csi_dt = MIPI_CSI2_DT_RGB888,
|
||||
},
|
||||
{
|
||||
.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 = MIPI_CSI2_DT_RAW8,
|
||||
.remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR },
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_SGBRG8,
|
||||
.code = MEDIA_BUS_FMT_SGBRG8_1X8,
|
||||
.depth = 8,
|
||||
.csi_dt = MIPI_CSI2_DT_RAW8,
|
||||
.remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG },
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_SGRBG8,
|
||||
.code = MEDIA_BUS_FMT_SGRBG8_1X8,
|
||||
.depth = 8,
|
||||
.csi_dt = MIPI_CSI2_DT_RAW8,
|
||||
.remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG },
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_SRGGB8,
|
||||
.code = MEDIA_BUS_FMT_SRGGB8_1X8,
|
||||
.depth = 8,
|
||||
.csi_dt = MIPI_CSI2_DT_RAW8,
|
||||
.remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB },
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_SBGGR10P,
|
||||
.code = MEDIA_BUS_FMT_SBGGR10_1X10,
|
||||
.depth = 10,
|
||||
.csi_dt = MIPI_CSI2_DT_RAW10,
|
||||
.remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR },
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_SGBRG10P,
|
||||
.code = MEDIA_BUS_FMT_SGBRG10_1X10,
|
||||
.depth = 10,
|
||||
.csi_dt = MIPI_CSI2_DT_RAW10,
|
||||
.remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG },
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_SGRBG10P,
|
||||
.code = MEDIA_BUS_FMT_SGRBG10_1X10,
|
||||
.depth = 10,
|
||||
.csi_dt = MIPI_CSI2_DT_RAW10,
|
||||
.remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG },
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_SRGGB10P,
|
||||
.code = MEDIA_BUS_FMT_SRGGB10_1X10,
|
||||
.depth = 10,
|
||||
.csi_dt = MIPI_CSI2_DT_RAW10,
|
||||
.remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB },
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_SBGGR12P,
|
||||
.code = MEDIA_BUS_FMT_SBGGR12_1X12,
|
||||
.depth = 12,
|
||||
.csi_dt = MIPI_CSI2_DT_RAW12,
|
||||
.remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR },
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_SGBRG12P,
|
||||
.code = MEDIA_BUS_FMT_SGBRG12_1X12,
|
||||
.depth = 12,
|
||||
.csi_dt = MIPI_CSI2_DT_RAW12,
|
||||
.remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG },
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_SGRBG12P,
|
||||
.code = MEDIA_BUS_FMT_SGRBG12_1X12,
|
||||
.depth = 12,
|
||||
.csi_dt = MIPI_CSI2_DT_RAW12,
|
||||
.remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG },
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_SRGGB12P,
|
||||
.code = MEDIA_BUS_FMT_SRGGB12_1X12,
|
||||
.depth = 12,
|
||||
.csi_dt = MIPI_CSI2_DT_RAW12,
|
||||
.remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB },
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_SBGGR14P,
|
||||
.code = MEDIA_BUS_FMT_SBGGR14_1X14,
|
||||
.depth = 14,
|
||||
.csi_dt = MIPI_CSI2_DT_RAW14,
|
||||
.remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR },
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_SGBRG14P,
|
||||
.code = MEDIA_BUS_FMT_SGBRG14_1X14,
|
||||
.depth = 14,
|
||||
.csi_dt = MIPI_CSI2_DT_RAW14,
|
||||
.remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG },
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_SGRBG14P,
|
||||
.code = MEDIA_BUS_FMT_SGRBG14_1X14,
|
||||
.depth = 14,
|
||||
.csi_dt = MIPI_CSI2_DT_RAW14,
|
||||
.remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG },
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_SRGGB14P,
|
||||
.code = MEDIA_BUS_FMT_SRGGB14_1X14,
|
||||
.depth = 14,
|
||||
.csi_dt = MIPI_CSI2_DT_RAW14,
|
||||
.remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB },
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_SBGGR16,
|
||||
.code = MEDIA_BUS_FMT_SBGGR16_1X16,
|
||||
.depth = 16,
|
||||
.csi_dt = MIPI_CSI2_DT_RAW16,
|
||||
.flags = CFE_FORMAT_FLAG_FE_OUT,
|
||||
.remap = { V4L2_PIX_FMT_SBGGR16, V4L2_PIX_FMT_PISP_COMP1_BGGR },
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_SGBRG16,
|
||||
.code = MEDIA_BUS_FMT_SGBRG16_1X16,
|
||||
.depth = 16,
|
||||
.csi_dt = MIPI_CSI2_DT_RAW16,
|
||||
.flags = CFE_FORMAT_FLAG_FE_OUT,
|
||||
.remap = { V4L2_PIX_FMT_SGBRG16, V4L2_PIX_FMT_PISP_COMP1_GBRG },
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_SGRBG16,
|
||||
.code = MEDIA_BUS_FMT_SGRBG16_1X16,
|
||||
.depth = 16,
|
||||
.csi_dt = MIPI_CSI2_DT_RAW16,
|
||||
.flags = CFE_FORMAT_FLAG_FE_OUT,
|
||||
.remap = { V4L2_PIX_FMT_SGRBG16, V4L2_PIX_FMT_PISP_COMP1_GRBG },
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_SRGGB16,
|
||||
.code = MEDIA_BUS_FMT_SRGGB16_1X16,
|
||||
.depth = 16,
|
||||
.csi_dt = MIPI_CSI2_DT_RAW16,
|
||||
.flags = CFE_FORMAT_FLAG_FE_OUT,
|
||||
.remap = { V4L2_PIX_FMT_SRGGB16, V4L2_PIX_FMT_PISP_COMP1_RGGB },
|
||||
},
|
||||
/* PiSP Compressed Mode 1 */
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_PISP_COMP1_RGGB,
|
||||
.code = MEDIA_BUS_FMT_SRGGB16_1X16,
|
||||
.depth = 8,
|
||||
.flags = CFE_FORMAT_FLAG_FE_OUT,
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_PISP_COMP1_BGGR,
|
||||
.code = MEDIA_BUS_FMT_SBGGR16_1X16,
|
||||
.depth = 8,
|
||||
.flags = CFE_FORMAT_FLAG_FE_OUT,
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_PISP_COMP1_GBRG,
|
||||
.code = MEDIA_BUS_FMT_SGBRG16_1X16,
|
||||
.depth = 8,
|
||||
.flags = CFE_FORMAT_FLAG_FE_OUT,
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_PISP_COMP1_GRBG,
|
||||
.code = MEDIA_BUS_FMT_SGRBG16_1X16,
|
||||
.depth = 8,
|
||||
.flags = CFE_FORMAT_FLAG_FE_OUT,
|
||||
},
|
||||
/* Greyscale format */
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_GREY,
|
||||
.code = MEDIA_BUS_FMT_Y8_1X8,
|
||||
.depth = 8,
|
||||
.csi_dt = MIPI_CSI2_DT_RAW8,
|
||||
.remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO },
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_Y10P,
|
||||
.code = MEDIA_BUS_FMT_Y10_1X10,
|
||||
.depth = 10,
|
||||
.csi_dt = MIPI_CSI2_DT_RAW10,
|
||||
.remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO },
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_Y12P,
|
||||
.code = MEDIA_BUS_FMT_Y12_1X12,
|
||||
.depth = 12,
|
||||
.csi_dt = MIPI_CSI2_DT_RAW12,
|
||||
.remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO },
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_Y14P,
|
||||
.code = MEDIA_BUS_FMT_Y14_1X14,
|
||||
.depth = 14,
|
||||
.csi_dt = MIPI_CSI2_DT_RAW14,
|
||||
.remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO },
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_Y16,
|
||||
.code = MEDIA_BUS_FMT_Y16_1X16,
|
||||
.depth = 16,
|
||||
.csi_dt = MIPI_CSI2_DT_RAW16,
|
||||
.flags = CFE_FORMAT_FLAG_FE_OUT,
|
||||
.remap = { V4L2_PIX_FMT_Y16, V4L2_PIX_FMT_PISP_COMP1_MONO },
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_PIX_FMT_PISP_COMP1_MONO,
|
||||
.code = MEDIA_BUS_FMT_Y16_1X16,
|
||||
.depth = 8,
|
||||
.flags = CFE_FORMAT_FLAG_FE_OUT,
|
||||
},
|
||||
|
||||
/* Embedded data formats */
|
||||
{
|
||||
.fourcc = V4L2_META_FMT_GENERIC_8,
|
||||
.code = MEDIA_BUS_FMT_META_8,
|
||||
.depth = 8,
|
||||
.csi_dt = MIPI_CSI2_DT_EMBEDDED_8B,
|
||||
.flags = CFE_FORMAT_FLAG_META_CAP,
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_META_FMT_GENERIC_CSI2_10,
|
||||
.code = MEDIA_BUS_FMT_META_10,
|
||||
.depth = 10,
|
||||
.csi_dt = MIPI_CSI2_DT_EMBEDDED_8B,
|
||||
.flags = CFE_FORMAT_FLAG_META_CAP,
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_META_FMT_GENERIC_CSI2_12,
|
||||
.code = MEDIA_BUS_FMT_META_12,
|
||||
.depth = 12,
|
||||
.csi_dt = MIPI_CSI2_DT_EMBEDDED_8B,
|
||||
.flags = CFE_FORMAT_FLAG_META_CAP,
|
||||
},
|
||||
|
||||
/* Frontend formats */
|
||||
{
|
||||
.fourcc = V4L2_META_FMT_RPI_FE_CFG,
|
||||
.code = MEDIA_BUS_FMT_FIXED,
|
||||
.flags = CFE_FORMAT_FLAG_META_OUT,
|
||||
},
|
||||
{
|
||||
.fourcc = V4L2_META_FMT_RPI_FE_STATS,
|
||||
.code = MEDIA_BUS_FMT_FIXED,
|
||||
.flags = CFE_FORMAT_FLAG_META_CAP,
|
||||
},
|
||||
};
|
||||
|
||||
#endif /* _CFE_FMTS_H_ */
|
202
drivers/media/platform/raspberrypi/rp1-cfe/cfe-trace.h
Normal file
202
drivers/media/platform/raspberrypi/rp1-cfe/cfe-trace.h
Normal file
@ -0,0 +1,202 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2024 Raspberry Pi Ltd.
|
||||
* Copyright (c) 2024 Ideas on Board Oy
|
||||
*/
|
||||
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM cfe
|
||||
|
||||
#if !defined(_CFE_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define _CFE_TRACE_H
|
||||
|
||||
#include <linux/tracepoint.h>
|
||||
#include <media/videobuf2-v4l2.h>
|
||||
|
||||
TRACE_EVENT(cfe_return_buffer,
|
||||
TP_PROTO(u32 node_id, u32 buf_idx, u32 queue_id),
|
||||
TP_ARGS(node_id, buf_idx, queue_id),
|
||||
TP_STRUCT__entry(
|
||||
__field(u32, node_id)
|
||||
__field(u32, buf_idx)
|
||||
__field(u32, queue_id)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->node_id = node_id;
|
||||
__entry->buf_idx = buf_idx;
|
||||
__entry->queue_id = queue_id;
|
||||
),
|
||||
TP_printk("node=%u buf=%u, queue=%u", __entry->node_id,
|
||||
__entry->buf_idx, __entry->queue_id)
|
||||
);
|
||||
|
||||
DECLARE_EVENT_CLASS(cfe_buffer_template,
|
||||
TP_PROTO(u32 node_id, struct vb2_buffer *buf),
|
||||
TP_ARGS(node_id, buf),
|
||||
TP_STRUCT__entry(
|
||||
__field(u32, node_id)
|
||||
__field(u32, buf_idx)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->node_id = node_id;
|
||||
__entry->buf_idx = buf->index;
|
||||
),
|
||||
TP_printk("node=%u buf=%u", __entry->node_id, __entry->buf_idx)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cfe_buffer_template, cfe_buffer_prepare,
|
||||
TP_PROTO(u32 node_id, struct vb2_buffer *buf),
|
||||
TP_ARGS(node_id, buf));
|
||||
|
||||
TRACE_EVENT(cfe_buffer_queue,
|
||||
TP_PROTO(u32 node_id, struct vb2_buffer *buf, bool schedule_now),
|
||||
TP_ARGS(node_id, buf, schedule_now),
|
||||
TP_STRUCT__entry(
|
||||
__field(u32, node_id)
|
||||
__field(u32, buf_idx)
|
||||
__field(bool, schedule_now)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->node_id = node_id;
|
||||
__entry->buf_idx = buf->index;
|
||||
__entry->schedule_now = schedule_now;
|
||||
),
|
||||
TP_printk("node=%u buf=%u%s", __entry->node_id, __entry->buf_idx,
|
||||
__entry->schedule_now ? " schedule immediately" : "")
|
||||
);
|
||||
|
||||
DEFINE_EVENT(cfe_buffer_template, cfe_csi2_schedule,
|
||||
TP_PROTO(u32 node_id, struct vb2_buffer *buf),
|
||||
TP_ARGS(node_id, buf));
|
||||
|
||||
DEFINE_EVENT(cfe_buffer_template, cfe_fe_schedule,
|
||||
TP_PROTO(u32 node_id, struct vb2_buffer *buf),
|
||||
TP_ARGS(node_id, buf));
|
||||
|
||||
TRACE_EVENT(cfe_buffer_complete,
|
||||
TP_PROTO(u32 node_id, struct vb2_v4l2_buffer *buf),
|
||||
TP_ARGS(node_id, buf),
|
||||
TP_STRUCT__entry(
|
||||
__field(u32, node_id)
|
||||
__field(u32, buf_idx)
|
||||
__field(u32, seq)
|
||||
__field(u64, ts)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->node_id = node_id;
|
||||
__entry->buf_idx = buf->vb2_buf.index;
|
||||
__entry->seq = buf->sequence;
|
||||
__entry->ts = buf->vb2_buf.timestamp;
|
||||
),
|
||||
TP_printk("node=%u buf=%u seq=%u ts=%llu", __entry->node_id,
|
||||
__entry->buf_idx, __entry->seq, __entry->ts)
|
||||
);
|
||||
|
||||
TRACE_EVENT(cfe_frame_start,
|
||||
TP_PROTO(u32 node_id, u32 fs_count),
|
||||
TP_ARGS(node_id, fs_count),
|
||||
TP_STRUCT__entry(
|
||||
__field(u32, node_id)
|
||||
__field(u32, fs_count)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->node_id = node_id;
|
||||
__entry->fs_count = fs_count;
|
||||
),
|
||||
TP_printk("node=%u fs_count=%u", __entry->node_id, __entry->fs_count)
|
||||
);
|
||||
|
||||
TRACE_EVENT(cfe_frame_end,
|
||||
TP_PROTO(u32 node_id, u32 fs_count),
|
||||
TP_ARGS(node_id, fs_count),
|
||||
TP_STRUCT__entry(
|
||||
__field(u32, node_id)
|
||||
__field(u32, fs_count)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->node_id = node_id;
|
||||
__entry->fs_count = fs_count;
|
||||
),
|
||||
TP_printk("node=%u fs_count=%u", __entry->node_id, __entry->fs_count)
|
||||
);
|
||||
|
||||
TRACE_EVENT(cfe_prepare_next_job,
|
||||
TP_PROTO(bool fe_enabled),
|
||||
TP_ARGS(fe_enabled),
|
||||
TP_STRUCT__entry(
|
||||
__field(bool, fe_enabled)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->fe_enabled = fe_enabled;
|
||||
),
|
||||
TP_printk("fe_enabled=%u", __entry->fe_enabled)
|
||||
);
|
||||
|
||||
/* These are copied from csi2.c */
|
||||
#define CSI2_STATUS_IRQ_FS(x) (BIT(0) << (x))
|
||||
#define CSI2_STATUS_IRQ_FE(x) (BIT(4) << (x))
|
||||
#define CSI2_STATUS_IRQ_FE_ACK(x) (BIT(8) << (x))
|
||||
#define CSI2_STATUS_IRQ_LE(x) (BIT(12) << (x))
|
||||
#define CSI2_STATUS_IRQ_LE_ACK(x) (BIT(16) << (x))
|
||||
|
||||
TRACE_EVENT(csi2_irq,
|
||||
TP_PROTO(u32 channel, u32 status, u32 dbg),
|
||||
TP_ARGS(channel, status, dbg),
|
||||
TP_STRUCT__entry(
|
||||
__field(u32, channel)
|
||||
__field(u32, status)
|
||||
__field(u32, dbg)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->channel = channel;
|
||||
__entry->status = status;
|
||||
__entry->dbg = dbg;
|
||||
),
|
||||
TP_printk("ch=%u flags=[ %s%s%s%s%s] frame=%u line=%u\n",
|
||||
__entry->channel,
|
||||
(__entry->status & CSI2_STATUS_IRQ_FS(__entry->channel)) ?
|
||||
"FS " : "",
|
||||
(__entry->status & CSI2_STATUS_IRQ_FE(__entry->channel)) ?
|
||||
"FE " : "",
|
||||
(__entry->status & CSI2_STATUS_IRQ_FE_ACK(__entry->channel)) ?
|
||||
"FE_ACK " : "",
|
||||
(__entry->status & CSI2_STATUS_IRQ_LE(__entry->channel)) ?
|
||||
"LE " : "",
|
||||
(__entry->status & CSI2_STATUS_IRQ_LE_ACK(__entry->channel)) ?
|
||||
"LE_ACK " : "",
|
||||
__entry->dbg >> 16, __entry->dbg & 0xffff)
|
||||
);
|
||||
|
||||
TRACE_EVENT(fe_irq,
|
||||
TP_PROTO(u32 status, u32 output_status, u32 frame_status,
|
||||
u32 error_status, u32 int_status),
|
||||
TP_ARGS(status, output_status, frame_status, error_status, int_status),
|
||||
TP_STRUCT__entry(
|
||||
__field(u32, status)
|
||||
__field(u32, output_status)
|
||||
__field(u32, frame_status)
|
||||
__field(u32, error_status)
|
||||
__field(u32, int_status)
|
||||
),
|
||||
TP_fast_assign(
|
||||
__entry->status = status;
|
||||
__entry->output_status = output_status;
|
||||
__entry->frame_status = frame_status;
|
||||
__entry->error_status = error_status;
|
||||
__entry->int_status = int_status;
|
||||
),
|
||||
TP_printk("status 0x%x out_status 0x%x frame_status 0x%x error_status 0x%x int_status 0x%x",
|
||||
__entry->status,
|
||||
__entry->output_status,
|
||||
__entry->frame_status,
|
||||
__entry->error_status,
|
||||
__entry->int_status)
|
||||
);
|
||||
|
||||
#endif /* _CFE_TRACE_H */
|
||||
|
||||
/* This part must be outside protection */
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
#define TRACE_INCLUDE_PATH .
|
||||
#define TRACE_INCLUDE_FILE ../../drivers/media/platform/raspberrypi/rp1-cfe/cfe-trace
|
||||
#include <trace/define_trace.h>
|
2504
drivers/media/platform/raspberrypi/rp1-cfe/cfe.c
Normal file
2504
drivers/media/platform/raspberrypi/rp1-cfe/cfe.c
Normal file
File diff suppressed because it is too large
Load Diff
43
drivers/media/platform/raspberrypi/rp1-cfe/cfe.h
Normal file
43
drivers/media/platform/raspberrypi/rp1-cfe/cfe.h
Normal file
@ -0,0 +1,43 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* RP1 CFE Driver
|
||||
*
|
||||
* Copyright (c) 2021-2024 Raspberry Pi Ltd.
|
||||
* Copyright (c) 2023-2024 Ideas on Board Oy
|
||||
*/
|
||||
#ifndef _RP1_CFE_
|
||||
#define _RP1_CFE_
|
||||
|
||||
#include <linux/media-bus-format.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
extern bool cfe_debug_verbose;
|
||||
|
||||
enum cfe_remap_types {
|
||||
CFE_REMAP_16BIT,
|
||||
CFE_REMAP_COMPRESSED,
|
||||
CFE_NUM_REMAP,
|
||||
};
|
||||
|
||||
#define CFE_FORMAT_FLAG_META_OUT BIT(0)
|
||||
#define CFE_FORMAT_FLAG_META_CAP BIT(1)
|
||||
#define CFE_FORMAT_FLAG_FE_OUT BIT(2)
|
||||
|
||||
struct cfe_fmt {
|
||||
u32 fourcc;
|
||||
u32 code;
|
||||
u8 depth;
|
||||
u8 csi_dt;
|
||||
u32 remap[CFE_NUM_REMAP];
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
extern const struct v4l2_mbus_framefmt cfe_default_format;
|
||||
|
||||
const struct cfe_fmt *find_format_by_code(u32 code);
|
||||
const struct cfe_fmt *find_format_by_pix(u32 pixelformat);
|
||||
u32 cfe_find_16bit_code(u32 code);
|
||||
u32 cfe_find_compressed_code(u32 code);
|
||||
|
||||
#endif
|
586
drivers/media/platform/raspberrypi/rp1-cfe/csi2.c
Normal file
586
drivers/media/platform/raspberrypi/rp1-cfe/csi2.c
Normal file
@ -0,0 +1,586 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* RP1 CSI-2 Driver
|
||||
*
|
||||
* Copyright (c) 2021-2024 Raspberry Pi Ltd.
|
||||
* Copyright (c) 2023-2024 Ideas on Board Oy
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
#include <media/videobuf2-dma-contig.h>
|
||||
|
||||
#include "cfe.h"
|
||||
#include "csi2.h"
|
||||
|
||||
#include "cfe-trace.h"
|
||||
|
||||
static bool csi2_track_errors;
|
||||
module_param_named(track_csi2_errors, csi2_track_errors, bool, 0);
|
||||
MODULE_PARM_DESC(track_csi2_errors, "track csi-2 errors");
|
||||
|
||||
#define csi2_dbg(csi2, fmt, arg...) dev_dbg((csi2)->v4l2_dev->dev, fmt, ##arg)
|
||||
#define csi2_err(csi2, fmt, arg...) dev_err((csi2)->v4l2_dev->dev, fmt, ##arg)
|
||||
|
||||
/* CSI2-DMA registers */
|
||||
#define CSI2_STATUS 0x000
|
||||
#define CSI2_QOS 0x004
|
||||
#define CSI2_DISCARDS_OVERFLOW 0x008
|
||||
#define CSI2_DISCARDS_INACTIVE 0x00c
|
||||
#define CSI2_DISCARDS_UNMATCHED 0x010
|
||||
#define CSI2_DISCARDS_LEN_LIMIT 0x014
|
||||
|
||||
#define CSI2_DISCARDS_AMOUNT_SHIFT 0
|
||||
#define CSI2_DISCARDS_AMOUNT_MASK GENMASK(23, 0)
|
||||
#define CSI2_DISCARDS_DT_SHIFT 24
|
||||
#define CSI2_DISCARDS_DT_MASK GENMASK(29, 24)
|
||||
#define CSI2_DISCARDS_VC_SHIFT 30
|
||||
#define CSI2_DISCARDS_VC_MASK GENMASK(31, 30)
|
||||
|
||||
#define CSI2_LLEV_PANICS 0x018
|
||||
#define CSI2_ULEV_PANICS 0x01c
|
||||
#define CSI2_IRQ_MASK 0x020
|
||||
#define CSI2_IRQ_MASK_IRQ_OVERFLOW BIT(0)
|
||||
#define CSI2_IRQ_MASK_IRQ_DISCARD_OVERFLOW BIT(1)
|
||||
#define CSI2_IRQ_MASK_IRQ_DISCARD_LENGTH_LIMIT BIT(2)
|
||||
#define CSI2_IRQ_MASK_IRQ_DISCARD_UNMATCHED BIT(3)
|
||||
#define CSI2_IRQ_MASK_IRQ_DISCARD_INACTIVE BIT(4)
|
||||
#define CSI2_IRQ_MASK_IRQ_ALL \
|
||||
(CSI2_IRQ_MASK_IRQ_OVERFLOW | CSI2_IRQ_MASK_IRQ_DISCARD_OVERFLOW | \
|
||||
CSI2_IRQ_MASK_IRQ_DISCARD_LENGTH_LIMIT | \
|
||||
CSI2_IRQ_MASK_IRQ_DISCARD_UNMATCHED | \
|
||||
CSI2_IRQ_MASK_IRQ_DISCARD_INACTIVE)
|
||||
|
||||
#define CSI2_CTRL 0x024
|
||||
#define CSI2_CH_CTRL(x) ((x) * 0x40 + 0x28)
|
||||
#define CSI2_CH_ADDR0(x) ((x) * 0x40 + 0x2c)
|
||||
#define CSI2_CH_ADDR1(x) ((x) * 0x40 + 0x3c)
|
||||
#define CSI2_CH_STRIDE(x) ((x) * 0x40 + 0x30)
|
||||
#define CSI2_CH_LENGTH(x) ((x) * 0x40 + 0x34)
|
||||
#define CSI2_CH_DEBUG(x) ((x) * 0x40 + 0x38)
|
||||
#define CSI2_CH_FRAME_SIZE(x) ((x) * 0x40 + 0x40)
|
||||
#define CSI2_CH_COMP_CTRL(x) ((x) * 0x40 + 0x44)
|
||||
#define CSI2_CH_FE_FRAME_ID(x) ((x) * 0x40 + 0x48)
|
||||
|
||||
/* CSI2_STATUS */
|
||||
#define CSI2_STATUS_IRQ_FS(x) (BIT(0) << (x))
|
||||
#define CSI2_STATUS_IRQ_FE(x) (BIT(4) << (x))
|
||||
#define CSI2_STATUS_IRQ_FE_ACK(x) (BIT(8) << (x))
|
||||
#define CSI2_STATUS_IRQ_LE(x) (BIT(12) << (x))
|
||||
#define CSI2_STATUS_IRQ_LE_ACK(x) (BIT(16) << (x))
|
||||
#define CSI2_STATUS_IRQ_CH_MASK(x) \
|
||||
(CSI2_STATUS_IRQ_FS(x) | CSI2_STATUS_IRQ_FE(x) | \
|
||||
CSI2_STATUS_IRQ_FE_ACK(x) | CSI2_STATUS_IRQ_LE(x) | \
|
||||
CSI2_STATUS_IRQ_LE_ACK(x))
|
||||
#define CSI2_STATUS_IRQ_OVERFLOW BIT(20)
|
||||
#define CSI2_STATUS_IRQ_DISCARD_OVERFLOW BIT(21)
|
||||
#define CSI2_STATUS_IRQ_DISCARD_LEN_LIMIT BIT(22)
|
||||
#define CSI2_STATUS_IRQ_DISCARD_UNMATCHED BIT(23)
|
||||
#define CSI2_STATUS_IRQ_DISCARD_INACTIVE BIT(24)
|
||||
|
||||
/* CSI2_CTRL */
|
||||
#define CSI2_CTRL_EOP_IS_EOL BIT(0)
|
||||
|
||||
/* CSI2_CH_CTRL */
|
||||
#define CSI2_CH_CTRL_DMA_EN BIT(0)
|
||||
#define CSI2_CH_CTRL_FORCE BIT(3)
|
||||
#define CSI2_CH_CTRL_AUTO_ARM BIT(4)
|
||||
#define CSI2_CH_CTRL_IRQ_EN_FS BIT(13)
|
||||
#define CSI2_CH_CTRL_IRQ_EN_FE BIT(14)
|
||||
#define CSI2_CH_CTRL_IRQ_EN_FE_ACK BIT(15)
|
||||
#define CSI2_CH_CTRL_IRQ_EN_LE BIT(16)
|
||||
#define CSI2_CH_CTRL_IRQ_EN_LE_ACK BIT(17)
|
||||
#define CSI2_CH_CTRL_FLUSH_FE BIT(28)
|
||||
#define CSI2_CH_CTRL_PACK_LINE BIT(29)
|
||||
#define CSI2_CH_CTRL_PACK_BYTES BIT(30)
|
||||
#define CSI2_CH_CTRL_CH_MODE_MASK GENMASK(2, 1)
|
||||
#define CSI2_CH_CTRL_VC_MASK GENMASK(6, 5)
|
||||
#define CSI2_CH_CTRL_DT_MASK GENMASK(12, 7)
|
||||
#define CSI2_CH_CTRL_LC_MASK GENMASK(27, 18)
|
||||
|
||||
/* CHx_COMPRESSION_CONTROL */
|
||||
#define CSI2_CH_COMP_CTRL_OFFSET_MASK GENMASK(15, 0)
|
||||
#define CSI2_CH_COMP_CTRL_SHIFT_MASK GENMASK(19, 16)
|
||||
#define CSI2_CH_COMP_CTRL_MODE_MASK GENMASK(25, 24)
|
||||
|
||||
static inline u32 csi2_reg_read(struct csi2_device *csi2, u32 offset)
|
||||
{
|
||||
return readl(csi2->base + offset);
|
||||
}
|
||||
|
||||
static inline void csi2_reg_write(struct csi2_device *csi2, u32 offset, u32 val)
|
||||
{
|
||||
writel(val, csi2->base + offset);
|
||||
}
|
||||
|
||||
static inline void set_field(u32 *valp, u32 field, u32 mask)
|
||||
{
|
||||
u32 val = *valp;
|
||||
|
||||
val &= ~mask;
|
||||
val |= (field << __ffs(mask)) & mask;
|
||||
*valp = val;
|
||||
}
|
||||
|
||||
static int csi2_regs_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct csi2_device *csi2 = s->private;
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_resume_and_get(csi2->v4l2_dev->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
#define DUMP(reg) seq_printf(s, #reg " \t0x%08x\n", csi2_reg_read(csi2, reg))
|
||||
#define DUMP_CH(idx, reg) seq_printf(s, #reg "(%u) \t0x%08x\n", idx, \
|
||||
csi2_reg_read(csi2, reg(idx)))
|
||||
|
||||
DUMP(CSI2_STATUS);
|
||||
DUMP(CSI2_DISCARDS_OVERFLOW);
|
||||
DUMP(CSI2_DISCARDS_INACTIVE);
|
||||
DUMP(CSI2_DISCARDS_UNMATCHED);
|
||||
DUMP(CSI2_DISCARDS_LEN_LIMIT);
|
||||
DUMP(CSI2_LLEV_PANICS);
|
||||
DUMP(CSI2_ULEV_PANICS);
|
||||
DUMP(CSI2_IRQ_MASK);
|
||||
DUMP(CSI2_CTRL);
|
||||
|
||||
for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; ++i) {
|
||||
DUMP_CH(i, CSI2_CH_CTRL);
|
||||
DUMP_CH(i, CSI2_CH_ADDR0);
|
||||
DUMP_CH(i, CSI2_CH_ADDR1);
|
||||
DUMP_CH(i, CSI2_CH_STRIDE);
|
||||
DUMP_CH(i, CSI2_CH_LENGTH);
|
||||
DUMP_CH(i, CSI2_CH_DEBUG);
|
||||
DUMP_CH(i, CSI2_CH_FRAME_SIZE);
|
||||
DUMP_CH(i, CSI2_CH_COMP_CTRL);
|
||||
DUMP_CH(i, CSI2_CH_FE_FRAME_ID);
|
||||
}
|
||||
|
||||
#undef DUMP
|
||||
#undef DUMP_CH
|
||||
|
||||
pm_runtime_put(csi2->v4l2_dev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_SHOW_ATTRIBUTE(csi2_regs);
|
||||
|
||||
static int csi2_errors_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct csi2_device *csi2 = s->private;
|
||||
unsigned long flags;
|
||||
u32 discards_table[DISCARDS_TABLE_NUM_VCS][DISCARDS_TABLE_NUM_ENTRIES];
|
||||
u32 discards_dt_table[DISCARDS_TABLE_NUM_ENTRIES];
|
||||
u32 overflows;
|
||||
|
||||
spin_lock_irqsave(&csi2->errors_lock, flags);
|
||||
|
||||
memcpy(discards_table, csi2->discards_table, sizeof(discards_table));
|
||||
memcpy(discards_dt_table, csi2->discards_dt_table,
|
||||
sizeof(discards_dt_table));
|
||||
overflows = csi2->overflows;
|
||||
|
||||
csi2->overflows = 0;
|
||||
memset(csi2->discards_table, 0, sizeof(discards_table));
|
||||
memset(csi2->discards_dt_table, 0, sizeof(discards_dt_table));
|
||||
|
||||
spin_unlock_irqrestore(&csi2->errors_lock, flags);
|
||||
|
||||
seq_printf(s, "Overflows %u\n", overflows);
|
||||
seq_puts(s, "Discards:\n");
|
||||
seq_puts(s, "VC OVLF LEN UNMATCHED INACTIVE\n");
|
||||
|
||||
for (unsigned int vc = 0; vc < DISCARDS_TABLE_NUM_VCS; ++vc) {
|
||||
seq_printf(s, "%u %10u %10u %10u %10u\n", vc,
|
||||
discards_table[vc][DISCARDS_TABLE_OVERFLOW],
|
||||
discards_table[vc][DISCARDS_TABLE_LENGTH_LIMIT],
|
||||
discards_table[vc][DISCARDS_TABLE_UNMATCHED],
|
||||
discards_table[vc][DISCARDS_TABLE_INACTIVE]);
|
||||
}
|
||||
|
||||
seq_printf(s, "Last DT %10u %10u %10u %10u\n",
|
||||
discards_dt_table[DISCARDS_TABLE_OVERFLOW],
|
||||
discards_dt_table[DISCARDS_TABLE_LENGTH_LIMIT],
|
||||
discards_dt_table[DISCARDS_TABLE_UNMATCHED],
|
||||
discards_dt_table[DISCARDS_TABLE_INACTIVE]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_SHOW_ATTRIBUTE(csi2_errors);
|
||||
|
||||
static void csi2_isr_handle_errors(struct csi2_device *csi2, u32 status)
|
||||
{
|
||||
spin_lock(&csi2->errors_lock);
|
||||
|
||||
if (status & CSI2_STATUS_IRQ_OVERFLOW)
|
||||
csi2->overflows++;
|
||||
|
||||
for (unsigned int i = 0; i < DISCARDS_TABLE_NUM_ENTRIES; ++i) {
|
||||
static const u32 discard_bits[] = {
|
||||
CSI2_STATUS_IRQ_DISCARD_OVERFLOW,
|
||||
CSI2_STATUS_IRQ_DISCARD_LEN_LIMIT,
|
||||
CSI2_STATUS_IRQ_DISCARD_UNMATCHED,
|
||||
CSI2_STATUS_IRQ_DISCARD_INACTIVE,
|
||||
};
|
||||
static const u8 discard_regs[] = {
|
||||
CSI2_DISCARDS_OVERFLOW,
|
||||
CSI2_DISCARDS_LEN_LIMIT,
|
||||
CSI2_DISCARDS_UNMATCHED,
|
||||
CSI2_DISCARDS_INACTIVE,
|
||||
};
|
||||
u32 amount;
|
||||
u8 dt, vc;
|
||||
u32 v;
|
||||
|
||||
if (!(status & discard_bits[i]))
|
||||
continue;
|
||||
|
||||
v = csi2_reg_read(csi2, discard_regs[i]);
|
||||
csi2_reg_write(csi2, discard_regs[i], 0);
|
||||
|
||||
amount = (v & CSI2_DISCARDS_AMOUNT_MASK) >>
|
||||
CSI2_DISCARDS_AMOUNT_SHIFT;
|
||||
dt = (v & CSI2_DISCARDS_DT_MASK) >> CSI2_DISCARDS_DT_SHIFT;
|
||||
vc = (v & CSI2_DISCARDS_VC_MASK) >> CSI2_DISCARDS_VC_SHIFT;
|
||||
|
||||
csi2->discards_table[vc][i] += amount;
|
||||
csi2->discards_dt_table[i] = dt;
|
||||
}
|
||||
|
||||
spin_unlock(&csi2->errors_lock);
|
||||
}
|
||||
|
||||
void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof)
|
||||
{
|
||||
u32 status;
|
||||
|
||||
status = csi2_reg_read(csi2, CSI2_STATUS);
|
||||
|
||||
/* Write value back to clear the interrupts */
|
||||
csi2_reg_write(csi2, CSI2_STATUS, status);
|
||||
|
||||
for (unsigned int i = 0; i < CSI2_NUM_CHANNELS; i++) {
|
||||
u32 dbg;
|
||||
|
||||
if ((status & CSI2_STATUS_IRQ_CH_MASK(i)) == 0)
|
||||
continue;
|
||||
|
||||
dbg = csi2_reg_read(csi2, CSI2_CH_DEBUG(i));
|
||||
|
||||
trace_csi2_irq(i, status, dbg);
|
||||
|
||||
sof[i] = !!(status & CSI2_STATUS_IRQ_FS(i));
|
||||
eof[i] = !!(status & CSI2_STATUS_IRQ_FE_ACK(i));
|
||||
}
|
||||
|
||||
if (csi2_track_errors)
|
||||
csi2_isr_handle_errors(csi2, status);
|
||||
}
|
||||
|
||||
void csi2_set_buffer(struct csi2_device *csi2, unsigned int channel,
|
||||
dma_addr_t dmaaddr, unsigned int stride, unsigned int size)
|
||||
{
|
||||
u64 addr = dmaaddr;
|
||||
/*
|
||||
* ADDRESS0 must be written last as it triggers the double buffering
|
||||
* mechanism for all buffer registers within the hardware.
|
||||
*/
|
||||
addr >>= 4;
|
||||
csi2_reg_write(csi2, CSI2_CH_LENGTH(channel), size >> 4);
|
||||
csi2_reg_write(csi2, CSI2_CH_STRIDE(channel), stride >> 4);
|
||||
csi2_reg_write(csi2, CSI2_CH_ADDR1(channel), addr >> 32);
|
||||
csi2_reg_write(csi2, CSI2_CH_ADDR0(channel), addr & 0xffffffff);
|
||||
}
|
||||
|
||||
void csi2_set_compression(struct csi2_device *csi2, unsigned int channel,
|
||||
enum csi2_compression_mode mode, unsigned int shift,
|
||||
unsigned int offset)
|
||||
{
|
||||
u32 compression = 0;
|
||||
|
||||
set_field(&compression, CSI2_CH_COMP_CTRL_OFFSET_MASK, offset);
|
||||
set_field(&compression, CSI2_CH_COMP_CTRL_SHIFT_MASK, shift);
|
||||
set_field(&compression, CSI2_CH_COMP_CTRL_MODE_MASK, mode);
|
||||
csi2_reg_write(csi2, CSI2_CH_COMP_CTRL(channel), compression);
|
||||
}
|
||||
|
||||
void csi2_start_channel(struct csi2_device *csi2, unsigned int channel,
|
||||
enum csi2_mode mode, bool auto_arm, bool pack_bytes,
|
||||
unsigned int width, unsigned int height,
|
||||
u8 vc, u8 dt)
|
||||
{
|
||||
u32 ctrl;
|
||||
|
||||
csi2_dbg(csi2, "%s [%u]\n", __func__, channel);
|
||||
|
||||
csi2_reg_write(csi2, CSI2_CH_CTRL(channel), 0);
|
||||
csi2_reg_write(csi2, CSI2_CH_DEBUG(channel), 0);
|
||||
csi2_reg_write(csi2, CSI2_STATUS, CSI2_STATUS_IRQ_CH_MASK(channel));
|
||||
|
||||
/* Enable channel and FS/FE interrupts. */
|
||||
ctrl = CSI2_CH_CTRL_DMA_EN | CSI2_CH_CTRL_IRQ_EN_FS |
|
||||
CSI2_CH_CTRL_IRQ_EN_FE_ACK | CSI2_CH_CTRL_PACK_LINE;
|
||||
/* PACK_BYTES ensures no striding for embedded data. */
|
||||
if (pack_bytes)
|
||||
ctrl |= CSI2_CH_CTRL_PACK_BYTES;
|
||||
|
||||
if (auto_arm)
|
||||
ctrl |= CSI2_CH_CTRL_AUTO_ARM;
|
||||
|
||||
if (width && height) {
|
||||
set_field(&ctrl, mode, CSI2_CH_CTRL_CH_MODE_MASK);
|
||||
csi2_reg_write(csi2, CSI2_CH_FRAME_SIZE(channel),
|
||||
(height << 16) | width);
|
||||
} else {
|
||||
set_field(&ctrl, 0x0, CSI2_CH_CTRL_CH_MODE_MASK);
|
||||
csi2_reg_write(csi2, CSI2_CH_FRAME_SIZE(channel), 0);
|
||||
}
|
||||
|
||||
set_field(&ctrl, vc, CSI2_CH_CTRL_VC_MASK);
|
||||
set_field(&ctrl, dt, CSI2_CH_CTRL_DT_MASK);
|
||||
csi2_reg_write(csi2, CSI2_CH_CTRL(channel), ctrl);
|
||||
csi2->num_lines[channel] = height;
|
||||
}
|
||||
|
||||
void csi2_stop_channel(struct csi2_device *csi2, unsigned int channel)
|
||||
{
|
||||
csi2_dbg(csi2, "%s [%u]\n", __func__, channel);
|
||||
|
||||
/* Channel disable. Use FORCE to allow stopping mid-frame. */
|
||||
csi2_reg_write(csi2, CSI2_CH_CTRL(channel), CSI2_CH_CTRL_FORCE);
|
||||
/* Latch the above change by writing to the ADDR0 register. */
|
||||
csi2_reg_write(csi2, CSI2_CH_ADDR0(channel), 0);
|
||||
/* Write this again, the HW needs it! */
|
||||
csi2_reg_write(csi2, CSI2_CH_ADDR0(channel), 0);
|
||||
}
|
||||
|
||||
void csi2_open_rx(struct csi2_device *csi2)
|
||||
{
|
||||
csi2_reg_write(csi2, CSI2_IRQ_MASK,
|
||||
csi2_track_errors ? CSI2_IRQ_MASK_IRQ_ALL : 0);
|
||||
|
||||
dphy_start(&csi2->dphy);
|
||||
|
||||
csi2_reg_write(csi2, CSI2_CTRL, CSI2_CTRL_EOP_IS_EOL);
|
||||
}
|
||||
|
||||
void csi2_close_rx(struct csi2_device *csi2)
|
||||
{
|
||||
dphy_stop(&csi2->dphy);
|
||||
|
||||
csi2_reg_write(csi2, CSI2_IRQ_MASK, 0);
|
||||
}
|
||||
|
||||
static int csi2_init_state(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *state)
|
||||
{
|
||||
struct v4l2_subdev_route routes[] = { {
|
||||
.sink_pad = CSI2_PAD_SINK,
|
||||
.sink_stream = 0,
|
||||
.source_pad = CSI2_PAD_FIRST_SOURCE,
|
||||
.source_stream = 0,
|
||||
.flags = V4L2_SUBDEV_ROUTE_FL_ACTIVE,
|
||||
} };
|
||||
|
||||
struct v4l2_subdev_krouting routing = {
|
||||
.num_routes = ARRAY_SIZE(routes),
|
||||
.routes = routes,
|
||||
};
|
||||
|
||||
int ret;
|
||||
|
||||
ret = v4l2_subdev_set_routing_with_fmt(sd, state, &routing,
|
||||
&cfe_default_format);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int csi2_pad_set_fmt(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct v4l2_subdev_format *format)
|
||||
{
|
||||
if (format->pad == CSI2_PAD_SINK) {
|
||||
/* Store the sink format and propagate it to the source. */
|
||||
|
||||
const struct cfe_fmt *cfe_fmt;
|
||||
|
||||
cfe_fmt = find_format_by_code(format->format.code);
|
||||
if (!cfe_fmt) {
|
||||
cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SRGGB10_1X10);
|
||||
format->format.code = cfe_fmt->code;
|
||||
}
|
||||
|
||||
struct v4l2_mbus_framefmt *fmt;
|
||||
|
||||
fmt = v4l2_subdev_state_get_format(state, format->pad,
|
||||
format->stream);
|
||||
if (!fmt)
|
||||
return -EINVAL;
|
||||
|
||||
*fmt = format->format;
|
||||
|
||||
fmt = v4l2_subdev_state_get_opposite_stream_format(state,
|
||||
format->pad,
|
||||
format->stream);
|
||||
if (!fmt)
|
||||
return -EINVAL;
|
||||
|
||||
format->format.field = V4L2_FIELD_NONE;
|
||||
|
||||
*fmt = format->format;
|
||||
} else {
|
||||
/* Only allow changing the source pad mbus code. */
|
||||
|
||||
struct v4l2_mbus_framefmt *sink_fmt, *source_fmt;
|
||||
u32 sink_code;
|
||||
u32 code;
|
||||
|
||||
sink_fmt = v4l2_subdev_state_get_opposite_stream_format(state,
|
||||
format->pad,
|
||||
format->stream);
|
||||
if (!sink_fmt)
|
||||
return -EINVAL;
|
||||
|
||||
source_fmt = v4l2_subdev_state_get_format(state, format->pad,
|
||||
format->stream);
|
||||
if (!source_fmt)
|
||||
return -EINVAL;
|
||||
|
||||
sink_code = sink_fmt->code;
|
||||
code = format->format.code;
|
||||
|
||||
/*
|
||||
* Only allow changing the mbus code to:
|
||||
* - The sink's mbus code
|
||||
* - The 16-bit version of the sink's mbus code
|
||||
* - The compressed version of the sink's mbus code
|
||||
*/
|
||||
if (code == sink_code ||
|
||||
code == cfe_find_16bit_code(sink_code) ||
|
||||
code == cfe_find_compressed_code(sink_code))
|
||||
source_fmt->code = code;
|
||||
|
||||
format->format.code = source_fmt->code;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int csi2_set_routing(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *state,
|
||||
enum v4l2_subdev_format_whence which,
|
||||
struct v4l2_subdev_krouting *routing)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = v4l2_subdev_routing_validate(sd, routing,
|
||||
V4L2_SUBDEV_ROUTING_ONLY_1_TO_1 |
|
||||
V4L2_SUBDEV_ROUTING_NO_SOURCE_MULTIPLEXING);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Only stream ID 0 allowed on source pads */
|
||||
for (unsigned int i = 0; i < routing->num_routes; ++i) {
|
||||
const struct v4l2_subdev_route *route = &routing->routes[i];
|
||||
|
||||
if (route->source_stream != 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = v4l2_subdev_set_routing_with_fmt(sd, state, routing,
|
||||
&cfe_default_format);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct v4l2_subdev_pad_ops csi2_subdev_pad_ops = {
|
||||
.get_fmt = v4l2_subdev_get_fmt,
|
||||
.set_fmt = csi2_pad_set_fmt,
|
||||
.set_routing = csi2_set_routing,
|
||||
.link_validate = v4l2_subdev_link_validate_default,
|
||||
};
|
||||
|
||||
static const struct media_entity_operations csi2_entity_ops = {
|
||||
.link_validate = v4l2_subdev_link_validate,
|
||||
.has_pad_interdep = v4l2_subdev_has_pad_interdep,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_ops csi2_subdev_ops = {
|
||||
.pad = &csi2_subdev_pad_ops,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_internal_ops csi2_internal_ops = {
|
||||
.init_state = csi2_init_state,
|
||||
};
|
||||
|
||||
int csi2_init(struct csi2_device *csi2, struct dentry *debugfs)
|
||||
{
|
||||
unsigned int ret;
|
||||
|
||||
spin_lock_init(&csi2->errors_lock);
|
||||
|
||||
csi2->dphy.dev = csi2->v4l2_dev->dev;
|
||||
dphy_probe(&csi2->dphy);
|
||||
|
||||
debugfs_create_file("csi2_regs", 0440, debugfs, csi2, &csi2_regs_fops);
|
||||
|
||||
if (csi2_track_errors)
|
||||
debugfs_create_file("csi2_errors", 0440, debugfs, csi2,
|
||||
&csi2_errors_fops);
|
||||
|
||||
csi2->pad[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
|
||||
|
||||
for (unsigned int i = CSI2_PAD_FIRST_SOURCE;
|
||||
i < CSI2_PAD_FIRST_SOURCE + CSI2_PAD_NUM_SOURCES; i++)
|
||||
csi2->pad[i].flags = MEDIA_PAD_FL_SOURCE;
|
||||
|
||||
ret = media_entity_pads_init(&csi2->sd.entity, ARRAY_SIZE(csi2->pad),
|
||||
csi2->pad);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Initialize subdev */
|
||||
v4l2_subdev_init(&csi2->sd, &csi2_subdev_ops);
|
||||
csi2->sd.internal_ops = &csi2_internal_ops;
|
||||
csi2->sd.entity.function = MEDIA_ENT_F_VID_IF_BRIDGE;
|
||||
csi2->sd.entity.ops = &csi2_entity_ops;
|
||||
csi2->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_STREAMS;
|
||||
csi2->sd.owner = THIS_MODULE;
|
||||
snprintf(csi2->sd.name, sizeof(csi2->sd.name), "csi2");
|
||||
|
||||
ret = v4l2_subdev_init_finalize(&csi2->sd);
|
||||
if (ret)
|
||||
goto err_entity_cleanup;
|
||||
|
||||
ret = v4l2_device_register_subdev(csi2->v4l2_dev, &csi2->sd);
|
||||
if (ret) {
|
||||
csi2_err(csi2, "Failed register csi2 subdev (%d)\n", ret);
|
||||
goto err_subdev_cleanup;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_subdev_cleanup:
|
||||
v4l2_subdev_cleanup(&csi2->sd);
|
||||
err_entity_cleanup:
|
||||
media_entity_cleanup(&csi2->sd.entity);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void csi2_uninit(struct csi2_device *csi2)
|
||||
{
|
||||
v4l2_device_unregister_subdev(&csi2->sd);
|
||||
v4l2_subdev_cleanup(&csi2->sd);
|
||||
media_entity_cleanup(&csi2->sd.entity);
|
||||
}
|
89
drivers/media/platform/raspberrypi/rp1-cfe/csi2.h
Normal file
89
drivers/media/platform/raspberrypi/rp1-cfe/csi2.h
Normal file
@ -0,0 +1,89 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* RP1 CSI-2 Driver
|
||||
*
|
||||
* Copyright (c) 2021-2024 Raspberry Pi Ltd.
|
||||
* Copyright (c) 2023-2024 Ideas on Board Oy
|
||||
*/
|
||||
|
||||
#ifndef _RP1_CSI2_
|
||||
#define _RP1_CSI2_
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/types.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/v4l2-subdev.h>
|
||||
|
||||
#include "dphy.h"
|
||||
|
||||
#define CSI2_NUM_CHANNELS 4
|
||||
|
||||
#define CSI2_PAD_SINK 0
|
||||
#define CSI2_PAD_FIRST_SOURCE 1
|
||||
#define CSI2_PAD_NUM_SOURCES 4
|
||||
#define CSI2_NUM_PADS 5
|
||||
|
||||
#define DISCARDS_TABLE_NUM_VCS 4
|
||||
|
||||
enum csi2_mode {
|
||||
CSI2_MODE_NORMAL = 0,
|
||||
CSI2_MODE_REMAP = 1,
|
||||
CSI2_MODE_COMPRESSED = 2,
|
||||
CSI2_MODE_FE_STREAMING = 3,
|
||||
};
|
||||
|
||||
enum csi2_compression_mode {
|
||||
CSI2_COMPRESSION_DELTA = 1,
|
||||
CSI2_COMPRESSION_SIMPLE = 2,
|
||||
CSI2_COMPRESSION_COMBINED = 3,
|
||||
};
|
||||
|
||||
enum discards_table_index {
|
||||
DISCARDS_TABLE_OVERFLOW = 0,
|
||||
DISCARDS_TABLE_LENGTH_LIMIT,
|
||||
DISCARDS_TABLE_UNMATCHED,
|
||||
DISCARDS_TABLE_INACTIVE,
|
||||
DISCARDS_TABLE_NUM_ENTRIES,
|
||||
};
|
||||
|
||||
struct csi2_device {
|
||||
/* Parent V4l2 device */
|
||||
struct v4l2_device *v4l2_dev;
|
||||
|
||||
void __iomem *base;
|
||||
|
||||
struct dphy_data dphy;
|
||||
|
||||
enum v4l2_mbus_type bus_type;
|
||||
unsigned int bus_flags;
|
||||
unsigned int num_lines[CSI2_NUM_CHANNELS];
|
||||
|
||||
struct media_pad pad[CSI2_NUM_PADS];
|
||||
struct v4l2_subdev sd;
|
||||
|
||||
/* lock for csi2 errors counters */
|
||||
spinlock_t errors_lock;
|
||||
u32 overflows;
|
||||
u32 discards_table[DISCARDS_TABLE_NUM_VCS][DISCARDS_TABLE_NUM_ENTRIES];
|
||||
u32 discards_dt_table[DISCARDS_TABLE_NUM_ENTRIES];
|
||||
};
|
||||
|
||||
void csi2_isr(struct csi2_device *csi2, bool *sof, bool *eof);
|
||||
void csi2_set_buffer(struct csi2_device *csi2, unsigned int channel,
|
||||
dma_addr_t dmaaddr, unsigned int stride,
|
||||
unsigned int size);
|
||||
void csi2_set_compression(struct csi2_device *csi2, unsigned int channel,
|
||||
enum csi2_compression_mode mode, unsigned int shift,
|
||||
unsigned int offset);
|
||||
void csi2_start_channel(struct csi2_device *csi2, unsigned int channel,
|
||||
enum csi2_mode mode, bool auto_arm,
|
||||
bool pack_bytes, unsigned int width,
|
||||
unsigned int height, u8 vc, u8 dt);
|
||||
void csi2_stop_channel(struct csi2_device *csi2, unsigned int channel);
|
||||
void csi2_open_rx(struct csi2_device *csi2);
|
||||
void csi2_close_rx(struct csi2_device *csi2);
|
||||
int csi2_init(struct csi2_device *csi2, struct dentry *debugfs);
|
||||
void csi2_uninit(struct csi2_device *csi2);
|
||||
|
||||
#endif
|
181
drivers/media/platform/raspberrypi/rp1-cfe/dphy.c
Normal file
181
drivers/media/platform/raspberrypi/rp1-cfe/dphy.c
Normal file
@ -0,0 +1,181 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* RP1 CSI-2 Driver
|
||||
*
|
||||
* Copyright (c) 2021-2024 Raspberry Pi Ltd.
|
||||
* Copyright (c) 2023-2024 Ideas on Board Oy
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "dphy.h"
|
||||
|
||||
#define dphy_dbg(dphy, fmt, arg...) dev_dbg((dphy)->dev, fmt, ##arg)
|
||||
#define dphy_err(dphy, fmt, arg...) dev_err((dphy)->dev, fmt, ##arg)
|
||||
|
||||
/* DW dphy Host registers */
|
||||
#define DPHY_VERSION 0x000
|
||||
#define DPHY_N_LANES 0x004
|
||||
#define DPHY_RESETN 0x008
|
||||
#define DPHY_PHY_SHUTDOWNZ 0x040
|
||||
#define DPHY_PHY_RSTZ 0x044
|
||||
#define DPHY_PHY_RX 0x048
|
||||
#define DPHY_PHY_STOPSTATE 0x04c
|
||||
#define DPHY_PHY_TST_CTRL0 0x050
|
||||
#define DPHY_PHY_TST_CTRL1 0x054
|
||||
#define DPHY_PHY2_TST_CTRL0 0x058
|
||||
#define DPHY_PHY2_TST_CTRL1 0x05c
|
||||
|
||||
/* DW dphy Host Transactions */
|
||||
#define DPHY_HS_RX_CTRL_LANE0_OFFSET 0x44
|
||||
#define DPHY_PLL_INPUT_DIV_OFFSET 0x17
|
||||
#define DPHY_PLL_LOOP_DIV_OFFSET 0x18
|
||||
#define DPHY_PLL_DIV_CTRL_OFFSET 0x19
|
||||
|
||||
static u32 dw_csi2_host_read(struct dphy_data *dphy, u32 offset)
|
||||
{
|
||||
return readl(dphy->base + offset);
|
||||
}
|
||||
|
||||
static void dw_csi2_host_write(struct dphy_data *dphy, u32 offset, u32 data)
|
||||
{
|
||||
writel(data, dphy->base + offset);
|
||||
}
|
||||
|
||||
static void set_tstclr(struct dphy_data *dphy, u32 val)
|
||||
{
|
||||
u32 ctrl0 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL0);
|
||||
|
||||
dw_csi2_host_write(dphy, DPHY_PHY_TST_CTRL0, (ctrl0 & ~1) | val);
|
||||
}
|
||||
|
||||
static void set_tstclk(struct dphy_data *dphy, u32 val)
|
||||
{
|
||||
u32 ctrl0 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL0);
|
||||
|
||||
dw_csi2_host_write(dphy, DPHY_PHY_TST_CTRL0, (ctrl0 & ~2) | (val << 1));
|
||||
}
|
||||
|
||||
static uint8_t get_tstdout(struct dphy_data *dphy)
|
||||
{
|
||||
u32 ctrl1 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL1);
|
||||
|
||||
return ((ctrl1 >> 8) & 0xff);
|
||||
}
|
||||
|
||||
static void set_testen(struct dphy_data *dphy, u32 val)
|
||||
{
|
||||
u32 ctrl1 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL1);
|
||||
|
||||
dw_csi2_host_write(dphy, DPHY_PHY_TST_CTRL1,
|
||||
(ctrl1 & ~(1 << 16)) | (val << 16));
|
||||
}
|
||||
|
||||
static void set_testdin(struct dphy_data *dphy, u32 val)
|
||||
{
|
||||
u32 ctrl1 = dw_csi2_host_read(dphy, DPHY_PHY_TST_CTRL1);
|
||||
|
||||
dw_csi2_host_write(dphy, DPHY_PHY_TST_CTRL1, (ctrl1 & ~0xff) | val);
|
||||
}
|
||||
|
||||
static uint8_t dphy_transaction(struct dphy_data *dphy, u8 test_code,
|
||||
uint8_t test_data)
|
||||
{
|
||||
/* See page 101 of the MIPI DPHY databook. */
|
||||
set_tstclk(dphy, 1);
|
||||
set_testen(dphy, 0);
|
||||
set_testdin(dphy, test_code);
|
||||
set_testen(dphy, 1);
|
||||
set_tstclk(dphy, 0);
|
||||
set_testen(dphy, 0);
|
||||
set_testdin(dphy, test_data);
|
||||
set_tstclk(dphy, 1);
|
||||
return get_tstdout(dphy);
|
||||
}
|
||||
|
||||
static void dphy_set_hsfreqrange(struct dphy_data *dphy, uint32_t mbps)
|
||||
{
|
||||
/* See Table 5-1 on page 65 of dphy databook */
|
||||
static const u16 hsfreqrange_table[][2] = {
|
||||
{ 89, 0b000000 }, { 99, 0b010000 }, { 109, 0b100000 },
|
||||
{ 129, 0b000001 }, { 139, 0b010001 }, { 149, 0b100001 },
|
||||
{ 169, 0b000010 }, { 179, 0b010010 }, { 199, 0b100010 },
|
||||
{ 219, 0b000011 }, { 239, 0b010011 }, { 249, 0b100011 },
|
||||
{ 269, 0b000100 }, { 299, 0b010100 }, { 329, 0b000101 },
|
||||
{ 359, 0b010101 }, { 399, 0b100101 }, { 449, 0b000110 },
|
||||
{ 499, 0b010110 }, { 549, 0b000111 }, { 599, 0b010111 },
|
||||
{ 649, 0b001000 }, { 699, 0b011000 }, { 749, 0b001001 },
|
||||
{ 799, 0b011001 }, { 849, 0b101001 }, { 899, 0b111001 },
|
||||
{ 949, 0b001010 }, { 999, 0b011010 }, { 1049, 0b101010 },
|
||||
{ 1099, 0b111010 }, { 1149, 0b001011 }, { 1199, 0b011011 },
|
||||
{ 1249, 0b101011 }, { 1299, 0b111011 }, { 1349, 0b001100 },
|
||||
{ 1399, 0b011100 }, { 1449, 0b101100 }, { 1500, 0b111100 },
|
||||
};
|
||||
unsigned int i;
|
||||
|
||||
if (mbps < 80 || mbps > 1500)
|
||||
dphy_err(dphy, "DPHY: Datarate %u Mbps out of range\n", mbps);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hsfreqrange_table) - 1; i++) {
|
||||
if (mbps <= hsfreqrange_table[i][0])
|
||||
break;
|
||||
}
|
||||
|
||||
dphy_transaction(dphy, DPHY_HS_RX_CTRL_LANE0_OFFSET,
|
||||
hsfreqrange_table[i][1] << 1);
|
||||
}
|
||||
|
||||
static void dphy_init(struct dphy_data *dphy)
|
||||
{
|
||||
dw_csi2_host_write(dphy, DPHY_PHY_RSTZ, 0);
|
||||
dw_csi2_host_write(dphy, DPHY_PHY_SHUTDOWNZ, 0);
|
||||
set_tstclk(dphy, 1);
|
||||
set_testen(dphy, 0);
|
||||
set_tstclr(dphy, 1);
|
||||
usleep_range(15, 20);
|
||||
set_tstclr(dphy, 0);
|
||||
usleep_range(15, 20);
|
||||
|
||||
dphy_set_hsfreqrange(dphy, dphy->dphy_rate);
|
||||
|
||||
usleep_range(5, 10);
|
||||
dw_csi2_host_write(dphy, DPHY_PHY_SHUTDOWNZ, 1);
|
||||
usleep_range(5, 10);
|
||||
dw_csi2_host_write(dphy, DPHY_PHY_RSTZ, 1);
|
||||
}
|
||||
|
||||
void dphy_start(struct dphy_data *dphy)
|
||||
{
|
||||
dphy_dbg(dphy, "%s: Link rate %u Mbps, %u data lanes\n", __func__,
|
||||
dphy->dphy_rate, dphy->active_lanes);
|
||||
|
||||
dw_csi2_host_write(dphy, DPHY_N_LANES, (dphy->active_lanes - 1));
|
||||
dphy_init(dphy);
|
||||
dw_csi2_host_write(dphy, DPHY_RESETN, 0xffffffff);
|
||||
usleep_range(10, 50);
|
||||
}
|
||||
|
||||
void dphy_stop(struct dphy_data *dphy)
|
||||
{
|
||||
dphy_dbg(dphy, "%s\n", __func__);
|
||||
|
||||
/* Set only one lane (lane 0) as active (ON) */
|
||||
dw_csi2_host_write(dphy, DPHY_N_LANES, 0);
|
||||
dw_csi2_host_write(dphy, DPHY_RESETN, 0);
|
||||
}
|
||||
|
||||
void dphy_probe(struct dphy_data *dphy)
|
||||
{
|
||||
u32 host_ver;
|
||||
u8 host_ver_major, host_ver_minor;
|
||||
|
||||
host_ver = dw_csi2_host_read(dphy, DPHY_VERSION);
|
||||
host_ver_major = (u8)((host_ver >> 24) - '0');
|
||||
host_ver_minor = (u8)((host_ver >> 16) - '0');
|
||||
host_ver_minor = host_ver_minor * 10;
|
||||
host_ver_minor += (u8)((host_ver >> 8) - '0');
|
||||
|
||||
dphy_dbg(dphy, "DW dphy Host HW v%u.%u\n", host_ver_major,
|
||||
host_ver_minor);
|
||||
}
|
27
drivers/media/platform/raspberrypi/rp1-cfe/dphy.h
Normal file
27
drivers/media/platform/raspberrypi/rp1-cfe/dphy.h
Normal file
@ -0,0 +1,27 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (c) 2021-2024 Raspberry Pi Ltd.
|
||||
* Copyright (c) 2023-2024 Ideas on Board Oy
|
||||
*/
|
||||
|
||||
#ifndef _RP1_DPHY_
|
||||
#define _RP1_DPHY_
|
||||
|
||||
#include <linux/io.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
struct dphy_data {
|
||||
struct device *dev;
|
||||
|
||||
void __iomem *base;
|
||||
|
||||
u32 dphy_rate;
|
||||
u32 max_lanes;
|
||||
u32 active_lanes;
|
||||
};
|
||||
|
||||
void dphy_probe(struct dphy_data *dphy);
|
||||
void dphy_start(struct dphy_data *dphy);
|
||||
void dphy_stop(struct dphy_data *dphy);
|
||||
|
||||
#endif
|
605
drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.c
Normal file
605
drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.c
Normal file
@ -0,0 +1,605 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* PiSP Front End Driver
|
||||
*
|
||||
* Copyright (c) 2021-2024 Raspberry Pi Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
#include <media/videobuf2-dma-contig.h>
|
||||
|
||||
#include "cfe.h"
|
||||
#include "pisp-fe.h"
|
||||
|
||||
#include "cfe-trace.h"
|
||||
|
||||
#define FE_VERSION 0x000
|
||||
#define FE_CONTROL 0x004
|
||||
#define FE_STATUS 0x008
|
||||
#define FE_FRAME_STATUS 0x00c
|
||||
#define FE_ERROR_STATUS 0x010
|
||||
#define FE_OUTPUT_STATUS 0x014
|
||||
#define FE_INT_EN 0x018
|
||||
#define FE_INT_STATUS 0x01c
|
||||
|
||||
/* CONTROL */
|
||||
#define FE_CONTROL_QUEUE BIT(0)
|
||||
#define FE_CONTROL_ABORT BIT(1)
|
||||
#define FE_CONTROL_RESET BIT(2)
|
||||
#define FE_CONTROL_LATCH_REGS BIT(3)
|
||||
|
||||
/* INT_EN / INT_STATUS */
|
||||
#define FE_INT_EOF BIT(0)
|
||||
#define FE_INT_SOF BIT(1)
|
||||
#define FE_INT_LINES0 BIT(8)
|
||||
#define FE_INT_LINES1 BIT(9)
|
||||
#define FE_INT_STATS BIT(16)
|
||||
#define FE_INT_QREADY BIT(24)
|
||||
|
||||
/* STATUS */
|
||||
#define FE_STATUS_QUEUED BIT(0)
|
||||
#define FE_STATUS_WAITING BIT(1)
|
||||
#define FE_STATUS_ACTIVE BIT(2)
|
||||
|
||||
#define PISP_FE_CONFIG_BASE_OFFSET 0x0040
|
||||
|
||||
#define PISP_FE_ENABLE_STATS_CLUSTER \
|
||||
(PISP_FE_ENABLE_STATS_CROP | PISP_FE_ENABLE_DECIMATE | \
|
||||
PISP_FE_ENABLE_BLC | PISP_FE_ENABLE_CDAF_STATS | \
|
||||
PISP_FE_ENABLE_AWB_STATS | PISP_FE_ENABLE_RGBY | \
|
||||
PISP_FE_ENABLE_LSC | PISP_FE_ENABLE_AGC_STATS)
|
||||
|
||||
#define PISP_FE_ENABLE_OUTPUT_CLUSTER(i) \
|
||||
((PISP_FE_ENABLE_CROP0 | PISP_FE_ENABLE_DOWNSCALE0 | \
|
||||
PISP_FE_ENABLE_COMPRESS0 | PISP_FE_ENABLE_OUTPUT0) << (4 * (i)))
|
||||
|
||||
struct pisp_fe_config_param {
|
||||
u32 dirty_flags;
|
||||
u32 dirty_flags_extra;
|
||||
size_t offset;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
static const struct pisp_fe_config_param pisp_fe_config_map[] = {
|
||||
/* *_dirty_flag_extra types */
|
||||
{ 0, PISP_FE_DIRTY_GLOBAL,
|
||||
offsetof(struct pisp_fe_config, global),
|
||||
sizeof(struct pisp_fe_global_config) },
|
||||
{ 0, PISP_FE_DIRTY_FLOATING,
|
||||
offsetof(struct pisp_fe_config, floating_stats),
|
||||
sizeof(struct pisp_fe_floating_stats_config) },
|
||||
{ 0, PISP_FE_DIRTY_OUTPUT_AXI,
|
||||
offsetof(struct pisp_fe_config, output_axi),
|
||||
sizeof(struct pisp_fe_output_axi_config) },
|
||||
/* *_dirty_flag types */
|
||||
{ PISP_FE_ENABLE_INPUT, 0,
|
||||
offsetof(struct pisp_fe_config, input),
|
||||
sizeof(struct pisp_fe_input_config) },
|
||||
{ PISP_FE_ENABLE_DECOMPRESS, 0,
|
||||
offsetof(struct pisp_fe_config, decompress),
|
||||
sizeof(struct pisp_decompress_config) },
|
||||
{ PISP_FE_ENABLE_DECOMPAND, 0,
|
||||
offsetof(struct pisp_fe_config, decompand),
|
||||
sizeof(struct pisp_fe_decompand_config) },
|
||||
{ PISP_FE_ENABLE_BLA, 0,
|
||||
offsetof(struct pisp_fe_config, bla),
|
||||
sizeof(struct pisp_bla_config) },
|
||||
{ PISP_FE_ENABLE_DPC, 0,
|
||||
offsetof(struct pisp_fe_config, dpc),
|
||||
sizeof(struct pisp_fe_dpc_config) },
|
||||
{ PISP_FE_ENABLE_STATS_CROP, 0,
|
||||
offsetof(struct pisp_fe_config, stats_crop),
|
||||
sizeof(struct pisp_fe_crop_config) },
|
||||
{ PISP_FE_ENABLE_BLC, 0,
|
||||
offsetof(struct pisp_fe_config, blc),
|
||||
sizeof(struct pisp_bla_config) },
|
||||
{ PISP_FE_ENABLE_CDAF_STATS, 0,
|
||||
offsetof(struct pisp_fe_config, cdaf_stats),
|
||||
sizeof(struct pisp_fe_cdaf_stats_config) },
|
||||
{ PISP_FE_ENABLE_AWB_STATS, 0,
|
||||
offsetof(struct pisp_fe_config, awb_stats),
|
||||
sizeof(struct pisp_fe_awb_stats_config) },
|
||||
{ PISP_FE_ENABLE_RGBY, 0,
|
||||
offsetof(struct pisp_fe_config, rgby),
|
||||
sizeof(struct pisp_fe_rgby_config) },
|
||||
{ PISP_FE_ENABLE_LSC, 0,
|
||||
offsetof(struct pisp_fe_config, lsc),
|
||||
sizeof(struct pisp_fe_lsc_config) },
|
||||
{ PISP_FE_ENABLE_AGC_STATS, 0,
|
||||
offsetof(struct pisp_fe_config, agc_stats),
|
||||
sizeof(struct pisp_agc_statistics) },
|
||||
{ PISP_FE_ENABLE_CROP0, 0,
|
||||
offsetof(struct pisp_fe_config, ch[0].crop),
|
||||
sizeof(struct pisp_fe_crop_config) },
|
||||
{ PISP_FE_ENABLE_DOWNSCALE0, 0,
|
||||
offsetof(struct pisp_fe_config, ch[0].downscale),
|
||||
sizeof(struct pisp_fe_downscale_config) },
|
||||
{ PISP_FE_ENABLE_COMPRESS0, 0,
|
||||
offsetof(struct pisp_fe_config, ch[0].compress),
|
||||
sizeof(struct pisp_compress_config) },
|
||||
{ PISP_FE_ENABLE_OUTPUT0, 0,
|
||||
offsetof(struct pisp_fe_config, ch[0].output),
|
||||
sizeof(struct pisp_fe_output_config) },
|
||||
{ PISP_FE_ENABLE_CROP1, 0,
|
||||
offsetof(struct pisp_fe_config, ch[1].crop),
|
||||
sizeof(struct pisp_fe_crop_config) },
|
||||
{ PISP_FE_ENABLE_DOWNSCALE1, 0,
|
||||
offsetof(struct pisp_fe_config, ch[1].downscale),
|
||||
sizeof(struct pisp_fe_downscale_config) },
|
||||
{ PISP_FE_ENABLE_COMPRESS1, 0,
|
||||
offsetof(struct pisp_fe_config, ch[1].compress),
|
||||
sizeof(struct pisp_compress_config) },
|
||||
{ PISP_FE_ENABLE_OUTPUT1, 0,
|
||||
offsetof(struct pisp_fe_config, ch[1].output),
|
||||
sizeof(struct pisp_fe_output_config) },
|
||||
};
|
||||
|
||||
#define pisp_fe_dbg(fe, fmt, arg...) dev_dbg((fe)->v4l2_dev->dev, fmt, ##arg)
|
||||
#define pisp_fe_info(fe, fmt, arg...) dev_info((fe)->v4l2_dev->dev, fmt, ##arg)
|
||||
#define pisp_fe_err(fe, fmt, arg...) dev_err((fe)->v4l2_dev->dev, fmt, ##arg)
|
||||
|
||||
static inline u32 pisp_fe_reg_read(struct pisp_fe_device *fe, u32 offset)
|
||||
{
|
||||
return readl(fe->base + offset);
|
||||
}
|
||||
|
||||
static inline void pisp_fe_reg_write(struct pisp_fe_device *fe, u32 offset,
|
||||
u32 val)
|
||||
{
|
||||
writel(val, fe->base + offset);
|
||||
}
|
||||
|
||||
static inline void pisp_fe_reg_write_relaxed(struct pisp_fe_device *fe,
|
||||
u32 offset, u32 val)
|
||||
{
|
||||
writel_relaxed(val, fe->base + offset);
|
||||
}
|
||||
|
||||
static int pisp_fe_regs_show(struct seq_file *s, void *data)
|
||||
{
|
||||
struct pisp_fe_device *fe = s->private;
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_resume_and_get(fe->v4l2_dev->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_LATCH_REGS);
|
||||
|
||||
#define DUMP(reg) seq_printf(s, #reg " \t0x%08x\n", pisp_fe_reg_read(fe, reg))
|
||||
DUMP(FE_VERSION);
|
||||
DUMP(FE_CONTROL);
|
||||
DUMP(FE_STATUS);
|
||||
DUMP(FE_FRAME_STATUS);
|
||||
DUMP(FE_ERROR_STATUS);
|
||||
DUMP(FE_OUTPUT_STATUS);
|
||||
DUMP(FE_INT_EN);
|
||||
DUMP(FE_INT_STATUS);
|
||||
#undef DUMP
|
||||
|
||||
pm_runtime_put(fe->v4l2_dev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_SHOW_ATTRIBUTE(pisp_fe_regs);
|
||||
|
||||
static void pisp_fe_config_write(struct pisp_fe_device *fe,
|
||||
struct pisp_fe_config *config,
|
||||
unsigned int start_offset, unsigned int size)
|
||||
{
|
||||
const unsigned int max_offset =
|
||||
offsetof(struct pisp_fe_config, ch[PISP_FE_NUM_OUTPUTS]);
|
||||
unsigned int end_offset;
|
||||
u32 *cfg = (u32 *)config;
|
||||
|
||||
start_offset = min(start_offset, max_offset);
|
||||
end_offset = min(start_offset + size, max_offset);
|
||||
|
||||
cfg += start_offset >> 2;
|
||||
for (unsigned int i = start_offset; i < end_offset; i += 4, cfg++)
|
||||
pisp_fe_reg_write_relaxed(fe, PISP_FE_CONFIG_BASE_OFFSET + i,
|
||||
*cfg);
|
||||
}
|
||||
|
||||
void pisp_fe_isr(struct pisp_fe_device *fe, bool *sof, bool *eof)
|
||||
{
|
||||
u32 status, int_status, out_status, frame_status, error_status;
|
||||
|
||||
pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_LATCH_REGS);
|
||||
status = pisp_fe_reg_read(fe, FE_STATUS);
|
||||
out_status = pisp_fe_reg_read(fe, FE_OUTPUT_STATUS);
|
||||
frame_status = pisp_fe_reg_read(fe, FE_FRAME_STATUS);
|
||||
error_status = pisp_fe_reg_read(fe, FE_ERROR_STATUS);
|
||||
|
||||
int_status = pisp_fe_reg_read(fe, FE_INT_STATUS);
|
||||
pisp_fe_reg_write(fe, FE_INT_STATUS, int_status);
|
||||
|
||||
trace_fe_irq(status, out_status, frame_status, error_status,
|
||||
int_status);
|
||||
|
||||
/* We do not report interrupts for the input/stream pad. */
|
||||
for (unsigned int i = 0; i < FE_NUM_PADS - 1; i++) {
|
||||
sof[i] = !!(int_status & FE_INT_SOF);
|
||||
eof[i] = !!(int_status & FE_INT_EOF);
|
||||
}
|
||||
}
|
||||
|
||||
static bool pisp_fe_validate_output(struct pisp_fe_config const *cfg,
|
||||
unsigned int c, struct v4l2_format const *f)
|
||||
{
|
||||
unsigned int wbytes;
|
||||
|
||||
wbytes = cfg->ch[c].output.format.width;
|
||||
if (cfg->ch[c].output.format.format & PISP_IMAGE_FORMAT_BPS_MASK)
|
||||
wbytes *= 2;
|
||||
|
||||
/* Check output image dimensions are nonzero and not too big */
|
||||
if (cfg->ch[c].output.format.width < 2 ||
|
||||
cfg->ch[c].output.format.height < 2 ||
|
||||
cfg->ch[c].output.format.height > f->fmt.pix.height ||
|
||||
cfg->ch[c].output.format.stride > f->fmt.pix.bytesperline ||
|
||||
wbytes > f->fmt.pix.bytesperline)
|
||||
return false;
|
||||
|
||||
/* Check for zero-sized crops, which could cause lockup */
|
||||
if ((cfg->global.enables & PISP_FE_ENABLE_CROP(c)) &&
|
||||
((cfg->ch[c].crop.offset_x >= (cfg->input.format.width & ~1) ||
|
||||
cfg->ch[c].crop.offset_y >= cfg->input.format.height ||
|
||||
cfg->ch[c].crop.width < 2 || cfg->ch[c].crop.height < 2)))
|
||||
return false;
|
||||
|
||||
if ((cfg->global.enables & PISP_FE_ENABLE_DOWNSCALE(c)) &&
|
||||
(cfg->ch[c].downscale.output_width < 2 ||
|
||||
cfg->ch[c].downscale.output_height < 2))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pisp_fe_validate_stats(struct pisp_fe_config const *cfg)
|
||||
{
|
||||
/* Check for zero-sized crop, which could cause lockup */
|
||||
return (!(cfg->global.enables & PISP_FE_ENABLE_STATS_CROP) ||
|
||||
(cfg->stats_crop.offset_x < (cfg->input.format.width & ~1) &&
|
||||
cfg->stats_crop.offset_y < cfg->input.format.height &&
|
||||
cfg->stats_crop.width >= 2 && cfg->stats_crop.height >= 2));
|
||||
}
|
||||
|
||||
int pisp_fe_validate_config(struct pisp_fe_device *fe,
|
||||
struct pisp_fe_config *cfg,
|
||||
struct v4l2_format const *f0,
|
||||
struct v4l2_format const *f1)
|
||||
{
|
||||
/*
|
||||
* Check the input is enabled, streaming and has nonzero size;
|
||||
* to avoid cases where the hardware might lock up or try to
|
||||
* read inputs from memory (which this driver doesn't support).
|
||||
*/
|
||||
if (!(cfg->global.enables & PISP_FE_ENABLE_INPUT) ||
|
||||
cfg->input.streaming != 1 || cfg->input.format.width < 2 ||
|
||||
cfg->input.format.height < 2) {
|
||||
pisp_fe_err(fe, "%s: Input config not valid", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < PISP_FE_NUM_OUTPUTS; i++) {
|
||||
if (!(cfg->global.enables & PISP_FE_ENABLE_OUTPUT(i))) {
|
||||
if (cfg->global.enables &
|
||||
PISP_FE_ENABLE_OUTPUT_CLUSTER(i)) {
|
||||
pisp_fe_err(fe, "%s: Output %u not valid",
|
||||
__func__, i);
|
||||
return -EINVAL;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!pisp_fe_validate_output(cfg, i, i ? f1 : f0))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if ((cfg->global.enables & PISP_FE_ENABLE_STATS_CLUSTER) &&
|
||||
!pisp_fe_validate_stats(cfg)) {
|
||||
pisp_fe_err(fe, "%s: Stats config not valid", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pisp_fe_submit_job(struct pisp_fe_device *fe, struct vb2_buffer **vb2_bufs,
|
||||
struct pisp_fe_config *cfg)
|
||||
{
|
||||
u64 addr;
|
||||
u32 status;
|
||||
|
||||
/*
|
||||
* Check output buffers exist and outputs are correctly configured.
|
||||
* If valid, set the buffer's DMA address; otherwise disable.
|
||||
*/
|
||||
for (unsigned int i = 0; i < PISP_FE_NUM_OUTPUTS; i++) {
|
||||
struct vb2_buffer *buf = vb2_bufs[FE_OUTPUT0_PAD + i];
|
||||
|
||||
if (!(cfg->global.enables & PISP_FE_ENABLE_OUTPUT(i)))
|
||||
continue;
|
||||
|
||||
addr = vb2_dma_contig_plane_dma_addr(buf, 0);
|
||||
cfg->output_buffer[i].addr_lo = addr & 0xffffffff;
|
||||
cfg->output_buffer[i].addr_hi = addr >> 32;
|
||||
}
|
||||
|
||||
if (vb2_bufs[FE_STATS_PAD]) {
|
||||
addr = vb2_dma_contig_plane_dma_addr(vb2_bufs[FE_STATS_PAD], 0);
|
||||
cfg->stats_buffer.addr_lo = addr & 0xffffffff;
|
||||
cfg->stats_buffer.addr_hi = addr >> 32;
|
||||
}
|
||||
|
||||
/* Set up ILINES interrupts 3/4 of the way down each output */
|
||||
cfg->ch[0].output.ilines =
|
||||
max(0x80u, (3u * cfg->ch[0].output.format.height) >> 2);
|
||||
cfg->ch[1].output.ilines =
|
||||
max(0x80u, (3u * cfg->ch[1].output.format.height) >> 2);
|
||||
|
||||
/*
|
||||
* The hardware must have consumed the previous config by now.
|
||||
* This read of status also serves as a memory barrier before the
|
||||
* sequence of relaxed writes which follow.
|
||||
*/
|
||||
status = pisp_fe_reg_read(fe, FE_STATUS);
|
||||
if (WARN_ON(status & FE_STATUS_QUEUED))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Unconditionally write buffers, global and input parameters.
|
||||
* Write cropping and output parameters whenever they are enabled.
|
||||
* Selectively write other parameters that have been marked as
|
||||
* changed through the dirty flags.
|
||||
*/
|
||||
pisp_fe_config_write(fe, cfg, 0,
|
||||
offsetof(struct pisp_fe_config, decompress));
|
||||
cfg->dirty_flags_extra &= ~PISP_FE_DIRTY_GLOBAL;
|
||||
cfg->dirty_flags &= ~PISP_FE_ENABLE_INPUT;
|
||||
cfg->dirty_flags |= (cfg->global.enables &
|
||||
(PISP_FE_ENABLE_STATS_CROP |
|
||||
PISP_FE_ENABLE_OUTPUT_CLUSTER(0) |
|
||||
PISP_FE_ENABLE_OUTPUT_CLUSTER(1)));
|
||||
for (unsigned int i = 0; i < ARRAY_SIZE(pisp_fe_config_map); i++) {
|
||||
const struct pisp_fe_config_param *p = &pisp_fe_config_map[i];
|
||||
|
||||
if (cfg->dirty_flags & p->dirty_flags ||
|
||||
cfg->dirty_flags_extra & p->dirty_flags_extra)
|
||||
pisp_fe_config_write(fe, cfg, p->offset, p->size);
|
||||
}
|
||||
|
||||
/* This final non-relaxed write serves as a memory barrier */
|
||||
pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_QUEUE);
|
||||
}
|
||||
|
||||
void pisp_fe_start(struct pisp_fe_device *fe)
|
||||
{
|
||||
pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_RESET);
|
||||
pisp_fe_reg_write(fe, FE_INT_STATUS, ~0);
|
||||
pisp_fe_reg_write(fe, FE_INT_EN, FE_INT_EOF | FE_INT_SOF |
|
||||
FE_INT_LINES0 | FE_INT_LINES1);
|
||||
fe->inframe_count = 0;
|
||||
}
|
||||
|
||||
void pisp_fe_stop(struct pisp_fe_device *fe)
|
||||
{
|
||||
pisp_fe_reg_write(fe, FE_INT_EN, 0);
|
||||
pisp_fe_reg_write(fe, FE_CONTROL, FE_CONTROL_ABORT);
|
||||
usleep_range(1000, 2000);
|
||||
WARN_ON(pisp_fe_reg_read(fe, FE_STATUS));
|
||||
pisp_fe_reg_write(fe, FE_INT_STATUS, ~0);
|
||||
}
|
||||
|
||||
static int pisp_fe_init_state(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *state)
|
||||
{
|
||||
struct v4l2_mbus_framefmt *fmt;
|
||||
|
||||
fmt = v4l2_subdev_state_get_format(state, FE_STREAM_PAD);
|
||||
*fmt = cfe_default_format;
|
||||
fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16;
|
||||
|
||||
fmt = v4l2_subdev_state_get_format(state, FE_CONFIG_PAD);
|
||||
fmt->code = MEDIA_BUS_FMT_FIXED;
|
||||
fmt->width = sizeof(struct pisp_fe_config);
|
||||
fmt->height = 1;
|
||||
|
||||
fmt = v4l2_subdev_state_get_format(state, FE_OUTPUT0_PAD);
|
||||
*fmt = cfe_default_format;
|
||||
fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16;
|
||||
|
||||
fmt = v4l2_subdev_state_get_format(state, FE_OUTPUT1_PAD);
|
||||
*fmt = cfe_default_format;
|
||||
fmt->code = MEDIA_BUS_FMT_SRGGB16_1X16;
|
||||
|
||||
fmt = v4l2_subdev_state_get_format(state, FE_STATS_PAD);
|
||||
fmt->code = MEDIA_BUS_FMT_FIXED;
|
||||
fmt->width = sizeof(struct pisp_statistics);
|
||||
fmt->height = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pisp_fe_pad_set_fmt(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct v4l2_subdev_format *format)
|
||||
{
|
||||
struct v4l2_mbus_framefmt *fmt;
|
||||
const struct cfe_fmt *cfe_fmt;
|
||||
|
||||
/* TODO: format propagation to source pads */
|
||||
/* TODO: format validation */
|
||||
|
||||
switch (format->pad) {
|
||||
case FE_STREAM_PAD:
|
||||
cfe_fmt = find_format_by_code(format->format.code);
|
||||
if (!cfe_fmt || !(cfe_fmt->flags & CFE_FORMAT_FLAG_FE_OUT))
|
||||
cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SRGGB16_1X16);
|
||||
|
||||
format->format.code = cfe_fmt->code;
|
||||
format->format.field = V4L2_FIELD_NONE;
|
||||
|
||||
fmt = v4l2_subdev_state_get_format(state, FE_STREAM_PAD);
|
||||
*fmt = format->format;
|
||||
|
||||
fmt = v4l2_subdev_state_get_format(state, FE_OUTPUT0_PAD);
|
||||
*fmt = format->format;
|
||||
|
||||
fmt = v4l2_subdev_state_get_format(state, FE_OUTPUT1_PAD);
|
||||
*fmt = format->format;
|
||||
|
||||
return 0;
|
||||
|
||||
case FE_OUTPUT0_PAD:
|
||||
case FE_OUTPUT1_PAD: {
|
||||
/*
|
||||
* TODO: we should allow scaling and cropping by allowing the
|
||||
* user to set the size here.
|
||||
*/
|
||||
struct v4l2_mbus_framefmt *sink_fmt, *source_fmt;
|
||||
u32 sink_code;
|
||||
u32 code;
|
||||
|
||||
cfe_fmt = find_format_by_code(format->format.code);
|
||||
if (!cfe_fmt || !(cfe_fmt->flags & CFE_FORMAT_FLAG_FE_OUT))
|
||||
cfe_fmt = find_format_by_code(MEDIA_BUS_FMT_SRGGB16_1X16);
|
||||
|
||||
format->format.code = cfe_fmt->code;
|
||||
|
||||
sink_fmt = v4l2_subdev_state_get_format(state, FE_STREAM_PAD);
|
||||
if (!sink_fmt)
|
||||
return -EINVAL;
|
||||
|
||||
source_fmt = v4l2_subdev_state_get_format(state, format->pad);
|
||||
if (!source_fmt)
|
||||
return -EINVAL;
|
||||
|
||||
sink_code = sink_fmt->code;
|
||||
code = format->format.code;
|
||||
|
||||
/*
|
||||
* If the source code from the user does not match the code in
|
||||
* the sink pad, check that the source code matches the
|
||||
* compressed version of the sink code.
|
||||
*/
|
||||
|
||||
if (code != sink_code &&
|
||||
code == cfe_find_compressed_code(sink_code))
|
||||
source_fmt->code = code;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
case FE_CONFIG_PAD:
|
||||
case FE_STATS_PAD:
|
||||
default:
|
||||
return v4l2_subdev_get_fmt(sd, state, format);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct v4l2_subdev_pad_ops pisp_fe_subdev_pad_ops = {
|
||||
.get_fmt = v4l2_subdev_get_fmt,
|
||||
.set_fmt = pisp_fe_pad_set_fmt,
|
||||
.link_validate = v4l2_subdev_link_validate_default,
|
||||
};
|
||||
|
||||
static int pisp_fe_link_validate(struct media_link *link)
|
||||
{
|
||||
struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(link->sink->entity);
|
||||
struct pisp_fe_device *fe = container_of(sd, struct pisp_fe_device, sd);
|
||||
|
||||
pisp_fe_dbg(fe, "%s: link \"%s\":%u -> \"%s\":%u\n", __func__,
|
||||
link->source->entity->name, link->source->index,
|
||||
link->sink->entity->name, link->sink->index);
|
||||
|
||||
if (link->sink->index == FE_STREAM_PAD)
|
||||
return v4l2_subdev_link_validate(link);
|
||||
|
||||
if (link->sink->index == FE_CONFIG_PAD)
|
||||
return 0;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct media_entity_operations pisp_fe_entity_ops = {
|
||||
.link_validate = pisp_fe_link_validate,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_ops pisp_fe_subdev_ops = {
|
||||
.pad = &pisp_fe_subdev_pad_ops,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_internal_ops pisp_fe_internal_ops = {
|
||||
.init_state = pisp_fe_init_state,
|
||||
};
|
||||
|
||||
int pisp_fe_init(struct pisp_fe_device *fe, struct dentry *debugfs)
|
||||
{
|
||||
int ret;
|
||||
|
||||
debugfs_create_file("fe_regs", 0440, debugfs, fe, &pisp_fe_regs_fops);
|
||||
|
||||
fe->hw_revision = pisp_fe_reg_read(fe, FE_VERSION);
|
||||
pisp_fe_info(fe, "PiSP FE HW v%u.%u\n",
|
||||
(fe->hw_revision >> 24) & 0xff,
|
||||
(fe->hw_revision >> 20) & 0x0f);
|
||||
|
||||
fe->pad[FE_STREAM_PAD].flags =
|
||||
MEDIA_PAD_FL_SINK | MEDIA_PAD_FL_MUST_CONNECT;
|
||||
fe->pad[FE_CONFIG_PAD].flags = MEDIA_PAD_FL_SINK;
|
||||
fe->pad[FE_OUTPUT0_PAD].flags = MEDIA_PAD_FL_SOURCE;
|
||||
fe->pad[FE_OUTPUT1_PAD].flags = MEDIA_PAD_FL_SOURCE;
|
||||
fe->pad[FE_STATS_PAD].flags = MEDIA_PAD_FL_SOURCE;
|
||||
|
||||
ret = media_entity_pads_init(&fe->sd.entity, ARRAY_SIZE(fe->pad),
|
||||
fe->pad);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Initialize subdev */
|
||||
v4l2_subdev_init(&fe->sd, &pisp_fe_subdev_ops);
|
||||
fe->sd.internal_ops = &pisp_fe_internal_ops;
|
||||
fe->sd.entity.function = MEDIA_ENT_F_PROC_VIDEO_SCALER;
|
||||
fe->sd.entity.ops = &pisp_fe_entity_ops;
|
||||
fe->sd.entity.name = "pisp-fe";
|
||||
fe->sd.flags = V4L2_SUBDEV_FL_HAS_DEVNODE;
|
||||
fe->sd.owner = THIS_MODULE;
|
||||
snprintf(fe->sd.name, sizeof(fe->sd.name), "pisp-fe");
|
||||
|
||||
ret = v4l2_subdev_init_finalize(&fe->sd);
|
||||
if (ret)
|
||||
goto err_entity_cleanup;
|
||||
|
||||
ret = v4l2_device_register_subdev(fe->v4l2_dev, &fe->sd);
|
||||
if (ret) {
|
||||
pisp_fe_err(fe, "Failed register pisp fe subdev (%d)\n", ret);
|
||||
goto err_subdev_cleanup;
|
||||
}
|
||||
|
||||
/* Must be in IDLE state (STATUS == 0) here. */
|
||||
WARN_ON(pisp_fe_reg_read(fe, FE_STATUS));
|
||||
|
||||
return 0;
|
||||
|
||||
err_subdev_cleanup:
|
||||
v4l2_subdev_cleanup(&fe->sd);
|
||||
err_entity_cleanup:
|
||||
media_entity_cleanup(&fe->sd.entity);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void pisp_fe_uninit(struct pisp_fe_device *fe)
|
||||
{
|
||||
v4l2_device_unregister_subdev(&fe->sd);
|
||||
v4l2_subdev_cleanup(&fe->sd);
|
||||
media_entity_cleanup(&fe->sd.entity);
|
||||
}
|
53
drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.h
Normal file
53
drivers/media/platform/raspberrypi/rp1-cfe/pisp-fe.h
Normal file
@ -0,0 +1,53 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* PiSP Front End Driver
|
||||
*
|
||||
* Copyright (c) 2021-2024 Raspberry Pi Ltd.
|
||||
*/
|
||||
#ifndef _PISP_FE_H_
|
||||
#define _PISP_FE_H_
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include <media/media-device.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/v4l2-subdev.h>
|
||||
|
||||
#include <linux/media/raspberrypi/pisp_fe_config.h>
|
||||
|
||||
enum pisp_fe_pads {
|
||||
FE_STREAM_PAD,
|
||||
FE_CONFIG_PAD,
|
||||
FE_OUTPUT0_PAD,
|
||||
FE_OUTPUT1_PAD,
|
||||
FE_STATS_PAD,
|
||||
FE_NUM_PADS
|
||||
};
|
||||
|
||||
struct pisp_fe_device {
|
||||
/* Parent V4l2 device */
|
||||
struct v4l2_device *v4l2_dev;
|
||||
void __iomem *base;
|
||||
u32 hw_revision;
|
||||
|
||||
u16 inframe_count;
|
||||
struct media_pad pad[FE_NUM_PADS];
|
||||
struct v4l2_subdev sd;
|
||||
};
|
||||
|
||||
void pisp_fe_isr(struct pisp_fe_device *fe, bool *sof, bool *eof);
|
||||
int pisp_fe_validate_config(struct pisp_fe_device *fe,
|
||||
struct pisp_fe_config *cfg,
|
||||
struct v4l2_format const *f0,
|
||||
struct v4l2_format const *f1);
|
||||
void pisp_fe_submit_job(struct pisp_fe_device *fe, struct vb2_buffer **vb2_bufs,
|
||||
struct pisp_fe_config *cfg);
|
||||
void pisp_fe_start(struct pisp_fe_device *fe);
|
||||
void pisp_fe_stop(struct pisp_fe_device *fe);
|
||||
int pisp_fe_init(struct pisp_fe_device *fe, struct dentry *debugfs);
|
||||
void pisp_fe_uninit(struct pisp_fe_device *fe);
|
||||
|
||||
#endif
|
273
include/uapi/linux/media/raspberrypi/pisp_fe_config.h
Normal file
273
include/uapi/linux/media/raspberrypi/pisp_fe_config.h
Normal file
@ -0,0 +1,273 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
|
||||
/*
|
||||
* RP1 PiSP Front End Driver Configuration structures
|
||||
*
|
||||
* Copyright (C) 2021 - Raspberry Pi Ltd.
|
||||
*
|
||||
*/
|
||||
#ifndef _UAPI_PISP_FE_CONFIG_
|
||||
#define _UAPI_PISP_FE_CONFIG_
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "pisp_common.h"
|
||||
#include "pisp_fe_statistics.h"
|
||||
|
||||
#define PISP_FE_NUM_OUTPUTS 2
|
||||
|
||||
enum pisp_fe_enable {
|
||||
PISP_FE_ENABLE_INPUT = 0x000001,
|
||||
PISP_FE_ENABLE_DECOMPRESS = 0x000002,
|
||||
PISP_FE_ENABLE_DECOMPAND = 0x000004,
|
||||
PISP_FE_ENABLE_BLA = 0x000008,
|
||||
PISP_FE_ENABLE_DPC = 0x000010,
|
||||
PISP_FE_ENABLE_STATS_CROP = 0x000020,
|
||||
PISP_FE_ENABLE_DECIMATE = 0x000040,
|
||||
PISP_FE_ENABLE_BLC = 0x000080,
|
||||
PISP_FE_ENABLE_CDAF_STATS = 0x000100,
|
||||
PISP_FE_ENABLE_AWB_STATS = 0x000200,
|
||||
PISP_FE_ENABLE_RGBY = 0x000400,
|
||||
PISP_FE_ENABLE_LSC = 0x000800,
|
||||
PISP_FE_ENABLE_AGC_STATS = 0x001000,
|
||||
PISP_FE_ENABLE_CROP0 = 0x010000,
|
||||
PISP_FE_ENABLE_DOWNSCALE0 = 0x020000,
|
||||
PISP_FE_ENABLE_COMPRESS0 = 0x040000,
|
||||
PISP_FE_ENABLE_OUTPUT0 = 0x080000,
|
||||
PISP_FE_ENABLE_CROP1 = 0x100000,
|
||||
PISP_FE_ENABLE_DOWNSCALE1 = 0x200000,
|
||||
PISP_FE_ENABLE_COMPRESS1 = 0x400000,
|
||||
PISP_FE_ENABLE_OUTPUT1 = 0x800000
|
||||
};
|
||||
|
||||
#define PISP_FE_ENABLE_CROP(i) (PISP_FE_ENABLE_CROP0 << (4 * (i)))
|
||||
#define PISP_FE_ENABLE_DOWNSCALE(i) (PISP_FE_ENABLE_DOWNSCALE0 << (4 * (i)))
|
||||
#define PISP_FE_ENABLE_COMPRESS(i) (PISP_FE_ENABLE_COMPRESS0 << (4 * (i)))
|
||||
#define PISP_FE_ENABLE_OUTPUT(i) (PISP_FE_ENABLE_OUTPUT0 << (4 * (i)))
|
||||
|
||||
/*
|
||||
* We use the enable flags to show when blocks are "dirty", but we need some
|
||||
* extra ones too.
|
||||
*/
|
||||
enum pisp_fe_dirty {
|
||||
PISP_FE_DIRTY_GLOBAL = 0x0001,
|
||||
PISP_FE_DIRTY_FLOATING = 0x0002,
|
||||
PISP_FE_DIRTY_OUTPUT_AXI = 0x0004
|
||||
};
|
||||
|
||||
struct pisp_fe_global_config {
|
||||
__u32 enables;
|
||||
__u8 bayer_order;
|
||||
__u8 pad[3];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pisp_fe_input_axi_config {
|
||||
/* burst length minus one, in the range 0..15; OR'd with flags */
|
||||
__u8 maxlen_flags;
|
||||
/* { prot[2:0], cache[3:0] } fields */
|
||||
__u8 cache_prot;
|
||||
/* QoS (only 4 LS bits are used) */
|
||||
__u16 qos;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pisp_fe_output_axi_config {
|
||||
/* burst length minus one, in the range 0..15; OR'd with flags */
|
||||
__u8 maxlen_flags;
|
||||
/* { prot[2:0], cache[3:0] } fields */
|
||||
__u8 cache_prot;
|
||||
/* QoS (4 bitfields of 4 bits each for different panic levels) */
|
||||
__u16 qos;
|
||||
/* For Panic mode: Output FIFO panic threshold */
|
||||
__u16 thresh;
|
||||
/* For Panic mode: Output FIFO statistics throttle threshold */
|
||||
__u16 throttle;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pisp_fe_input_config {
|
||||
__u8 streaming;
|
||||
__u8 pad[3];
|
||||
struct pisp_image_format_config format;
|
||||
struct pisp_fe_input_axi_config axi;
|
||||
/* Extra cycles delay before issuing each burst request */
|
||||
__u8 holdoff;
|
||||
__u8 pad2[3];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pisp_fe_output_config {
|
||||
struct pisp_image_format_config format;
|
||||
__u16 ilines;
|
||||
__u8 pad[2];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pisp_fe_input_buffer_config {
|
||||
__u32 addr_lo;
|
||||
__u32 addr_hi;
|
||||
__u16 frame_id;
|
||||
__u16 pad;
|
||||
} __attribute__((packed));
|
||||
|
||||
#define PISP_FE_DECOMPAND_LUT_SIZE 65
|
||||
|
||||
struct pisp_fe_decompand_config {
|
||||
__u16 lut[PISP_FE_DECOMPAND_LUT_SIZE];
|
||||
__u16 pad;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pisp_fe_dpc_config {
|
||||
__u8 coeff_level;
|
||||
__u8 coeff_range;
|
||||
__u8 coeff_range2;
|
||||
#define PISP_FE_DPC_FLAG_FOLDBACK 1
|
||||
#define PISP_FE_DPC_FLAG_VFLAG 2
|
||||
__u8 flags;
|
||||
} __attribute__((packed));
|
||||
|
||||
#define PISP_FE_LSC_LUT_SIZE 16
|
||||
|
||||
struct pisp_fe_lsc_config {
|
||||
__u8 shift;
|
||||
__u8 pad0;
|
||||
__u16 scale;
|
||||
__u16 centre_x;
|
||||
__u16 centre_y;
|
||||
__u16 lut[PISP_FE_LSC_LUT_SIZE];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pisp_fe_rgby_config {
|
||||
__u16 gain_r;
|
||||
__u16 gain_g;
|
||||
__u16 gain_b;
|
||||
__u8 maxflag;
|
||||
__u8 pad;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pisp_fe_agc_stats_config {
|
||||
__u16 offset_x;
|
||||
__u16 offset_y;
|
||||
__u16 size_x;
|
||||
__u16 size_y;
|
||||
/* each weight only 4 bits */
|
||||
__u8 weights[PISP_AGC_STATS_NUM_ZONES / 2];
|
||||
__u16 row_offset_x;
|
||||
__u16 row_offset_y;
|
||||
__u16 row_size_x;
|
||||
__u16 row_size_y;
|
||||
__u8 row_shift;
|
||||
__u8 float_shift;
|
||||
__u8 pad1[2];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pisp_fe_awb_stats_config {
|
||||
__u16 offset_x;
|
||||
__u16 offset_y;
|
||||
__u16 size_x;
|
||||
__u16 size_y;
|
||||
__u8 shift;
|
||||
__u8 pad[3];
|
||||
__u16 r_lo;
|
||||
__u16 r_hi;
|
||||
__u16 g_lo;
|
||||
__u16 g_hi;
|
||||
__u16 b_lo;
|
||||
__u16 b_hi;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pisp_fe_floating_stats_region {
|
||||
__u16 offset_x;
|
||||
__u16 offset_y;
|
||||
__u16 size_x;
|
||||
__u16 size_y;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pisp_fe_floating_stats_config {
|
||||
struct pisp_fe_floating_stats_region
|
||||
regions[PISP_FLOATING_STATS_NUM_ZONES];
|
||||
} __attribute__((packed));
|
||||
|
||||
#define PISP_FE_CDAF_NUM_WEIGHTS 8
|
||||
|
||||
struct pisp_fe_cdaf_stats_config {
|
||||
__u16 noise_constant;
|
||||
__u16 noise_slope;
|
||||
__u16 offset_x;
|
||||
__u16 offset_y;
|
||||
__u16 size_x;
|
||||
__u16 size_y;
|
||||
__u16 skip_x;
|
||||
__u16 skip_y;
|
||||
__u32 mode;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pisp_fe_stats_buffer_config {
|
||||
__u32 addr_lo;
|
||||
__u32 addr_hi;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pisp_fe_crop_config {
|
||||
__u16 offset_x;
|
||||
__u16 offset_y;
|
||||
__u16 width;
|
||||
__u16 height;
|
||||
} __attribute__((packed));
|
||||
|
||||
enum pisp_fe_downscale_flags {
|
||||
/* downscale the four Bayer components independently... */
|
||||
DOWNSCALE_BAYER = 1,
|
||||
/* ...without trying to preserve their spatial relationship */
|
||||
DOWNSCALE_BIN = 2,
|
||||
};
|
||||
|
||||
struct pisp_fe_downscale_config {
|
||||
__u8 xin;
|
||||
__u8 xout;
|
||||
__u8 yin;
|
||||
__u8 yout;
|
||||
__u8 flags; /* enum pisp_fe_downscale_flags */
|
||||
__u8 pad[3];
|
||||
__u16 output_width;
|
||||
__u16 output_height;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pisp_fe_output_buffer_config {
|
||||
__u32 addr_lo;
|
||||
__u32 addr_hi;
|
||||
} __attribute__((packed));
|
||||
|
||||
/* Each of the two output channels/branches: */
|
||||
struct pisp_fe_output_branch_config {
|
||||
struct pisp_fe_crop_config crop;
|
||||
struct pisp_fe_downscale_config downscale;
|
||||
struct pisp_compress_config compress;
|
||||
struct pisp_fe_output_config output;
|
||||
__u32 pad;
|
||||
} __attribute__((packed));
|
||||
|
||||
/* And finally one to rule them all: */
|
||||
struct pisp_fe_config {
|
||||
/* I/O configuration: */
|
||||
struct pisp_fe_stats_buffer_config stats_buffer;
|
||||
struct pisp_fe_output_buffer_config output_buffer[PISP_FE_NUM_OUTPUTS];
|
||||
struct pisp_fe_input_buffer_config input_buffer;
|
||||
/* processing configuration: */
|
||||
struct pisp_fe_global_config global;
|
||||
struct pisp_fe_input_config input;
|
||||
struct pisp_decompress_config decompress;
|
||||
struct pisp_fe_decompand_config decompand;
|
||||
struct pisp_bla_config bla;
|
||||
struct pisp_fe_dpc_config dpc;
|
||||
struct pisp_fe_crop_config stats_crop;
|
||||
__u32 spare1; /* placeholder for future decimate configuration */
|
||||
struct pisp_bla_config blc;
|
||||
struct pisp_fe_rgby_config rgby;
|
||||
struct pisp_fe_lsc_config lsc;
|
||||
struct pisp_fe_agc_stats_config agc_stats;
|
||||
struct pisp_fe_awb_stats_config awb_stats;
|
||||
struct pisp_fe_cdaf_stats_config cdaf_stats;
|
||||
struct pisp_fe_floating_stats_config floating_stats;
|
||||
struct pisp_fe_output_axi_config output_axi;
|
||||
struct pisp_fe_output_branch_config ch[PISP_FE_NUM_OUTPUTS];
|
||||
/* non-register fields: */
|
||||
__u32 dirty_flags; /* these use pisp_fe_enable */
|
||||
__u32 dirty_flags_extra; /* these use pisp_fe_dirty */
|
||||
} __attribute__((packed));
|
||||
|
||||
#endif /* _UAPI_PISP_FE_CONFIG_ */
|
64
include/uapi/linux/media/raspberrypi/pisp_fe_statistics.h
Normal file
64
include/uapi/linux/media/raspberrypi/pisp_fe_statistics.h
Normal file
@ -0,0 +1,64 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */
|
||||
/*
|
||||
* RP1 PiSP Front End statistics definitions
|
||||
*
|
||||
* Copyright (C) 2021 - Raspberry Pi Ltd.
|
||||
*
|
||||
*/
|
||||
#ifndef _UAPI_PISP_FE_STATISTICS_H_
|
||||
#define _UAPI_PISP_FE_STATISTICS_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#define PISP_FLOATING_STATS_NUM_ZONES 4
|
||||
#define PISP_AGC_STATS_NUM_BINS 1024
|
||||
#define PISP_AGC_STATS_SIZE 16
|
||||
#define PISP_AGC_STATS_NUM_ZONES (PISP_AGC_STATS_SIZE * PISP_AGC_STATS_SIZE)
|
||||
#define PISP_AGC_STATS_NUM_ROW_SUMS 512
|
||||
|
||||
struct pisp_agc_statistics_zone {
|
||||
__u64 Y_sum;
|
||||
__u32 counted;
|
||||
__u32 pad;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pisp_agc_statistics {
|
||||
__u32 row_sums[PISP_AGC_STATS_NUM_ROW_SUMS];
|
||||
/*
|
||||
* 32-bits per bin means an image (just less than) 16384x16384 pixels
|
||||
* in size can weight every pixel from 0 to 15.
|
||||
*/
|
||||
__u32 histogram[PISP_AGC_STATS_NUM_BINS];
|
||||
struct pisp_agc_statistics_zone floating[PISP_FLOATING_STATS_NUM_ZONES];
|
||||
} __attribute__((packed));
|
||||
|
||||
#define PISP_AWB_STATS_SIZE 32
|
||||
#define PISP_AWB_STATS_NUM_ZONES (PISP_AWB_STATS_SIZE * PISP_AWB_STATS_SIZE)
|
||||
|
||||
struct pisp_awb_statistics_zone {
|
||||
__u32 R_sum;
|
||||
__u32 G_sum;
|
||||
__u32 B_sum;
|
||||
__u32 counted;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pisp_awb_statistics {
|
||||
struct pisp_awb_statistics_zone zones[PISP_AWB_STATS_NUM_ZONES];
|
||||
struct pisp_awb_statistics_zone floating[PISP_FLOATING_STATS_NUM_ZONES];
|
||||
} __attribute__((packed));
|
||||
|
||||
#define PISP_CDAF_STATS_SIZE 8
|
||||
#define PISP_CDAF_STATS_NUM_FOMS (PISP_CDAF_STATS_SIZE * PISP_CDAF_STATS_SIZE)
|
||||
|
||||
struct pisp_cdaf_statistics {
|
||||
__u64 foms[PISP_CDAF_STATS_NUM_FOMS];
|
||||
__u64 floating[PISP_FLOATING_STATS_NUM_ZONES];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct pisp_statistics {
|
||||
struct pisp_awb_statistics awb;
|
||||
struct pisp_agc_statistics agc;
|
||||
struct pisp_cdaf_statistics cdaf;
|
||||
} __attribute__((packed));
|
||||
|
||||
#endif /* _UAPI_PISP_FE_STATISTICS_H_ */
|
Loading…
Reference in New Issue
Block a user