mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
media: mali-c55: Add Mali-C55 ISP driver
Add a driver for Arm's Mali-C55 Image Signal Processor. The driver is V4L2 and Media Controller compliant and creates subdevices to manage the ISP itself, its internal test pattern generator as well as the crop, scaler and output format functionality for each of its two output devices. Tested-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> Acked-by: Nayden Kanchev <nayden.kanchev@arm.com> Co-developed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com> Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com> Signed-off-by: Daniel Scally <dan.scally@ideasonboard.com> Signed-off-by: Hans Verkuil <hverkuil+cisco@kernel.org> [hverkuil: remove deprecated vb2_ops_wait_prepare/finish callbacks]
This commit is contained in:
committed by
Hans Verkuil
parent
8d0bbed21e
commit
d5f281f3dd
@@ -65,6 +65,7 @@ config VIDEO_MUX
|
||||
source "drivers/media/platform/allegro-dvt/Kconfig"
|
||||
source "drivers/media/platform/amlogic/Kconfig"
|
||||
source "drivers/media/platform/amphion/Kconfig"
|
||||
source "drivers/media/platform/arm/Kconfig"
|
||||
source "drivers/media/platform/aspeed/Kconfig"
|
||||
source "drivers/media/platform/atmel/Kconfig"
|
||||
source "drivers/media/platform/broadcom/Kconfig"
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
obj-y += allegro-dvt/
|
||||
obj-y += amlogic/
|
||||
obj-y += amphion/
|
||||
obj-y += arm/
|
||||
obj-y += aspeed/
|
||||
obj-y += atmel/
|
||||
obj-y += broadcom/
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
comment "ARM media platform drivers"
|
||||
|
||||
source "drivers/media/platform/arm/mali-c55/Kconfig"
|
||||
@@ -0,0 +1,2 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-y += mali-c55/
|
||||
@@ -0,0 +1,17 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config VIDEO_MALI_C55
|
||||
tristate "ARM Mali-C55 Image Signal Processor driver"
|
||||
depends on ARCH_VEXPRESS || ARCH_RENESAS || COMPILE_TEST
|
||||
depends on V4L_PLATFORM_DRIVERS
|
||||
depends on VIDEO_DEV && OF
|
||||
select GENERIC_PHY_MIPI_DPHY
|
||||
select MEDIA_CONTROLLER
|
||||
select V4L2_FWNODE
|
||||
select VIDEO_V4L2_SUBDEV_API
|
||||
select VIDEOBUF2_DMA_CONTIG
|
||||
select VIDEOBUF2_VMALLOC
|
||||
help
|
||||
Enable this to support Arm's Mali-C55 Image Signal Processor.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called mali-c55.
|
||||
@@ -0,0 +1,9 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
mali-c55-y := mali-c55-capture.o \
|
||||
mali-c55-core.o \
|
||||
mali-c55-isp.o \
|
||||
mali-c55-resizer.o \
|
||||
mali-c55-tpg.o
|
||||
|
||||
obj-$(CONFIG_VIDEO_MALI_C55) += mali-c55.o
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,254 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* ARM Mali-C55 ISP Driver - Common definitions
|
||||
*
|
||||
* Copyright (C) 2025 Ideas on Board Oy
|
||||
*/
|
||||
|
||||
#ifndef _MALI_C55_COMMON_H
|
||||
#define _MALI_C55_COMMON_H
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include <media/media-device.h>
|
||||
#include <media/v4l2-async.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-dev.h>
|
||||
#include <media/v4l2-device.h>
|
||||
#include <media/v4l2-subdev.h>
|
||||
#include <media/videobuf2-core.h>
|
||||
#include <media/videobuf2-v4l2.h>
|
||||
|
||||
#define MALI_C55_DRIVER_NAME "mali-c55"
|
||||
|
||||
/* min and max values for the image sizes */
|
||||
#define MALI_C55_MIN_WIDTH 640U
|
||||
#define MALI_C55_MIN_HEIGHT 480U
|
||||
#define MALI_C55_MAX_WIDTH 8192U
|
||||
#define MALI_C55_MAX_HEIGHT 8192U
|
||||
#define MALI_C55_DEFAULT_WIDTH 1920U
|
||||
#define MALI_C55_DEFAULT_HEIGHT 1080U
|
||||
|
||||
#define MALI_C55_DEFAULT_MEDIA_BUS_FMT MEDIA_BUS_FMT_RGB121212_1X36
|
||||
|
||||
#define MALI_C55_NUM_CLKS 3
|
||||
#define MALI_C55_NUM_RESETS 3
|
||||
|
||||
struct device;
|
||||
struct mali_c55;
|
||||
struct mali_c55_cap_dev;
|
||||
struct media_pipeline;
|
||||
struct platform_device;
|
||||
struct resource;
|
||||
|
||||
enum mali_c55_isp_pads {
|
||||
MALI_C55_ISP_PAD_SINK_VIDEO,
|
||||
MALI_C55_ISP_PAD_SOURCE_VIDEO,
|
||||
MALI_C55_ISP_PAD_SOURCE_BYPASS,
|
||||
MALI_C55_ISP_NUM_PADS,
|
||||
};
|
||||
|
||||
struct mali_c55_tpg {
|
||||
struct mali_c55 *mali_c55;
|
||||
struct v4l2_subdev sd;
|
||||
struct media_pad pad;
|
||||
struct mali_c55_tpg_ctrls {
|
||||
struct v4l2_ctrl_handler handler;
|
||||
struct v4l2_ctrl *vblank;
|
||||
} ctrls;
|
||||
};
|
||||
|
||||
struct mali_c55_isp {
|
||||
struct mali_c55 *mali_c55;
|
||||
struct v4l2_subdev sd;
|
||||
struct media_pad pads[MALI_C55_ISP_NUM_PADS];
|
||||
struct v4l2_ctrl_handler handler;
|
||||
struct media_pad *remote_src;
|
||||
/* Mutex to guard vb2 start/stop streaming */
|
||||
struct mutex capture_lock;
|
||||
unsigned int frame_sequence;
|
||||
};
|
||||
|
||||
enum mali_c55_resizer_ids {
|
||||
MALI_C55_RSZ_FR,
|
||||
MALI_C55_RSZ_DS,
|
||||
MALI_C55_NUM_RSZS,
|
||||
};
|
||||
|
||||
enum mali_c55_rsz_pads {
|
||||
MALI_C55_RSZ_SINK_PAD,
|
||||
MALI_C55_RSZ_SOURCE_PAD,
|
||||
MALI_C55_RSZ_SINK_BYPASS_PAD,
|
||||
MALI_C55_RSZ_NUM_PADS
|
||||
};
|
||||
|
||||
struct mali_c55_resizer {
|
||||
struct mali_c55 *mali_c55;
|
||||
struct mali_c55_cap_dev *cap_dev;
|
||||
enum mali_c55_resizer_ids id;
|
||||
struct v4l2_subdev sd;
|
||||
struct media_pad pads[MALI_C55_RSZ_NUM_PADS];
|
||||
unsigned int num_routes;
|
||||
};
|
||||
|
||||
enum mali_c55_cap_devs {
|
||||
MALI_C55_CAP_DEV_FR,
|
||||
MALI_C55_CAP_DEV_DS,
|
||||
MALI_C55_NUM_CAP_DEVS
|
||||
};
|
||||
|
||||
struct mali_c55_format_info {
|
||||
u32 fourcc;
|
||||
/*
|
||||
* The output formats can be produced by a couple of different media bus
|
||||
* formats, depending on how the ISP is configured.
|
||||
*/
|
||||
unsigned int mbus_codes[2];
|
||||
bool is_raw;
|
||||
struct {
|
||||
u32 base_mode;
|
||||
u32 uv_plane;
|
||||
} registers;
|
||||
};
|
||||
|
||||
struct mali_c55_isp_format_info {
|
||||
u32 code;
|
||||
u32 shifted_code;
|
||||
bool bypass;
|
||||
u32 order;
|
||||
};
|
||||
|
||||
enum mali_c55_planes {
|
||||
MALI_C55_PLANE_Y,
|
||||
MALI_C55_PLANE_UV,
|
||||
MALI_C55_NUM_PLANES
|
||||
};
|
||||
|
||||
struct mali_c55_buffer {
|
||||
struct vb2_v4l2_buffer vb;
|
||||
unsigned int planes_pending;
|
||||
struct list_head queue;
|
||||
dma_addr_t addrs[MALI_C55_NUM_PLANES];
|
||||
};
|
||||
|
||||
struct mali_c55_cap_dev {
|
||||
struct mali_c55 *mali_c55;
|
||||
struct mali_c55_resizer *rsz;
|
||||
struct video_device vdev;
|
||||
struct media_pad pad;
|
||||
struct vb2_queue queue;
|
||||
/* Mutex to provide to vb2 */
|
||||
struct mutex lock;
|
||||
unsigned int reg_offset;
|
||||
|
||||
struct {
|
||||
const struct mali_c55_format_info *info;
|
||||
struct v4l2_pix_format_mplane format;
|
||||
} format;
|
||||
|
||||
struct {
|
||||
/* Spinlock to guard buffer queue */
|
||||
spinlock_t lock;
|
||||
/* Spinlock to guard the queue of buffers being processed */
|
||||
spinlock_t processing_lock;
|
||||
struct list_head input;
|
||||
struct list_head processing;
|
||||
} buffers;
|
||||
};
|
||||
|
||||
enum mali_c55_config_spaces {
|
||||
MALI_C55_CONFIG_PONG,
|
||||
MALI_C55_CONFIG_PING,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mali_c55_context - Fields relating to a single camera context
|
||||
*
|
||||
* @mali_c55: Pointer to the main struct mali_c55
|
||||
* @registers: A pointer to some allocated memory holding register
|
||||
* values to be written to the hardware at frame interrupt
|
||||
* @base: Base address of the config space in the hardware
|
||||
* @lock: A spinlock to protect against writes to @registers whilst that
|
||||
* space is being copied to the hardware
|
||||
* @list: A list head to facilitate a context queue
|
||||
*/
|
||||
struct mali_c55_context {
|
||||
struct mali_c55 *mali_c55;
|
||||
u32 *registers;
|
||||
phys_addr_t base;
|
||||
/* Spinlock to prevent simultaneous access of register space */
|
||||
spinlock_t lock;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct mali_c55 {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
struct clk_bulk_data clks[MALI_C55_NUM_CLKS];
|
||||
struct reset_control_bulk_data resets[MALI_C55_NUM_RESETS];
|
||||
int irqnum;
|
||||
|
||||
u16 capabilities;
|
||||
bool inline_mode;
|
||||
struct media_device media_dev;
|
||||
struct v4l2_device v4l2_dev;
|
||||
struct v4l2_async_notifier notifier;
|
||||
struct media_pipeline pipe;
|
||||
|
||||
struct mali_c55_tpg tpg;
|
||||
struct mali_c55_isp isp;
|
||||
struct mali_c55_resizer resizers[MALI_C55_NUM_RSZS];
|
||||
struct mali_c55_cap_dev cap_devs[MALI_C55_NUM_CAP_DEVS];
|
||||
|
||||
struct mali_c55_context context;
|
||||
u32 next_config;
|
||||
};
|
||||
|
||||
void mali_c55_write(struct mali_c55 *mali_c55, unsigned int addr, u32 val);
|
||||
void mali_c55_cap_dev_write(struct mali_c55_cap_dev *cap_dev, unsigned int addr,
|
||||
u32 val);
|
||||
void mali_c55_update_bits(struct mali_c55 *mali_c55, unsigned int addr,
|
||||
u32 mask, u32 val);
|
||||
u32 mali_c55_read(struct mali_c55 *mali_c55, unsigned int addr);
|
||||
void mali_c55_ctx_write(struct mali_c55 *mali_c55, unsigned int addr, u32 val);
|
||||
u32 mali_c55_ctx_read(struct mali_c55 *mali_c55, unsigned int addr);
|
||||
void mali_c55_ctx_update_bits(struct mali_c55 *mali_c55, unsigned int addr,
|
||||
u32 mask, u32 val);
|
||||
|
||||
int mali_c55_config_write(struct mali_c55_context *ctx,
|
||||
enum mali_c55_config_spaces cfg_space,
|
||||
bool force_synchronous);
|
||||
|
||||
int mali_c55_register_isp(struct mali_c55 *mali_c55);
|
||||
int mali_c55_register_tpg(struct mali_c55 *mali_c55);
|
||||
void mali_c55_unregister_tpg(struct mali_c55 *mali_c55);
|
||||
void mali_c55_unregister_isp(struct mali_c55 *mali_c55);
|
||||
int mali_c55_register_resizers(struct mali_c55 *mali_c55);
|
||||
void mali_c55_unregister_resizers(struct mali_c55 *mali_c55);
|
||||
int mali_c55_register_capture_devs(struct mali_c55 *mali_c55);
|
||||
void mali_c55_unregister_capture_devs(struct mali_c55 *mali_c55);
|
||||
struct mali_c55_context *mali_c55_get_active_context(struct mali_c55 *mali_c55);
|
||||
void mali_c55_set_plane_done(struct mali_c55_cap_dev *cap_dev,
|
||||
enum mali_c55_planes plane);
|
||||
void mali_c55_set_next_buffer(struct mali_c55_cap_dev *cap_dev);
|
||||
void mali_c55_isp_queue_event_sof(struct mali_c55 *mali_c55);
|
||||
|
||||
bool mali_c55_format_is_raw(unsigned int mbus_code);
|
||||
|
||||
const struct mali_c55_isp_format_info *
|
||||
mali_c55_isp_fmt_next(const struct mali_c55_isp_format_info *fmt);
|
||||
const struct mali_c55_isp_format_info *
|
||||
mali_c55_isp_get_mbus_config_by_code(u32 code);
|
||||
const struct mali_c55_isp_format_info *
|
||||
mali_c55_isp_get_mbus_config_by_shifted_code(u32 code);
|
||||
const struct mali_c55_isp_format_info *
|
||||
mali_c55_isp_get_mbus_config_by_index(u32 index);
|
||||
bool mali_c55_pipeline_ready(struct mali_c55 *mali_c55);
|
||||
|
||||
#endif /* _MALI_C55_COMMON_H */
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,318 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* ARM Mali-C55 ISP Driver - Register definitions
|
||||
*
|
||||
* Copyright (C) 2025 Ideas on Board Oy
|
||||
*/
|
||||
|
||||
#ifndef _MALI_C55_REGISTERS_H
|
||||
#define _MALI_C55_REGISTERS_H
|
||||
|
||||
#include <linux/bits.h>
|
||||
|
||||
/* ISP Common 0x00000 - 0x000ff */
|
||||
|
||||
#define MALI_C55_REG_API 0x00000
|
||||
#define MALI_C55_REG_PRODUCT 0x00004
|
||||
#define MALI_C55_REG_VERSION 0x00008
|
||||
#define MALI_C55_REG_REVISION 0x0000c
|
||||
#define MALI_C55_REG_PULSE_MODE 0x0003c
|
||||
#define MALI_C55_REG_INPUT_MODE_REQUEST 0x0009c
|
||||
#define MALI_C55_INPUT_SAFE_STOP 0x00
|
||||
#define MALI_C55_INPUT_SAFE_START 0x01
|
||||
#define MALI_C55_REG_MODE_STATUS 0x000a0
|
||||
#define MALI_C55_REG_INTERRUPT_MASK_VECTOR 0x00030
|
||||
#define MALI_C55_INTERRUPT_MASK_ALL GENMASK(31, 0)
|
||||
|
||||
#define MALI_C55_REG_GLOBAL_MONITOR 0x00050
|
||||
|
||||
#define MALI_C55_REG_GEN_VIDEO 0x00080
|
||||
#define MALI_C55_REG_GEN_VIDEO_ON_MASK BIT(0)
|
||||
#define MALI_C55_REG_GEN_VIDEO_MULTI_MASK BIT(1)
|
||||
#define MALI_C55_REG_GEN_PREFETCH_MASK GENMASK(31, 16)
|
||||
|
||||
#define MALI_C55_REG_MCU_CONFIG 0x00020
|
||||
#define MALI_C55_REG_MCU_CONFIG_OVERRIDE_MASK BIT(0)
|
||||
#define MALI_C55_REG_MCU_CONFIG_WRITE_MASK BIT(1)
|
||||
#define MALI_C55_MCU_CONFIG_WRITE(x) ((x) << 1)
|
||||
#define MALI_C55_REG_MCU_CONFIG_WRITE_PING BIT(1)
|
||||
#define MALI_C55_REG_MCU_CONFIG_WRITE_PONG 0x00
|
||||
#define MALI_C55_REG_MULTI_CONTEXT_MODE_MASK BIT(8)
|
||||
#define MALI_C55_REG_PING_PONG_READ 0x00024
|
||||
#define MALI_C55_REG_PING_PONG_READ_MASK BIT(2)
|
||||
|
||||
#define MALI_C55_REG_INTERRUPT_CLEAR_VECTOR 0x00034
|
||||
#define MALI_C55_REG_INTERRUPT_CLEAR 0x00040
|
||||
#define MALI_C55_REG_INTERRUPT_STATUS_VECTOR 0x00044
|
||||
|
||||
enum mali_c55_interrupts {
|
||||
MALI_C55_IRQ_ISP_START,
|
||||
MALI_C55_IRQ_ISP_DONE,
|
||||
MALI_C55_IRQ_MCM_ERROR,
|
||||
MALI_C55_IRQ_BROKEN_FRAME_ERROR,
|
||||
MALI_C55_IRQ_MET_AF_DONE,
|
||||
MALI_C55_IRQ_MET_AEXP_DONE,
|
||||
MALI_C55_IRQ_MET_AWB_DONE,
|
||||
MALI_C55_IRQ_AEXP_1024_DONE,
|
||||
MALI_C55_IRQ_IRIDIX_MET_DONE,
|
||||
MALI_C55_IRQ_LUT_INIT_DONE,
|
||||
MALI_C55_IRQ_FR_Y_DONE,
|
||||
MALI_C55_IRQ_FR_UV_DONE,
|
||||
MALI_C55_IRQ_DS_Y_DONE,
|
||||
MALI_C55_IRQ_DS_UV_DONE,
|
||||
MALI_C55_IRQ_LINEARIZATION_DONE,
|
||||
MALI_C55_IRQ_RAW_FRONTEND_DONE,
|
||||
MALI_C55_IRQ_NOISE_REDUCTION_DONE,
|
||||
MALI_C55_IRQ_IRIDIX_DONE,
|
||||
MALI_C55_IRQ_BAYER2RGB_DONE,
|
||||
MALI_C55_IRQ_WATCHDOG_TIMER,
|
||||
MALI_C55_IRQ_FRAME_COLLISION,
|
||||
MALI_C55_IRQ_UNUSED,
|
||||
MALI_C55_IRQ_DMA_ERROR,
|
||||
MALI_C55_IRQ_INPUT_STOPPED,
|
||||
MALI_C55_IRQ_MET_AWB_TARGET1_HIT,
|
||||
MALI_C55_IRQ_MET_AWB_TARGET2_HIT,
|
||||
MALI_C55_NUM_IRQ_BITS
|
||||
};
|
||||
|
||||
#define MALI_C55_INTERRUPT_BIT(x) BIT(x)
|
||||
|
||||
#define MALI_C55_REG_GLOBAL_PARAMETER_STATUS 0x00068
|
||||
#define MALI_C55_GPS_PONG_FITTED BIT(0)
|
||||
#define MALI_C55_GPS_WDR_FITTED BIT(1)
|
||||
#define MALI_C55_GPS_COMPRESSION_FITTED BIT(2)
|
||||
#define MALI_C55_GPS_TEMPER_FITTED BIT(3)
|
||||
#define MALI_C55_GPS_SINTER_LITE_FITTED BIT(4)
|
||||
#define MALI_C55_GPS_SINTER_FITTED BIT(5)
|
||||
#define MALI_C55_GPS_IRIDIX_LTM_FITTED BIT(6)
|
||||
#define MALI_C55_GPS_IRIDIX_GTM_FITTED BIT(7)
|
||||
#define MALI_C55_GPS_CNR_FITTED BIT(8)
|
||||
#define MALI_C55_GPS_FRSCALER_FITTED BIT(9)
|
||||
#define MALI_C55_GPS_DS_PIPE_FITTED BIT(10)
|
||||
|
||||
#define MALI_C55_REG_BLANKING 0x00084
|
||||
#define MALI_C55_REG_HBLANK_MASK GENMASK(15, 0)
|
||||
#define MALI_C55_REG_VBLANK_MASK GENMASK(31, 16)
|
||||
#define MALI_C55_VBLANK(x) ((x) << 16)
|
||||
|
||||
#define MALI_C55_REG_HC_START 0x00088
|
||||
#define MALI_C55_HC_START(h) (((h) & 0xffff) << 16)
|
||||
#define MALI_C55_REG_HC_SIZE 0x0008c
|
||||
#define MALI_C55_HC_SIZE(h) ((h) & 0xffff)
|
||||
#define MALI_C55_REG_VC_START_SIZE 0x00094
|
||||
#define MALI_C55_VC_START(v) ((v) & 0xffff)
|
||||
#define MALI_C55_VC_SIZE(v) (((v) & 0xffff) << 16)
|
||||
|
||||
/* Ping/Pong Configuration Space */
|
||||
#define MALI_C55_REG_BASE_ADDR 0x18e88
|
||||
#define MALI_C55_REG_BYPASS_0 0x18eac
|
||||
#define MALI_C55_REG_BYPASS_0_VIDEO_TEST BIT(0)
|
||||
#define MALI_C55_REG_BYPASS_0_INPUT_FMT BIT(1)
|
||||
#define MALI_C55_REG_BYPASS_0_DECOMPANDER BIT(2)
|
||||
#define MALI_C55_REG_BYPASS_0_SENSOR_OFFSET_WDR BIT(3)
|
||||
#define MALI_C55_REG_BYPASS_0_GAIN_WDR BIT(4)
|
||||
#define MALI_C55_REG_BYPASS_0_FRAME_STITCH BIT(5)
|
||||
#define MALI_C55_REG_BYPASS_1 0x18eb0
|
||||
#define MALI_C55_REG_BYPASS_1_DIGI_GAIN BIT(0)
|
||||
#define MALI_C55_REG_BYPASS_1_FE_SENSOR_OFFS BIT(1)
|
||||
#define MALI_C55_REG_BYPASS_1_FE_SQRT BIT(2)
|
||||
#define MALI_C55_REG_BYPASS_1_RAW_FE BIT(3)
|
||||
#define MALI_C55_REG_BYPASS_2 0x18eb8
|
||||
#define MALI_C55_REG_BYPASS_2_SINTER BIT(0)
|
||||
#define MALI_C55_REG_BYPASS_2_TEMPER BIT(1)
|
||||
#define MALI_C55_REG_BYPASS_3 0x18ebc
|
||||
#define MALI_C55_REG_BYPASS_3_SQUARE_BE BIT(0)
|
||||
#define MALI_C55_REG_BYPASS_3_SENSOR_OFFSET_PRE_SH BIT(1)
|
||||
#define MALI_C55_REG_BYPASS_3_MESH_SHADING BIT(3)
|
||||
#define MALI_C55_REG_BYPASS_3_WHITE_BALANCE BIT(4)
|
||||
#define MALI_C55_REG_BYPASS_3_IRIDIX BIT(5)
|
||||
#define MALI_C55_REG_BYPASS_3_IRIDIX_GAIN BIT(6)
|
||||
#define MALI_C55_REG_BYPASS_4 0x18ec0
|
||||
#define MALI_C55_REG_BYPASS_4_DEMOSAIC_RGB BIT(1)
|
||||
#define MALI_C55_REG_BYPASS_4_PF_CORRECTION BIT(3)
|
||||
#define MALI_C55_REG_BYPASS_4_CCM BIT(4)
|
||||
#define MALI_C55_REG_BYPASS_4_CNR BIT(5)
|
||||
#define MALI_C55_REG_FR_BYPASS 0x18ec4
|
||||
#define MALI_C55_REG_DS_BYPASS 0x18ec8
|
||||
#define MALI_C55_BYPASS_CROP BIT(0)
|
||||
#define MALI_C55_BYPASS_SCALER BIT(1)
|
||||
#define MALI_C55_BYPASS_GAMMA_RGB BIT(2)
|
||||
#define MALI_C55_BYPASS_SHARPEN BIT(3)
|
||||
#define MALI_C55_BYPASS_CS_CONV BIT(4)
|
||||
#define MALI_C55_REG_ISP_RAW_BYPASS 0x18ecc
|
||||
#define MALI_C55_ISP_RAW_BYPASS_BYPASS_MASK BIT(0)
|
||||
#define MALI_C55_ISP_RAW_BYPASS_FR_BYPASS_MASK GENMASK(9, 8)
|
||||
#define MALI_C55_ISP_RAW_BYPASS_RAW_FR_BYPASS (2 << 8)
|
||||
#define MALI_C55_ISP_RAW_BYPASS_RGB_FR_BYPASS (1 << 8)
|
||||
#define MALI_C55_ISP_RAW_BYPASS_DS_PIPE_DISABLE BIT(1)
|
||||
#define MALI_C55_ISP_RAW_BYPASS_RAW_BYPASS BIT(0)
|
||||
|
||||
#define MALI_C55_REG_ACTIVE_WIDTH_MASK 0xffff
|
||||
#define MALI_C55_REG_ACTIVE_HEIGHT_MASK 0xffff0000
|
||||
#define MALI_C55_REG_BAYER_ORDER 0x18e8c
|
||||
#define MALI_C55_BAYER_ORDER_MASK GENMASK(1, 0)
|
||||
#define MALI_C55_BAYER_ORDER_RGGB 0
|
||||
#define MALI_C55_BAYER_ORDER_GRBG 1
|
||||
#define MALI_C55_BAYER_ORDER_GBRG 2
|
||||
#define MALI_C55_BAYER_ORDER_BGGR 3
|
||||
|
||||
#define MALI_C55_REG_TPG_CH0 0x18ed8
|
||||
#define MALI_C55_TEST_PATTERN_ON_OFF BIT(0)
|
||||
#define MALI_C55_TEST_PATTERN_RGB_MASK BIT(1)
|
||||
#define MALI_C55_TEST_PATTERN_RGB(x) ((x) << 1)
|
||||
#define MALI_C55_REG_TPG_R_BACKGROUND 0x18ee0
|
||||
#define MALI_C55_REG_TPG_G_BACKGROUND 0x18ee4
|
||||
#define MALI_C55_REG_TPG_B_BACKGROUND 0x18ee8
|
||||
#define MALI_C55_TPG_BACKGROUND_MAX 0xfffff
|
||||
#define MALI_C55_REG_INPUT_WIDTH 0x18f98
|
||||
#define MALI_C55_INPUT_WIDTH_MASK GENMASK(18, 16)
|
||||
#define MALI_C55_INPUT_WIDTH_8BIT (0 << 16)
|
||||
#define MALI_C55_INPUT_WIDTH_10BIT (1 << 16)
|
||||
#define MALI_C55_INPUT_WIDTH_12BIT (2 << 16)
|
||||
#define MALI_C55_INPUT_WIDTH_14BIT (3 << 16)
|
||||
#define MALI_C55_INPUT_WIDTH_16BIT (4 << 16)
|
||||
#define MALI_C55_INPUT_WIDTH_20BIT (5 << 16)
|
||||
#define MALI_C55_REG_SPACE_SIZE 0x4000
|
||||
#define MALI_C55_REG_CONFIG_SPACES_OFFSET 0x0ab6c
|
||||
#define MALI_C55_CONFIG_SPACE_SIZE 0x1231c
|
||||
|
||||
#define MALI_C55_REG_SINTER_CONFIG 0x19348
|
||||
#define MALI_C55_SINTER_VIEW_FILTER_MASK GENMASK(1, 0)
|
||||
#define MALI_C55_SINTER_SCALE_MODE_MASK GENMASK(3, 2)
|
||||
#define MALI_C55_SINTER_ENABLE_MASK BIT(4)
|
||||
#define MALI_C55_SINTER_FILTER_SELECT_MASK BIT(5)
|
||||
#define MALI_C55_SINTER_INT_SELECT_MASK BIT(6)
|
||||
#define MALI_C55_SINTER_RM_ENABLE_MASK BIT(7)
|
||||
|
||||
/* Temper DMA */
|
||||
#define MALI_C55_REG_TEMPER_DMA_IO 0x1ab78
|
||||
#define MALI_C55_TEMPER_DMA_WRITE_ON BIT(0)
|
||||
#define MALI_C55_TEMPER_DMA_READ_ON BIT(1)
|
||||
|
||||
/* Colour Correction Matrix Configuration */
|
||||
#define MALI_C55_REG_CCM_ENABLE 0x1b07c
|
||||
#define MALI_C55_CCM_ENABLE_MASK BIT(0)
|
||||
#define MALI_C55_REG_CCM_COEF_R_R 0x1b080
|
||||
#define MALI_C55_REG_CCM_COEF_R_G 0x1b084
|
||||
#define MALI_C55_REG_CCM_COEF_R_B 0x1b088
|
||||
#define MALI_C55_REG_CCM_COEF_G_R 0x1b090
|
||||
#define MALI_C55_REG_CCM_COEF_G_G 0x1b094
|
||||
#define MALI_C55_REG_CCM_COEF_G_B 0x1b098
|
||||
#define MALI_C55_REG_CCM_COEF_B_R 0x1b0a0
|
||||
#define MALI_C55_REG_CCM_COEF_B_G 0x1b0a4
|
||||
#define MALI_C55_REG_CCM_COEF_B_B 0x1b0a8
|
||||
#define MALI_C55_CCM_COEF_MASK GENMASK(12, 0)
|
||||
#define MALI_C55_REG_CCM_ANTIFOG_GAIN_R 0x1b0b0
|
||||
#define MALI_C55_REG_CCM_ANTIFOG_GAIN_G 0x1b0b4
|
||||
#define MALI_C55_REG_CCM_ANTIFOG_GAIN_B 0x1b0b8
|
||||
#define MALI_C55_CCM_ANTIFOG_GAIN_MASK GENMASK(11, 0)
|
||||
#define MALI_C55_REG_CCM_ANTIFOG_OFFSET_R 0x1b0c0
|
||||
#define MALI_C55_REG_CCM_ANTIFOG_OFFSET_G 0x1b0c4
|
||||
#define MALI_C55_REG_CCM_ANTIFOG_OFFSET_B 0x1b0c8
|
||||
#define MALI_C55_CCM_ANTIFOG_OFFSET_MASK GENMASK(11, 0)
|
||||
|
||||
/*
|
||||
* The Mali-C55 ISP has up to two output pipes; known as full resolution and
|
||||
* down scaled. The register space for these is laid out identically, but offset
|
||||
* by 372 bytes.
|
||||
*/
|
||||
#define MALI_C55_CAP_DEV_FR_REG_OFFSET 0x0
|
||||
#define MALI_C55_CAP_DEV_DS_REG_OFFSET 0x174
|
||||
|
||||
#define MALI_C55_REG_CS_CONV_CONFIG 0x1c098
|
||||
#define MALI_C55_CS_CONV_MATRIX_MASK BIT(0)
|
||||
#define MALI_C55_CS_CONV_FILTER_MASK BIT(1)
|
||||
#define MALI_C55_CS_CONV_HORZ_DOWNSAMPLE_MASK BIT(2)
|
||||
#define MALI_C55_CS_CONV_VERT_DOWNSAMPLE_MASK BIT(3)
|
||||
#define MALI_C55_CS_CONV_FILTER_ENABLE (0x01 << 1)
|
||||
#define MALI_C55_CS_CONV_HORZ_DOWNSAMPLE_ENABLE (0x01 << 2)
|
||||
#define MALI_C55_CS_CONV_VERT_DOWNSAMPLE_ENABLE (0x01 << 3)
|
||||
#define MALI_C55_REG_Y_WRITER_MODE 0x1c0ec
|
||||
#define MALI_C55_REG_UV_WRITER_MODE 0x1c144
|
||||
#define MALI_C55_WRITER_MODE_MASK GENMASK(4, 0)
|
||||
#define MALI_C55_OUTPUT_DISABLED 0
|
||||
#define MALI_C55_OUTPUT_RGB32 1
|
||||
#define MALI_C55_OUTPUT_A2R10G10B10 2
|
||||
#define MALI_C55_OUTPUT_RGB565 3
|
||||
#define MALI_C55_OUTPUT_RGB24 4
|
||||
#define MALI_C55_OUTPUT_GEN32 5
|
||||
#define MALI_C55_OUTPUT_RAW16 6
|
||||
#define MALI_C55_OUTPUT_AYUV 8
|
||||
#define MALI_C55_OUTPUT_Y410 9
|
||||
#define MALI_C55_OUTPUT_YUY2 10
|
||||
#define MALI_C55_OUTPUT_UYVY 11
|
||||
#define MALI_C55_OUTPUT_Y210 12
|
||||
#define MALI_C55_OUTPUT_NV12_21 13
|
||||
#define MALI_C55_OUTPUT_YUV_420_422 17
|
||||
#define MALI_C55_OUTPUT_P210_P010 19
|
||||
#define MALI_C55_OUTPUT_YUV422 20
|
||||
#define MALI_C55_WRITER_SUBMODE_MASK GENMASK(7, 6)
|
||||
#define MALI_C55_WRITER_SUBMODE(x) ((x) << 6)
|
||||
#define MALI_C55_OUTPUT_PLANE_ALT0 0
|
||||
#define MALI_C55_OUTPUT_PLANE_ALT1 1
|
||||
#define MALI_C55_OUTPUT_PLANE_ALT2 2
|
||||
#define MALI_C55_WRITER_FRAME_WRITE_MASK BIT(9)
|
||||
#define MALI_C55_WRITER_FRAME_WRITE_ENABLE (0x01 << 9)
|
||||
#define MALI_C55_REG_ACTIVE_OUT_Y_SIZE 0x1c0f0
|
||||
#define MALI_C55_REG_ACTIVE_OUT_UV_SIZE 0x1c148
|
||||
#define MALI_C55_REG_ACTIVE_OUT_SIZE_W(w) ((w) << 0)
|
||||
#define MALI_C55_REG_ACTIVE_OUT_SIZE_H(h) ((h) << 16)
|
||||
#define MALI_C55_REG_Y_WRITER_BANKS_BASE 0x1c0f4
|
||||
#define MALI_C55_REG_Y_WRITER_BANKS_CONFIG 0x1c108
|
||||
#define MALI_C55_REG_Y_WRITER_MAX_BANKS_MASK GENMASK(2, 0)
|
||||
#define MALI_C55_REG_Y_WRITER_BANKS_RESTART BIT(3)
|
||||
#define MALI_C55_REG_Y_WRITER_OFFSET 0x1c10c
|
||||
#define MALI_C55_REG_UV_WRITER_BANKS_BASE 0x1c14c
|
||||
#define MALI_C55_REG_UV_WRITER_BANKS_CONFIG 0x1c160
|
||||
#define MALI_C55_REG_UV_WRITER_MAX_BANKS_MASK GENMASK(2, 0)
|
||||
#define MALI_C55_REG_UV_WRITER_BANKS_RESTART BIT(3)
|
||||
#define MALI_C55_REG_UV_WRITER_OFFSET 0x1c164
|
||||
|
||||
#define MALI_C55_REG_TEST_GEN_CH0_OFF_ON
|
||||
#define MALI_C55_REG_TEST_GEN_CH0_PATTERN_TYPE 0x18edc
|
||||
|
||||
#define MALI_C55_REG_CROP_EN 0x1c028
|
||||
#define MALI_C55_CROP_ENABLE BIT(0)
|
||||
#define MALI_C55_REG_CROP_X_START 0x1c02c
|
||||
#define MALI_C55_REG_CROP_Y_START 0x1c030
|
||||
#define MALI_C55_REG_CROP_X_SIZE 0x1c034
|
||||
#define MALI_C55_REG_CROP_Y_SIZE 0x1c038
|
||||
#define MALI_C55_REG_SCALER_TIMEOUT_EN 0x1c040
|
||||
#define MALI_C55_SCALER_TIMEOUT_EN BIT(4)
|
||||
#define MALI_C55_SCALER_TIMEOUT(t) ((t) << 16)
|
||||
#define MALI_C55_REG_SCALER_IN_WIDTH 0x1c044
|
||||
#define MALI_C55_REG_SCALER_IN_HEIGHT 0x1c048
|
||||
#define MALI_C55_REG_SCALER_OUT_WIDTH 0x1c04c
|
||||
#define MALI_C55_REG_SCALER_OUT_HEIGHT 0x1c050
|
||||
#define MALI_C55_REG_SCALER_HFILT_TINC 0x1c054
|
||||
#define MALI_C55_REG_SCALER_HFILT_COEF 0x1c058
|
||||
#define MALI_C55_REG_SCALER_VFILT_TINC 0x1c05c
|
||||
#define MALI_C55_REG_SCALER_VFILT_COEF 0x1c060
|
||||
|
||||
#define MALI_C55_REG_GAMMA_RGB_ENABLE 0x1c064
|
||||
#define MALI_C55_GAMMA_ENABLE_MASK BIT(0)
|
||||
#define MALI_C55_REG_GAMMA_GAINS_1 0x1c068
|
||||
#define MALI_C55_GAMMA_GAIN_R_MASK GENMASK(11, 0)
|
||||
#define MALI_C55_GAMMA_GAIN_G_MASK GENMASK(27, 16)
|
||||
#define MALI_C55_REG_GAMMA_GAINS_2 0x1c06c
|
||||
#define MALI_C55_GAMMA_GAIN_B_MASK GENMASK(11, 0)
|
||||
#define MALI_C55_REG_GAMMA_OFFSETS_1 0x1c070
|
||||
#define MALI_C55_GAMMA_OFFSET_R_MASK GENMASK(11, 0)
|
||||
#define MALI_C55_GAMMA_OFFSET_G_MASK GENMASK(27, 16)
|
||||
#define MALI_C55_REG_GAMMA_OFFSETS_2 0x1c074
|
||||
#define MALI_C55_GAMMA_OFFSET_B_MASK GENMASK(11, 0)
|
||||
|
||||
/*
|
||||
* A re-definition of an above register. These will usually be written on a per
|
||||
* capture device basis and handled with mali_c55_cap_dev_write(), but on
|
||||
* startup is written by core.c
|
||||
*/
|
||||
#define MALI_C55_REG_FR_GAMMA_RGB_ENABLE 0x1c064
|
||||
#define MALI_C55_REG_DS_GAMMA_RGB_ENABLE 0x1c1d8
|
||||
|
||||
#define MALI_C55_REG_FR_SCALER_HFILT 0x34a8
|
||||
#define MALI_C55_REG_FR_SCALER_VFILT 0x44a8
|
||||
#define MALI_C55_REG_DS_SCALER_HFILT 0x14a8
|
||||
#define MALI_C55_REG_DS_SCALER_VFILT 0x24a8
|
||||
|
||||
#endif /* _MALI_C55_REGISTERS_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,437 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* ARM Mali-C55 ISP Driver - Test pattern generator
|
||||
*
|
||||
* Copyright (C) 2025 Ideas on Board Oy
|
||||
*/
|
||||
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <media/media-entity.h>
|
||||
#include <media/v4l2-ctrls.h>
|
||||
#include <media/v4l2-event.h>
|
||||
#include <media/v4l2-subdev.h>
|
||||
|
||||
#include "mali-c55-common.h"
|
||||
#include "mali-c55-registers.h"
|
||||
|
||||
#define MALI_C55_TPG_SRC_PAD 0
|
||||
#define MALI_C55_TPG_FIXED_HBLANK 0x20
|
||||
#define MALI_C55_TPG_DEFAULT_MIN_VBLANK 66
|
||||
#define MALI_C55_TPG_DEFAULT_DEF_VBLANK 626
|
||||
#define MALI_C55_TPG_MAX_VBLANK 0xffff
|
||||
#define MALI_C55_TPG_PIXEL_RATE 100000000
|
||||
|
||||
static const char * const mali_c55_tpg_test_pattern_menu[] = {
|
||||
"Flat field",
|
||||
"Horizontal gradient",
|
||||
"Vertical gradient",
|
||||
"Vertical bars",
|
||||
"Arbitrary rectangle",
|
||||
"White frame on black field"
|
||||
};
|
||||
|
||||
static const u32 mali_c55_tpg_mbus_codes[] = {
|
||||
MEDIA_BUS_FMT_SRGGB20_1X20,
|
||||
MEDIA_BUS_FMT_RGB202020_1X60,
|
||||
};
|
||||
|
||||
static void mali_c55_tpg_update_vblank(struct mali_c55_tpg *tpg,
|
||||
struct v4l2_mbus_framefmt *format)
|
||||
{
|
||||
unsigned int def_vblank;
|
||||
unsigned int min_vblank;
|
||||
unsigned int hts;
|
||||
int tgt_fps;
|
||||
|
||||
hts = format->width + MALI_C55_TPG_FIXED_HBLANK;
|
||||
|
||||
/*
|
||||
* The ISP has minimum vertical blanking requirements that must be
|
||||
* adhered to by the TPG. The minimum is a function of the Iridix blocks
|
||||
* clocking requirements and the width of the image and horizontal
|
||||
* blanking, but if we assume the worst case iVariance and sVariance
|
||||
* values then it boils down to the below (plus one to the numerator to
|
||||
* ensure the answer is rounded up).
|
||||
*/
|
||||
min_vblank = 15 + (120501 / hts);
|
||||
|
||||
/*
|
||||
* We need to set a sensible default vblank for whatever format height
|
||||
* we happen to be given from set_fmt(). This function just targets
|
||||
* an even multiple of 15fps. If we can't get 15fps, let's target 5fps.
|
||||
* If we can't get 5fps we'll take whatever the minimum vblank gives us.
|
||||
*/
|
||||
tgt_fps = MALI_C55_TPG_PIXEL_RATE / hts / (format->height + min_vblank);
|
||||
|
||||
if (tgt_fps < 5)
|
||||
def_vblank = min_vblank;
|
||||
else
|
||||
def_vblank = (MALI_C55_TPG_PIXEL_RATE / hts
|
||||
/ max(rounddown(tgt_fps, 15), 5)) - format->height;
|
||||
|
||||
def_vblank = ALIGN_DOWN(def_vblank, 2);
|
||||
|
||||
__v4l2_ctrl_modify_range(tpg->ctrls.vblank, min_vblank,
|
||||
MALI_C55_TPG_MAX_VBLANK, 1, def_vblank);
|
||||
__v4l2_ctrl_s_ctrl(tpg->ctrls.vblank, def_vblank);
|
||||
}
|
||||
|
||||
static int mali_c55_tpg_s_ctrl(struct v4l2_ctrl *ctrl)
|
||||
{
|
||||
struct mali_c55_tpg *tpg = container_of(ctrl->handler,
|
||||
struct mali_c55_tpg,
|
||||
ctrls.handler);
|
||||
struct mali_c55 *mali_c55 = container_of(tpg, struct mali_c55, tpg);
|
||||
int ret = 0;
|
||||
|
||||
if (!pm_runtime_get_if_in_use(mali_c55->dev))
|
||||
return 0;
|
||||
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_TEST_PATTERN:
|
||||
mali_c55_ctx_write(mali_c55,
|
||||
MALI_C55_REG_TEST_GEN_CH0_PATTERN_TYPE,
|
||||
ctrl->val);
|
||||
break;
|
||||
case V4L2_CID_VBLANK:
|
||||
mali_c55_update_bits(mali_c55, MALI_C55_REG_BLANKING,
|
||||
MALI_C55_REG_VBLANK_MASK,
|
||||
MALI_C55_VBLANK(ctrl->val));
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
pm_runtime_put_autosuspend(mali_c55->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct v4l2_ctrl_ops mali_c55_tpg_ctrl_ops = {
|
||||
.s_ctrl = &mali_c55_tpg_s_ctrl,
|
||||
};
|
||||
|
||||
static void mali_c55_tpg_configure(struct mali_c55_tpg *tpg,
|
||||
struct v4l2_subdev_state *state)
|
||||
{
|
||||
struct v4l2_mbus_framefmt *fmt;
|
||||
u32 test_pattern_format;
|
||||
|
||||
/*
|
||||
* hblank needs setting, but is a read-only control and thus won't be
|
||||
* called during __v4l2_ctrl_handler_setup(). Do it here instead.
|
||||
*/
|
||||
mali_c55_update_bits(tpg->mali_c55, MALI_C55_REG_BLANKING,
|
||||
MALI_C55_REG_HBLANK_MASK,
|
||||
MALI_C55_TPG_FIXED_HBLANK);
|
||||
mali_c55_update_bits(tpg->mali_c55, MALI_C55_REG_GEN_VIDEO,
|
||||
MALI_C55_REG_GEN_VIDEO_MULTI_MASK,
|
||||
MALI_C55_REG_GEN_VIDEO_MULTI_MASK);
|
||||
|
||||
fmt = v4l2_subdev_state_get_format(state, MALI_C55_TPG_SRC_PAD);
|
||||
|
||||
test_pattern_format = fmt->code == MEDIA_BUS_FMT_RGB202020_1X60 ?
|
||||
0x01 : 0x0;
|
||||
|
||||
mali_c55_ctx_update_bits(tpg->mali_c55, MALI_C55_REG_TPG_CH0,
|
||||
MALI_C55_TEST_PATTERN_RGB_MASK,
|
||||
MALI_C55_TEST_PATTERN_RGB(test_pattern_format));
|
||||
}
|
||||
|
||||
static int mali_c55_tpg_enum_mbus_code(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct v4l2_subdev_mbus_code_enum *code)
|
||||
{
|
||||
if (code->index >= ARRAY_SIZE(mali_c55_tpg_mbus_codes))
|
||||
return -EINVAL;
|
||||
|
||||
code->code = mali_c55_tpg_mbus_codes[code->index];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mali_c55_tpg_enum_frame_size(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct v4l2_subdev_frame_size_enum *fse)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (fse->index > 0)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mali_c55_tpg_mbus_codes); i++) {
|
||||
if (fse->code == mali_c55_tpg_mbus_codes[i])
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(mali_c55_tpg_mbus_codes))
|
||||
return -EINVAL;
|
||||
|
||||
fse->min_width = MALI_C55_MIN_WIDTH;
|
||||
fse->max_width = MALI_C55_MAX_WIDTH;
|
||||
fse->min_height = MALI_C55_MIN_HEIGHT;
|
||||
fse->max_height = MALI_C55_MAX_HEIGHT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mali_c55_tpg_set_fmt(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *state,
|
||||
struct v4l2_subdev_format *format)
|
||||
{
|
||||
struct mali_c55_tpg *tpg = container_of(sd, struct mali_c55_tpg, sd);
|
||||
struct v4l2_mbus_framefmt *fmt;
|
||||
unsigned int i;
|
||||
|
||||
fmt = v4l2_subdev_state_get_format(state, MALI_C55_TPG_SRC_PAD);
|
||||
fmt->code = format->format.code;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mali_c55_tpg_mbus_codes); i++) {
|
||||
if (fmt->code == mali_c55_tpg_mbus_codes[i])
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(mali_c55_tpg_mbus_codes))
|
||||
fmt->code = MEDIA_BUS_FMT_SRGGB20_1X20;
|
||||
|
||||
/*
|
||||
* The TPG says that the test frame timing generation logic expects a
|
||||
* minimum framesize of 4x4 pixels, but given the rest of the ISP can't
|
||||
* handle anything smaller than 128x128 it seems pointless to allow a
|
||||
* smaller frame.
|
||||
*/
|
||||
fmt->width = clamp(format->format.width, MALI_C55_MIN_WIDTH,
|
||||
MALI_C55_MAX_WIDTH);
|
||||
fmt->height = clamp(format->format.height, MALI_C55_MIN_HEIGHT,
|
||||
MALI_C55_MAX_HEIGHT);
|
||||
|
||||
format->format = *fmt;
|
||||
|
||||
if (format->which == V4L2_SUBDEV_FORMAT_TRY)
|
||||
return 0;
|
||||
|
||||
mali_c55_tpg_update_vblank(tpg, fmt);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mali_c55_tpg_enable_streams(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *state, u32 pad,
|
||||
u64 streams_mask)
|
||||
{
|
||||
struct mali_c55_tpg *tpg = container_of(sd, struct mali_c55_tpg, sd);
|
||||
struct mali_c55 *mali_c55 = container_of(tpg, struct mali_c55, tpg);
|
||||
|
||||
/*
|
||||
* We only have a source pad and a single stream, and v4l2-core already
|
||||
* validated both so we don't need to do that. One might reasonably
|
||||
* expect the framesize to be set here given it's configurable in
|
||||
* .set_fmt(), but it's done in the ISP subdevice's .enable_streams()
|
||||
* instead, as the same register is also used to indicate the size of
|
||||
* the data coming from the sensor.
|
||||
*/
|
||||
mali_c55_tpg_configure(tpg, state);
|
||||
__v4l2_ctrl_handler_setup(sd->ctrl_handler);
|
||||
|
||||
mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_TPG_CH0,
|
||||
MALI_C55_TEST_PATTERN_ON_OFF,
|
||||
MALI_C55_TEST_PATTERN_ON_OFF);
|
||||
mali_c55_update_bits(mali_c55, MALI_C55_REG_GEN_VIDEO,
|
||||
MALI_C55_REG_GEN_VIDEO_ON_MASK,
|
||||
MALI_C55_REG_GEN_VIDEO_ON_MASK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mali_c55_tpg_disable_streams(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *state, u32 pad,
|
||||
u64 streams_mask)
|
||||
{
|
||||
struct mali_c55_tpg *tpg = container_of(sd, struct mali_c55_tpg, sd);
|
||||
struct mali_c55 *mali_c55 = container_of(tpg, struct mali_c55, tpg);
|
||||
|
||||
mali_c55_ctx_update_bits(mali_c55, MALI_C55_REG_TPG_CH0,
|
||||
MALI_C55_TEST_PATTERN_ON_OFF, 0x00);
|
||||
mali_c55_update_bits(mali_c55, MALI_C55_REG_GEN_VIDEO,
|
||||
MALI_C55_REG_GEN_VIDEO_ON_MASK, 0x00);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct v4l2_subdev_pad_ops mali_c55_tpg_pad_ops = {
|
||||
.enum_mbus_code = mali_c55_tpg_enum_mbus_code,
|
||||
.enum_frame_size = mali_c55_tpg_enum_frame_size,
|
||||
.get_fmt = v4l2_subdev_get_fmt,
|
||||
.set_fmt = mali_c55_tpg_set_fmt,
|
||||
.enable_streams = mali_c55_tpg_enable_streams,
|
||||
.disable_streams = mali_c55_tpg_disable_streams,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_core_ops mali_c55_isp_core_ops = {
|
||||
.subscribe_event = v4l2_ctrl_subdev_subscribe_event,
|
||||
.unsubscribe_event = v4l2_event_subdev_unsubscribe,
|
||||
};
|
||||
|
||||
static const struct v4l2_subdev_ops mali_c55_tpg_ops = {
|
||||
.core = &mali_c55_isp_core_ops,
|
||||
.pad = &mali_c55_tpg_pad_ops,
|
||||
};
|
||||
|
||||
static int mali_c55_tpg_init_state(struct v4l2_subdev *sd,
|
||||
struct v4l2_subdev_state *state)
|
||||
{
|
||||
struct v4l2_mbus_framefmt *fmt =
|
||||
v4l2_subdev_state_get_format(state, MALI_C55_TPG_SRC_PAD);
|
||||
|
||||
fmt->width = MALI_C55_DEFAULT_WIDTH;
|
||||
fmt->height = MALI_C55_DEFAULT_HEIGHT;
|
||||
fmt->field = V4L2_FIELD_NONE;
|
||||
fmt->code = MEDIA_BUS_FMT_SRGGB20_1X20;
|
||||
fmt->colorspace = V4L2_COLORSPACE_RAW;
|
||||
fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace);
|
||||
fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
|
||||
fmt->quantization = V4L2_MAP_QUANTIZATION_DEFAULT(false,
|
||||
fmt->colorspace,
|
||||
fmt->ycbcr_enc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct v4l2_subdev_internal_ops mali_c55_tpg_internal_ops = {
|
||||
.init_state = mali_c55_tpg_init_state,
|
||||
};
|
||||
|
||||
static int mali_c55_tpg_init_controls(struct mali_c55 *mali_c55)
|
||||
{
|
||||
struct mali_c55_tpg_ctrls *ctrls = &mali_c55->tpg.ctrls;
|
||||
struct v4l2_ctrl *pixel_rate;
|
||||
struct v4l2_ctrl *hblank;
|
||||
int ret;
|
||||
|
||||
ret = v4l2_ctrl_handler_init(&ctrls->handler, 4);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
v4l2_ctrl_new_std_menu_items(&ctrls->handler, &mali_c55_tpg_ctrl_ops,
|
||||
V4L2_CID_TEST_PATTERN,
|
||||
ARRAY_SIZE(mali_c55_tpg_test_pattern_menu) - 1,
|
||||
0, 3, mali_c55_tpg_test_pattern_menu);
|
||||
|
||||
/*
|
||||
* We fix hblank at the minimum allowed value and control framerate
|
||||
* solely through the vblank control.
|
||||
*/
|
||||
hblank = v4l2_ctrl_new_std(&ctrls->handler, &mali_c55_tpg_ctrl_ops,
|
||||
V4L2_CID_HBLANK, MALI_C55_TPG_FIXED_HBLANK,
|
||||
MALI_C55_TPG_FIXED_HBLANK, 1,
|
||||
MALI_C55_TPG_FIXED_HBLANK);
|
||||
if (hblank)
|
||||
hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;
|
||||
|
||||
ctrls->vblank = v4l2_ctrl_new_std(&ctrls->handler,
|
||||
&mali_c55_tpg_ctrl_ops,
|
||||
V4L2_CID_VBLANK,
|
||||
MALI_C55_TPG_DEFAULT_MIN_VBLANK,
|
||||
MALI_C55_TPG_MAX_VBLANK, 1,
|
||||
MALI_C55_TPG_DEFAULT_DEF_VBLANK);
|
||||
|
||||
pixel_rate = v4l2_ctrl_new_std(&ctrls->handler, &mali_c55_tpg_ctrl_ops,
|
||||
V4L2_CID_PIXEL_RATE,
|
||||
MALI_C55_TPG_PIXEL_RATE,
|
||||
MALI_C55_TPG_PIXEL_RATE, 1,
|
||||
MALI_C55_TPG_PIXEL_RATE);
|
||||
if (pixel_rate)
|
||||
pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;
|
||||
|
||||
if (ctrls->handler.error) {
|
||||
dev_err(mali_c55->dev, "Error during v4l2 controls init\n");
|
||||
v4l2_ctrl_handler_free(&ctrls->handler);
|
||||
return ctrls->handler.error;
|
||||
}
|
||||
|
||||
mali_c55->tpg.sd.ctrl_handler = &ctrls->handler;
|
||||
mali_c55->tpg.sd.state_lock = ctrls->handler.lock;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int mali_c55_register_tpg(struct mali_c55 *mali_c55)
|
||||
{
|
||||
struct mali_c55_tpg *tpg = &mali_c55->tpg;
|
||||
struct v4l2_subdev *sd = &tpg->sd;
|
||||
struct media_pad *pad = &tpg->pad;
|
||||
int ret;
|
||||
|
||||
v4l2_subdev_init(sd, &mali_c55_tpg_ops);
|
||||
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS;
|
||||
sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
|
||||
sd->internal_ops = &mali_c55_tpg_internal_ops;
|
||||
strscpy(sd->name, MALI_C55_DRIVER_NAME " tpg", sizeof(sd->name));
|
||||
|
||||
pad->flags = MEDIA_PAD_FL_SOURCE;
|
||||
ret = media_entity_pads_init(&sd->entity, 1, pad);
|
||||
if (ret) {
|
||||
dev_err(mali_c55->dev,
|
||||
"Failed to initialize media entity pads\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mali_c55_tpg_init_controls(mali_c55);
|
||||
if (ret) {
|
||||
dev_err(mali_c55->dev,
|
||||
"Error initialising controls\n");
|
||||
goto err_cleanup_media_entity;
|
||||
}
|
||||
|
||||
ret = v4l2_subdev_init_finalize(sd);
|
||||
if (ret)
|
||||
goto err_free_ctrl_handler;
|
||||
|
||||
ret = v4l2_device_register_subdev(&mali_c55->v4l2_dev, sd);
|
||||
if (ret) {
|
||||
dev_err(mali_c55->dev, "Failed to register tpg subdev\n");
|
||||
goto err_cleanup_subdev;
|
||||
}
|
||||
|
||||
/*
|
||||
* By default the colour settings lead to a very dim image that is
|
||||
* nearly indistinguishable from black on some monitor settings. Ramp
|
||||
* them up a bit so the image is brighter.
|
||||
*/
|
||||
mali_c55_ctx_write(mali_c55, MALI_C55_REG_TPG_R_BACKGROUND,
|
||||
MALI_C55_TPG_BACKGROUND_MAX);
|
||||
mali_c55_ctx_write(mali_c55, MALI_C55_REG_TPG_G_BACKGROUND,
|
||||
MALI_C55_TPG_BACKGROUND_MAX);
|
||||
mali_c55_ctx_write(mali_c55, MALI_C55_REG_TPG_B_BACKGROUND,
|
||||
MALI_C55_TPG_BACKGROUND_MAX);
|
||||
|
||||
tpg->mali_c55 = mali_c55;
|
||||
|
||||
return 0;
|
||||
|
||||
err_cleanup_subdev:
|
||||
v4l2_subdev_cleanup(sd);
|
||||
err_free_ctrl_handler:
|
||||
v4l2_ctrl_handler_free(&tpg->ctrls.handler);
|
||||
err_cleanup_media_entity:
|
||||
media_entity_cleanup(&sd->entity);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void mali_c55_unregister_tpg(struct mali_c55 *mali_c55)
|
||||
{
|
||||
struct mali_c55_tpg *tpg = &mali_c55->tpg;
|
||||
|
||||
if (!tpg->mali_c55)
|
||||
return;
|
||||
|
||||
v4l2_device_unregister_subdev(&tpg->sd);
|
||||
v4l2_ctrl_handler_free(&tpg->ctrls.handler);
|
||||
v4l2_subdev_cleanup(&tpg->sd);
|
||||
media_entity_cleanup(&tpg->sd.entity);
|
||||
}
|
||||
Reference in New Issue
Block a user