summaryrefslogtreecommitdiff
path: root/drivers/media/platform/qcom
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/platform/qcom')
-rw-r--r--drivers/media/platform/qcom/camss-8x16/camss-vfe.h123
-rw-r--r--drivers/media/platform/qcom/camss/Makefile (renamed from drivers/media/platform/qcom/camss-8x16/Makefile)4
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid.c (renamed from drivers/media/platform/qcom/camss-8x16/camss-csid.c)471
-rw-r--r--drivers/media/platform/qcom/camss/camss-csid.h (renamed from drivers/media/platform/qcom/camss-8x16/camss-csid.h)17
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c176
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c256
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy.c (renamed from drivers/media/platform/qcom/camss-8x16/camss-csiphy.c)363
-rw-r--r--drivers/media/platform/qcom/camss/camss-csiphy.h (renamed from drivers/media/platform/qcom/camss-8x16/camss-csiphy.h)37
-rw-r--r--drivers/media/platform/qcom/camss/camss-ispif.c (renamed from drivers/media/platform/qcom/camss-8x16/camss-ispif.c)264
-rw-r--r--drivers/media/platform/qcom/camss/camss-ispif.h (renamed from drivers/media/platform/qcom/camss-8x16/camss-ispif.h)23
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-4-1.c1018
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe-4-7.c1140
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe.c (renamed from drivers/media/platform/qcom/camss-8x16/camss-vfe.c)1569
-rw-r--r--drivers/media/platform/qcom/camss/camss-vfe.h186
-rw-r--r--drivers/media/platform/qcom/camss/camss-video.c (renamed from drivers/media/platform/qcom/camss-8x16/camss-video.c)133
-rw-r--r--drivers/media/platform/qcom/camss/camss-video.h (renamed from drivers/media/platform/qcom/camss-8x16/camss-video.h)12
-rw-r--r--drivers/media/platform/qcom/camss/camss.c (renamed from drivers/media/platform/qcom/camss-8x16/camss.c)450
-rw-r--r--drivers/media/platform/qcom/camss/camss.h (renamed from drivers/media/platform/qcom/camss-8x16/camss.h)43
-rw-r--r--drivers/media/platform/qcom/venus/Makefile3
-rw-r--r--drivers/media/platform/qcom/venus/core.c107
-rw-r--r--drivers/media/platform/qcom/venus/core.h100
-rw-r--r--drivers/media/platform/qcom/venus/helpers.c568
-rw-r--r--drivers/media/platform/qcom/venus/helpers.h23
-rw-r--r--drivers/media/platform/qcom/venus/hfi.c12
-rw-r--r--drivers/media/platform/qcom/venus/hfi.h10
-rw-r--r--drivers/media/platform/qcom/venus/hfi_cmds.c62
-rw-r--r--drivers/media/platform/qcom/venus/hfi_helper.h112
-rw-r--r--drivers/media/platform/qcom/venus/hfi_msgs.c407
-rw-r--r--drivers/media/platform/qcom/venus/hfi_parser.c278
-rw-r--r--drivers/media/platform/qcom/venus/hfi_parser.h110
-rw-r--r--drivers/media/platform/qcom/venus/hfi_venus.c108
-rw-r--r--drivers/media/platform/qcom/venus/hfi_venus_io.h10
-rw-r--r--drivers/media/platform/qcom/venus/vdec.c329
-rw-r--r--drivers/media/platform/qcom/venus/vdec_ctrls.c10
-rw-r--r--drivers/media/platform/qcom/venus/venc.c227
-rw-r--r--drivers/media/platform/qcom/venus/venc_ctrls.c10
36 files changed, 6248 insertions, 2523 deletions
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.h b/drivers/media/platform/qcom/camss-8x16/camss-vfe.h
deleted file mode 100644
index 53d5b66a9dfb..000000000000
--- a/drivers/media/platform/qcom/camss-8x16/camss-vfe.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * camss-vfe.h
- *
- * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module
- *
- * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
- * Copyright (C) 2015-2017 Linaro Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-#ifndef QC_MSM_CAMSS_VFE_H
-#define QC_MSM_CAMSS_VFE_H
-
-#include <linux/clk.h>
-#include <linux/spinlock_types.h>
-#include <media/media-entity.h>
-#include <media/v4l2-device.h>
-#include <media/v4l2-subdev.h>
-
-#include "camss-video.h"
-
-#define MSM_VFE_PAD_SINK 0
-#define MSM_VFE_PAD_SRC 1
-#define MSM_VFE_PADS_NUM 2
-
-#define MSM_VFE_LINE_NUM 4
-#define MSM_VFE_IMAGE_MASTERS_NUM 7
-#define MSM_VFE_COMPOSITE_IRQ_NUM 4
-
-#define MSM_VFE_VFE0_UB_SIZE 1023
-#define MSM_VFE_VFE0_UB_SIZE_RDI (MSM_VFE_VFE0_UB_SIZE / 3)
-#define MSM_VFE_VFE1_UB_SIZE 1535
-#define MSM_VFE_VFE1_UB_SIZE_RDI (MSM_VFE_VFE1_UB_SIZE / 3)
-
-enum vfe_output_state {
- VFE_OUTPUT_OFF,
- VFE_OUTPUT_RESERVED,
- VFE_OUTPUT_SINGLE,
- VFE_OUTPUT_CONTINUOUS,
- VFE_OUTPUT_IDLE,
- VFE_OUTPUT_STOPPING
-};
-
-enum vfe_line_id {
- VFE_LINE_NONE = -1,
- VFE_LINE_RDI0 = 0,
- VFE_LINE_RDI1 = 1,
- VFE_LINE_RDI2 = 2,
- VFE_LINE_PIX = 3
-};
-
-struct vfe_output {
- u8 wm_num;
- u8 wm_idx[3];
-
- int active_buf;
- struct camss_buffer *buf[2];
- struct camss_buffer *last_buffer;
- struct list_head pending_bufs;
-
- unsigned int drop_update_idx;
-
- enum vfe_output_state state;
- unsigned int sequence;
- int wait_sof;
- int wait_reg_update;
- struct completion sof;
- struct completion reg_update;
-};
-
-struct vfe_line {
- enum vfe_line_id id;
- struct v4l2_subdev subdev;
- struct media_pad pads[MSM_VFE_PADS_NUM];
- struct v4l2_mbus_framefmt fmt[MSM_VFE_PADS_NUM];
- struct v4l2_rect compose;
- struct v4l2_rect crop;
- struct camss_video video_out;
- struct vfe_output output;
-};
-
-struct vfe_device {
- u8 id;
- void __iomem *base;
- u32 irq;
- char irq_name[30];
- struct camss_clock *clock;
- int nclocks;
- struct completion reset_complete;
- struct completion halt_complete;
- struct mutex power_lock;
- int power_count;
- struct mutex stream_lock;
- int stream_count;
- spinlock_t output_lock;
- enum vfe_line_id wm_output_map[MSM_VFE_IMAGE_MASTERS_NUM];
- struct vfe_line line[MSM_VFE_LINE_NUM];
- u32 reg_update;
- u8 was_streaming;
-};
-
-struct resources;
-
-int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res);
-
-int msm_vfe_register_entities(struct vfe_device *vfe,
- struct v4l2_device *v4l2_dev);
-
-void msm_vfe_unregister_entities(struct vfe_device *vfe);
-
-void msm_vfe_get_vfe_id(struct media_entity *entity, u8 *id);
-void msm_vfe_get_vfe_line_id(struct media_entity *entity, enum vfe_line_id *id);
-
-void msm_vfe_stop_streaming(struct vfe_device *vfe);
-
-#endif /* QC_MSM_CAMSS_VFE_H */
diff --git a/drivers/media/platform/qcom/camss-8x16/Makefile b/drivers/media/platform/qcom/camss/Makefile
index 3c4024fbb768..f5e6e255f2a1 100644
--- a/drivers/media/platform/qcom/camss-8x16/Makefile
+++ b/drivers/media/platform/qcom/camss/Makefile
@@ -3,8 +3,12 @@
qcom-camss-objs += \
camss.o \
camss-csid.o \
+ camss-csiphy-2ph-1-0.o \
+ camss-csiphy-3ph-1-0.o \
camss-csiphy.o \
camss-ispif.o \
+ camss-vfe-4-1.o \
+ camss-vfe-4-7.o \
camss-vfe.o \
camss-video.o \
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c
index 226f36ef7419..729b31891466 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-csid.c
+++ b/drivers/media/platform/qcom/camss/camss-csid.c
@@ -1,19 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* camss-csid.c
*
* Qualcomm MSM Camera Subsystem - CSID (CSI Decoder) Module
*
* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
- * Copyright (C) 2015-2017 Linaro Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * Copyright (C) 2015-2018 Linaro Ltd.
*/
#include <linux/clk.h>
#include <linux/completion.h>
@@ -21,9 +13,11 @@
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <media/media-entity.h>
#include <media/v4l2-device.h>
+#include <media/v4l2-event.h>
#include <media/v4l2-subdev.h>
#include "camss-csid.h"
@@ -34,21 +28,35 @@
#define CAMSS_CSID_HW_VERSION 0x0
#define CAMSS_CSID_CORE_CTRL_0 0x004
#define CAMSS_CSID_CORE_CTRL_1 0x008
-#define CAMSS_CSID_RST_CMD 0x00c
-#define CAMSS_CSID_CID_LUT_VC_n(n) (0x010 + 0x4 * (n))
-#define CAMSS_CSID_CID_n_CFG(n) (0x020 + 0x4 * (n))
-#define CAMSS_CSID_IRQ_CLEAR_CMD 0x060
-#define CAMSS_CSID_IRQ_MASK 0x064
-#define CAMSS_CSID_IRQ_STATUS 0x068
-#define CAMSS_CSID_TG_CTRL 0x0a0
+#define CAMSS_CSID_RST_CMD(v) ((v) == CAMSS_8x16 ? 0x00c : 0x010)
+#define CAMSS_CSID_CID_LUT_VC_n(v, n) \
+ (((v) == CAMSS_8x16 ? 0x010 : 0x014) + 0x4 * (n))
+#define CAMSS_CSID_CID_n_CFG(v, n) \
+ (((v) == CAMSS_8x16 ? 0x020 : 0x024) + 0x4 * (n))
+#define CAMSS_CSID_CID_n_CFG_ISPIF_EN BIT(0)
+#define CAMSS_CSID_CID_n_CFG_RDI_EN BIT(1)
+#define CAMSS_CSID_CID_n_CFG_DECODE_FORMAT_SHIFT 4
+#define CAMSS_CSID_CID_n_CFG_PLAIN_FORMAT_8 (0 << 8)
+#define CAMSS_CSID_CID_n_CFG_PLAIN_FORMAT_16 (1 << 8)
+#define CAMSS_CSID_CID_n_CFG_PLAIN_ALIGNMENT_LSB (0 << 9)
+#define CAMSS_CSID_CID_n_CFG_PLAIN_ALIGNMENT_MSB (1 << 9)
+#define CAMSS_CSID_CID_n_CFG_RDI_MODE_RAW_DUMP (0 << 10)
+#define CAMSS_CSID_CID_n_CFG_RDI_MODE_PLAIN_PACKING (1 << 10)
+#define CAMSS_CSID_IRQ_CLEAR_CMD(v) ((v) == CAMSS_8x16 ? 0x060 : 0x064)
+#define CAMSS_CSID_IRQ_MASK(v) ((v) == CAMSS_8x16 ? 0x064 : 0x068)
+#define CAMSS_CSID_IRQ_STATUS(v) ((v) == CAMSS_8x16 ? 0x068 : 0x06c)
+#define CAMSS_CSID_TG_CTRL(v) ((v) == CAMSS_8x16 ? 0x0a0 : 0x0a8)
#define CAMSS_CSID_TG_CTRL_DISABLE 0xa06436
#define CAMSS_CSID_TG_CTRL_ENABLE 0xa06437
-#define CAMSS_CSID_TG_VC_CFG 0x0a4
+#define CAMSS_CSID_TG_VC_CFG(v) ((v) == CAMSS_8x16 ? 0x0a4 : 0x0ac)
#define CAMSS_CSID_TG_VC_CFG_H_BLANKING 0x3ff
#define CAMSS_CSID_TG_VC_CFG_V_BLANKING 0x7f
-#define CAMSS_CSID_TG_DT_n_CGG_0(n) (0x0ac + 0xc * (n))
-#define CAMSS_CSID_TG_DT_n_CGG_1(n) (0x0b0 + 0xc * (n))
-#define CAMSS_CSID_TG_DT_n_CGG_2(n) (0x0b4 + 0xc * (n))
+#define CAMSS_CSID_TG_DT_n_CGG_0(v, n) \
+ (((v) == CAMSS_8x16 ? 0x0ac : 0x0b4) + 0xc * (n))
+#define CAMSS_CSID_TG_DT_n_CGG_1(v, n) \
+ (((v) == CAMSS_8x16 ? 0x0b0 : 0x0b8) + 0xc * (n))
+#define CAMSS_CSID_TG_DT_n_CGG_2(v, n) \
+ (((v) == CAMSS_8x16 ? 0x0b4 : 0x0bc) + 0xc * (n))
#define DATA_TYPE_EMBEDDED_DATA_8BIT 0x12
#define DATA_TYPE_YUV422_8BIT 0x1e
@@ -56,15 +64,17 @@
#define DATA_TYPE_RAW_8BIT 0x2a
#define DATA_TYPE_RAW_10BIT 0x2b
#define DATA_TYPE_RAW_12BIT 0x2c
+#define DATA_TYPE_RAW_14BIT 0x2d
#define DECODE_FORMAT_UNCOMPRESSED_6_BIT 0x0
#define DECODE_FORMAT_UNCOMPRESSED_8_BIT 0x1
#define DECODE_FORMAT_UNCOMPRESSED_10_BIT 0x2
#define DECODE_FORMAT_UNCOMPRESSED_12_BIT 0x3
+#define DECODE_FORMAT_UNCOMPRESSED_14_BIT 0x8
#define CSID_RESET_TIMEOUT_MS 500
-struct csid_fmts {
+struct csid_format {
u32 code;
u8 data_type;
u8 decode_format;
@@ -72,7 +82,7 @@ struct csid_fmts {
u8 spp; /* bus samples per pixel */
};
-static const struct csid_fmts csid_input_fmts[] = {
+static const struct csid_format csid_formats_8x16[] = {
{
MEDIA_BUS_FMT_UYVY8_2X8,
DATA_TYPE_YUV422_8BIT,
@@ -184,20 +194,241 @@ static const struct csid_fmts csid_input_fmts[] = {
DECODE_FORMAT_UNCOMPRESSED_12_BIT,
12,
1,
- }
+ },
+ {
+ MEDIA_BUS_FMT_Y10_1X10,
+ DATA_TYPE_RAW_10BIT,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
};
-static const struct csid_fmts *csid_get_fmt_entry(u32 code)
+static const struct csid_format csid_formats_8x96[] = {
+ {
+ MEDIA_BUS_FMT_UYVY8_2X8,
+ DATA_TYPE_YUV422_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 2,
+ },
+ {
+ MEDIA_BUS_FMT_VYUY8_2X8,
+ DATA_TYPE_YUV422_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 2,
+ },
+ {
+ MEDIA_BUS_FMT_YUYV8_2X8,
+ DATA_TYPE_YUV422_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 2,
+ },
+ {
+ MEDIA_BUS_FMT_YVYU8_2X8,
+ DATA_TYPE_YUV422_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 2,
+ },
+ {
+ MEDIA_BUS_FMT_SBGGR8_1X8,
+ DATA_TYPE_RAW_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGBRG8_1X8,
+ DATA_TYPE_RAW_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG8_1X8,
+ DATA_TYPE_RAW_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB8_1X8,
+ DATA_TYPE_RAW_8BIT,
+ DECODE_FORMAT_UNCOMPRESSED_8_BIT,
+ 8,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ DATA_TYPE_RAW_10BIT,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ DATA_TYPE_RAW_10BIT,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ DATA_TYPE_RAW_10BIT,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ DATA_TYPE_RAW_10BIT,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SBGGR12_1X12,
+ DATA_TYPE_RAW_12BIT,
+ DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGBRG12_1X12,
+ DATA_TYPE_RAW_12BIT,
+ DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+ DATA_TYPE_RAW_12BIT,
+ DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB12_1X12,
+ DATA_TYPE_RAW_12BIT,
+ DECODE_FORMAT_UNCOMPRESSED_12_BIT,
+ 12,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SBGGR14_1X14,
+ DATA_TYPE_RAW_14BIT,
+ DECODE_FORMAT_UNCOMPRESSED_14_BIT,
+ 14,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGBRG14_1X14,
+ DATA_TYPE_RAW_14BIT,
+ DECODE_FORMAT_UNCOMPRESSED_14_BIT,
+ 14,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SGRBG14_1X14,
+ DATA_TYPE_RAW_14BIT,
+ DECODE_FORMAT_UNCOMPRESSED_14_BIT,
+ 14,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_SRGGB14_1X14,
+ DATA_TYPE_RAW_14BIT,
+ DECODE_FORMAT_UNCOMPRESSED_14_BIT,
+ 14,
+ 1,
+ },
+ {
+ MEDIA_BUS_FMT_Y10_1X10,
+ DATA_TYPE_RAW_10BIT,
+ DECODE_FORMAT_UNCOMPRESSED_10_BIT,
+ 10,
+ 1,
+ },
+};
+
+static u32 csid_find_code(u32 *code, unsigned int n_code,
+ unsigned int index, u32 req_code)
+{
+ int i;
+
+ if (!req_code && (index >= n_code))
+ return 0;
+
+ for (i = 0; i < n_code; i++)
+ if (req_code) {
+ if (req_code == code[i])
+ return req_code;
+ } else {
+ if (i == index)
+ return code[i];
+ }
+
+ return code[0];
+}
+
+static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code,
+ unsigned int index, u32 src_req_code)
+{
+ if (csid->camss->version == CAMSS_8x16) {
+ if (index > 0)
+ return 0;
+
+ return sink_code;
+ } else if (csid->camss->version == CAMSS_8x96) {
+ switch (sink_code) {
+ case MEDIA_BUS_FMT_SBGGR10_1X10:
+ {
+ u32 src_code[] = {
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE,
+ };
+
+ return csid_find_code(src_code, ARRAY_SIZE(src_code),
+ index, src_req_code);
+ }
+ case MEDIA_BUS_FMT_Y10_1X10:
+ {
+ u32 src_code[] = {
+ MEDIA_BUS_FMT_Y10_1X10,
+ MEDIA_BUS_FMT_Y10_2X8_PADHI_LE,
+ };
+
+ return csid_find_code(src_code, ARRAY_SIZE(src_code),
+ index, src_req_code);
+ }
+ default:
+ if (index > 0)
+ return 0;
+
+ return sink_code;
+ }
+ } else {
+ return 0;
+ }
+}
+
+static const struct csid_format *csid_get_fmt_entry(
+ const struct csid_format *formats,
+ unsigned int nformat,
+ u32 code)
{
unsigned int i;
- for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++)
- if (code == csid_input_fmts[i].code)
- return &csid_input_fmts[i];
+ for (i = 0; i < nformat; i++)
+ if (code == formats[i].code)
+ return &formats[i];
WARN(1, "Unknown format\n");
- return &csid_input_fmts[0];
+ return &formats[0];
}
/*
@@ -210,10 +441,11 @@ static const struct csid_fmts *csid_get_fmt_entry(u32 code)
static irqreturn_t csid_isr(int irq, void *dev)
{
struct csid_device *csid = dev;
+ enum camss_version ver = csid->camss->version;
u32 value;
- value = readl_relaxed(csid->base + CAMSS_CSID_IRQ_STATUS);
- writel_relaxed(value, csid->base + CAMSS_CSID_IRQ_CLEAR_CMD);
+ value = readl_relaxed(csid->base + CAMSS_CSID_IRQ_STATUS(ver));
+ writel_relaxed(value, csid->base + CAMSS_CSID_IRQ_CLEAR_CMD(ver));
if ((value >> 11) & 0x1)
complete(&csid->reset_complete);
@@ -227,7 +459,7 @@ static irqreturn_t csid_isr(int irq, void *dev)
*/
static int csid_set_clock_rates(struct csid_device *csid)
{
- struct device *dev = to_device_index(csid, csid->id);
+ struct device *dev = csid->camss->dev;
u32 pixel_clock;
int i, j;
int ret;
@@ -240,11 +472,16 @@ static int csid_set_clock_rates(struct csid_device *csid)
struct camss_clock *clock = &csid->clock[i];
if (!strcmp(clock->name, "csi0") ||
- !strcmp(clock->name, "csi1")) {
- u8 bpp = csid_get_fmt_entry(
- csid->fmt[MSM_CSIPHY_PAD_SINK].code)->bpp;
+ !strcmp(clock->name, "csi1") ||
+ !strcmp(clock->name, "csi2") ||
+ !strcmp(clock->name, "csi3")) {
+ const struct csid_format *f = csid_get_fmt_entry(
+ csid->formats,
+ csid->nformats,
+ csid->fmt[MSM_CSIPHY_PAD_SINK].code);
u8 num_lanes = csid->phy.lane_cnt;
- u64 min_rate = pixel_clock * bpp / (2 * num_lanes * 4);
+ u64 min_rate = pixel_clock * f->bpp /
+ (2 * num_lanes * 4);
long rate;
camss_add_clock_margin(&min_rate);
@@ -294,13 +531,13 @@ static int csid_reset(struct csid_device *csid)
reinit_completion(&csid->reset_complete);
- writel_relaxed(0x7fff, csid->base + CAMSS_CSID_RST_CMD);
+ writel_relaxed(0x7fff, csid->base +
+ CAMSS_CSID_RST_CMD(csid->camss->version));
time = wait_for_completion_timeout(&csid->reset_complete,
msecs_to_jiffies(CSID_RESET_TIMEOUT_MS));
if (!time) {
- dev_err(to_device_index(csid, csid->id),
- "CSID reset timeout\n");
+ dev_err(csid->camss->dev, "CSID reset timeout\n");
return -EIO;
}
@@ -317,25 +554,33 @@ static int csid_reset(struct csid_device *csid)
static int csid_set_power(struct v4l2_subdev *sd, int on)
{
struct csid_device *csid = v4l2_get_subdevdata(sd);
- struct device *dev = to_device_index(csid, csid->id);
+ struct device *dev = csid->camss->dev;
int ret;
if (on) {
u32 hw_version;
- ret = regulator_enable(csid->vdda);
+ ret = pm_runtime_get_sync(dev);
if (ret < 0)
return ret;
+ ret = regulator_enable(csid->vdda);
+ if (ret < 0) {
+ pm_runtime_put_sync(dev);
+ return ret;
+ }
+
ret = csid_set_clock_rates(csid);
if (ret < 0) {
regulator_disable(csid->vdda);
+ pm_runtime_put_sync(dev);
return ret;
}
ret = camss_enable_clocks(csid->nclocks, csid->clock, dev);
if (ret < 0) {
regulator_disable(csid->vdda);
+ pm_runtime_put_sync(dev);
return ret;
}
@@ -346,6 +591,7 @@ static int csid_set_power(struct v4l2_subdev *sd, int on)
disable_irq(csid->irq);
camss_disable_clocks(csid->nclocks, csid->clock);
regulator_disable(csid->vdda);
+ pm_runtime_put_sync(dev);
return ret;
}
@@ -355,6 +601,7 @@ static int csid_set_power(struct v4l2_subdev *sd, int on)
disable_irq(csid->irq);
camss_disable_clocks(csid->nclocks, csid->clock);
ret = regulator_disable(csid->vdda);
+ pm_runtime_put_sync(dev);
}
return ret;
@@ -373,6 +620,7 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable)
{
struct csid_device *csid = v4l2_get_subdevdata(sd);
struct csid_testgen_config *tg = &csid->testgen;
+ enum camss_version ver = csid->camss->version;
u32 val;
if (enable) {
@@ -383,7 +631,7 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable)
ret = v4l2_ctrl_handler_setup(&csid->ctrls);
if (ret < 0) {
- dev_err(to_device_index(csid, csid->id),
+ dev_err(csid->camss->dev,
"could not sync v4l2 controls: %d\n", ret);
return ret;
}
@@ -392,40 +640,47 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable)
!media_entity_remote_pad(&csid->pads[MSM_CSID_PAD_SINK]))
return -ENOLINK;
- dt = csid_get_fmt_entry(csid->fmt[MSM_CSID_PAD_SRC].code)->
- data_type;
-
if (tg->enabled) {
/* Config Test Generator */
struct v4l2_mbus_framefmt *f =
&csid->fmt[MSM_CSID_PAD_SRC];
- u8 bpp = csid_get_fmt_entry(f->code)->bpp;
- u8 spp = csid_get_fmt_entry(f->code)->spp;
- u32 num_bytes_per_line = f->width * bpp * spp / 8;
+ const struct csid_format *format = csid_get_fmt_entry(
+ csid->formats, csid->nformats, f->code);
+ u32 num_bytes_per_line =
+ f->width * format->bpp * format->spp / 8;
u32 num_lines = f->height;
/* 31:24 V blank, 23:13 H blank, 3:2 num of active DT */
/* 1:0 VC */
val = ((CAMSS_CSID_TG_VC_CFG_V_BLANKING & 0xff) << 24) |
((CAMSS_CSID_TG_VC_CFG_H_BLANKING & 0x7ff) << 13);
- writel_relaxed(val, csid->base + CAMSS_CSID_TG_VC_CFG);
+ writel_relaxed(val, csid->base +
+ CAMSS_CSID_TG_VC_CFG(ver));
/* 28:16 bytes per lines, 12:0 num of lines */
val = ((num_bytes_per_line & 0x1fff) << 16) |
(num_lines & 0x1fff);
writel_relaxed(val, csid->base +
- CAMSS_CSID_TG_DT_n_CGG_0(0));
+ CAMSS_CSID_TG_DT_n_CGG_0(ver, 0));
+
+ dt = format->data_type;
/* 5:0 data type */
val = dt;
writel_relaxed(val, csid->base +
- CAMSS_CSID_TG_DT_n_CGG_1(0));
+ CAMSS_CSID_TG_DT_n_CGG_1(ver, 0));
/* 2:0 output test pattern */
val = tg->payload_mode;
writel_relaxed(val, csid->base +
- CAMSS_CSID_TG_DT_n_CGG_2(0));
+ CAMSS_CSID_TG_DT_n_CGG_2(ver, 0));
+
+ df = format->decode_format;
} else {
+ struct v4l2_mbus_framefmt *f =
+ &csid->fmt[MSM_CSID_PAD_SINK];
+ const struct csid_format *format = csid_get_fmt_entry(
+ csid->formats, csid->nformats, f->code);
struct csid_phy_config *phy = &csid->phy;
val = phy->lane_cnt - 1;
@@ -439,30 +694,54 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable)
writel_relaxed(val,
csid->base + CAMSS_CSID_CORE_CTRL_1);
+
+ dt = format->data_type;
+ df = format->decode_format;
}
/* Config LUT */
dt_shift = (cid % 4) * 8;
- df = csid_get_fmt_entry(csid->fmt[MSM_CSID_PAD_SINK].code)->
- decode_format;
- val = readl_relaxed(csid->base + CAMSS_CSID_CID_LUT_VC_n(vc));
+ val = readl_relaxed(csid->base +
+ CAMSS_CSID_CID_LUT_VC_n(ver, vc));
val &= ~(0xff << dt_shift);
val |= dt << dt_shift;
- writel_relaxed(val, csid->base + CAMSS_CSID_CID_LUT_VC_n(vc));
+ writel_relaxed(val, csid->base +
+ CAMSS_CSID_CID_LUT_VC_n(ver, vc));
+
+ val = CAMSS_CSID_CID_n_CFG_ISPIF_EN;
+ val |= CAMSS_CSID_CID_n_CFG_RDI_EN;
+ val |= df << CAMSS_CSID_CID_n_CFG_DECODE_FORMAT_SHIFT;
+ val |= CAMSS_CSID_CID_n_CFG_RDI_MODE_RAW_DUMP;
+
+ if (csid->camss->version == CAMSS_8x96) {
+ u32 sink_code = csid->fmt[MSM_CSID_PAD_SINK].code;
+ u32 src_code = csid->fmt[MSM_CSID_PAD_SRC].code;
+
+ if ((sink_code == MEDIA_BUS_FMT_SBGGR10_1X10 &&
+ src_code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE) ||
+ (sink_code == MEDIA_BUS_FMT_Y10_1X10 &&
+ src_code == MEDIA_BUS_FMT_Y10_2X8_PADHI_LE)) {
+ val |= CAMSS_CSID_CID_n_CFG_RDI_MODE_PLAIN_PACKING;
+ val |= CAMSS_CSID_CID_n_CFG_PLAIN_FORMAT_16;
+ val |= CAMSS_CSID_CID_n_CFG_PLAIN_ALIGNMENT_LSB;
+ }
+ }
- val = (df << 4) | 0x3;
- writel_relaxed(val, csid->base + CAMSS_CSID_CID_n_CFG(cid));
+ writel_relaxed(val, csid->base +
+ CAMSS_CSID_CID_n_CFG(ver, cid));
if (tg->enabled) {
val = CAMSS_CSID_TG_CTRL_ENABLE;
- writel_relaxed(val, csid->base + CAMSS_CSID_TG_CTRL);
+ writel_relaxed(val, csid->base +
+ CAMSS_CSID_TG_CTRL(ver));
}
} else {
if (tg->enabled) {
val = CAMSS_CSID_TG_CTRL_DISABLE;
- writel_relaxed(val, csid->base + CAMSS_CSID_TG_CTRL);
+ writel_relaxed(val, csid->base +
+ CAMSS_CSID_TG_CTRL(ver));
}
}
@@ -510,12 +789,12 @@ static void csid_try_format(struct csid_device *csid,
case MSM_CSID_PAD_SINK:
/* Set format on sink pad */
- for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++)
- if (fmt->code == csid_input_fmts[i].code)
+ for (i = 0; i < csid->nformats; i++)
+ if (fmt->code == csid->formats[i].code)
break;
/* If not found, use UYVY as default */
- if (i >= ARRAY_SIZE(csid_input_fmts))
+ if (i >= csid->nformats)
fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
fmt->width = clamp_t(u32, fmt->width, 1, 8191);
@@ -528,23 +807,23 @@ static void csid_try_format(struct csid_device *csid,
case MSM_CSID_PAD_SRC:
if (csid->testgen_mode->cur.val == 0) {
- /* Test generator is disabled, keep pad formats */
- /* in sync - set and return a format same as sink pad */
- struct v4l2_mbus_framefmt format;
+ /* Test generator is disabled, */
+ /* keep pad formats in sync */
+ u32 code = fmt->code;
- format = *__csid_get_format(csid, cfg,
- MSM_CSID_PAD_SINK, which);
- *fmt = format;
+ *fmt = *__csid_get_format(csid, cfg,
+ MSM_CSID_PAD_SINK, which);
+ fmt->code = csid_src_pad_code(csid, fmt->code, 0, code);
} else {
- /* Test generator is enabled, set format on source*/
+ /* Test generator is enabled, set format on source */
/* pad to allow test generator usage */
- for (i = 0; i < ARRAY_SIZE(csid_input_fmts); i++)
- if (csid_input_fmts[i].code == fmt->code)
+ for (i = 0; i < csid->nformats; i++)
+ if (csid->formats[i].code == fmt->code)
break;
/* If not found, use UYVY as default */
- if (i >= ARRAY_SIZE(csid_input_fmts))
+ if (i >= csid->nformats)
fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
fmt->width = clamp_t(u32, fmt->width, 1, 8191);
@@ -570,27 +849,29 @@ static int csid_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_mbus_code_enum *code)
{
struct csid_device *csid = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *format;
if (code->pad == MSM_CSID_PAD_SINK) {
- if (code->index >= ARRAY_SIZE(csid_input_fmts))
+ if (code->index >= csid->nformats)
return -EINVAL;
- code->code = csid_input_fmts[code->index].code;
+ code->code = csid->formats[code->index].code;
} else {
if (csid->testgen_mode->cur.val == 0) {
- if (code->index > 0)
- return -EINVAL;
+ struct v4l2_mbus_framefmt *sink_fmt;
- format = __csid_get_format(csid, cfg, MSM_CSID_PAD_SINK,
- code->which);
+ sink_fmt = __csid_get_format(csid, cfg,
+ MSM_CSID_PAD_SINK,
+ code->which);
- code->code = format->code;
+ code->code = csid_src_pad_code(csid, sink_fmt->code,
+ code->index, 0);
+ if (!code->code)
+ return -EINVAL;
} else {
- if (code->index >= ARRAY_SIZE(csid_input_fmts))
+ if (code->index >= csid->nformats)
return -EINVAL;
- code->code = csid_input_fmts[code->index].code;
+ code->code = csid->formats[code->index].code;
}
}
@@ -798,17 +1079,30 @@ static const struct v4l2_ctrl_ops csid_ctrl_ops = {
*
* Return 0 on success or a negative error code otherwise
*/
-int msm_csid_subdev_init(struct csid_device *csid,
+int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid,
const struct resources *res, u8 id)
{
- struct device *dev = to_device_index(csid, id);
+ struct device *dev = camss->dev;
struct platform_device *pdev = to_platform_device(dev);
struct resource *r;
int i, j;
int ret;
+ csid->camss = camss;
csid->id = id;
+ if (camss->version == CAMSS_8x16) {
+ csid->formats = csid_formats_8x16;
+ csid->nformats =
+ ARRAY_SIZE(csid_formats_8x16);
+ } else if (camss->version == CAMSS_8x96) {
+ csid->formats = csid_formats_8x96;
+ csid->nformats =
+ ARRAY_SIZE(csid_formats_8x96);
+ } else {
+ return -EINVAL;
+ }
+
/* Memory */
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]);
@@ -980,6 +1274,8 @@ static int csid_link_setup(struct media_entity *entity,
static const struct v4l2_subdev_core_ops csid_core_ops = {
.s_power = csid_set_power,
+ .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
+ .unsubscribe_event = v4l2_event_subdev_unsubscribe,
};
static const struct v4l2_subdev_video_ops csid_video_ops = {
@@ -1020,12 +1316,13 @@ int msm_csid_register_entity(struct csid_device *csid,
{
struct v4l2_subdev *sd = &csid->subdev;
struct media_pad *pads = csid->pads;
- struct device *dev = to_device_index(csid, csid->id);
+ struct device *dev = csid->camss->dev;
int ret;
v4l2_subdev_init(sd, &csid_v4l2_ops);
sd->internal_ops = &csid_v4l2_internal_ops;
- sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
+ V4L2_SUBDEV_FL_HAS_EVENTS;
snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d",
MSM_CSID_NAME, csid->id);
v4l2_set_subdevdata(sd, csid);
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h
index 8682d3081bc3..1824b3745e10 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-csid.h
+++ b/drivers/media/platform/qcom/camss/camss-csid.h
@@ -1,19 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* camss-csid.h
*
* Qualcomm MSM Camera Subsystem - CSID (CSI Decoder) Module
*
* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
- * Copyright (C) 2015-2017 Linaro Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * Copyright (C) 2015-2018 Linaro Ltd.
*/
#ifndef QC_MSM_CAMSS_CSID_H
#define QC_MSM_CAMSS_CSID_H
@@ -50,6 +42,7 @@ struct csid_phy_config {
};
struct csid_device {
+ struct camss *camss;
u8 id;
struct v4l2_subdev subdev;
struct media_pad pads[MSM_CSID_PADS_NUM];
@@ -65,11 +58,13 @@ struct csid_device {
struct v4l2_mbus_framefmt fmt[MSM_CSID_PADS_NUM];
struct v4l2_ctrl_handler ctrls;
struct v4l2_ctrl *testgen_mode;
+ const struct csid_format *formats;
+ unsigned int nformats;
};
struct resources;
-int msm_csid_subdev_init(struct csid_device *csid,
+int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid,
const struct resources *res, u8 id);
int msm_csid_register_entity(struct csid_device *csid,
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c b/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c
new file mode 100644
index 000000000000..c832539397d7
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * camss-csiphy-2ph-1-0.c
+ *
+ * Qualcomm MSM Camera Subsystem - CSIPHY Module 2phase v1.0
+ *
+ * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2016-2018 Linaro Ltd.
+ */
+
+#include "camss-csiphy.h"
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#define CAMSS_CSI_PHY_LNn_CFG2(n) (0x004 + 0x40 * (n))
+#define CAMSS_CSI_PHY_LNn_CFG3(n) (0x008 + 0x40 * (n))
+#define CAMSS_CSI_PHY_GLBL_RESET 0x140
+#define CAMSS_CSI_PHY_GLBL_PWR_CFG 0x144
+#define CAMSS_CSI_PHY_GLBL_IRQ_CMD 0x164
+#define CAMSS_CSI_PHY_HW_VERSION 0x188
+#define CAMSS_CSI_PHY_INTERRUPT_STATUSn(n) (0x18c + 0x4 * (n))
+#define CAMSS_CSI_PHY_INTERRUPT_MASKn(n) (0x1ac + 0x4 * (n))
+#define CAMSS_CSI_PHY_INTERRUPT_CLEARn(n) (0x1cc + 0x4 * (n))
+#define CAMSS_CSI_PHY_GLBL_T_INIT_CFG0 0x1ec
+#define CAMSS_CSI_PHY_T_WAKEUP_CFG0 0x1f4
+
+static void csiphy_hw_version_read(struct csiphy_device *csiphy,
+ struct device *dev)
+{
+ u8 hw_version = readl_relaxed(csiphy->base +
+ CAMSS_CSI_PHY_HW_VERSION);
+
+ dev_dbg(dev, "CSIPHY HW Version = 0x%02x\n", hw_version);
+}
+
+/*
+ * csiphy_reset - Perform software reset on CSIPHY module
+ * @csiphy: CSIPHY device
+ */
+static void csiphy_reset(struct csiphy_device *csiphy)
+{
+ writel_relaxed(0x1, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET);
+ usleep_range(5000, 8000);
+ writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET);
+}
+
+/*
+ * csiphy_settle_cnt_calc - Calculate settle count value
+ *
+ * Helper function to calculate settle count value. This is
+ * based on the CSI2 T_hs_settle parameter which in turn
+ * is calculated based on the CSI2 transmitter pixel clock
+ * frequency.
+ *
+ * Return settle count value or 0 if the CSI2 pixel clock
+ * frequency is not available
+ */
+static u8 csiphy_settle_cnt_calc(u32 pixel_clock, u8 bpp, u8 num_lanes,
+ u32 timer_clk_rate)
+{
+ u32 mipi_clock; /* Hz */
+ u32 ui; /* ps */
+ u32 timer_period; /* ps */
+ u32 t_hs_prepare_max; /* ps */
+ u32 t_hs_prepare_zero_min; /* ps */
+ u32 t_hs_settle; /* ps */
+ u8 settle_cnt;
+
+ mipi_clock = pixel_clock * bpp / (2 * num_lanes);
+ ui = div_u64(1000000000000LL, mipi_clock);
+ ui /= 2;
+ t_hs_prepare_max = 85000 + 6 * ui;
+ t_hs_prepare_zero_min = 145000 + 10 * ui;
+ t_hs_settle = (t_hs_prepare_max + t_hs_prepare_zero_min) / 2;
+
+ timer_period = div_u64(1000000000000LL, timer_clk_rate);
+ settle_cnt = t_hs_settle / timer_period - 1;
+
+ return settle_cnt;
+}
+
+static void csiphy_lanes_enable(struct csiphy_device *csiphy,
+ struct csiphy_config *cfg,
+ u32 pixel_clock, u8 bpp, u8 lane_mask)
+{
+ struct csiphy_lanes_cfg *c = &cfg->csi2->lane_cfg;
+ u8 settle_cnt;
+ u8 val, l = 0;
+ int i = 0;
+
+ settle_cnt = csiphy_settle_cnt_calc(pixel_clock, bpp, c->num_data,
+ csiphy->timer_clk_rate);
+
+ writel_relaxed(0x1, csiphy->base +
+ CAMSS_CSI_PHY_GLBL_T_INIT_CFG0);
+ writel_relaxed(0x1, csiphy->base +
+ CAMSS_CSI_PHY_T_WAKEUP_CFG0);
+
+ val = 0x1;
+ val |= lane_mask << 1;
+ writel_relaxed(val, csiphy->base + CAMSS_CSI_PHY_GLBL_PWR_CFG);
+
+ val = cfg->combo_mode << 4;
+ writel_relaxed(val, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET);
+
+ for (i = 0; i <= c->num_data; i++) {
+ if (i == c->num_data)
+ l = c->clk.pos;
+ else
+ l = c->data[i].pos;
+
+ writel_relaxed(0x10, csiphy->base +
+ CAMSS_CSI_PHY_LNn_CFG2(l));
+ writel_relaxed(settle_cnt, csiphy->base +
+ CAMSS_CSI_PHY_LNn_CFG3(l));
+ writel_relaxed(0x3f, csiphy->base +
+ CAMSS_CSI_PHY_INTERRUPT_MASKn(l));
+ writel_relaxed(0x3f, csiphy->base +
+ CAMSS_CSI_PHY_INTERRUPT_CLEARn(l));
+ }
+}
+
+static void csiphy_lanes_disable(struct csiphy_device *csiphy,
+ struct csiphy_config *cfg)
+{
+ struct csiphy_lanes_cfg *c = &cfg->csi2->lane_cfg;
+ u8 l = 0;
+ int i = 0;
+
+ for (i = 0; i <= c->num_data; i++) {
+ if (i == c->num_data)
+ l = c->clk.pos;
+ else
+ l = c->data[i].pos;
+
+ writel_relaxed(0x0, csiphy->base +
+ CAMSS_CSI_PHY_LNn_CFG2(l));
+ }
+
+ writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_PWR_CFG);
+}
+
+/*
+ * csiphy_isr - CSIPHY module interrupt handler
+ * @irq: Interrupt line
+ * @dev: CSIPHY device
+ *
+ * Return IRQ_HANDLED on success
+ */
+static irqreturn_t csiphy_isr(int irq, void *dev)
+{
+ struct csiphy_device *csiphy = dev;
+ u8 i;
+
+ for (i = 0; i < 8; i++) {
+ u8 val = readl_relaxed(csiphy->base +
+ CAMSS_CSI_PHY_INTERRUPT_STATUSn(i));
+ writel_relaxed(val, csiphy->base +
+ CAMSS_CSI_PHY_INTERRUPT_CLEARn(i));
+ writel_relaxed(0x1, csiphy->base + CAMSS_CSI_PHY_GLBL_IRQ_CMD);
+ writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_IRQ_CMD);
+ writel_relaxed(0x0, csiphy->base +
+ CAMSS_CSI_PHY_INTERRUPT_CLEARn(i));
+ }
+
+ return IRQ_HANDLED;
+}
+
+const struct csiphy_hw_ops csiphy_ops_2ph_1_0 = {
+ .hw_version_read = csiphy_hw_version_read,
+ .reset = csiphy_reset,
+ .lanes_enable = csiphy_lanes_enable,
+ .lanes_disable = csiphy_lanes_disable,
+ .isr = csiphy_isr,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c
new file mode 100644
index 000000000000..bcd0dfd33618
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c
@@ -0,0 +1,256 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * camss-csiphy-3ph-1-0.c
+ *
+ * Qualcomm MSM Camera Subsystem - CSIPHY Module 3phase v1.0
+ *
+ * Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2016-2018 Linaro Ltd.
+ */
+
+#include "camss-csiphy.h"
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#define CSIPHY_3PH_LNn_CFG1(n) (0x000 + 0x100 * (n))
+#define CSIPHY_3PH_LNn_CFG1_SWI_REC_DLY_PRG (BIT(7) | BIT(6))
+#define CSIPHY_3PH_LNn_CFG2(n) (0x004 + 0x100 * (n))
+#define CSIPHY_3PH_LNn_CFG2_LP_REC_EN_INT BIT(3)
+#define CSIPHY_3PH_LNn_CFG3(n) (0x008 + 0x100 * (n))
+#define CSIPHY_3PH_LNn_CFG4(n) (0x00c + 0x100 * (n))
+#define CSIPHY_3PH_LNn_CFG4_T_HS_CLK_MISS 0xa4
+#define CSIPHY_3PH_LNn_CFG5(n) (0x010 + 0x100 * (n))
+#define CSIPHY_3PH_LNn_CFG5_T_HS_DTERM 0x02
+#define CSIPHY_3PH_LNn_CFG5_HS_REC_EQ_FQ_INT 0x50
+#define CSIPHY_3PH_LNn_TEST_IMP(n) (0x01c + 0x100 * (n))
+#define CSIPHY_3PH_LNn_TEST_IMP_HS_TERM_IMP 0xa
+#define CSIPHY_3PH_LNn_MISC1(n) (0x028 + 0x100 * (n))
+#define CSIPHY_3PH_LNn_MISC1_IS_CLKLANE BIT(2)
+#define CSIPHY_3PH_LNn_CFG6(n) (0x02c + 0x100 * (n))
+#define CSIPHY_3PH_LNn_CFG6_SWI_FORCE_INIT_EXIT BIT(0)
+#define CSIPHY_3PH_LNn_CFG7(n) (0x030 + 0x100 * (n))
+#define CSIPHY_3PH_LNn_CFG7_SWI_T_INIT 0x2
+#define CSIPHY_3PH_LNn_CFG8(n) (0x034 + 0x100 * (n))
+#define CSIPHY_3PH_LNn_CFG8_SWI_SKIP_WAKEUP BIT(0)
+#define CSIPHY_3PH_LNn_CFG8_SKEW_FILTER_ENABLE BIT(1)
+#define CSIPHY_3PH_LNn_CFG9(n) (0x038 + 0x100 * (n))
+#define CSIPHY_3PH_LNn_CFG9_SWI_T_WAKEUP 0x1
+#define CSIPHY_3PH_LNn_CSI_LANE_CTRL15(n) (0x03c + 0x100 * (n))
+#define CSIPHY_3PH_LNn_CSI_LANE_CTRL15_SWI_SOT_SYMBOL 0xb8
+
+#define CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(n) (0x800 + 0x4 * (n))
+#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_COMMON_PWRDN_B BIT(0)
+#define CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_SHOW_REV_ID BIT(1)
+#define CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(n) (0x8b0 + 0x4 * (n))
+
+static void csiphy_hw_version_read(struct csiphy_device *csiphy,
+ struct device *dev)
+{
+ u32 hw_version;
+
+ writel(CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_SHOW_REV_ID,
+ csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(6));
+
+ hw_version = readl_relaxed(csiphy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(12));
+ hw_version |= readl_relaxed(csiphy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(13)) << 8;
+ hw_version |= readl_relaxed(csiphy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(14)) << 16;
+ hw_version |= readl_relaxed(csiphy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(15)) << 24;
+
+ dev_err(dev, "CSIPHY 3PH HW Version = 0x%08x\n", hw_version);
+}
+
+/*
+ * csiphy_reset - Perform software reset on CSIPHY module
+ * @csiphy: CSIPHY device
+ */
+static void csiphy_reset(struct csiphy_device *csiphy)
+{
+ writel_relaxed(0x1, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(0));
+ usleep_range(5000, 8000);
+ writel_relaxed(0x0, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(0));
+}
+
+static irqreturn_t csiphy_isr(int irq, void *dev)
+{
+ struct csiphy_device *csiphy = dev;
+ int i;
+
+ for (i = 0; i < 11; i++) {
+ int c = i + 22;
+ u8 val = readl_relaxed(csiphy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(i));
+
+ writel_relaxed(val, csiphy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(c));
+ }
+
+ writel_relaxed(0x1, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(10));
+ writel_relaxed(0x0, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(10));
+
+ for (i = 22; i < 33; i++)
+ writel_relaxed(0x0, csiphy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(i));
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * csiphy_settle_cnt_calc - Calculate settle count value
+ *
+ * Helper function to calculate settle count value. This is
+ * based on the CSI2 T_hs_settle parameter which in turn
+ * is calculated based on the CSI2 transmitter pixel clock
+ * frequency.
+ *
+ * Return settle count value or 0 if the CSI2 pixel clock
+ * frequency is not available
+ */
+static u8 csiphy_settle_cnt_calc(u32 pixel_clock, u8 bpp, u8 num_lanes,
+ u32 timer_clk_rate)
+{
+ u32 mipi_clock; /* Hz */
+ u32 ui; /* ps */
+ u32 timer_period; /* ps */
+ u32 t_hs_prepare_max; /* ps */
+ u32 t_hs_settle; /* ps */
+ u8 settle_cnt;
+
+ mipi_clock = pixel_clock * bpp / (2 * num_lanes);
+ ui = div_u64(1000000000000LL, mipi_clock);
+ ui /= 2;
+ t_hs_prepare_max = 85000 + 6 * ui;
+ t_hs_settle = t_hs_prepare_max;
+
+ timer_period = div_u64(1000000000000LL, timer_clk_rate);
+ settle_cnt = t_hs_settle / timer_period - 6;
+
+ return settle_cnt;
+}
+
+static void csiphy_lanes_enable(struct csiphy_device *csiphy,
+ struct csiphy_config *cfg,
+ u32 pixel_clock, u8 bpp, u8 lane_mask)
+{
+ struct csiphy_lanes_cfg *c = &cfg->csi2->lane_cfg;
+ u8 settle_cnt;
+ u8 val, l = 0;
+ int i;
+
+ settle_cnt = csiphy_settle_cnt_calc(pixel_clock, bpp, c->num_data,
+ csiphy->timer_clk_rate);
+
+ val = BIT(c->clk.pos);
+ for (i = 0; i < c->num_data; i++)
+ val |= BIT(c->data[i].pos * 2);
+
+ writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(5));
+
+ val = CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_COMMON_PWRDN_B;
+ writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(6));
+
+ for (i = 0; i <= c->num_data; i++) {
+ if (i == c->num_data)
+ l = 7;
+ else
+ l = c->data[i].pos * 2;
+
+ val = CSIPHY_3PH_LNn_CFG1_SWI_REC_DLY_PRG;
+ val |= 0x17;
+ writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG1(l));
+
+ val = CSIPHY_3PH_LNn_CFG2_LP_REC_EN_INT;
+ writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG2(l));
+
+ val = settle_cnt;
+ writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG3(l));
+
+ val = CSIPHY_3PH_LNn_CFG5_T_HS_DTERM |
+ CSIPHY_3PH_LNn_CFG5_HS_REC_EQ_FQ_INT;
+ writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG5(l));
+
+ val = CSIPHY_3PH_LNn_CFG6_SWI_FORCE_INIT_EXIT;
+ writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG6(l));
+
+ val = CSIPHY_3PH_LNn_CFG7_SWI_T_INIT;
+ writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG7(l));
+
+ val = CSIPHY_3PH_LNn_CFG8_SWI_SKIP_WAKEUP |
+ CSIPHY_3PH_LNn_CFG8_SKEW_FILTER_ENABLE;
+ writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG8(l));
+
+ val = CSIPHY_3PH_LNn_CFG9_SWI_T_WAKEUP;
+ writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG9(l));
+
+ val = CSIPHY_3PH_LNn_TEST_IMP_HS_TERM_IMP;
+ writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_TEST_IMP(l));
+
+ val = CSIPHY_3PH_LNn_CSI_LANE_CTRL15_SWI_SOT_SYMBOL;
+ writel_relaxed(val, csiphy->base +
+ CSIPHY_3PH_LNn_CSI_LANE_CTRL15(l));
+ }
+
+ val = CSIPHY_3PH_LNn_CFG1_SWI_REC_DLY_PRG;
+ writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG1(l));
+
+ val = CSIPHY_3PH_LNn_CFG4_T_HS_CLK_MISS;
+ writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_CFG4(l));
+
+ val = CSIPHY_3PH_LNn_MISC1_IS_CLKLANE;
+ writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_MISC1(l));
+
+ val = 0xff;
+ writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(11));
+
+ val = 0xff;
+ writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(12));
+
+ val = 0xfb;
+ writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(13));
+
+ val = 0xff;
+ writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(14));
+
+ val = 0x7f;
+ writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(15));
+
+ val = 0xff;
+ writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(16));
+
+ val = 0xff;
+ writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(17));
+
+ val = 0xef;
+ writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(18));
+
+ val = 0xff;
+ writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(19));
+
+ val = 0xff;
+ writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(20));
+
+ val = 0xff;
+ writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(21));
+}
+
+static void csiphy_lanes_disable(struct csiphy_device *csiphy,
+ struct csiphy_config *cfg)
+{
+ writel_relaxed(0, csiphy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(5));
+
+ writel_relaxed(0, csiphy->base +
+ CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(6));
+}
+
+const struct csiphy_hw_ops csiphy_ops_3ph_1_0 = {
+ .hw_version_read = csiphy_hw_version_read,
+ .reset = csiphy_reset,
+ .lanes_enable = csiphy_lanes_enable,
+ .lanes_disable = csiphy_lanes_disable,
+ .isr = csiphy_isr,
+};
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c
index 7e61caba6a2d..4559f3b1b38c 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-csiphy.c
+++ b/drivers/media/platform/qcom/camss/camss-csiphy.c
@@ -1,19 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* camss-csiphy.c
*
* Qualcomm MSM Camera Subsystem - CSIPHY Module
*
* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
- * Copyright (C) 2016-2017 Linaro Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * Copyright (C) 2016-2018 Linaro Ltd.
*/
#include <linux/clk.h>
#include <linux/delay.h>
@@ -21,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <media/media-entity.h>
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
@@ -30,131 +23,75 @@
#define MSM_CSIPHY_NAME "msm_csiphy"
-#define CAMSS_CSI_PHY_LNn_CFG2(n) (0x004 + 0x40 * (n))
-#define CAMSS_CSI_PHY_LNn_CFG3(n) (0x008 + 0x40 * (n))
-#define CAMSS_CSI_PHY_GLBL_RESET 0x140
-#define CAMSS_CSI_PHY_GLBL_PWR_CFG 0x144
-#define CAMSS_CSI_PHY_GLBL_IRQ_CMD 0x164
-#define CAMSS_CSI_PHY_HW_VERSION 0x188
-#define CAMSS_CSI_PHY_INTERRUPT_STATUSn(n) (0x18c + 0x4 * (n))
-#define CAMSS_CSI_PHY_INTERRUPT_MASKn(n) (0x1ac + 0x4 * (n))
-#define CAMSS_CSI_PHY_INTERRUPT_CLEARn(n) (0x1cc + 0x4 * (n))
-#define CAMSS_CSI_PHY_GLBL_T_INIT_CFG0 0x1ec
-#define CAMSS_CSI_PHY_T_WAKEUP_CFG0 0x1f4
-
-static const struct {
+struct csiphy_format {
u32 code;
u8 bpp;
-} csiphy_formats[] = {
- {
- MEDIA_BUS_FMT_UYVY8_2X8,
- 8,
- },
- {
- MEDIA_BUS_FMT_VYUY8_2X8,
- 8,
- },
- {
- MEDIA_BUS_FMT_YUYV8_2X8,
- 8,
- },
- {
- MEDIA_BUS_FMT_YVYU8_2X8,
- 8,
- },
- {
- MEDIA_BUS_FMT_SBGGR8_1X8,
- 8,
- },
- {
- MEDIA_BUS_FMT_SGBRG8_1X8,
- 8,
- },
- {
- MEDIA_BUS_FMT_SGRBG8_1X8,
- 8,
- },
- {
- MEDIA_BUS_FMT_SRGGB8_1X8,
- 8,
- },
- {
- MEDIA_BUS_FMT_SBGGR10_1X10,
- 10,
- },
- {
- MEDIA_BUS_FMT_SGBRG10_1X10,
- 10,
- },
- {
- MEDIA_BUS_FMT_SGRBG10_1X10,
- 10,
- },
- {
- MEDIA_BUS_FMT_SRGGB10_1X10,
- 10,
- },
- {
- MEDIA_BUS_FMT_SBGGR12_1X12,
- 12,
- },
- {
- MEDIA_BUS_FMT_SGBRG12_1X12,
- 12,
- },
- {
- MEDIA_BUS_FMT_SGRBG12_1X12,
- 12,
- },
- {
- MEDIA_BUS_FMT_SRGGB12_1X12,
- 12,
- }
+};
+
+static const struct csiphy_format csiphy_formats_8x16[] = {
+ { MEDIA_BUS_FMT_UYVY8_2X8, 8 },
+ { MEDIA_BUS_FMT_VYUY8_2X8, 8 },
+ { MEDIA_BUS_FMT_YUYV8_2X8, 8 },
+ { MEDIA_BUS_FMT_YVYU8_2X8, 8 },
+ { MEDIA_BUS_FMT_SBGGR8_1X8, 8 },
+ { MEDIA_BUS_FMT_SGBRG8_1X8, 8 },
+ { MEDIA_BUS_FMT_SGRBG8_1X8, 8 },
+ { MEDIA_BUS_FMT_SRGGB8_1X8, 8 },
+ { MEDIA_BUS_FMT_SBGGR10_1X10, 10 },
+ { MEDIA_BUS_FMT_SGBRG10_1X10, 10 },
+ { MEDIA_BUS_FMT_SGRBG10_1X10, 10 },
+ { MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
+ { MEDIA_BUS_FMT_SBGGR12_1X12, 12 },
+ { MEDIA_BUS_FMT_SGBRG12_1X12, 12 },
+ { MEDIA_BUS_FMT_SGRBG12_1X12, 12 },
+ { MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
+ { MEDIA_BUS_FMT_Y10_1X10, 10 },
+};
+
+static const struct csiphy_format csiphy_formats_8x96[] = {
+ { MEDIA_BUS_FMT_UYVY8_2X8, 8 },
+ { MEDIA_BUS_FMT_VYUY8_2X8, 8 },
+ { MEDIA_BUS_FMT_YUYV8_2X8, 8 },
+ { MEDIA_BUS_FMT_YVYU8_2X8, 8 },
+ { MEDIA_BUS_FMT_SBGGR8_1X8, 8 },
+ { MEDIA_BUS_FMT_SGBRG8_1X8, 8 },
+ { MEDIA_BUS_FMT_SGRBG8_1X8, 8 },
+ { MEDIA_BUS_FMT_SRGGB8_1X8, 8 },
+ { MEDIA_BUS_FMT_SBGGR10_1X10, 10 },
+ { MEDIA_BUS_FMT_SGBRG10_1X10, 10 },
+ { MEDIA_BUS_FMT_SGRBG10_1X10, 10 },
+ { MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
+ { MEDIA_BUS_FMT_SBGGR12_1X12, 12 },
+ { MEDIA_BUS_FMT_SGBRG12_1X12, 12 },
+ { MEDIA_BUS_FMT_SGRBG12_1X12, 12 },
+ { MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
+ { MEDIA_BUS_FMT_SBGGR14_1X14, 14 },
+ { MEDIA_BUS_FMT_SGBRG14_1X14, 14 },
+ { MEDIA_BUS_FMT_SGRBG14_1X14, 14 },
+ { MEDIA_BUS_FMT_SRGGB14_1X14, 14 },
+ { MEDIA_BUS_FMT_Y10_1X10, 10 },
};
/*
* csiphy_get_bpp - map media bus format to bits per pixel
+ * @formats: supported media bus formats array
+ * @nformats: size of @formats array
* @code: media bus format code
*
* Return number of bits per pixel
*/
-static u8 csiphy_get_bpp(u32 code)
+static u8 csiphy_get_bpp(const struct csiphy_format *formats,
+ unsigned int nformats, u32 code)
{
unsigned int i;
- for (i = 0; i < ARRAY_SIZE(csiphy_formats); i++)
- if (code == csiphy_formats[i].code)
- return csiphy_formats[i].bpp;
+ for (i = 0; i < nformats; i++)
+ if (code == formats[i].code)
+ return formats[i].bpp;
WARN(1, "Unknown format\n");
- return csiphy_formats[0].bpp;
-}
-
-/*
- * csiphy_isr - CSIPHY module interrupt handler
- * @irq: Interrupt line
- * @dev: CSIPHY device
- *
- * Return IRQ_HANDLED on success
- */
-static irqreturn_t csiphy_isr(int irq, void *dev)
-{
- struct csiphy_device *csiphy = dev;
- u8 i;
-
- for (i = 0; i < 8; i++) {
- u8 val = readl_relaxed(csiphy->base +
- CAMSS_CSI_PHY_INTERRUPT_STATUSn(i));
- writel_relaxed(val, csiphy->base +
- CAMSS_CSI_PHY_INTERRUPT_CLEARn(i));
- writel_relaxed(0x1, csiphy->base + CAMSS_CSI_PHY_GLBL_IRQ_CMD);
- writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_IRQ_CMD);
- writel_relaxed(0x0, csiphy->base +
- CAMSS_CSI_PHY_INTERRUPT_CLEARn(i));
- }
-
- return IRQ_HANDLED;
+ return formats[0].bpp;
}
/*
@@ -163,7 +100,7 @@ static irqreturn_t csiphy_isr(int irq, void *dev)
*/
static int csiphy_set_clock_rates(struct csiphy_device *csiphy)
{
- struct device *dev = to_device_index(csiphy, csiphy->id);
+ struct device *dev = csiphy->camss->dev;
u32 pixel_clock;
int i, j;
int ret;
@@ -176,8 +113,10 @@ static int csiphy_set_clock_rates(struct csiphy_device *csiphy)
struct camss_clock *clock = &csiphy->clock[i];
if (!strcmp(clock->name, "csiphy0_timer") ||
- !strcmp(clock->name, "csiphy1_timer")) {
- u8 bpp = csiphy_get_bpp(
+ !strcmp(clock->name, "csiphy1_timer") ||
+ !strcmp(clock->name, "csiphy2_timer")) {
+ u8 bpp = csiphy_get_bpp(csiphy->formats,
+ csiphy->nformats,
csiphy->fmt[MSM_CSIPHY_PAD_SINK].code);
u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data;
u64 min_rate = pixel_clock * bpp / (2 * num_lanes * 4);
@@ -221,17 +160,6 @@ static int csiphy_set_clock_rates(struct csiphy_device *csiphy)
}
/*
- * csiphy_reset - Perform software reset on CSIPHY module
- * @csiphy: CSIPHY device
- */
-static void csiphy_reset(struct csiphy_device *csiphy)
-{
- writel_relaxed(0x1, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET);
- usleep_range(5000, 8000);
- writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET);
-}
-
-/*
* csiphy_set_power - Power on/off CSIPHY module
* @sd: CSIPHY V4L2 subdevice
* @on: Requested power state
@@ -241,31 +169,38 @@ static void csiphy_reset(struct csiphy_device *csiphy)
static int csiphy_set_power(struct v4l2_subdev *sd, int on)
{
struct csiphy_device *csiphy = v4l2_get_subdevdata(sd);
- struct device *dev = to_device_index(csiphy, csiphy->id);
+ struct device *dev = csiphy->camss->dev;
if (on) {
- u8 hw_version;
int ret;
- ret = csiphy_set_clock_rates(csiphy);
+ ret = pm_runtime_get_sync(dev);
if (ret < 0)
return ret;
+ ret = csiphy_set_clock_rates(csiphy);
+ if (ret < 0) {
+ pm_runtime_put_sync(dev);
+ return ret;
+ }
+
ret = camss_enable_clocks(csiphy->nclocks, csiphy->clock, dev);
- if (ret < 0)
+ if (ret < 0) {
+ pm_runtime_put_sync(dev);
return ret;
+ }
enable_irq(csiphy->irq);
- csiphy_reset(csiphy);
+ csiphy->ops->reset(csiphy);
- hw_version = readl_relaxed(csiphy->base +
- CAMSS_CSI_PHY_HW_VERSION);
- dev_dbg(dev, "CSIPHY HW Version = 0x%02x\n", hw_version);
+ csiphy->ops->hw_version_read(csiphy, dev);
} else {
disable_irq(csiphy->irq);
camss_disable_clocks(csiphy->nclocks, csiphy->clock);
+
+ pm_runtime_put_sync(dev);
}
return 0;
@@ -291,58 +226,6 @@ static u8 csiphy_get_lane_mask(struct csiphy_lanes_cfg *lane_cfg)
}
/*
- * csiphy_settle_cnt_calc - Calculate settle count value
- * @csiphy: CSIPHY device
- *
- * Helper function to calculate settle count value. This is
- * based on the CSI2 T_hs_settle parameter which in turn
- * is calculated based on the CSI2 transmitter pixel clock
- * frequency.
- *
- * Return settle count value or 0 if the CSI2 pixel clock
- * frequency is not available
- */
-static u8 csiphy_settle_cnt_calc(struct csiphy_device *csiphy)
-{
- u8 bpp = csiphy_get_bpp(
- csiphy->fmt[MSM_CSIPHY_PAD_SINK].code);
- u8 num_lanes = csiphy->cfg.csi2->lane_cfg.num_data;
- u32 pixel_clock; /* Hz */
- u32 mipi_clock; /* Hz */
- u32 ui; /* ps */
- u32 timer_period; /* ps */
- u32 t_hs_prepare_max; /* ps */
- u32 t_hs_prepare_zero_min; /* ps */
- u32 t_hs_settle; /* ps */
- u8 settle_cnt;
- int ret;
-
- ret = camss_get_pixel_clock(&csiphy->subdev.entity, &pixel_clock);
- if (ret) {
- dev_err(to_device_index(csiphy, csiphy->id),
- "Cannot get CSI2 transmitter's pixel clock\n");
- return 0;
- }
- if (!pixel_clock) {
- dev_err(to_device_index(csiphy, csiphy->id),
- "Got pixel clock == 0, cannot continue\n");
- return 0;
- }
-
- mipi_clock = pixel_clock * bpp / (2 * num_lanes);
- ui = div_u64(1000000000000LL, mipi_clock);
- ui /= 2;
- t_hs_prepare_max = 85000 + 6 * ui;
- t_hs_prepare_zero_min = 145000 + 10 * ui;
- t_hs_settle = (t_hs_prepare_max + t_hs_prepare_zero_min) / 2;
-
- timer_period = div_u64(1000000000000LL, csiphy->timer_clk_rate);
- settle_cnt = t_hs_settle / timer_period;
-
- return settle_cnt;
-}
-
-/*
* csiphy_stream_on - Enable streaming on CSIPHY module
* @csiphy: CSIPHY device
*
@@ -354,14 +237,24 @@ static u8 csiphy_settle_cnt_calc(struct csiphy_device *csiphy)
static int csiphy_stream_on(struct csiphy_device *csiphy)
{
struct csiphy_config *cfg = &csiphy->cfg;
+ u32 pixel_clock;
u8 lane_mask = csiphy_get_lane_mask(&cfg->csi2->lane_cfg);
- u8 settle_cnt;
+ u8 bpp = csiphy_get_bpp(csiphy->formats, csiphy->nformats,
+ csiphy->fmt[MSM_CSIPHY_PAD_SINK].code);
u8 val;
- int i = 0;
+ int ret;
- settle_cnt = csiphy_settle_cnt_calc(csiphy);
- if (!settle_cnt)
+ ret = camss_get_pixel_clock(&csiphy->subdev.entity, &pixel_clock);
+ if (ret) {
+ dev_err(csiphy->camss->dev,
+ "Cannot get CSI2 transmitter's pixel clock\n");
return -EINVAL;
+ }
+ if (!pixel_clock) {
+ dev_err(csiphy->camss->dev,
+ "Got pixel clock == 0, cannot continue\n");
+ return -EINVAL;
+ }
val = readl_relaxed(csiphy->base_clk_mux);
if (cfg->combo_mode && (lane_mask & 0x18) == 0x18) {
@@ -372,34 +265,9 @@ static int csiphy_stream_on(struct csiphy_device *csiphy)
val |= cfg->csid_id;
}
writel_relaxed(val, csiphy->base_clk_mux);
+ wmb();
- writel_relaxed(0x1, csiphy->base +
- CAMSS_CSI_PHY_GLBL_T_INIT_CFG0);
- writel_relaxed(0x1, csiphy->base +
- CAMSS_CSI_PHY_T_WAKEUP_CFG0);
-
- val = 0x1;
- val |= lane_mask << 1;
- writel_relaxed(val, csiphy->base + CAMSS_CSI_PHY_GLBL_PWR_CFG);
-
- val = cfg->combo_mode << 4;
- writel_relaxed(val, csiphy->base + CAMSS_CSI_PHY_GLBL_RESET);
-
- while (lane_mask) {
- if (lane_mask & 0x1) {
- writel_relaxed(0x10, csiphy->base +
- CAMSS_CSI_PHY_LNn_CFG2(i));
- writel_relaxed(settle_cnt, csiphy->base +
- CAMSS_CSI_PHY_LNn_CFG3(i));
- writel_relaxed(0x3f, csiphy->base +
- CAMSS_CSI_PHY_INTERRUPT_MASKn(i));
- writel_relaxed(0x3f, csiphy->base +
- CAMSS_CSI_PHY_INTERRUPT_CLEARn(i));
- }
-
- lane_mask >>= 1;
- i++;
- }
+ csiphy->ops->lanes_enable(csiphy, cfg, pixel_clock, bpp, lane_mask);
return 0;
}
@@ -412,19 +280,7 @@ static int csiphy_stream_on(struct csiphy_device *csiphy)
*/
static void csiphy_stream_off(struct csiphy_device *csiphy)
{
- u8 lane_mask = csiphy_get_lane_mask(&csiphy->cfg.csi2->lane_cfg);
- int i = 0;
-
- while (lane_mask) {
- if (lane_mask & 0x1)
- writel_relaxed(0x0, csiphy->base +
- CAMSS_CSI_PHY_LNn_CFG2(i));
-
- lane_mask >>= 1;
- i++;
- }
-
- writel_relaxed(0x0, csiphy->base + CAMSS_CSI_PHY_GLBL_PWR_CFG);
+ csiphy->ops->lanes_disable(csiphy, &csiphy->cfg);
}
@@ -489,12 +345,12 @@ static void csiphy_try_format(struct csiphy_device *csiphy,
case MSM_CSIPHY_PAD_SINK:
/* Set format on sink pad */
- for (i = 0; i < ARRAY_SIZE(csiphy_formats); i++)
- if (fmt->code == csiphy_formats[i].code)
+ for (i = 0; i < csiphy->nformats; i++)
+ if (fmt->code == csiphy->formats[i].code)
break;
/* If not found, use UYVY as default */
- if (i >= ARRAY_SIZE(csiphy_formats))
+ if (i >= csiphy->nformats)
fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
fmt->width = clamp_t(u32, fmt->width, 1, 8191);
@@ -530,10 +386,10 @@ static int csiphy_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *format;
if (code->pad == MSM_CSIPHY_PAD_SINK) {
- if (code->index >= ARRAY_SIZE(csiphy_formats))
+ if (code->index >= csiphy->nformats)
return -EINVAL;
- code->code = csiphy_formats[code->index].code;
+ code->code = csiphy->formats[code->index].code;
} else {
if (code->index > 0)
return -EINVAL;
@@ -677,18 +533,32 @@ static int csiphy_init_formats(struct v4l2_subdev *sd,
*
* Return 0 on success or a negative error code otherwise
*/
-int msm_csiphy_subdev_init(struct csiphy_device *csiphy,
+int msm_csiphy_subdev_init(struct camss *camss,
+ struct csiphy_device *csiphy,
const struct resources *res, u8 id)
{
- struct device *dev = to_device_index(csiphy, id);
+ struct device *dev = camss->dev;
struct platform_device *pdev = to_platform_device(dev);
struct resource *r;
int i, j;
int ret;
+ csiphy->camss = camss;
csiphy->id = id;
csiphy->cfg.combo_mode = 0;
+ if (camss->version == CAMSS_8x16) {
+ csiphy->ops = &csiphy_ops_2ph_1_0;
+ csiphy->formats = csiphy_formats_8x16;
+ csiphy->nformats = ARRAY_SIZE(csiphy_formats_8x16);
+ } else if (camss->version == CAMSS_8x96) {
+ csiphy->ops = &csiphy_ops_3ph_1_0;
+ csiphy->formats = csiphy_formats_8x96;
+ csiphy->nformats = ARRAY_SIZE(csiphy_formats_8x96);
+ } else {
+ return -EINVAL;
+ }
+
/* Memory */
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]);
@@ -717,7 +587,8 @@ int msm_csiphy_subdev_init(struct csiphy_device *csiphy,
csiphy->irq = r->start;
snprintf(csiphy->irq_name, sizeof(csiphy->irq_name), "%s_%s%d",
dev_name(dev), MSM_CSIPHY_NAME, csiphy->id);
- ret = devm_request_irq(dev, csiphy->irq, csiphy_isr,
+
+ ret = devm_request_irq(dev, csiphy->irq, csiphy->ops->isr,
IRQF_TRIGGER_RISING, csiphy->irq_name, csiphy);
if (ret < 0) {
dev_err(dev, "request_irq failed: %d\n", ret);
@@ -846,7 +717,7 @@ int msm_csiphy_register_entity(struct csiphy_device *csiphy,
{
struct v4l2_subdev *sd = &csiphy->subdev;
struct media_pad *pads = csiphy->pads;
- struct device *dev = to_device_index(csiphy, csiphy->id);
+ struct device *dev = csiphy->camss->dev;
int ret;
v4l2_subdev_init(sd, &csiphy_v4l2_ops);
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h
index ba8781122065..376f865ad383 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-csiphy.h
+++ b/drivers/media/platform/qcom/camss/camss-csiphy.h
@@ -1,24 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* camss-csiphy.h
*
* Qualcomm MSM Camera Subsystem - CSIPHY Module
*
* Copyright (c) 2011-2015, The Linux Foundation. All rights reserved.
- * Copyright (C) 2016-2017 Linaro Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * Copyright (C) 2016-2018 Linaro Ltd.
*/
#ifndef QC_MSM_CAMSS_CSIPHY_H
#define QC_MSM_CAMSS_CSIPHY_H
#include <linux/clk.h>
+#include <linux/interrupt.h>
#include <media/media-entity.h>
#include <media/v4l2-device.h>
#include <media/v4l2-mediabus.h>
@@ -49,7 +42,22 @@ struct csiphy_config {
struct csiphy_csi2_cfg *csi2;
};
+struct csiphy_device;
+
+struct csiphy_hw_ops {
+ void (*hw_version_read)(struct csiphy_device *csiphy,
+ struct device *dev);
+ void (*reset)(struct csiphy_device *csiphy);
+ void (*lanes_enable)(struct csiphy_device *csiphy,
+ struct csiphy_config *cfg,
+ u32 pixel_clock, u8 bpp, u8 lane_mask);
+ void (*lanes_disable)(struct csiphy_device *csiphy,
+ struct csiphy_config *cfg);
+ irqreturn_t (*isr)(int irq, void *dev);
+};
+
struct csiphy_device {
+ struct camss *camss;
u8 id;
struct v4l2_subdev subdev;
struct media_pad pads[MSM_CSIPHY_PADS_NUM];
@@ -62,11 +70,15 @@ struct csiphy_device {
u32 timer_clk_rate;
struct csiphy_config cfg;
struct v4l2_mbus_framefmt fmt[MSM_CSIPHY_PADS_NUM];
+ const struct csiphy_hw_ops *ops;
+ const struct csiphy_format *formats;
+ unsigned int nformats;
};
struct resources;
-int msm_csiphy_subdev_init(struct csiphy_device *csiphy,
+int msm_csiphy_subdev_init(struct camss *camss,
+ struct csiphy_device *csiphy,
const struct resources *res, u8 id);
int msm_csiphy_register_entity(struct csiphy_device *csiphy,
@@ -74,4 +86,7 @@ int msm_csiphy_register_entity(struct csiphy_device *csiphy,
void msm_csiphy_unregister_entity(struct csiphy_device *csiphy);
+extern const struct csiphy_hw_ops csiphy_ops_2ph_1_0;
+extern const struct csiphy_hw_ops csiphy_ops_3ph_1_0;
+
#endif /* QC_MSM_CAMSS_CSIPHY_H */
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c
index 9d1af9353c1d..7f269021d08c 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-ispif.c
+++ b/drivers/media/platform/qcom/camss/camss-ispif.c
@@ -1,19 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* camss-ispif.c
*
* Qualcomm MSM Camera Subsystem - ISPIF (ISP Interface) Module
*
* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
- * Copyright (C) 2015-2017 Linaro Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * Copyright (C) 2015-2018 Linaro Ltd.
*/
#include <linux/clk.h>
#include <linux/completion.h>
@@ -22,6 +14,7 @@
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <media/media-entity.h>
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
@@ -31,12 +24,6 @@
#define MSM_ISPIF_NAME "msm_ispif"
-#define ispif_line_array(ptr_line) \
- ((const struct ispif_line (*)[]) &(ptr_line[-(ptr_line->id)]))
-
-#define to_ispif(ptr_line) \
- container_of(ispif_line_array(ptr_line), struct ispif_device, ptr_line)
-
#define ISPIF_RST_CMD_0 0x008
#define ISPIF_RST_CMD_0_STROBED_RST_EN (1 << 0)
#define ISPIF_RST_CMD_0_MISC_LOGIC_RST (1 << 1)
@@ -89,6 +76,13 @@
(0x254 + 0x200 * (m) + 0x4 * (n))
#define ISPIF_VFE_m_RDI_INTF_n_CID_MASK(m, n) \
(0x264 + 0x200 * (m) + 0x4 * (n))
+/* PACK_CFG registers are 8x96 only */
+#define ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_0(m, n) \
+ (0x270 + 0x200 * (m) + 0x4 * (n))
+#define ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_1(m, n) \
+ (0x27c + 0x200 * (m) + 0x4 * (n))
+#define ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_0_CID_c_PLAIN(c) \
+ (1 << ((cid % 8) * 4))
#define ISPIF_VFE_m_PIX_INTF_n_STATUS(m, n) \
(0x2c0 + 0x200 * (m) + 0x4 * (n))
#define ISPIF_VFE_m_RDI_INTF_n_STATUS(m, n) \
@@ -109,7 +103,7 @@ enum ispif_intf_cmd {
CMD_ALL_NO_CHANGE = 0xffffffff,
};
-static const u32 ispif_formats[] = {
+static const u32 ispif_formats_8x16[] = {
MEDIA_BUS_FMT_UYVY8_2X8,
MEDIA_BUS_FMT_VYUY8_2X8,
MEDIA_BUS_FMT_YUYV8_2X8,
@@ -126,16 +120,107 @@ static const u32 ispif_formats[] = {
MEDIA_BUS_FMT_SGBRG12_1X12,
MEDIA_BUS_FMT_SGRBG12_1X12,
MEDIA_BUS_FMT_SRGGB12_1X12,
+ MEDIA_BUS_FMT_Y10_1X10,
+};
+
+static const u32 ispif_formats_8x96[] = {
+ MEDIA_BUS_FMT_UYVY8_2X8,
+ MEDIA_BUS_FMT_VYUY8_2X8,
+ MEDIA_BUS_FMT_YUYV8_2X8,
+ MEDIA_BUS_FMT_YVYU8_2X8,
+ MEDIA_BUS_FMT_SBGGR8_1X8,
+ MEDIA_BUS_FMT_SGBRG8_1X8,
+ MEDIA_BUS_FMT_SGRBG8_1X8,
+ MEDIA_BUS_FMT_SRGGB8_1X8,
+ MEDIA_BUS_FMT_SBGGR10_1X10,
+ MEDIA_BUS_FMT_SGBRG10_1X10,
+ MEDIA_BUS_FMT_SGRBG10_1X10,
+ MEDIA_BUS_FMT_SRGGB10_1X10,
+ MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE,
+ MEDIA_BUS_FMT_SBGGR12_1X12,
+ MEDIA_BUS_FMT_SGBRG12_1X12,
+ MEDIA_BUS_FMT_SGRBG12_1X12,
+ MEDIA_BUS_FMT_SRGGB12_1X12,
+ MEDIA_BUS_FMT_SBGGR14_1X14,
+ MEDIA_BUS_FMT_SGBRG14_1X14,
+ MEDIA_BUS_FMT_SGRBG14_1X14,
+ MEDIA_BUS_FMT_SRGGB14_1X14,
+ MEDIA_BUS_FMT_Y10_1X10,
+ MEDIA_BUS_FMT_Y10_2X8_PADHI_LE,
};
/*
- * ispif_isr - ISPIF module interrupt handler
+ * ispif_isr_8x96 - ISPIF module interrupt handler for 8x96
* @irq: Interrupt line
* @dev: ISPIF device
*
* Return IRQ_HANDLED on success
*/
-static irqreturn_t ispif_isr(int irq, void *dev)
+static irqreturn_t ispif_isr_8x96(int irq, void *dev)
+{
+ struct ispif_device *ispif = dev;
+ u32 value0, value1, value2, value3, value4, value5;
+
+ value0 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_0(0));
+ value1 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_1(0));
+ value2 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_2(0));
+ value3 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_0(1));
+ value4 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_1(1));
+ value5 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_2(1));
+
+ writel_relaxed(value0, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_0(0));
+ writel_relaxed(value1, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_1(0));
+ writel_relaxed(value2, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_2(0));
+ writel_relaxed(value3, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_0(1));
+ writel_relaxed(value4, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_1(1));
+ writel_relaxed(value5, ispif->base + ISPIF_VFE_m_IRQ_CLEAR_2(1));
+
+ writel(0x1, ispif->base + ISPIF_IRQ_GLOBAL_CLEAR_CMD);
+
+ if ((value0 >> 27) & 0x1)
+ complete(&ispif->reset_complete);
+
+ if (unlikely(value0 & ISPIF_VFE_m_IRQ_STATUS_0_PIX0_OVERFLOW))
+ dev_err_ratelimited(to_device(ispif), "VFE0 pix0 overflow\n");
+
+ if (unlikely(value0 & ISPIF_VFE_m_IRQ_STATUS_0_RDI0_OVERFLOW))
+ dev_err_ratelimited(to_device(ispif), "VFE0 rdi0 overflow\n");
+
+ if (unlikely(value1 & ISPIF_VFE_m_IRQ_STATUS_1_PIX1_OVERFLOW))
+ dev_err_ratelimited(to_device(ispif), "VFE0 pix1 overflow\n");
+
+ if (unlikely(value1 & ISPIF_VFE_m_IRQ_STATUS_1_RDI1_OVERFLOW))
+ dev_err_ratelimited(to_device(ispif), "VFE0 rdi1 overflow\n");
+
+ if (unlikely(value2 & ISPIF_VFE_m_IRQ_STATUS_2_RDI2_OVERFLOW))
+ dev_err_ratelimited(to_device(ispif), "VFE0 rdi2 overflow\n");
+
+ if (unlikely(value3 & ISPIF_VFE_m_IRQ_STATUS_0_PIX0_OVERFLOW))
+ dev_err_ratelimited(to_device(ispif), "VFE1 pix0 overflow\n");
+
+ if (unlikely(value3 & ISPIF_VFE_m_IRQ_STATUS_0_RDI0_OVERFLOW))
+ dev_err_ratelimited(to_device(ispif), "VFE1 rdi0 overflow\n");
+
+ if (unlikely(value4 & ISPIF_VFE_m_IRQ_STATUS_1_PIX1_OVERFLOW))
+ dev_err_ratelimited(to_device(ispif), "VFE1 pix1 overflow\n");
+
+ if (unlikely(value4 & ISPIF_VFE_m_IRQ_STATUS_1_RDI1_OVERFLOW))
+ dev_err_ratelimited(to_device(ispif), "VFE1 rdi1 overflow\n");
+
+ if (unlikely(value5 & ISPIF_VFE_m_IRQ_STATUS_2_RDI2_OVERFLOW))
+ dev_err_ratelimited(to_device(ispif), "VFE1 rdi2 overflow\n");
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * ispif_isr_8x16 - ISPIF module interrupt handler for 8x16
+ * @irq: Interrupt line
+ * @dev: ISPIF device
+ *
+ * Return IRQ_HANDLED on success
+ */
+static irqreturn_t ispif_isr_8x16(int irq, void *dev)
{
struct ispif_device *ispif = dev;
u32 value0, value1, value2;
@@ -183,6 +268,14 @@ static int ispif_reset(struct ispif_device *ispif)
u32 val;
int ret;
+ ret = camss_pm_domain_on(to_camss(ispif), PM_DOMAIN_VFE0);
+ if (ret < 0)
+ return ret;
+
+ ret = camss_pm_domain_on(to_camss(ispif), PM_DOMAIN_VFE1);
+ if (ret < 0)
+ return ret;
+
ret = camss_enable_clocks(ispif->nclocks_for_reset,
ispif->clock_for_reset,
to_device(ispif));
@@ -215,12 +308,15 @@ static int ispif_reset(struct ispif_device *ispif)
msecs_to_jiffies(ISPIF_RESET_TIMEOUT_MS));
if (!time) {
dev_err(to_device(ispif), "ISPIF reset timeout\n");
- return -EIO;
+ ret = -EIO;
}
camss_disable_clocks(ispif->nclocks_for_reset, ispif->clock_for_reset);
- return 0;
+ camss_pm_domain_off(to_camss(ispif), PM_DOMAIN_VFE0);
+ camss_pm_domain_off(to_camss(ispif), PM_DOMAIN_VFE1);
+
+ return ret;
}
/*
@@ -233,7 +329,7 @@ static int ispif_reset(struct ispif_device *ispif)
static int ispif_set_power(struct v4l2_subdev *sd, int on)
{
struct ispif_line *line = v4l2_get_subdevdata(sd);
- struct ispif_device *ispif = to_ispif(line);
+ struct ispif_device *ispif = line->ispif;
struct device *dev = to_device(ispif);
int ret = 0;
@@ -246,12 +342,19 @@ static int ispif_set_power(struct v4l2_subdev *sd, int on)
goto exit;
}
- ret = camss_enable_clocks(ispif->nclocks, ispif->clock, dev);
+ ret = pm_runtime_get_sync(dev);
if (ret < 0)
goto exit;
+ ret = camss_enable_clocks(ispif->nclocks, ispif->clock, dev);
+ if (ret < 0) {
+ pm_runtime_put_sync(dev);
+ goto exit;
+ }
+
ret = ispif_reset(ispif);
if (ret < 0) {
+ pm_runtime_put_sync(dev);
camss_disable_clocks(ispif->nclocks, ispif->clock);
goto exit;
}
@@ -266,6 +369,7 @@ static int ispif_set_power(struct v4l2_subdev *sd, int on)
goto exit;
} else if (ispif->power_count == 1) {
camss_disable_clocks(ispif->nclocks, ispif->clock);
+ pm_runtime_put_sync(dev);
}
ispif->power_count--;
@@ -578,6 +682,55 @@ static void ispif_config_irq(struct ispif_device *ispif, enum ispif_intf intf,
}
/*
+ * ispif_config_pack - Config packing for PRDI mode
+ * @ispif: ISPIF device
+ * @code: media bus format code
+ * @intf: VFE interface
+ * @cid: desired CID to handle
+ * @vfe: VFE HW module id
+ * @enable: enable or disable
+ */
+static void ispif_config_pack(struct ispif_device *ispif, u32 code,
+ enum ispif_intf intf, u8 cid, u8 vfe, u8 enable)
+{
+ u32 addr, val;
+
+ if (code != MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE &&
+ code != MEDIA_BUS_FMT_Y10_2X8_PADHI_LE)
+ return;
+
+ switch (intf) {
+ case RDI0:
+ if (cid < 8)
+ addr = ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_0(vfe, 0);
+ else
+ addr = ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_1(vfe, 0);
+ break;
+ case RDI1:
+ if (cid < 8)
+ addr = ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_0(vfe, 1);
+ else
+ addr = ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_1(vfe, 1);
+ break;
+ case RDI2:
+ if (cid < 8)
+ addr = ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_0(vfe, 2);
+ else
+ addr = ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_1(vfe, 2);
+ break;
+ default:
+ return;
+ }
+
+ if (enable)
+ val = ISPIF_VFE_m_RDI_INTF_n_PACK_CFG_0_CID_c_PLAIN(cid);
+ else
+ val = 0;
+
+ writel_relaxed(val, ispif->base + addr);
+}
+
+/*
* ispif_set_intf_cmd - Set command to enable/disable interface
* @ispif: ISPIF device
* @cmd: interface command
@@ -619,7 +772,7 @@ static void ispif_set_intf_cmd(struct ispif_device *ispif, u8 cmd,
static int ispif_set_stream(struct v4l2_subdev *sd, int enable)
{
struct ispif_line *line = v4l2_get_subdevdata(sd);
- struct ispif_device *ispif = to_ispif(line);
+ struct ispif_device *ispif = line->ispif;
enum ispif_intf intf = line->interface;
u8 csid = line->csid_id;
u8 vfe = line->vfe_id;
@@ -645,6 +798,10 @@ static int ispif_set_stream(struct v4l2_subdev *sd, int enable)
ispif_select_csid(ispif, intf, csid, vfe, 1);
ispif_select_cid(ispif, intf, cid, vfe, 1);
ispif_config_irq(ispif, intf, vfe, 1);
+ if (to_camss(ispif)->version == CAMSS_8x96)
+ ispif_config_pack(ispif,
+ line->fmt[MSM_ISPIF_PAD_SINK].code,
+ intf, cid, vfe, 1);
ispif_set_intf_cmd(ispif, CMD_ENABLE_FRAME_BOUNDARY,
intf, vfe, vc);
} else {
@@ -658,6 +815,10 @@ static int ispif_set_stream(struct v4l2_subdev *sd, int enable)
return ret;
mutex_lock(&ispif->config_lock);
+ if (to_camss(ispif)->version == CAMSS_8x96)
+ ispif_config_pack(ispif,
+ line->fmt[MSM_ISPIF_PAD_SINK].code,
+ intf, cid, vfe, 0);
ispif_config_irq(ispif, intf, vfe, 0);
ispif_select_cid(ispif, intf, cid, vfe, 0);
ispif_select_csid(ispif, intf, csid, vfe, 0);
@@ -710,12 +871,12 @@ static void ispif_try_format(struct ispif_line *line,
case MSM_ISPIF_PAD_SINK:
/* Set format on sink pad */
- for (i = 0; i < ARRAY_SIZE(ispif_formats); i++)
- if (fmt->code == ispif_formats[i])
+ for (i = 0; i < line->nformats; i++)
+ if (fmt->code == line->formats[i])
break;
/* If not found, use UYVY as default */
- if (i >= ARRAY_SIZE(ispif_formats))
+ if (i >= line->nformats)
fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
fmt->width = clamp_t(u32, fmt->width, 1, 8191);
@@ -753,10 +914,10 @@ static int ispif_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *format;
if (code->pad == MSM_ISPIF_PAD_SINK) {
- if (code->index >= ARRAY_SIZE(ispif_formats))
+ if (code->index >= line->nformats)
return -EINVAL;
- code->code = ispif_formats[code->index];
+ code->code = line->formats[code->index];
} else {
if (code->index > 0)
return -EINVAL;
@@ -907,6 +1068,36 @@ int msm_ispif_subdev_init(struct ispif_device *ispif,
int i;
int ret;
+ /* Number of ISPIF lines - same as number of CSID hardware modules */
+ if (to_camss(ispif)->version == CAMSS_8x16)
+ ispif->line_num = 2;
+ else if (to_camss(ispif)->version == CAMSS_8x96)
+ ispif->line_num = 4;
+ else
+ return -EINVAL;
+
+ ispif->line = kcalloc(ispif->line_num, sizeof(*ispif->line),
+ GFP_KERNEL);
+ if (!ispif->line)
+ return -ENOMEM;
+
+ for (i = 0; i < ispif->line_num; i++) {
+ ispif->line[i].ispif = ispif;
+ ispif->line[i].id = i;
+
+ if (to_camss(ispif)->version == CAMSS_8x16) {
+ ispif->line[i].formats = ispif_formats_8x16;
+ ispif->line[i].nformats =
+ ARRAY_SIZE(ispif_formats_8x16);
+ } else if (to_camss(ispif)->version == CAMSS_8x96) {
+ ispif->line[i].formats = ispif_formats_8x96;
+ ispif->line[i].nformats =
+ ARRAY_SIZE(ispif_formats_8x96);
+ } else {
+ return -EINVAL;
+ }
+ }
+
/* Memory */
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]);
@@ -935,8 +1126,14 @@ int msm_ispif_subdev_init(struct ispif_device *ispif,
ispif->irq = r->start;
snprintf(ispif->irq_name, sizeof(ispif->irq_name), "%s_%s",
dev_name(dev), MSM_ISPIF_NAME);
- ret = devm_request_irq(dev, ispif->irq, ispif_isr,
+ if (to_camss(ispif)->version == CAMSS_8x16)
+ ret = devm_request_irq(dev, ispif->irq, ispif_isr_8x16,
+ IRQF_TRIGGER_RISING, ispif->irq_name, ispif);
+ else if (to_camss(ispif)->version == CAMSS_8x96)
+ ret = devm_request_irq(dev, ispif->irq, ispif_isr_8x96,
IRQF_TRIGGER_RISING, ispif->irq_name, ispif);
+ else
+ ret = -EINVAL;
if (ret < 0) {
dev_err(dev, "request_irq failed: %d\n", ret);
return ret;
@@ -987,9 +1184,6 @@ int msm_ispif_subdev_init(struct ispif_device *ispif,
clock->nfreqs = 0;
}
- for (i = 0; i < ARRAY_SIZE(ispif->line); i++)
- ispif->line[i].id = i;
-
mutex_init(&ispif->power_lock);
ispif->power_count = 0;
@@ -1108,7 +1302,7 @@ int msm_ispif_register_entities(struct ispif_device *ispif,
int ret;
int i;
- for (i = 0; i < ARRAY_SIZE(ispif->line); i++) {
+ for (i = 0; i < ispif->line_num; i++) {
struct v4l2_subdev *sd = &ispif->line[i].subdev;
struct media_pad *pads = ispif->line[i].pads;
@@ -1169,7 +1363,7 @@ void msm_ispif_unregister_entities(struct ispif_device *ispif)
mutex_destroy(&ispif->power_lock);
mutex_destroy(&ispif->config_lock);
- for (i = 0; i < ARRAY_SIZE(ispif->line); i++) {
+ for (i = 0; i < ispif->line_num; i++) {
struct v4l2_subdev *sd = &ispif->line[i].subdev;
v4l2_device_unregister_subdev(sd);
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-ispif.h b/drivers/media/platform/qcom/camss/camss-ispif.h
index f668306020c3..1a5ba2425a42 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-ispif.h
+++ b/drivers/media/platform/qcom/camss/camss-ispif.h
@@ -1,19 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* camss-ispif.h
*
* Qualcomm MSM Camera Subsystem - ISPIF (ISP Interface) Module
*
* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
- * Copyright (C) 2015-2017 Linaro Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * Copyright (C) 2015-2018 Linaro Ltd.
*/
#ifndef QC_MSM_CAMSS_ISPIF_H
#define QC_MSM_CAMSS_ISPIF_H
@@ -23,14 +15,11 @@
#include <media/v4l2-device.h>
#include <media/v4l2-subdev.h>
-/* Number of ISPIF lines - same as number of CSID hardware modules */
-#define MSM_ISPIF_LINE_NUM 2
-
#define MSM_ISPIF_PAD_SINK 0
#define MSM_ISPIF_PAD_SRC 1
#define MSM_ISPIF_PADS_NUM 2
-#define MSM_ISPIF_VFE_NUM 1
+#define MSM_ISPIF_VFE_NUM 2
enum ispif_intf {
PIX0,
@@ -46,6 +35,7 @@ struct ispif_intf_cmd_reg {
};
struct ispif_line {
+ struct ispif_device *ispif;
u8 id;
u8 csid_id;
u8 vfe_id;
@@ -53,6 +43,8 @@ struct ispif_line {
struct v4l2_subdev subdev;
struct media_pad pads[MSM_ISPIF_PADS_NUM];
struct v4l2_mbus_framefmt fmt[MSM_ISPIF_PADS_NUM];
+ const u32 *formats;
+ unsigned int nformats;
};
struct ispif_device {
@@ -69,7 +61,8 @@ struct ispif_device {
struct mutex power_lock;
struct ispif_intf_cmd_reg intf_cmd[MSM_ISPIF_VFE_NUM];
struct mutex config_lock;
- struct ispif_line line[MSM_ISPIF_LINE_NUM];
+ unsigned int line_num;
+ struct ispif_line *line;
};
struct resources_ispif;
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c
new file mode 100644
index 000000000000..da3a9fed9f2d
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c
@@ -0,0 +1,1018 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * camss-vfe-4-1.c
+ *
+ * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module v4.1
+ *
+ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2015-2018 Linaro Ltd.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+
+#include "camss-vfe.h"
+
+#define VFE_0_HW_VERSION 0x000
+
+#define VFE_0_GLOBAL_RESET_CMD 0x00c
+#define VFE_0_GLOBAL_RESET_CMD_CORE BIT(0)
+#define VFE_0_GLOBAL_RESET_CMD_CAMIF BIT(1)
+#define VFE_0_GLOBAL_RESET_CMD_BUS BIT(2)
+#define VFE_0_GLOBAL_RESET_CMD_BUS_BDG BIT(3)
+#define VFE_0_GLOBAL_RESET_CMD_REGISTER BIT(4)
+#define VFE_0_GLOBAL_RESET_CMD_TIMER BIT(5)
+#define VFE_0_GLOBAL_RESET_CMD_PM BIT(6)
+#define VFE_0_GLOBAL_RESET_CMD_BUS_MISR BIT(7)
+#define VFE_0_GLOBAL_RESET_CMD_TESTGEN BIT(8)
+
+#define VFE_0_MODULE_CFG 0x018
+#define VFE_0_MODULE_CFG_DEMUX BIT(2)
+#define VFE_0_MODULE_CFG_CHROMA_UPSAMPLE BIT(3)
+#define VFE_0_MODULE_CFG_SCALE_ENC BIT(23)
+#define VFE_0_MODULE_CFG_CROP_ENC BIT(27)
+
+#define VFE_0_CORE_CFG 0x01c
+#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR 0x4
+#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCRYCB 0x5
+#define VFE_0_CORE_CFG_PIXEL_PATTERN_CBYCRY 0x6
+#define VFE_0_CORE_CFG_PIXEL_PATTERN_CRYCBY 0x7
+
+#define VFE_0_IRQ_CMD 0x024
+#define VFE_0_IRQ_CMD_GLOBAL_CLEAR BIT(0)
+
+#define VFE_0_IRQ_MASK_0 0x028
+#define VFE_0_IRQ_MASK_0_CAMIF_SOF BIT(0)
+#define VFE_0_IRQ_MASK_0_CAMIF_EOF BIT(1)
+#define VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n) BIT((n) + 5)
+#define VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(n) \
+ ((n) == VFE_LINE_PIX ? BIT(4) : VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n))
+#define VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(n) BIT((n) + 8)
+#define VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(n) BIT((n) + 25)
+#define VFE_0_IRQ_MASK_0_RESET_ACK BIT(31)
+#define VFE_0_IRQ_MASK_1 0x02c
+#define VFE_0_IRQ_MASK_1_CAMIF_ERROR BIT(0)
+#define VFE_0_IRQ_MASK_1_VIOLATION BIT(7)
+#define VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK BIT(8)
+#define VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(n) BIT((n) + 9)
+#define VFE_0_IRQ_MASK_1_RDIn_SOF(n) BIT((n) + 29)
+
+#define VFE_0_IRQ_CLEAR_0 0x030
+#define VFE_0_IRQ_CLEAR_1 0x034
+
+#define VFE_0_IRQ_STATUS_0 0x038
+#define VFE_0_IRQ_STATUS_0_CAMIF_SOF BIT(0)
+#define VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n) BIT((n) + 5)
+#define VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(n) \
+ ((n) == VFE_LINE_PIX ? BIT(4) : VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n))
+#define VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(n) BIT((n) + 8)
+#define VFE_0_IRQ_STATUS_0_IMAGE_COMPOSITE_DONE_n(n) BIT((n) + 25)
+#define VFE_0_IRQ_STATUS_0_RESET_ACK BIT(31)
+#define VFE_0_IRQ_STATUS_1 0x03c
+#define VFE_0_IRQ_STATUS_1_VIOLATION BIT(7)
+#define VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK BIT(8)
+#define VFE_0_IRQ_STATUS_1_RDIn_SOF(n) BIT((n) + 29)
+
+#define VFE_0_IRQ_COMPOSITE_MASK_0 0x40
+#define VFE_0_VIOLATION_STATUS 0x48
+
+#define VFE_0_BUS_CMD 0x4c
+#define VFE_0_BUS_CMD_Mx_RLD_CMD(x) BIT(x)
+
+#define VFE_0_BUS_CFG 0x050
+
+#define VFE_0_BUS_XBAR_CFG_x(x) (0x58 + 0x4 * ((x) / 2))
+#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN BIT(1)
+#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA (0x3 << 4)
+#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT 8
+#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA 0
+#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 5
+#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 6
+#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 7
+
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(n) (0x06c + 0x24 * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT 0
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT 1
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(n) (0x070 + 0x24 * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(n) (0x074 + 0x24 * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(n) (0x078 + 0x24 * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT 2
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK (0x1f << 2)
+
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(n) (0x07c + 0x24 * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT 16
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(n) (0x080 + 0x24 * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(n) (0x084 + 0x24 * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(n) \
+ (0x088 + 0x24 * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(n) \
+ (0x08c + 0x24 * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF 0xffffffff
+
+#define VFE_0_BUS_PING_PONG_STATUS 0x268
+
+#define VFE_0_BUS_BDG_CMD 0x2c0
+#define VFE_0_BUS_BDG_CMD_HALT_REQ 1
+
+#define VFE_0_BUS_BDG_QOS_CFG_0 0x2c4
+#define VFE_0_BUS_BDG_QOS_CFG_0_CFG 0xaaa5aaa5
+#define VFE_0_BUS_BDG_QOS_CFG_1 0x2c8
+#define VFE_0_BUS_BDG_QOS_CFG_2 0x2cc
+#define VFE_0_BUS_BDG_QOS_CFG_3 0x2d0
+#define VFE_0_BUS_BDG_QOS_CFG_4 0x2d4
+#define VFE_0_BUS_BDG_QOS_CFG_5 0x2d8
+#define VFE_0_BUS_BDG_QOS_CFG_6 0x2dc
+#define VFE_0_BUS_BDG_QOS_CFG_7 0x2e0
+#define VFE_0_BUS_BDG_QOS_CFG_7_CFG 0x0001aaa5
+
+#define VFE_0_RDI_CFG_x(x) (0x2e8 + (0x4 * (x)))
+#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT 28
+#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK (0xf << 28)
+#define VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT 4
+#define VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK (0xf << 4)
+#define VFE_0_RDI_CFG_x_RDI_EN_BIT BIT(2)
+#define VFE_0_RDI_CFG_x_MIPI_EN_BITS 0x3
+#define VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(r) BIT(16 + (r))
+
+#define VFE_0_CAMIF_CMD 0x2f4
+#define VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY 0
+#define VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY 1
+#define VFE_0_CAMIF_CMD_NO_CHANGE 3
+#define VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS BIT(2)
+#define VFE_0_CAMIF_CFG 0x2f8
+#define VFE_0_CAMIF_CFG_VFE_OUTPUT_EN BIT(6)
+#define VFE_0_CAMIF_FRAME_CFG 0x300
+#define VFE_0_CAMIF_WINDOW_WIDTH_CFG 0x304
+#define VFE_0_CAMIF_WINDOW_HEIGHT_CFG 0x308
+#define VFE_0_CAMIF_SUBSAMPLE_CFG_0 0x30c
+#define VFE_0_CAMIF_IRQ_SUBSAMPLE_PATTERN 0x314
+#define VFE_0_CAMIF_STATUS 0x31c
+#define VFE_0_CAMIF_STATUS_HALT BIT(31)
+
+#define VFE_0_REG_UPDATE 0x378
+#define VFE_0_REG_UPDATE_RDIn(n) BIT(1 + (n))
+#define VFE_0_REG_UPDATE_line_n(n) \
+ ((n) == VFE_LINE_PIX ? 1 : VFE_0_REG_UPDATE_RDIn(n))
+
+#define VFE_0_DEMUX_CFG 0x424
+#define VFE_0_DEMUX_CFG_PERIOD 0x3
+#define VFE_0_DEMUX_GAIN_0 0x428
+#define VFE_0_DEMUX_GAIN_0_CH0_EVEN (0x80 << 0)
+#define VFE_0_DEMUX_GAIN_0_CH0_ODD (0x80 << 16)
+#define VFE_0_DEMUX_GAIN_1 0x42c
+#define VFE_0_DEMUX_GAIN_1_CH1 (0x80 << 0)
+#define VFE_0_DEMUX_GAIN_1_CH2 (0x80 << 16)
+#define VFE_0_DEMUX_EVEN_CFG 0x438
+#define VFE_0_DEMUX_EVEN_CFG_PATTERN_YUYV 0x9cac
+#define VFE_0_DEMUX_EVEN_CFG_PATTERN_YVYU 0xac9c
+#define VFE_0_DEMUX_EVEN_CFG_PATTERN_UYVY 0xc9ca
+#define VFE_0_DEMUX_EVEN_CFG_PATTERN_VYUY 0xcac9
+#define VFE_0_DEMUX_ODD_CFG 0x43c
+#define VFE_0_DEMUX_ODD_CFG_PATTERN_YUYV 0x9cac
+#define VFE_0_DEMUX_ODD_CFG_PATTERN_YVYU 0xac9c
+#define VFE_0_DEMUX_ODD_CFG_PATTERN_UYVY 0xc9ca
+#define VFE_0_DEMUX_ODD_CFG_PATTERN_VYUY 0xcac9
+
+#define VFE_0_SCALE_ENC_Y_CFG 0x75c
+#define VFE_0_SCALE_ENC_Y_H_IMAGE_SIZE 0x760
+#define VFE_0_SCALE_ENC_Y_H_PHASE 0x764
+#define VFE_0_SCALE_ENC_Y_V_IMAGE_SIZE 0x76c
+#define VFE_0_SCALE_ENC_Y_V_PHASE 0x770
+#define VFE_0_SCALE_ENC_CBCR_CFG 0x778
+#define VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE 0x77c
+#define VFE_0_SCALE_ENC_CBCR_H_PHASE 0x780
+#define VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE 0x790
+#define VFE_0_SCALE_ENC_CBCR_V_PHASE 0x794
+
+#define VFE_0_CROP_ENC_Y_WIDTH 0x854
+#define VFE_0_CROP_ENC_Y_HEIGHT 0x858
+#define VFE_0_CROP_ENC_CBCR_WIDTH 0x85c
+#define VFE_0_CROP_ENC_CBCR_HEIGHT 0x860
+
+#define VFE_0_CLAMP_ENC_MAX_CFG 0x874
+#define VFE_0_CLAMP_ENC_MAX_CFG_CH0 (0xff << 0)
+#define VFE_0_CLAMP_ENC_MAX_CFG_CH1 (0xff << 8)
+#define VFE_0_CLAMP_ENC_MAX_CFG_CH2 (0xff << 16)
+#define VFE_0_CLAMP_ENC_MIN_CFG 0x878
+#define VFE_0_CLAMP_ENC_MIN_CFG_CH0 (0x0 << 0)
+#define VFE_0_CLAMP_ENC_MIN_CFG_CH1 (0x0 << 8)
+#define VFE_0_CLAMP_ENC_MIN_CFG_CH2 (0x0 << 16)
+
+#define VFE_0_CGC_OVERRIDE_1 0x974
+#define VFE_0_CGC_OVERRIDE_1_IMAGE_Mx_CGC_OVERRIDE(x) BIT(x)
+
+#define CAMIF_TIMEOUT_SLEEP_US 1000
+#define CAMIF_TIMEOUT_ALL_US 1000000
+
+#define MSM_VFE_VFE0_UB_SIZE 1023
+#define MSM_VFE_VFE0_UB_SIZE_RDI (MSM_VFE_VFE0_UB_SIZE / 3)
+
+static void vfe_hw_version_read(struct vfe_device *vfe, struct device *dev)
+{
+ u32 hw_version = readl_relaxed(vfe->base + VFE_0_HW_VERSION);
+
+ dev_dbg(dev, "VFE HW Version = 0x%08x\n", hw_version);
+}
+
+static u16 vfe_get_ub_size(u8 vfe_id)
+{
+ if (vfe_id == 0)
+ return MSM_VFE_VFE0_UB_SIZE_RDI;
+
+ return 0;
+}
+
+static inline void vfe_reg_clr(struct vfe_device *vfe, u32 reg, u32 clr_bits)
+{
+ u32 bits = readl_relaxed(vfe->base + reg);
+
+ writel_relaxed(bits & ~clr_bits, vfe->base + reg);
+}
+
+static inline void vfe_reg_set(struct vfe_device *vfe, u32 reg, u32 set_bits)
+{
+ u32 bits = readl_relaxed(vfe->base + reg);
+
+ writel_relaxed(bits | set_bits, vfe->base + reg);
+}
+
+static void vfe_global_reset(struct vfe_device *vfe)
+{
+ u32 reset_bits = VFE_0_GLOBAL_RESET_CMD_TESTGEN |
+ VFE_0_GLOBAL_RESET_CMD_BUS_MISR |
+ VFE_0_GLOBAL_RESET_CMD_PM |
+ VFE_0_GLOBAL_RESET_CMD_TIMER |
+ VFE_0_GLOBAL_RESET_CMD_REGISTER |
+ VFE_0_GLOBAL_RESET_CMD_BUS_BDG |
+ VFE_0_GLOBAL_RESET_CMD_BUS |
+ VFE_0_GLOBAL_RESET_CMD_CAMIF |
+ VFE_0_GLOBAL_RESET_CMD_CORE;
+
+ writel_relaxed(reset_bits, vfe->base + VFE_0_GLOBAL_RESET_CMD);
+}
+
+static void vfe_halt_request(struct vfe_device *vfe)
+{
+ writel_relaxed(VFE_0_BUS_BDG_CMD_HALT_REQ,
+ vfe->base + VFE_0_BUS_BDG_CMD);
+}
+
+static void vfe_halt_clear(struct vfe_device *vfe)
+{
+ writel_relaxed(0x0, vfe->base + VFE_0_BUS_BDG_CMD);
+}
+
+static void vfe_wm_enable(struct vfe_device *vfe, u8 wm, u8 enable)
+{
+ if (enable)
+ vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm),
+ 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT);
+ else
+ vfe_reg_clr(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm),
+ 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT);
+}
+
+static void vfe_wm_frame_based(struct vfe_device *vfe, u8 wm, u8 enable)
+{
+ if (enable)
+ vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm),
+ 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT);
+ else
+ vfe_reg_clr(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm),
+ 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT);
+}
+
+#define CALC_WORD(width, M, N) (((width) * (M) + (N) - 1) / (N))
+
+static int vfe_word_per_line(u32 format, u32 pixel_per_line)
+{
+ int val = 0;
+
+ switch (format) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ val = CALC_WORD(pixel_per_line, 1, 8);
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ val = CALC_WORD(pixel_per_line, 2, 8);
+ break;
+ }
+
+ return val;
+}
+
+static void vfe_get_wm_sizes(struct v4l2_pix_format_mplane *pix, u8 plane,
+ u16 *width, u16 *height, u16 *bytesperline)
+{
+ switch (pix->pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ *width = pix->width;
+ *height = pix->height;
+ *bytesperline = pix->plane_fmt[0].bytesperline;
+ if (plane == 1)
+ *height /= 2;
+ break;
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ *width = pix->width;
+ *height = pix->height;
+ *bytesperline = pix->plane_fmt[0].bytesperline;
+ break;
+ }
+}
+
+static void vfe_wm_line_based(struct vfe_device *vfe, u32 wm,
+ struct v4l2_pix_format_mplane *pix,
+ u8 plane, u32 enable)
+{
+ u32 reg;
+
+ if (enable) {
+ u16 width = 0, height = 0, bytesperline = 0, wpl;
+
+ vfe_get_wm_sizes(pix, plane, &width, &height, &bytesperline);
+
+ wpl = vfe_word_per_line(pix->pixelformat, width);
+
+ reg = height - 1;
+ reg |= ((wpl + 1) / 2 - 1) << 16;
+
+ writel_relaxed(reg, vfe->base +
+ VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm));
+
+ wpl = vfe_word_per_line(pix->pixelformat, bytesperline);
+
+ reg = 0x3;
+ reg |= (height - 1) << 4;
+ reg |= wpl << 16;
+
+ writel_relaxed(reg, vfe->base +
+ VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm));
+ } else {
+ writel_relaxed(0, vfe->base +
+ VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm));
+ writel_relaxed(0, vfe->base +
+ VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm));
+ }
+}
+
+static void vfe_wm_set_framedrop_period(struct vfe_device *vfe, u8 wm, u8 per)
+{
+ u32 reg;
+
+ reg = readl_relaxed(vfe->base +
+ VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm));
+
+ reg &= ~(VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK);
+
+ reg |= (per << VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT)
+ & VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK;
+
+ writel_relaxed(reg,
+ vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm));
+}
+
+static void vfe_wm_set_framedrop_pattern(struct vfe_device *vfe, u8 wm,
+ u32 pattern)
+{
+ writel_relaxed(pattern,
+ vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(wm));
+}
+
+static void vfe_wm_set_ub_cfg(struct vfe_device *vfe, u8 wm,
+ u16 offset, u16 depth)
+{
+ u32 reg;
+
+ reg = (offset << VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT) |
+ depth;
+ writel_relaxed(reg, vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(wm));
+}
+
+static void vfe_bus_reload_wm(struct vfe_device *vfe, u8 wm)
+{
+ wmb();
+ writel_relaxed(VFE_0_BUS_CMD_Mx_RLD_CMD(wm), vfe->base + VFE_0_BUS_CMD);
+ wmb();
+}
+
+static void vfe_wm_set_ping_addr(struct vfe_device *vfe, u8 wm, u32 addr)
+{
+ writel_relaxed(addr,
+ vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(wm));
+}
+
+static void vfe_wm_set_pong_addr(struct vfe_device *vfe, u8 wm, u32 addr)
+{
+ writel_relaxed(addr,
+ vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(wm));
+}
+
+static int vfe_wm_get_ping_pong_status(struct vfe_device *vfe, u8 wm)
+{
+ u32 reg;
+
+ reg = readl_relaxed(vfe->base + VFE_0_BUS_PING_PONG_STATUS);
+
+ return (reg >> wm) & 0x1;
+}
+
+static void vfe_bus_enable_wr_if(struct vfe_device *vfe, u8 enable)
+{
+ if (enable)
+ writel_relaxed(0x10000009, vfe->base + VFE_0_BUS_CFG);
+ else
+ writel_relaxed(0, vfe->base + VFE_0_BUS_CFG);
+}
+
+static void vfe_bus_connect_wm_to_rdi(struct vfe_device *vfe, u8 wm,
+ enum vfe_line_id id)
+{
+ u32 reg;
+
+ reg = VFE_0_RDI_CFG_x_MIPI_EN_BITS;
+ reg |= VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(id);
+ vfe_reg_set(vfe, VFE_0_RDI_CFG_x(0), reg);
+
+ reg = VFE_0_RDI_CFG_x_RDI_EN_BIT;
+ reg |= ((3 * id) << VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT) &
+ VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK;
+ vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id), reg);
+
+ switch (id) {
+ case VFE_LINE_RDI0:
+ default:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 <<
+ VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+ break;
+ case VFE_LINE_RDI1:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 <<
+ VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+ break;
+ case VFE_LINE_RDI2:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 <<
+ VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+ break;
+ }
+
+ if (wm % 2 == 1)
+ reg <<= 16;
+
+ vfe_reg_set(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg);
+}
+
+static void vfe_wm_set_subsample(struct vfe_device *vfe, u8 wm)
+{
+ writel_relaxed(VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF,
+ vfe->base +
+ VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(wm));
+}
+
+static void vfe_bus_disconnect_wm_from_rdi(struct vfe_device *vfe, u8 wm,
+ enum vfe_line_id id)
+{
+ u32 reg;
+
+ reg = VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(id);
+ vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(0), reg);
+
+ reg = VFE_0_RDI_CFG_x_RDI_EN_BIT;
+ vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id), reg);
+
+ switch (id) {
+ case VFE_LINE_RDI0:
+ default:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 <<
+ VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+ break;
+ case VFE_LINE_RDI1:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 <<
+ VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+ break;
+ case VFE_LINE_RDI2:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 <<
+ VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+ break;
+ }
+
+ if (wm % 2 == 1)
+ reg <<= 16;
+
+ vfe_reg_clr(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg);
+}
+
+static void vfe_set_xbar_cfg(struct vfe_device *vfe, struct vfe_output *output,
+ u8 enable)
+{
+ struct vfe_line *line = container_of(output, struct vfe_line, output);
+ u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
+ u32 reg;
+ unsigned int i;
+
+ for (i = 0; i < output->wm_num; i++) {
+ if (i == 0) {
+ reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA <<
+ VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+ } else if (i == 1) {
+ reg = VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN;
+ if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV16)
+ reg |= VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA;
+ } else {
+ /* On current devices output->wm_num is always <= 2 */
+ break;
+ }
+
+ if (output->wm_idx[i] % 2 == 1)
+ reg <<= 16;
+
+ if (enable)
+ vfe_reg_set(vfe,
+ VFE_0_BUS_XBAR_CFG_x(output->wm_idx[i]),
+ reg);
+ else
+ vfe_reg_clr(vfe,
+ VFE_0_BUS_XBAR_CFG_x(output->wm_idx[i]),
+ reg);
+ }
+}
+
+static void vfe_set_realign_cfg(struct vfe_device *vfe, struct vfe_line *line,
+ u8 enable)
+{
+ /* empty */
+}
+static void vfe_set_rdi_cid(struct vfe_device *vfe, enum vfe_line_id id, u8 cid)
+{
+ vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id),
+ VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK);
+
+ vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id),
+ cid << VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT);
+}
+
+static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
+{
+ vfe->reg_update |= VFE_0_REG_UPDATE_line_n(line_id);
+ wmb();
+ writel_relaxed(vfe->reg_update, vfe->base + VFE_0_REG_UPDATE);
+ wmb();
+}
+
+static inline void vfe_reg_update_clear(struct vfe_device *vfe,
+ enum vfe_line_id line_id)
+{
+ vfe->reg_update &= ~VFE_0_REG_UPDATE_line_n(line_id);
+}
+
+static void vfe_enable_irq_wm_line(struct vfe_device *vfe, u8 wm,
+ enum vfe_line_id line_id, u8 enable)
+{
+ u32 irq_en0 = VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(wm) |
+ VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id);
+ u32 irq_en1 = VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(wm) |
+ VFE_0_IRQ_MASK_1_RDIn_SOF(line_id);
+
+ if (enable) {
+ vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0);
+ vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1);
+ } else {
+ vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0);
+ vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1);
+ }
+}
+
+static void vfe_enable_irq_pix_line(struct vfe_device *vfe, u8 comp,
+ enum vfe_line_id line_id, u8 enable)
+{
+ struct vfe_output *output = &vfe->line[line_id].output;
+ unsigned int i;
+ u32 irq_en0;
+ u32 irq_en1;
+ u32 comp_mask = 0;
+
+ irq_en0 = VFE_0_IRQ_MASK_0_CAMIF_SOF;
+ irq_en0 |= VFE_0_IRQ_MASK_0_CAMIF_EOF;
+ irq_en0 |= VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(comp);
+ irq_en0 |= VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id);
+ irq_en1 = VFE_0_IRQ_MASK_1_CAMIF_ERROR;
+ for (i = 0; i < output->wm_num; i++) {
+ irq_en1 |= VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(
+ output->wm_idx[i]);
+ comp_mask |= (1 << output->wm_idx[i]) << comp * 8;
+ }
+
+ if (enable) {
+ vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0);
+ vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1);
+ vfe_reg_set(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask);
+ } else {
+ vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0);
+ vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1);
+ vfe_reg_clr(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask);
+ }
+}
+
+static void vfe_enable_irq_common(struct vfe_device *vfe)
+{
+ u32 irq_en0 = VFE_0_IRQ_MASK_0_RESET_ACK;
+ u32 irq_en1 = VFE_0_IRQ_MASK_1_VIOLATION |
+ VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK;
+
+ vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0);
+ vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1);
+}
+
+static void vfe_set_demux_cfg(struct vfe_device *vfe, struct vfe_line *line)
+{
+ u32 val, even_cfg, odd_cfg;
+
+ writel_relaxed(VFE_0_DEMUX_CFG_PERIOD, vfe->base + VFE_0_DEMUX_CFG);
+
+ val = VFE_0_DEMUX_GAIN_0_CH0_EVEN | VFE_0_DEMUX_GAIN_0_CH0_ODD;
+ writel_relaxed(val, vfe->base + VFE_0_DEMUX_GAIN_0);
+
+ val = VFE_0_DEMUX_GAIN_1_CH1 | VFE_0_DEMUX_GAIN_1_CH2;
+ writel_relaxed(val, vfe->base + VFE_0_DEMUX_GAIN_1);
+
+ switch (line->fmt[MSM_VFE_PAD_SINK].code) {
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_YUYV;
+ odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_YUYV;
+ break;
+ case MEDIA_BUS_FMT_YVYU8_2X8:
+ even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_YVYU;
+ odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_YVYU;
+ break;
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ default:
+ even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_UYVY;
+ odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_UYVY;
+ break;
+ case MEDIA_BUS_FMT_VYUY8_2X8:
+ even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_VYUY;
+ odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_VYUY;
+ break;
+ }
+
+ writel_relaxed(even_cfg, vfe->base + VFE_0_DEMUX_EVEN_CFG);
+ writel_relaxed(odd_cfg, vfe->base + VFE_0_DEMUX_ODD_CFG);
+}
+
+static inline u8 vfe_calc_interp_reso(u16 input, u16 output)
+{
+ if (input / output >= 16)
+ return 0;
+
+ if (input / output >= 8)
+ return 1;
+
+ if (input / output >= 4)
+ return 2;
+
+ return 3;
+}
+
+static void vfe_set_scale_cfg(struct vfe_device *vfe, struct vfe_line *line)
+{
+ u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
+ u32 reg;
+ u16 input, output;
+ u8 interp_reso;
+ u32 phase_mult;
+
+ writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_Y_CFG);
+
+ input = line->fmt[MSM_VFE_PAD_SINK].width;
+ output = line->compose.width;
+ reg = (output << 16) | input;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_H_IMAGE_SIZE);
+
+ interp_reso = vfe_calc_interp_reso(input, output);
+ phase_mult = input * (1 << (13 + interp_reso)) / output;
+ reg = (interp_reso << 20) | phase_mult;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_H_PHASE);
+
+ input = line->fmt[MSM_VFE_PAD_SINK].height;
+ output = line->compose.height;
+ reg = (output << 16) | input;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_V_IMAGE_SIZE);
+
+ interp_reso = vfe_calc_interp_reso(input, output);
+ phase_mult = input * (1 << (13 + interp_reso)) / output;
+ reg = (interp_reso << 20) | phase_mult;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_V_PHASE);
+
+ writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_CBCR_CFG);
+
+ input = line->fmt[MSM_VFE_PAD_SINK].width;
+ output = line->compose.width / 2;
+ reg = (output << 16) | input;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE);
+
+ interp_reso = vfe_calc_interp_reso(input, output);
+ phase_mult = input * (1 << (13 + interp_reso)) / output;
+ reg = (interp_reso << 20) | phase_mult;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_PHASE);
+
+ input = line->fmt[MSM_VFE_PAD_SINK].height;
+ output = line->compose.height;
+ if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21)
+ output = line->compose.height / 2;
+ reg = (output << 16) | input;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE);
+
+ interp_reso = vfe_calc_interp_reso(input, output);
+ phase_mult = input * (1 << (13 + interp_reso)) / output;
+ reg = (interp_reso << 20) | phase_mult;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_PHASE);
+}
+
+static void vfe_set_crop_cfg(struct vfe_device *vfe, struct vfe_line *line)
+{
+ u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
+ u32 reg;
+ u16 first, last;
+
+ first = line->crop.left;
+ last = line->crop.left + line->crop.width - 1;
+ reg = (first << 16) | last;
+ writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_Y_WIDTH);
+
+ first = line->crop.top;
+ last = line->crop.top + line->crop.height - 1;
+ reg = (first << 16) | last;
+ writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_Y_HEIGHT);
+
+ first = line->crop.left / 2;
+ last = line->crop.left / 2 + line->crop.width / 2 - 1;
+ reg = (first << 16) | last;
+ writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_CBCR_WIDTH);
+
+ first = line->crop.top;
+ last = line->crop.top + line->crop.height - 1;
+ if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21) {
+ first = line->crop.top / 2;
+ last = line->crop.top / 2 + line->crop.height / 2 - 1;
+ }
+ reg = (first << 16) | last;
+ writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_CBCR_HEIGHT);
+}
+
+static void vfe_set_clamp_cfg(struct vfe_device *vfe)
+{
+ u32 val = VFE_0_CLAMP_ENC_MAX_CFG_CH0 |
+ VFE_0_CLAMP_ENC_MAX_CFG_CH1 |
+ VFE_0_CLAMP_ENC_MAX_CFG_CH2;
+
+ writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MAX_CFG);
+
+ val = VFE_0_CLAMP_ENC_MIN_CFG_CH0 |
+ VFE_0_CLAMP_ENC_MIN_CFG_CH1 |
+ VFE_0_CLAMP_ENC_MIN_CFG_CH2;
+
+ writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MIN_CFG);
+}
+
+static void vfe_set_qos(struct vfe_device *vfe)
+{
+ u32 val = VFE_0_BUS_BDG_QOS_CFG_0_CFG;
+ u32 val7 = VFE_0_BUS_BDG_QOS_CFG_7_CFG;
+
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_0);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_1);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_2);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_3);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_4);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_5);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_6);
+ writel_relaxed(val7, vfe->base + VFE_0_BUS_BDG_QOS_CFG_7);
+}
+
+static void vfe_set_ds(struct vfe_device *vfe)
+{
+ /* empty */
+}
+
+static void vfe_set_cgc_override(struct vfe_device *vfe, u8 wm, u8 enable)
+{
+ u32 val = VFE_0_CGC_OVERRIDE_1_IMAGE_Mx_CGC_OVERRIDE(wm);
+
+ if (enable)
+ vfe_reg_set(vfe, VFE_0_CGC_OVERRIDE_1, val);
+ else
+ vfe_reg_clr(vfe, VFE_0_CGC_OVERRIDE_1, val);
+
+ wmb();
+}
+
+static void vfe_set_camif_cfg(struct vfe_device *vfe, struct vfe_line *line)
+{
+ u32 val;
+
+ switch (line->fmt[MSM_VFE_PAD_SINK].code) {
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ val = VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR;
+ break;
+ case MEDIA_BUS_FMT_YVYU8_2X8:
+ val = VFE_0_CORE_CFG_PIXEL_PATTERN_YCRYCB;
+ break;
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ default:
+ val = VFE_0_CORE_CFG_PIXEL_PATTERN_CBYCRY;
+ break;
+ case MEDIA_BUS_FMT_VYUY8_2X8:
+ val = VFE_0_CORE_CFG_PIXEL_PATTERN_CRYCBY;
+ break;
+ }
+
+ writel_relaxed(val, vfe->base + VFE_0_CORE_CFG);
+
+ val = line->fmt[MSM_VFE_PAD_SINK].width * 2;
+ val |= line->fmt[MSM_VFE_PAD_SINK].height << 16;
+ writel_relaxed(val, vfe->base + VFE_0_CAMIF_FRAME_CFG);
+
+ val = line->fmt[MSM_VFE_PAD_SINK].width * 2 - 1;
+ writel_relaxed(val, vfe->base + VFE_0_CAMIF_WINDOW_WIDTH_CFG);
+
+ val = line->fmt[MSM_VFE_PAD_SINK].height - 1;
+ writel_relaxed(val, vfe->base + VFE_0_CAMIF_WINDOW_HEIGHT_CFG);
+
+ val = 0xffffffff;
+ writel_relaxed(val, vfe->base + VFE_0_CAMIF_SUBSAMPLE_CFG_0);
+
+ val = 0xffffffff;
+ writel_relaxed(val, vfe->base + VFE_0_CAMIF_IRQ_SUBSAMPLE_PATTERN);
+
+ val = VFE_0_RDI_CFG_x_MIPI_EN_BITS;
+ vfe_reg_set(vfe, VFE_0_RDI_CFG_x(0), val);
+
+ val = VFE_0_CAMIF_CFG_VFE_OUTPUT_EN;
+ writel_relaxed(val, vfe->base + VFE_0_CAMIF_CFG);
+}
+
+static void vfe_set_camif_cmd(struct vfe_device *vfe, u8 enable)
+{
+ u32 cmd;
+
+ cmd = VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS | VFE_0_CAMIF_CMD_NO_CHANGE;
+ writel_relaxed(cmd, vfe->base + VFE_0_CAMIF_CMD);
+ wmb();
+
+ if (enable)
+ cmd = VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY;
+ else
+ cmd = VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY;
+
+ writel_relaxed(cmd, vfe->base + VFE_0_CAMIF_CMD);
+}
+
+static void vfe_set_module_cfg(struct vfe_device *vfe, u8 enable)
+{
+ u32 val = VFE_0_MODULE_CFG_DEMUX |
+ VFE_0_MODULE_CFG_CHROMA_UPSAMPLE |
+ VFE_0_MODULE_CFG_SCALE_ENC |
+ VFE_0_MODULE_CFG_CROP_ENC;
+
+ if (enable)
+ writel_relaxed(val, vfe->base + VFE_0_MODULE_CFG);
+ else
+ writel_relaxed(0x0, vfe->base + VFE_0_MODULE_CFG);
+}
+
+static int vfe_camif_wait_for_stop(struct vfe_device *vfe, struct device *dev)
+{
+ u32 val;
+ int ret;
+
+ ret = readl_poll_timeout(vfe->base + VFE_0_CAMIF_STATUS,
+ val,
+ (val & VFE_0_CAMIF_STATUS_HALT),
+ CAMIF_TIMEOUT_SLEEP_US,
+ CAMIF_TIMEOUT_ALL_US);
+ if (ret < 0)
+ dev_err(dev, "%s: camif stop timeout\n", __func__);
+
+ return ret;
+}
+
+static void vfe_isr_read(struct vfe_device *vfe, u32 *value0, u32 *value1)
+{
+ *value0 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_0);
+ *value1 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_1);
+
+ writel_relaxed(*value0, vfe->base + VFE_0_IRQ_CLEAR_0);
+ writel_relaxed(*value1, vfe->base + VFE_0_IRQ_CLEAR_1);
+
+ wmb();
+ writel_relaxed(VFE_0_IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_0_IRQ_CMD);
+}
+
+static void vfe_violation_read(struct vfe_device *vfe)
+{
+ u32 violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS);
+
+ pr_err_ratelimited("VFE: violation = 0x%08x\n", violation);
+}
+
+/*
+ * vfe_isr - ISPIF module interrupt handler
+ * @irq: Interrupt line
+ * @dev: VFE device
+ *
+ * Return IRQ_HANDLED on success
+ */
+static irqreturn_t vfe_isr(int irq, void *dev)
+{
+ struct vfe_device *vfe = dev;
+ u32 value0, value1;
+ int i, j;
+
+ vfe->ops->isr_read(vfe, &value0, &value1);
+
+ trace_printk("VFE: status0 = 0x%08x, status1 = 0x%08x\n",
+ value0, value1);
+
+ if (value0 & VFE_0_IRQ_STATUS_0_RESET_ACK)
+ vfe->isr_ops.reset_ack(vfe);
+
+ if (value1 & VFE_0_IRQ_STATUS_1_VIOLATION)
+ vfe->ops->violation_read(vfe);
+
+ if (value1 & VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK)
+ vfe->isr_ops.halt_ack(vfe);
+
+ for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++)
+ if (value0 & VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(i))
+ vfe->isr_ops.reg_update(vfe, i);
+
+ if (value0 & VFE_0_IRQ_STATUS_0_CAMIF_SOF)
+ vfe->isr_ops.sof(vfe, VFE_LINE_PIX);
+
+ for (i = VFE_LINE_RDI0; i <= VFE_LINE_RDI2; i++)
+ if (value1 & VFE_0_IRQ_STATUS_1_RDIn_SOF(i))
+ vfe->isr_ops.sof(vfe, i);
+
+ for (i = 0; i < MSM_VFE_COMPOSITE_IRQ_NUM; i++)
+ if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_COMPOSITE_DONE_n(i)) {
+ vfe->isr_ops.comp_done(vfe, i);
+ for (j = 0; j < ARRAY_SIZE(vfe->wm_output_map); j++)
+ if (vfe->wm_output_map[j] == VFE_LINE_PIX)
+ value0 &= ~VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(j);
+ }
+
+ for (i = 0; i < MSM_VFE_IMAGE_MASTERS_NUM; i++)
+ if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(i))
+ vfe->isr_ops.wm_done(vfe, i);
+
+ return IRQ_HANDLED;
+}
+
+const struct vfe_hw_ops vfe_ops_4_1 = {
+ .hw_version_read = vfe_hw_version_read,
+ .get_ub_size = vfe_get_ub_size,
+ .global_reset = vfe_global_reset,
+ .halt_request = vfe_halt_request,
+ .halt_clear = vfe_halt_clear,
+ .wm_enable = vfe_wm_enable,
+ .wm_frame_based = vfe_wm_frame_based,
+ .wm_line_based = vfe_wm_line_based,
+ .wm_set_framedrop_period = vfe_wm_set_framedrop_period,
+ .wm_set_framedrop_pattern = vfe_wm_set_framedrop_pattern,
+ .wm_set_ub_cfg = vfe_wm_set_ub_cfg,
+ .bus_reload_wm = vfe_bus_reload_wm,
+ .wm_set_ping_addr = vfe_wm_set_ping_addr,
+ .wm_set_pong_addr = vfe_wm_set_pong_addr,
+ .wm_get_ping_pong_status = vfe_wm_get_ping_pong_status,
+ .bus_enable_wr_if = vfe_bus_enable_wr_if,
+ .bus_connect_wm_to_rdi = vfe_bus_connect_wm_to_rdi,
+ .wm_set_subsample = vfe_wm_set_subsample,
+ .bus_disconnect_wm_from_rdi = vfe_bus_disconnect_wm_from_rdi,
+ .set_xbar_cfg = vfe_set_xbar_cfg,
+ .set_realign_cfg = vfe_set_realign_cfg,
+ .set_rdi_cid = vfe_set_rdi_cid,
+ .reg_update = vfe_reg_update,
+ .reg_update_clear = vfe_reg_update_clear,
+ .enable_irq_wm_line = vfe_enable_irq_wm_line,
+ .enable_irq_pix_line = vfe_enable_irq_pix_line,
+ .enable_irq_common = vfe_enable_irq_common,
+ .set_demux_cfg = vfe_set_demux_cfg,
+ .set_scale_cfg = vfe_set_scale_cfg,
+ .set_crop_cfg = vfe_set_crop_cfg,
+ .set_clamp_cfg = vfe_set_clamp_cfg,
+ .set_qos = vfe_set_qos,
+ .set_ds = vfe_set_ds,
+ .set_cgc_override = vfe_set_cgc_override,
+ .set_camif_cfg = vfe_set_camif_cfg,
+ .set_camif_cmd = vfe_set_camif_cmd,
+ .set_module_cfg = vfe_set_module_cfg,
+ .camif_wait_for_stop = vfe_camif_wait_for_stop,
+ .isr_read = vfe_isr_read,
+ .violation_read = vfe_violation_read,
+ .isr = vfe_isr,
+};
diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-7.c b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c
new file mode 100644
index 000000000000..4c584bffd179
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c
@@ -0,0 +1,1140 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * camss-vfe-4-7.c
+ *
+ * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module v4.7
+ *
+ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2015-2018 Linaro Ltd.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/iopoll.h>
+
+#include "camss-vfe.h"
+
+#define VFE_0_HW_VERSION 0x000
+
+#define VFE_0_GLOBAL_RESET_CMD 0x018
+#define VFE_0_GLOBAL_RESET_CMD_CORE BIT(0)
+#define VFE_0_GLOBAL_RESET_CMD_CAMIF BIT(1)
+#define VFE_0_GLOBAL_RESET_CMD_BUS BIT(2)
+#define VFE_0_GLOBAL_RESET_CMD_BUS_BDG BIT(3)
+#define VFE_0_GLOBAL_RESET_CMD_REGISTER BIT(4)
+#define VFE_0_GLOBAL_RESET_CMD_PM BIT(5)
+#define VFE_0_GLOBAL_RESET_CMD_BUS_MISR BIT(6)
+#define VFE_0_GLOBAL_RESET_CMD_TESTGEN BIT(7)
+#define VFE_0_GLOBAL_RESET_CMD_DSP BIT(8)
+#define VFE_0_GLOBAL_RESET_CMD_IDLE_CGC BIT(9)
+
+#define VFE_0_MODULE_LENS_EN 0x040
+#define VFE_0_MODULE_LENS_EN_DEMUX BIT(2)
+#define VFE_0_MODULE_LENS_EN_CHROMA_UPSAMPLE BIT(3)
+
+#define VFE_0_MODULE_ZOOM_EN 0x04c
+#define VFE_0_MODULE_ZOOM_EN_SCALE_ENC BIT(1)
+#define VFE_0_MODULE_ZOOM_EN_CROP_ENC BIT(2)
+#define VFE_0_MODULE_ZOOM_EN_REALIGN_BUF BIT(9)
+
+#define VFE_0_CORE_CFG 0x050
+#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR 0x4
+#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCRYCB 0x5
+#define VFE_0_CORE_CFG_PIXEL_PATTERN_CBYCRY 0x6
+#define VFE_0_CORE_CFG_PIXEL_PATTERN_CRYCBY 0x7
+#define VFE_0_CORE_CFG_COMPOSITE_REG_UPDATE_EN BIT(4)
+
+#define VFE_0_IRQ_CMD 0x058
+#define VFE_0_IRQ_CMD_GLOBAL_CLEAR BIT(0)
+
+#define VFE_0_IRQ_MASK_0 0x05c
+#define VFE_0_IRQ_MASK_0_CAMIF_SOF BIT(0)
+#define VFE_0_IRQ_MASK_0_CAMIF_EOF BIT(1)
+#define VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n) BIT((n) + 5)
+#define VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(n) \
+ ((n) == VFE_LINE_PIX ? BIT(4) : VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n))
+#define VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(n) BIT((n) + 8)
+#define VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(n) BIT((n) + 25)
+#define VFE_0_IRQ_MASK_0_RESET_ACK BIT(31)
+#define VFE_0_IRQ_MASK_1 0x060
+#define VFE_0_IRQ_MASK_1_CAMIF_ERROR BIT(0)
+#define VFE_0_IRQ_MASK_1_VIOLATION BIT(7)
+#define VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK BIT(8)
+#define VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(n) BIT((n) + 9)
+#define VFE_0_IRQ_MASK_1_RDIn_SOF(n) BIT((n) + 29)
+
+#define VFE_0_IRQ_CLEAR_0 0x064
+#define VFE_0_IRQ_CLEAR_1 0x068
+
+#define VFE_0_IRQ_STATUS_0 0x06c
+#define VFE_0_IRQ_STATUS_0_CAMIF_SOF BIT(0)
+#define VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n) BIT((n) + 5)
+#define VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(n) \
+ ((n) == VFE_LINE_PIX ? BIT(4) : VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n))
+#define VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(n) BIT((n) + 8)
+#define VFE_0_IRQ_STATUS_0_IMAGE_COMPOSITE_DONE_n(n) BIT((n) + 25)
+#define VFE_0_IRQ_STATUS_0_RESET_ACK BIT(31)
+#define VFE_0_IRQ_STATUS_1 0x070
+#define VFE_0_IRQ_STATUS_1_VIOLATION BIT(7)
+#define VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK BIT(8)
+#define VFE_0_IRQ_STATUS_1_RDIn_SOF(n) BIT((n) + 29)
+
+#define VFE_0_IRQ_COMPOSITE_MASK_0 0x074
+#define VFE_0_VIOLATION_STATUS 0x07c
+
+#define VFE_0_BUS_CMD 0x80
+#define VFE_0_BUS_CMD_Mx_RLD_CMD(x) BIT(x)
+
+#define VFE_0_BUS_CFG 0x084
+
+#define VFE_0_BUS_XBAR_CFG_x(x) (0x90 + 0x4 * ((x) / 2))
+#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN BIT(2)
+#define VFE_0_BUS_XBAR_CFG_x_M_REALIGN_BUF_EN BIT(3)
+#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTRA (0x1 << 4)
+#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER (0x2 << 4)
+#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA (0x3 << 4)
+#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT 8
+#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA 0x0
+#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 0xc
+#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 0xd
+#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 0xe
+
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(n) (0x0a0 + 0x2c * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT 0
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(n) (0x0a4 + 0x2c * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(n) (0x0ac + 0x2c * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(n) (0x0b4 + 0x2c * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_BASED_SHIFT 1
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT 2
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK (0x1f << 2)
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(n) (0x0b8 + 0x2c * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT 16
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(n) (0x0bc + 0x2c * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(n) (0x0c0 + 0x2c * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(n) \
+ (0x0c4 + 0x2c * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(n) \
+ (0x0c8 + 0x2c * (n))
+#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF 0xffffffff
+
+#define VFE_0_BUS_PING_PONG_STATUS 0x338
+
+#define VFE_0_BUS_BDG_CMD 0x400
+#define VFE_0_BUS_BDG_CMD_HALT_REQ 1
+
+#define VFE_0_BUS_BDG_QOS_CFG_0 0x404
+#define VFE_0_BUS_BDG_QOS_CFG_0_CFG 0xaaa9aaa9
+#define VFE_0_BUS_BDG_QOS_CFG_1 0x408
+#define VFE_0_BUS_BDG_QOS_CFG_2 0x40c
+#define VFE_0_BUS_BDG_QOS_CFG_3 0x410
+#define VFE_0_BUS_BDG_QOS_CFG_4 0x414
+#define VFE_0_BUS_BDG_QOS_CFG_5 0x418
+#define VFE_0_BUS_BDG_QOS_CFG_6 0x41c
+#define VFE_0_BUS_BDG_QOS_CFG_7 0x420
+#define VFE_0_BUS_BDG_QOS_CFG_7_CFG 0x0001aaa9
+
+#define VFE_0_BUS_BDG_DS_CFG_0 0x424
+#define VFE_0_BUS_BDG_DS_CFG_0_CFG 0xcccc0011
+#define VFE_0_BUS_BDG_DS_CFG_1 0x428
+#define VFE_0_BUS_BDG_DS_CFG_2 0x42c
+#define VFE_0_BUS_BDG_DS_CFG_3 0x430
+#define VFE_0_BUS_BDG_DS_CFG_4 0x434
+#define VFE_0_BUS_BDG_DS_CFG_5 0x438
+#define VFE_0_BUS_BDG_DS_CFG_6 0x43c
+#define VFE_0_BUS_BDG_DS_CFG_7 0x440
+#define VFE_0_BUS_BDG_DS_CFG_8 0x444
+#define VFE_0_BUS_BDG_DS_CFG_9 0x448
+#define VFE_0_BUS_BDG_DS_CFG_10 0x44c
+#define VFE_0_BUS_BDG_DS_CFG_11 0x450
+#define VFE_0_BUS_BDG_DS_CFG_12 0x454
+#define VFE_0_BUS_BDG_DS_CFG_13 0x458
+#define VFE_0_BUS_BDG_DS_CFG_14 0x45c
+#define VFE_0_BUS_BDG_DS_CFG_15 0x460
+#define VFE_0_BUS_BDG_DS_CFG_16 0x464
+#define VFE_0_BUS_BDG_DS_CFG_16_CFG 0x40000103
+
+#define VFE_0_RDI_CFG_x(x) (0x46c + (0x4 * (x)))
+#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT 28
+#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK (0xf << 28)
+#define VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT 4
+#define VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK (0xf << 4)
+#define VFE_0_RDI_CFG_x_RDI_EN_BIT BIT(2)
+#define VFE_0_RDI_CFG_x_MIPI_EN_BITS 0x3
+
+#define VFE_0_CAMIF_CMD 0x478
+#define VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY 0
+#define VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY 1
+#define VFE_0_CAMIF_CMD_NO_CHANGE 3
+#define VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS BIT(2)
+#define VFE_0_CAMIF_CFG 0x47c
+#define VFE_0_CAMIF_CFG_VFE_OUTPUT_EN BIT(6)
+#define VFE_0_CAMIF_FRAME_CFG 0x484
+#define VFE_0_CAMIF_WINDOW_WIDTH_CFG 0x488
+#define VFE_0_CAMIF_WINDOW_HEIGHT_CFG 0x48c
+#define VFE_0_CAMIF_SUBSAMPLE_CFG 0x490
+#define VFE_0_CAMIF_IRQ_FRAMEDROP_PATTERN 0x498
+#define VFE_0_CAMIF_IRQ_SUBSAMPLE_PATTERN 0x49c
+#define VFE_0_CAMIF_STATUS 0x4a4
+#define VFE_0_CAMIF_STATUS_HALT BIT(31)
+
+#define VFE_0_REG_UPDATE 0x4ac
+#define VFE_0_REG_UPDATE_RDIn(n) BIT(1 + (n))
+#define VFE_0_REG_UPDATE_line_n(n) \
+ ((n) == VFE_LINE_PIX ? 1 : VFE_0_REG_UPDATE_RDIn(n))
+
+#define VFE_0_DEMUX_CFG 0x560
+#define VFE_0_DEMUX_CFG_PERIOD 0x3
+#define VFE_0_DEMUX_GAIN_0 0x564
+#define VFE_0_DEMUX_GAIN_0_CH0_EVEN (0x80 << 0)
+#define VFE_0_DEMUX_GAIN_0_CH0_ODD (0x80 << 16)
+#define VFE_0_DEMUX_GAIN_1 0x568
+#define VFE_0_DEMUX_GAIN_1_CH1 (0x80 << 0)
+#define VFE_0_DEMUX_GAIN_1_CH2 (0x80 << 16)
+#define VFE_0_DEMUX_EVEN_CFG 0x574
+#define VFE_0_DEMUX_EVEN_CFG_PATTERN_YUYV 0x9cac
+#define VFE_0_DEMUX_EVEN_CFG_PATTERN_YVYU 0xac9c
+#define VFE_0_DEMUX_EVEN_CFG_PATTERN_UYVY 0xc9ca
+#define VFE_0_DEMUX_EVEN_CFG_PATTERN_VYUY 0xcac9
+#define VFE_0_DEMUX_ODD_CFG 0x578
+#define VFE_0_DEMUX_ODD_CFG_PATTERN_YUYV 0x9cac
+#define VFE_0_DEMUX_ODD_CFG_PATTERN_YVYU 0xac9c
+#define VFE_0_DEMUX_ODD_CFG_PATTERN_UYVY 0xc9ca
+#define VFE_0_DEMUX_ODD_CFG_PATTERN_VYUY 0xcac9
+
+#define VFE_0_SCALE_ENC_Y_CFG 0x91c
+#define VFE_0_SCALE_ENC_Y_H_IMAGE_SIZE 0x920
+#define VFE_0_SCALE_ENC_Y_H_PHASE 0x924
+#define VFE_0_SCALE_ENC_Y_V_IMAGE_SIZE 0x934
+#define VFE_0_SCALE_ENC_Y_V_PHASE 0x938
+#define VFE_0_SCALE_ENC_CBCR_CFG 0x948
+#define VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE 0x94c
+#define VFE_0_SCALE_ENC_CBCR_H_PHASE 0x950
+#define VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE 0x960
+#define VFE_0_SCALE_ENC_CBCR_V_PHASE 0x964
+
+#define VFE_0_CROP_ENC_Y_WIDTH 0x974
+#define VFE_0_CROP_ENC_Y_HEIGHT 0x978
+#define VFE_0_CROP_ENC_CBCR_WIDTH 0x97c
+#define VFE_0_CROP_ENC_CBCR_HEIGHT 0x980
+
+#define VFE_0_CLAMP_ENC_MAX_CFG 0x984
+#define VFE_0_CLAMP_ENC_MAX_CFG_CH0 (0xff << 0)
+#define VFE_0_CLAMP_ENC_MAX_CFG_CH1 (0xff << 8)
+#define VFE_0_CLAMP_ENC_MAX_CFG_CH2 (0xff << 16)
+#define VFE_0_CLAMP_ENC_MIN_CFG 0x988
+#define VFE_0_CLAMP_ENC_MIN_CFG_CH0 (0x0 << 0)
+#define VFE_0_CLAMP_ENC_MIN_CFG_CH1 (0x0 << 8)
+#define VFE_0_CLAMP_ENC_MIN_CFG_CH2 (0x0 << 16)
+
+#define VFE_0_REALIGN_BUF_CFG 0xaac
+#define VFE_0_REALIGN_BUF_CFG_CB_ODD_PIXEL BIT(2)
+#define VFE_0_REALIGN_BUF_CFG_CR_ODD_PIXEL BIT(3)
+#define VFE_0_REALIGN_BUF_CFG_HSUB_ENABLE BIT(4)
+
+#define CAMIF_TIMEOUT_SLEEP_US 1000
+#define CAMIF_TIMEOUT_ALL_US 1000000
+
+#define MSM_VFE_VFE0_UB_SIZE 2047
+#define MSM_VFE_VFE0_UB_SIZE_RDI (MSM_VFE_VFE0_UB_SIZE / 3)
+#define MSM_VFE_VFE1_UB_SIZE 1535
+#define MSM_VFE_VFE1_UB_SIZE_RDI (MSM_VFE_VFE1_UB_SIZE / 3)
+
+static void vfe_hw_version_read(struct vfe_device *vfe, struct device *dev)
+{
+ u32 hw_version = readl_relaxed(vfe->base + VFE_0_HW_VERSION);
+
+ dev_err(dev, "VFE HW Version = 0x%08x\n", hw_version);
+}
+
+static u16 vfe_get_ub_size(u8 vfe_id)
+{
+ if (vfe_id == 0)
+ return MSM_VFE_VFE0_UB_SIZE_RDI;
+ else if (vfe_id == 1)
+ return MSM_VFE_VFE1_UB_SIZE_RDI;
+
+ return 0;
+}
+
+static inline void vfe_reg_clr(struct vfe_device *vfe, u32 reg, u32 clr_bits)
+{
+ u32 bits = readl_relaxed(vfe->base + reg);
+
+ writel_relaxed(bits & ~clr_bits, vfe->base + reg);
+}
+
+static inline void vfe_reg_set(struct vfe_device *vfe, u32 reg, u32 set_bits)
+{
+ u32 bits = readl_relaxed(vfe->base + reg);
+
+ writel_relaxed(bits | set_bits, vfe->base + reg);
+}
+
+static void vfe_global_reset(struct vfe_device *vfe)
+{
+ u32 reset_bits = VFE_0_GLOBAL_RESET_CMD_IDLE_CGC |
+ VFE_0_GLOBAL_RESET_CMD_DSP |
+ VFE_0_GLOBAL_RESET_CMD_TESTGEN |
+ VFE_0_GLOBAL_RESET_CMD_BUS_MISR |
+ VFE_0_GLOBAL_RESET_CMD_PM |
+ VFE_0_GLOBAL_RESET_CMD_REGISTER |
+ VFE_0_GLOBAL_RESET_CMD_BUS_BDG |
+ VFE_0_GLOBAL_RESET_CMD_BUS |
+ VFE_0_GLOBAL_RESET_CMD_CAMIF |
+ VFE_0_GLOBAL_RESET_CMD_CORE;
+
+ writel_relaxed(BIT(31), vfe->base + VFE_0_IRQ_MASK_0);
+ wmb();
+ writel_relaxed(reset_bits, vfe->base + VFE_0_GLOBAL_RESET_CMD);
+}
+
+static void vfe_halt_request(struct vfe_device *vfe)
+{
+ writel_relaxed(VFE_0_BUS_BDG_CMD_HALT_REQ,
+ vfe->base + VFE_0_BUS_BDG_CMD);
+}
+
+static void vfe_halt_clear(struct vfe_device *vfe)
+{
+ writel_relaxed(0x0, vfe->base + VFE_0_BUS_BDG_CMD);
+}
+
+static void vfe_wm_enable(struct vfe_device *vfe, u8 wm, u8 enable)
+{
+ if (enable)
+ vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm),
+ 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT);
+ else
+ vfe_reg_clr(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm),
+ 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT);
+}
+
+static void vfe_wm_frame_based(struct vfe_device *vfe, u8 wm, u8 enable)
+{
+ if (enable)
+ vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm),
+ 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_BASED_SHIFT);
+ else
+ vfe_reg_clr(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm),
+ 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_BASED_SHIFT);
+}
+
+#define CALC_WORD(width, M, N) (((width) * (M) + (N) - 1) / (N))
+
+static int vfe_word_per_line_by_pixel(u32 format, u32 pixel_per_line)
+{
+ int val = 0;
+
+ switch (format) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ val = CALC_WORD(pixel_per_line, 1, 8);
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ val = CALC_WORD(pixel_per_line, 2, 8);
+ break;
+ }
+
+ return val;
+}
+
+static int vfe_word_per_line_by_bytes(u32 bytes_per_line)
+{
+ return CALC_WORD(bytes_per_line, 1, 8);
+}
+
+static void vfe_get_wm_sizes(struct v4l2_pix_format_mplane *pix, u8 plane,
+ u16 *width, u16 *height, u16 *bytesperline)
+{
+ switch (pix->pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ *width = pix->width;
+ *height = pix->height;
+ *bytesperline = pix->plane_fmt[0].bytesperline;
+ if (plane == 1)
+ *height /= 2;
+ break;
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ *width = pix->width;
+ *height = pix->height;
+ *bytesperline = pix->plane_fmt[0].bytesperline;
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_VYUY:
+ case V4L2_PIX_FMT_UYVY:
+ *width = pix->width;
+ *height = pix->height;
+ *bytesperline = pix->plane_fmt[plane].bytesperline;
+ break;
+
+ }
+}
+
+static void vfe_wm_line_based(struct vfe_device *vfe, u32 wm,
+ struct v4l2_pix_format_mplane *pix,
+ u8 plane, u32 enable)
+{
+ u32 reg;
+
+ if (enable) {
+ u16 width = 0, height = 0, bytesperline = 0, wpl;
+
+ vfe_get_wm_sizes(pix, plane, &width, &height, &bytesperline);
+
+ wpl = vfe_word_per_line_by_pixel(pix->pixelformat, width);
+
+ reg = height - 1;
+ reg |= ((wpl + 3) / 4 - 1) << 16;
+
+ writel_relaxed(reg, vfe->base +
+ VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm));
+
+ wpl = vfe_word_per_line_by_bytes(bytesperline);
+
+ reg = 0x3;
+ reg |= (height - 1) << 2;
+ reg |= ((wpl + 1) / 2) << 16;
+
+ writel_relaxed(reg, vfe->base +
+ VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm));
+ } else {
+ writel_relaxed(0, vfe->base +
+ VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm));
+ writel_relaxed(0, vfe->base +
+ VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm));
+ }
+}
+
+static void vfe_wm_set_framedrop_period(struct vfe_device *vfe, u8 wm, u8 per)
+{
+ u32 reg;
+
+ reg = readl_relaxed(vfe->base +
+ VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm));
+
+ reg &= ~(VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK);
+
+ reg |= (per << VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT)
+ & VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK;
+
+ writel_relaxed(reg,
+ vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm));
+}
+
+static void vfe_wm_set_framedrop_pattern(struct vfe_device *vfe, u8 wm,
+ u32 pattern)
+{
+ writel_relaxed(pattern,
+ vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(wm));
+}
+
+static void vfe_wm_set_ub_cfg(struct vfe_device *vfe, u8 wm,
+ u16 offset, u16 depth)
+{
+ u32 reg;
+
+ reg = (offset << VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT) |
+ depth;
+ writel_relaxed(reg, vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(wm));
+}
+
+static void vfe_bus_reload_wm(struct vfe_device *vfe, u8 wm)
+{
+ wmb();
+ writel_relaxed(VFE_0_BUS_CMD_Mx_RLD_CMD(wm), vfe->base + VFE_0_BUS_CMD);
+ wmb();
+}
+
+static void vfe_wm_set_ping_addr(struct vfe_device *vfe, u8 wm, u32 addr)
+{
+ writel_relaxed(addr,
+ vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(wm));
+}
+
+static void vfe_wm_set_pong_addr(struct vfe_device *vfe, u8 wm, u32 addr)
+{
+ writel_relaxed(addr,
+ vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(wm));
+}
+
+static int vfe_wm_get_ping_pong_status(struct vfe_device *vfe, u8 wm)
+{
+ u32 reg;
+
+ reg = readl_relaxed(vfe->base + VFE_0_BUS_PING_PONG_STATUS);
+
+ return (reg >> wm) & 0x1;
+}
+
+static void vfe_bus_enable_wr_if(struct vfe_device *vfe, u8 enable)
+{
+ if (enable)
+ writel_relaxed(0x101, vfe->base + VFE_0_BUS_CFG);
+ else
+ writel_relaxed(0, vfe->base + VFE_0_BUS_CFG);
+}
+
+static void vfe_bus_connect_wm_to_rdi(struct vfe_device *vfe, u8 wm,
+ enum vfe_line_id id)
+{
+ u32 reg;
+
+ reg = VFE_0_RDI_CFG_x_MIPI_EN_BITS;
+ vfe_reg_set(vfe, VFE_0_RDI_CFG_x(0), reg);
+
+ reg = VFE_0_RDI_CFG_x_RDI_EN_BIT;
+ reg |= ((3 * id) << VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT) &
+ VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK;
+ vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id), reg);
+
+ switch (id) {
+ case VFE_LINE_RDI0:
+ default:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 <<
+ VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+ break;
+ case VFE_LINE_RDI1:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 <<
+ VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+ break;
+ case VFE_LINE_RDI2:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 <<
+ VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+ break;
+ }
+
+ if (wm % 2 == 1)
+ reg <<= 16;
+
+ vfe_reg_set(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg);
+}
+
+static void vfe_wm_set_subsample(struct vfe_device *vfe, u8 wm)
+{
+ writel_relaxed(VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF,
+ vfe->base +
+ VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(wm));
+}
+
+static void vfe_bus_disconnect_wm_from_rdi(struct vfe_device *vfe, u8 wm,
+ enum vfe_line_id id)
+{
+ u32 reg;
+
+ reg = VFE_0_RDI_CFG_x_RDI_EN_BIT;
+ vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id), reg);
+
+ switch (id) {
+ case VFE_LINE_RDI0:
+ default:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 <<
+ VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+ break;
+ case VFE_LINE_RDI1:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 <<
+ VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+ break;
+ case VFE_LINE_RDI2:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 <<
+ VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+ break;
+ }
+
+ if (wm % 2 == 1)
+ reg <<= 16;
+
+ vfe_reg_clr(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg);
+}
+
+static void vfe_set_xbar_cfg(struct vfe_device *vfe, struct vfe_output *output,
+ u8 enable)
+{
+ struct vfe_line *line = container_of(output, struct vfe_line, output);
+ u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
+ u32 reg;
+
+ switch (p) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA <<
+ VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
+
+ if (output->wm_idx[0] % 2 == 1)
+ reg <<= 16;
+
+ if (enable)
+ vfe_reg_set(vfe,
+ VFE_0_BUS_XBAR_CFG_x(output->wm_idx[0]),
+ reg);
+ else
+ vfe_reg_clr(vfe,
+ VFE_0_BUS_XBAR_CFG_x(output->wm_idx[0]),
+ reg);
+
+ reg = VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN;
+ if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV16)
+ reg |= VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA;
+
+ if (output->wm_idx[1] % 2 == 1)
+ reg <<= 16;
+
+ if (enable)
+ vfe_reg_set(vfe,
+ VFE_0_BUS_XBAR_CFG_x(output->wm_idx[1]),
+ reg);
+ else
+ vfe_reg_clr(vfe,
+ VFE_0_BUS_XBAR_CFG_x(output->wm_idx[1]),
+ reg);
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
+ case V4L2_PIX_FMT_VYUY:
+ case V4L2_PIX_FMT_UYVY:
+ reg = VFE_0_BUS_XBAR_CFG_x_M_REALIGN_BUF_EN;
+ reg |= VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN;
+
+ if (p == V4L2_PIX_FMT_YUYV || p == V4L2_PIX_FMT_YVYU)
+ reg |= VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA;
+
+ if (output->wm_idx[0] % 2 == 1)
+ reg <<= 16;
+
+ if (enable)
+ vfe_reg_set(vfe,
+ VFE_0_BUS_XBAR_CFG_x(output->wm_idx[0]),
+ reg);
+ else
+ vfe_reg_clr(vfe,
+ VFE_0_BUS_XBAR_CFG_x(output->wm_idx[0]),
+ reg);
+ break;
+ default:
+ break;
+ }
+}
+
+static void vfe_set_realign_cfg(struct vfe_device *vfe, struct vfe_line *line,
+ u8 enable)
+{
+ u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
+ u32 val = VFE_0_MODULE_ZOOM_EN_REALIGN_BUF;
+
+ if (p != V4L2_PIX_FMT_YUYV && p != V4L2_PIX_FMT_YVYU &&
+ p != V4L2_PIX_FMT_VYUY && p != V4L2_PIX_FMT_UYVY)
+ return;
+
+ if (enable) {
+ vfe_reg_set(vfe, VFE_0_MODULE_ZOOM_EN, val);
+ } else {
+ vfe_reg_clr(vfe, VFE_0_MODULE_ZOOM_EN, val);
+ return;
+ }
+
+ val = VFE_0_REALIGN_BUF_CFG_HSUB_ENABLE;
+
+ if (p == V4L2_PIX_FMT_UYVY || p == V4L2_PIX_FMT_YUYV)
+ val |= VFE_0_REALIGN_BUF_CFG_CR_ODD_PIXEL;
+ else
+ val |= VFE_0_REALIGN_BUF_CFG_CB_ODD_PIXEL;
+
+ writel_relaxed(val, vfe->base + VFE_0_REALIGN_BUF_CFG);
+}
+
+static void vfe_set_rdi_cid(struct vfe_device *vfe, enum vfe_line_id id, u8 cid)
+{
+ vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id),
+ VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK);
+
+ vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id),
+ cid << VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT);
+}
+
+static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
+{
+ vfe->reg_update |= VFE_0_REG_UPDATE_line_n(line_id);
+ wmb();
+ writel_relaxed(vfe->reg_update, vfe->base + VFE_0_REG_UPDATE);
+ wmb();
+}
+
+static inline void vfe_reg_update_clear(struct vfe_device *vfe,
+ enum vfe_line_id line_id)
+{
+ vfe->reg_update &= ~VFE_0_REG_UPDATE_line_n(line_id);
+}
+
+static void vfe_enable_irq_wm_line(struct vfe_device *vfe, u8 wm,
+ enum vfe_line_id line_id, u8 enable)
+{
+ u32 irq_en0 = VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(wm) |
+ VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id);
+ u32 irq_en1 = VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(wm) |
+ VFE_0_IRQ_MASK_1_RDIn_SOF(line_id);
+
+ if (enable) {
+ vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0);
+ vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1);
+ } else {
+ vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0);
+ vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1);
+ }
+}
+
+static void vfe_enable_irq_pix_line(struct vfe_device *vfe, u8 comp,
+ enum vfe_line_id line_id, u8 enable)
+{
+ struct vfe_output *output = &vfe->line[line_id].output;
+ unsigned int i;
+ u32 irq_en0;
+ u32 irq_en1;
+ u32 comp_mask = 0;
+
+ irq_en0 = VFE_0_IRQ_MASK_0_CAMIF_SOF;
+ irq_en0 |= VFE_0_IRQ_MASK_0_CAMIF_EOF;
+ irq_en0 |= VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(comp);
+ irq_en0 |= VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id);
+ irq_en1 = VFE_0_IRQ_MASK_1_CAMIF_ERROR;
+ for (i = 0; i < output->wm_num; i++) {
+ irq_en1 |= VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(
+ output->wm_idx[i]);
+ comp_mask |= (1 << output->wm_idx[i]) << comp * 8;
+ }
+
+ if (enable) {
+ vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0);
+ vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1);
+ vfe_reg_set(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask);
+ } else {
+ vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0);
+ vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1);
+ vfe_reg_clr(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask);
+ }
+}
+
+static void vfe_enable_irq_common(struct vfe_device *vfe)
+{
+ u32 irq_en0 = VFE_0_IRQ_MASK_0_RESET_ACK;
+ u32 irq_en1 = VFE_0_IRQ_MASK_1_VIOLATION |
+ VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK;
+
+ vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0);
+ vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1);
+}
+
+static void vfe_set_demux_cfg(struct vfe_device *vfe, struct vfe_line *line)
+{
+ u32 val, even_cfg, odd_cfg;
+
+ writel_relaxed(VFE_0_DEMUX_CFG_PERIOD, vfe->base + VFE_0_DEMUX_CFG);
+
+ val = VFE_0_DEMUX_GAIN_0_CH0_EVEN | VFE_0_DEMUX_GAIN_0_CH0_ODD;
+ writel_relaxed(val, vfe->base + VFE_0_DEMUX_GAIN_0);
+
+ val = VFE_0_DEMUX_GAIN_1_CH1 | VFE_0_DEMUX_GAIN_1_CH2;
+ writel_relaxed(val, vfe->base + VFE_0_DEMUX_GAIN_1);
+
+ switch (line->fmt[MSM_VFE_PAD_SINK].code) {
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_YUYV;
+ odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_YUYV;
+ break;
+ case MEDIA_BUS_FMT_YVYU8_2X8:
+ even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_YVYU;
+ odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_YVYU;
+ break;
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ default:
+ even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_UYVY;
+ odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_UYVY;
+ break;
+ case MEDIA_BUS_FMT_VYUY8_2X8:
+ even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_VYUY;
+ odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_VYUY;
+ break;
+ }
+
+ writel_relaxed(even_cfg, vfe->base + VFE_0_DEMUX_EVEN_CFG);
+ writel_relaxed(odd_cfg, vfe->base + VFE_0_DEMUX_ODD_CFG);
+}
+
+static inline u8 vfe_calc_interp_reso(u16 input, u16 output)
+{
+ if (input / output >= 16)
+ return 0;
+
+ if (input / output >= 8)
+ return 1;
+
+ if (input / output >= 4)
+ return 2;
+
+ return 3;
+}
+
+static void vfe_set_scale_cfg(struct vfe_device *vfe, struct vfe_line *line)
+{
+ u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
+ u32 reg;
+ u16 input, output;
+ u8 interp_reso;
+ u32 phase_mult;
+
+ writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_Y_CFG);
+
+ input = line->fmt[MSM_VFE_PAD_SINK].width - 1;
+ output = line->compose.width - 1;
+ reg = (output << 16) | input;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_H_IMAGE_SIZE);
+
+ interp_reso = vfe_calc_interp_reso(input, output);
+ phase_mult = input * (1 << (14 + interp_reso)) / output;
+ reg = (interp_reso << 28) | phase_mult;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_H_PHASE);
+
+ input = line->fmt[MSM_VFE_PAD_SINK].height - 1;
+ output = line->compose.height - 1;
+ reg = (output << 16) | input;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_V_IMAGE_SIZE);
+
+ interp_reso = vfe_calc_interp_reso(input, output);
+ phase_mult = input * (1 << (14 + interp_reso)) / output;
+ reg = (interp_reso << 28) | phase_mult;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_V_PHASE);
+
+ writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_CBCR_CFG);
+
+ input = line->fmt[MSM_VFE_PAD_SINK].width - 1;
+ output = line->compose.width / 2 - 1;
+ reg = (output << 16) | input;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE);
+
+ interp_reso = vfe_calc_interp_reso(input, output);
+ phase_mult = input * (1 << (14 + interp_reso)) / output;
+ reg = (interp_reso << 28) | phase_mult;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_PHASE);
+
+ input = line->fmt[MSM_VFE_PAD_SINK].height - 1;
+ output = line->compose.height - 1;
+ if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21)
+ output = line->compose.height / 2 - 1;
+ reg = (output << 16) | input;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE);
+
+ interp_reso = vfe_calc_interp_reso(input, output);
+ phase_mult = input * (1 << (14 + interp_reso)) / output;
+ reg = (interp_reso << 28) | phase_mult;
+ writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_PHASE);
+}
+
+static void vfe_set_crop_cfg(struct vfe_device *vfe, struct vfe_line *line)
+{
+ u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
+ u32 reg;
+ u16 first, last;
+
+ first = line->crop.left;
+ last = line->crop.left + line->crop.width - 1;
+ reg = (first << 16) | last;
+ writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_Y_WIDTH);
+
+ first = line->crop.top;
+ last = line->crop.top + line->crop.height - 1;
+ reg = (first << 16) | last;
+ writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_Y_HEIGHT);
+
+ first = line->crop.left / 2;
+ last = line->crop.left / 2 + line->crop.width / 2 - 1;
+ reg = (first << 16) | last;
+ writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_CBCR_WIDTH);
+
+ first = line->crop.top;
+ last = line->crop.top + line->crop.height - 1;
+ if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21) {
+ first = line->crop.top / 2;
+ last = line->crop.top / 2 + line->crop.height / 2 - 1;
+ }
+ reg = (first << 16) | last;
+ writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_CBCR_HEIGHT);
+}
+
+static void vfe_set_clamp_cfg(struct vfe_device *vfe)
+{
+ u32 val = VFE_0_CLAMP_ENC_MAX_CFG_CH0 |
+ VFE_0_CLAMP_ENC_MAX_CFG_CH1 |
+ VFE_0_CLAMP_ENC_MAX_CFG_CH2;
+
+ writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MAX_CFG);
+
+ val = VFE_0_CLAMP_ENC_MIN_CFG_CH0 |
+ VFE_0_CLAMP_ENC_MIN_CFG_CH1 |
+ VFE_0_CLAMP_ENC_MIN_CFG_CH2;
+
+ writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MIN_CFG);
+}
+
+static void vfe_set_qos(struct vfe_device *vfe)
+{
+ u32 val = VFE_0_BUS_BDG_QOS_CFG_0_CFG;
+ u32 val7 = VFE_0_BUS_BDG_QOS_CFG_7_CFG;
+
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_0);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_1);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_2);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_3);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_4);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_5);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_6);
+ writel_relaxed(val7, vfe->base + VFE_0_BUS_BDG_QOS_CFG_7);
+}
+
+static void vfe_set_ds(struct vfe_device *vfe)
+{
+ u32 val = VFE_0_BUS_BDG_DS_CFG_0_CFG;
+ u32 val16 = VFE_0_BUS_BDG_DS_CFG_16_CFG;
+
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_0);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_1);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_2);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_3);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_4);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_5);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_6);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_7);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_8);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_9);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_10);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_11);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_12);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_13);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_14);
+ writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_DS_CFG_15);
+ writel_relaxed(val16, vfe->base + VFE_0_BUS_BDG_DS_CFG_16);
+}
+
+static void vfe_set_cgc_override(struct vfe_device *vfe, u8 wm, u8 enable)
+{
+ /* empty */
+}
+
+static void vfe_set_camif_cfg(struct vfe_device *vfe, struct vfe_line *line)
+{
+ u32 val;
+
+ switch (line->fmt[MSM_VFE_PAD_SINK].code) {
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ val = VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR;
+ break;
+ case MEDIA_BUS_FMT_YVYU8_2X8:
+ val = VFE_0_CORE_CFG_PIXEL_PATTERN_YCRYCB;
+ break;
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ default:
+ val = VFE_0_CORE_CFG_PIXEL_PATTERN_CBYCRY;
+ break;
+ case MEDIA_BUS_FMT_VYUY8_2X8:
+ val = VFE_0_CORE_CFG_PIXEL_PATTERN_CRYCBY;
+ break;
+ }
+
+ val |= VFE_0_CORE_CFG_COMPOSITE_REG_UPDATE_EN;
+ writel_relaxed(val, vfe->base + VFE_0_CORE_CFG);
+
+ val = line->fmt[MSM_VFE_PAD_SINK].width * 2 - 1;
+ val |= (line->fmt[MSM_VFE_PAD_SINK].height - 1) << 16;
+ writel_relaxed(val, vfe->base + VFE_0_CAMIF_FRAME_CFG);
+
+ val = line->fmt[MSM_VFE_PAD_SINK].width * 2 - 1;
+ writel_relaxed(val, vfe->base + VFE_0_CAMIF_WINDOW_WIDTH_CFG);
+
+ val = line->fmt[MSM_VFE_PAD_SINK].height - 1;
+ writel_relaxed(val, vfe->base + VFE_0_CAMIF_WINDOW_HEIGHT_CFG);
+
+ val = 0xffffffff;
+ writel_relaxed(val, vfe->base + VFE_0_CAMIF_SUBSAMPLE_CFG);
+
+ val = 0xffffffff;
+ writel_relaxed(val, vfe->base + VFE_0_CAMIF_IRQ_FRAMEDROP_PATTERN);
+
+ val = 0xffffffff;
+ writel_relaxed(val, vfe->base + VFE_0_CAMIF_IRQ_SUBSAMPLE_PATTERN);
+
+ val = VFE_0_RDI_CFG_x_MIPI_EN_BITS;
+ vfe_reg_set(vfe, VFE_0_RDI_CFG_x(0), val);
+
+ val = VFE_0_CAMIF_CFG_VFE_OUTPUT_EN;
+ writel_relaxed(val, vfe->base + VFE_0_CAMIF_CFG);
+}
+
+static void vfe_set_camif_cmd(struct vfe_device *vfe, u8 enable)
+{
+ u32 cmd;
+
+ cmd = VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS | VFE_0_CAMIF_CMD_NO_CHANGE;
+ writel_relaxed(cmd, vfe->base + VFE_0_CAMIF_CMD);
+ wmb();
+
+ if (enable)
+ cmd = VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY;
+ else
+ cmd = VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY;
+
+ writel_relaxed(cmd, vfe->base + VFE_0_CAMIF_CMD);
+}
+
+static void vfe_set_module_cfg(struct vfe_device *vfe, u8 enable)
+{
+ u32 val_lens = VFE_0_MODULE_LENS_EN_DEMUX |
+ VFE_0_MODULE_LENS_EN_CHROMA_UPSAMPLE;
+ u32 val_zoom = VFE_0_MODULE_ZOOM_EN_SCALE_ENC |
+ VFE_0_MODULE_ZOOM_EN_CROP_ENC;
+
+ if (enable) {
+ vfe_reg_set(vfe, VFE_0_MODULE_LENS_EN, val_lens);
+ vfe_reg_set(vfe, VFE_0_MODULE_ZOOM_EN, val_zoom);
+ } else {
+ vfe_reg_clr(vfe, VFE_0_MODULE_LENS_EN, val_lens);
+ vfe_reg_clr(vfe, VFE_0_MODULE_ZOOM_EN, val_zoom);
+ }
+}
+
+static int vfe_camif_wait_for_stop(struct vfe_device *vfe, struct device *dev)
+{
+ u32 val;
+ int ret;
+
+ ret = readl_poll_timeout(vfe->base + VFE_0_CAMIF_STATUS,
+ val,
+ (val & VFE_0_CAMIF_STATUS_HALT),
+ CAMIF_TIMEOUT_SLEEP_US,
+ CAMIF_TIMEOUT_ALL_US);
+ if (ret < 0)
+ dev_err(dev, "%s: camif stop timeout\n", __func__);
+
+ return ret;
+}
+
+static void vfe_isr_read(struct vfe_device *vfe, u32 *value0, u32 *value1)
+{
+ *value0 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_0);
+ *value1 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_1);
+
+ writel_relaxed(*value0, vfe->base + VFE_0_IRQ_CLEAR_0);
+ writel_relaxed(*value1, vfe->base + VFE_0_IRQ_CLEAR_1);
+
+ wmb();
+ writel_relaxed(VFE_0_IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_0_IRQ_CMD);
+}
+
+static void vfe_violation_read(struct vfe_device *vfe)
+{
+ u32 violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS);
+
+ pr_err_ratelimited("VFE: violation = 0x%08x\n", violation);
+}
+
+/*
+ * vfe_isr - ISPIF module interrupt handler
+ * @irq: Interrupt line
+ * @dev: VFE device
+ *
+ * Return IRQ_HANDLED on success
+ */
+static irqreturn_t vfe_isr(int irq, void *dev)
+{
+ struct vfe_device *vfe = dev;
+ u32 value0, value1;
+ int i, j;
+
+ vfe->ops->isr_read(vfe, &value0, &value1);
+
+ trace_printk("VFE: status0 = 0x%08x, status1 = 0x%08x\n",
+ value0, value1);
+
+ if (value0 & VFE_0_IRQ_STATUS_0_RESET_ACK)
+ vfe->isr_ops.reset_ack(vfe);
+
+ if (value1 & VFE_0_IRQ_STATUS_1_VIOLATION)
+ vfe->ops->violation_read(vfe);
+
+ if (value1 & VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK)
+ vfe->isr_ops.halt_ack(vfe);
+
+ for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++)
+ if (value0 & VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(i))
+ vfe->isr_ops.reg_update(vfe, i);
+
+ if (value0 & VFE_0_IRQ_STATUS_0_CAMIF_SOF)
+ vfe->isr_ops.sof(vfe, VFE_LINE_PIX);
+
+ for (i = VFE_LINE_RDI0; i <= VFE_LINE_RDI2; i++)
+ if (value1 & VFE_0_IRQ_STATUS_1_RDIn_SOF(i))
+ vfe->isr_ops.sof(vfe, i);
+
+ for (i = 0; i < MSM_VFE_COMPOSITE_IRQ_NUM; i++)
+ if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_COMPOSITE_DONE_n(i)) {
+ vfe->isr_ops.comp_done(vfe, i);
+ for (j = 0; j < ARRAY_SIZE(vfe->wm_output_map); j++)
+ if (vfe->wm_output_map[j] == VFE_LINE_PIX)
+ value0 &= ~VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(j);
+ }
+
+ for (i = 0; i < MSM_VFE_IMAGE_MASTERS_NUM; i++)
+ if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(i))
+ vfe->isr_ops.wm_done(vfe, i);
+
+ return IRQ_HANDLED;
+}
+
+const struct vfe_hw_ops vfe_ops_4_7 = {
+ .hw_version_read = vfe_hw_version_read,
+ .get_ub_size = vfe_get_ub_size,
+ .global_reset = vfe_global_reset,
+ .halt_request = vfe_halt_request,
+ .halt_clear = vfe_halt_clear,
+ .wm_enable = vfe_wm_enable,
+ .wm_frame_based = vfe_wm_frame_based,
+ .wm_line_based = vfe_wm_line_based,
+ .wm_set_framedrop_period = vfe_wm_set_framedrop_period,
+ .wm_set_framedrop_pattern = vfe_wm_set_framedrop_pattern,
+ .wm_set_ub_cfg = vfe_wm_set_ub_cfg,
+ .bus_reload_wm = vfe_bus_reload_wm,
+ .wm_set_ping_addr = vfe_wm_set_ping_addr,
+ .wm_set_pong_addr = vfe_wm_set_pong_addr,
+ .wm_get_ping_pong_status = vfe_wm_get_ping_pong_status,
+ .bus_enable_wr_if = vfe_bus_enable_wr_if,
+ .bus_connect_wm_to_rdi = vfe_bus_connect_wm_to_rdi,
+ .wm_set_subsample = vfe_wm_set_subsample,
+ .bus_disconnect_wm_from_rdi = vfe_bus_disconnect_wm_from_rdi,
+ .set_xbar_cfg = vfe_set_xbar_cfg,
+ .set_realign_cfg = vfe_set_realign_cfg,
+ .set_rdi_cid = vfe_set_rdi_cid,
+ .reg_update = vfe_reg_update,
+ .reg_update_clear = vfe_reg_update_clear,
+ .enable_irq_wm_line = vfe_enable_irq_wm_line,
+ .enable_irq_pix_line = vfe_enable_irq_pix_line,
+ .enable_irq_common = vfe_enable_irq_common,
+ .set_demux_cfg = vfe_set_demux_cfg,
+ .set_scale_cfg = vfe_set_scale_cfg,
+ .set_crop_cfg = vfe_set_crop_cfg,
+ .set_clamp_cfg = vfe_set_clamp_cfg,
+ .set_qos = vfe_set_qos,
+ .set_ds = vfe_set_ds,
+ .set_cgc_override = vfe_set_cgc_override,
+ .set_camif_cfg = vfe_set_camif_cfg,
+ .set_camif_cmd = vfe_set_camif_cmd,
+ .set_module_cfg = vfe_set_module_cfg,
+ .camif_wait_for_stop = vfe_camif_wait_for_stop,
+ .isr_read = vfe_isr_read,
+ .violation_read = vfe_violation_read,
+ .isr = vfe_isr,
+};
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c
index a6329a8a7c4a..ed6a557de65d 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-vfe.c
+++ b/drivers/media/platform/qcom/camss/camss-vfe.c
@@ -1,28 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* camss-vfe.c
*
* Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module
*
* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
- * Copyright (C) 2015-2017 Linaro Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * Copyright (C) 2015-2018 Linaro Ltd.
*/
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/interrupt.h>
#include <linux/iommu.h>
-#include <linux/iopoll.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/spinlock_types.h>
#include <linux/spinlock.h>
#include <media/media-entity.h>
@@ -38,194 +30,7 @@
((const struct vfe_line (*)[]) &(ptr_line[-(ptr_line->id)]))
#define to_vfe(ptr_line) \
- container_of(vfe_line_array(ptr_line), struct vfe_device, ptr_line)
-
-#define VFE_0_HW_VERSION 0x000
-
-#define VFE_0_GLOBAL_RESET_CMD 0x00c
-#define VFE_0_GLOBAL_RESET_CMD_CORE (1 << 0)
-#define VFE_0_GLOBAL_RESET_CMD_CAMIF (1 << 1)
-#define VFE_0_GLOBAL_RESET_CMD_BUS (1 << 2)
-#define VFE_0_GLOBAL_RESET_CMD_BUS_BDG (1 << 3)
-#define VFE_0_GLOBAL_RESET_CMD_REGISTER (1 << 4)
-#define VFE_0_GLOBAL_RESET_CMD_TIMER (1 << 5)
-#define VFE_0_GLOBAL_RESET_CMD_PM (1 << 6)
-#define VFE_0_GLOBAL_RESET_CMD_BUS_MISR (1 << 7)
-#define VFE_0_GLOBAL_RESET_CMD_TESTGEN (1 << 8)
-
-#define VFE_0_MODULE_CFG 0x018
-#define VFE_0_MODULE_CFG_DEMUX (1 << 2)
-#define VFE_0_MODULE_CFG_CHROMA_UPSAMPLE (1 << 3)
-#define VFE_0_MODULE_CFG_SCALE_ENC (1 << 23)
-#define VFE_0_MODULE_CFG_CROP_ENC (1 << 27)
-
-#define VFE_0_CORE_CFG 0x01c
-#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR 0x4
-#define VFE_0_CORE_CFG_PIXEL_PATTERN_YCRYCB 0x5
-#define VFE_0_CORE_CFG_PIXEL_PATTERN_CBYCRY 0x6
-#define VFE_0_CORE_CFG_PIXEL_PATTERN_CRYCBY 0x7
-
-#define VFE_0_IRQ_CMD 0x024
-#define VFE_0_IRQ_CMD_GLOBAL_CLEAR (1 << 0)
-
-#define VFE_0_IRQ_MASK_0 0x028
-#define VFE_0_IRQ_MASK_0_CAMIF_SOF (1 << 0)
-#define VFE_0_IRQ_MASK_0_CAMIF_EOF (1 << 1)
-#define VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n) (1 << ((n) + 5))
-#define VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(n) \
- ((n) == VFE_LINE_PIX ? (1 << 4) : VFE_0_IRQ_MASK_0_RDIn_REG_UPDATE(n))
-#define VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(n) (1 << ((n) + 8))
-#define VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(n) (1 << ((n) + 25))
-#define VFE_0_IRQ_MASK_0_RESET_ACK (1 << 31)
-#define VFE_0_IRQ_MASK_1 0x02c
-#define VFE_0_IRQ_MASK_1_CAMIF_ERROR (1 << 0)
-#define VFE_0_IRQ_MASK_1_VIOLATION (1 << 7)
-#define VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK (1 << 8)
-#define VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(n) (1 << ((n) + 9))
-#define VFE_0_IRQ_MASK_1_RDIn_SOF(n) (1 << ((n) + 29))
-
-#define VFE_0_IRQ_CLEAR_0 0x030
-#define VFE_0_IRQ_CLEAR_1 0x034
-
-#define VFE_0_IRQ_STATUS_0 0x038
-#define VFE_0_IRQ_STATUS_0_CAMIF_SOF (1 << 0)
-#define VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n) (1 << ((n) + 5))
-#define VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(n) \
- ((n) == VFE_LINE_PIX ? (1 << 4) : VFE_0_IRQ_STATUS_0_RDIn_REG_UPDATE(n))
-#define VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(n) (1 << ((n) + 8))
-#define VFE_0_IRQ_STATUS_0_IMAGE_COMPOSITE_DONE_n(n) (1 << ((n) + 25))
-#define VFE_0_IRQ_STATUS_0_RESET_ACK (1 << 31)
-#define VFE_0_IRQ_STATUS_1 0x03c
-#define VFE_0_IRQ_STATUS_1_VIOLATION (1 << 7)
-#define VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK (1 << 8)
-#define VFE_0_IRQ_STATUS_1_RDIn_SOF(n) (1 << ((n) + 29))
-
-#define VFE_0_IRQ_COMPOSITE_MASK_0 0x40
-#define VFE_0_VIOLATION_STATUS 0x48
-
-#define VFE_0_BUS_CMD 0x4c
-#define VFE_0_BUS_CMD_Mx_RLD_CMD(x) (1 << (x))
-
-#define VFE_0_BUS_CFG 0x050
-
-#define VFE_0_BUS_XBAR_CFG_x(x) (0x58 + 0x4 * ((x) / 2))
-#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN (1 << 1)
-#define VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA (0x3 << 4)
-#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT 8
-#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA 0
-#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 5
-#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 6
-#define VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 7
-
-#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(n) (0x06c + 0x24 * (n))
-#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT 0
-#define VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT 1
-#define VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(n) (0x070 + 0x24 * (n))
-#define VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(n) (0x074 + 0x24 * (n))
-#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(n) (0x078 + 0x24 * (n))
-#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT 2
-#define VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK (0x1F << 2)
-
-#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(n) (0x07c + 0x24 * (n))
-#define VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT 16
-#define VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(n) (0x080 + 0x24 * (n))
-#define VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(n) (0x084 + 0x24 * (n))
-#define VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(n) \
- (0x088 + 0x24 * (n))
-#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(n) \
- (0x08c + 0x24 * (n))
-#define VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF 0xffffffff
-
-#define VFE_0_BUS_PING_PONG_STATUS 0x268
-
-#define VFE_0_BUS_BDG_CMD 0x2c0
-#define VFE_0_BUS_BDG_CMD_HALT_REQ 1
-
-#define VFE_0_BUS_BDG_QOS_CFG_0 0x2c4
-#define VFE_0_BUS_BDG_QOS_CFG_0_CFG 0xaaa5aaa5
-#define VFE_0_BUS_BDG_QOS_CFG_1 0x2c8
-#define VFE_0_BUS_BDG_QOS_CFG_2 0x2cc
-#define VFE_0_BUS_BDG_QOS_CFG_3 0x2d0
-#define VFE_0_BUS_BDG_QOS_CFG_4 0x2d4
-#define VFE_0_BUS_BDG_QOS_CFG_5 0x2d8
-#define VFE_0_BUS_BDG_QOS_CFG_6 0x2dc
-#define VFE_0_BUS_BDG_QOS_CFG_7 0x2e0
-#define VFE_0_BUS_BDG_QOS_CFG_7_CFG 0x0001aaa5
-
-#define VFE_0_RDI_CFG_x(x) (0x2e8 + (0x4 * (x)))
-#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT 28
-#define VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK (0xf << 28)
-#define VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT 4
-#define VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK (0xf << 4)
-#define VFE_0_RDI_CFG_x_RDI_EN_BIT (1 << 2)
-#define VFE_0_RDI_CFG_x_MIPI_EN_BITS 0x3
-#define VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(r) (1 << (16 + (r)))
-
-#define VFE_0_CAMIF_CMD 0x2f4
-#define VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY 0
-#define VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY 1
-#define VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS (1 << 2)
-#define VFE_0_CAMIF_CFG 0x2f8
-#define VFE_0_CAMIF_CFG_VFE_OUTPUT_EN (1 << 6)
-#define VFE_0_CAMIF_FRAME_CFG 0x300
-#define VFE_0_CAMIF_WINDOW_WIDTH_CFG 0x304
-#define VFE_0_CAMIF_WINDOW_HEIGHT_CFG 0x308
-#define VFE_0_CAMIF_SUBSAMPLE_CFG_0 0x30c
-#define VFE_0_CAMIF_IRQ_SUBSAMPLE_PATTERN 0x314
-#define VFE_0_CAMIF_STATUS 0x31c
-#define VFE_0_CAMIF_STATUS_HALT (1 << 31)
-
-#define VFE_0_REG_UPDATE 0x378
-#define VFE_0_REG_UPDATE_RDIn(n) (1 << (1 + (n)))
-#define VFE_0_REG_UPDATE_line_n(n) \
- ((n) == VFE_LINE_PIX ? 1 : VFE_0_REG_UPDATE_RDIn(n))
-
-#define VFE_0_DEMUX_CFG 0x424
-#define VFE_0_DEMUX_CFG_PERIOD 0x3
-#define VFE_0_DEMUX_GAIN_0 0x428
-#define VFE_0_DEMUX_GAIN_0_CH0_EVEN (0x80 << 0)
-#define VFE_0_DEMUX_GAIN_0_CH0_ODD (0x80 << 16)
-#define VFE_0_DEMUX_GAIN_1 0x42c
-#define VFE_0_DEMUX_GAIN_1_CH1 (0x80 << 0)
-#define VFE_0_DEMUX_GAIN_1_CH2 (0x80 << 16)
-#define VFE_0_DEMUX_EVEN_CFG 0x438
-#define VFE_0_DEMUX_EVEN_CFG_PATTERN_YUYV 0x9cac
-#define VFE_0_DEMUX_EVEN_CFG_PATTERN_YVYU 0xac9c
-#define VFE_0_DEMUX_EVEN_CFG_PATTERN_UYVY 0xc9ca
-#define VFE_0_DEMUX_EVEN_CFG_PATTERN_VYUY 0xcac9
-#define VFE_0_DEMUX_ODD_CFG 0x43c
-#define VFE_0_DEMUX_ODD_CFG_PATTERN_YUYV 0x9cac
-#define VFE_0_DEMUX_ODD_CFG_PATTERN_YVYU 0xac9c
-#define VFE_0_DEMUX_ODD_CFG_PATTERN_UYVY 0xc9ca
-#define VFE_0_DEMUX_ODD_CFG_PATTERN_VYUY 0xcac9
-
-#define VFE_0_SCALE_ENC_Y_CFG 0x75c
-#define VFE_0_SCALE_ENC_Y_H_IMAGE_SIZE 0x760
-#define VFE_0_SCALE_ENC_Y_H_PHASE 0x764
-#define VFE_0_SCALE_ENC_Y_V_IMAGE_SIZE 0x76c
-#define VFE_0_SCALE_ENC_Y_V_PHASE 0x770
-#define VFE_0_SCALE_ENC_CBCR_CFG 0x778
-#define VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE 0x77c
-#define VFE_0_SCALE_ENC_CBCR_H_PHASE 0x780
-#define VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE 0x790
-#define VFE_0_SCALE_ENC_CBCR_V_PHASE 0x794
-
-#define VFE_0_CROP_ENC_Y_WIDTH 0x854
-#define VFE_0_CROP_ENC_Y_HEIGHT 0x858
-#define VFE_0_CROP_ENC_CBCR_WIDTH 0x85c
-#define VFE_0_CROP_ENC_CBCR_HEIGHT 0x860
-
-#define VFE_0_CLAMP_ENC_MAX_CFG 0x874
-#define VFE_0_CLAMP_ENC_MAX_CFG_CH0 (0xff << 0)
-#define VFE_0_CLAMP_ENC_MAX_CFG_CH1 (0xff << 8)
-#define VFE_0_CLAMP_ENC_MAX_CFG_CH2 (0xff << 16)
-#define VFE_0_CLAMP_ENC_MIN_CFG 0x878
-#define VFE_0_CLAMP_ENC_MIN_CFG_CH0 (0x0 << 0)
-#define VFE_0_CLAMP_ENC_MIN_CFG_CH1 (0x0 << 8)
-#define VFE_0_CLAMP_ENC_MIN_CFG_CH2 (0x0 << 16)
-
-#define VFE_0_CGC_OVERRIDE_1 0x974
-#define VFE_0_CGC_OVERRIDE_1_IMAGE_Mx_CGC_OVERRIDE(x) (1 << (x))
+ container_of(vfe_line_array(ptr_line), struct vfe_device, line)
/* VFE reset timeout */
#define VFE_RESET_TIMEOUT_MS 50
@@ -238,633 +43,230 @@
#define VFE_NEXT_SOF_MS 500
-#define CAMIF_TIMEOUT_SLEEP_US 1000
-#define CAMIF_TIMEOUT_ALL_US 1000000
-
#define SCALER_RATIO_MAX 16
-static const struct {
+struct vfe_format {
u32 code;
u8 bpp;
-} vfe_formats[] = {
- {
- MEDIA_BUS_FMT_UYVY8_2X8,
- 8,
- },
- {
- MEDIA_BUS_FMT_VYUY8_2X8,
- 8,
- },
- {
- MEDIA_BUS_FMT_YUYV8_2X8,
- 8,
- },
- {
- MEDIA_BUS_FMT_YVYU8_2X8,
- 8,
- },
- {
- MEDIA_BUS_FMT_SBGGR8_1X8,
- 8,
- },
- {
- MEDIA_BUS_FMT_SGBRG8_1X8,
- 8,
- },
- {
- MEDIA_BUS_FMT_SGRBG8_1X8,
- 8,
- },
- {
- MEDIA_BUS_FMT_SRGGB8_1X8,
- 8,
- },
- {
- MEDIA_BUS_FMT_SBGGR10_1X10,
- 10,
- },
- {
- MEDIA_BUS_FMT_SGBRG10_1X10,
- 10,
- },
- {
- MEDIA_BUS_FMT_SGRBG10_1X10,
- 10,
- },
- {
- MEDIA_BUS_FMT_SRGGB10_1X10,
- 10,
- },
- {
- MEDIA_BUS_FMT_SBGGR12_1X12,
- 12,
- },
- {
- MEDIA_BUS_FMT_SGBRG12_1X12,
- 12,
- },
- {
- MEDIA_BUS_FMT_SGRBG12_1X12,
- 12,
- },
- {
- MEDIA_BUS_FMT_SRGGB12_1X12,
- 12,
- }
+};
+
+static const struct vfe_format formats_rdi_8x16[] = {
+ { MEDIA_BUS_FMT_UYVY8_2X8, 8 },
+ { MEDIA_BUS_FMT_VYUY8_2X8, 8 },
+ { MEDIA_BUS_FMT_YUYV8_2X8, 8 },
+ { MEDIA_BUS_FMT_YVYU8_2X8, 8 },
+ { MEDIA_BUS_FMT_SBGGR8_1X8, 8 },
+ { MEDIA_BUS_FMT_SGBRG8_1X8, 8 },
+ { MEDIA_BUS_FMT_SGRBG8_1X8, 8 },
+ { MEDIA_BUS_FMT_SRGGB8_1X8, 8 },
+ { MEDIA_BUS_FMT_SBGGR10_1X10, 10 },
+ { MEDIA_BUS_FMT_SGBRG10_1X10, 10 },
+ { MEDIA_BUS_FMT_SGRBG10_1X10, 10 },
+ { MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
+ { MEDIA_BUS_FMT_SBGGR12_1X12, 12 },
+ { MEDIA_BUS_FMT_SGBRG12_1X12, 12 },
+ { MEDIA_BUS_FMT_SGRBG12_1X12, 12 },
+ { MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
+ { MEDIA_BUS_FMT_Y10_1X10, 10 },
+};
+
+static const struct vfe_format formats_pix_8x16[] = {
+ { MEDIA_BUS_FMT_UYVY8_2X8, 8 },
+ { MEDIA_BUS_FMT_VYUY8_2X8, 8 },
+ { MEDIA_BUS_FMT_YUYV8_2X8, 8 },
+ { MEDIA_BUS_FMT_YVYU8_2X8, 8 },
+};
+
+static const struct vfe_format formats_rdi_8x96[] = {
+ { MEDIA_BUS_FMT_UYVY8_2X8, 8 },
+ { MEDIA_BUS_FMT_VYUY8_2X8, 8 },
+ { MEDIA_BUS_FMT_YUYV8_2X8, 8 },
+ { MEDIA_BUS_FMT_YVYU8_2X8, 8 },
+ { MEDIA_BUS_FMT_SBGGR8_1X8, 8 },
+ { MEDIA_BUS_FMT_SGBRG8_1X8, 8 },
+ { MEDIA_BUS_FMT_SGRBG8_1X8, 8 },
+ { MEDIA_BUS_FMT_SRGGB8_1X8, 8 },
+ { MEDIA_BUS_FMT_SBGGR10_1X10, 10 },
+ { MEDIA_BUS_FMT_SGBRG10_1X10, 10 },
+ { MEDIA_BUS_FMT_SGRBG10_1X10, 10 },
+ { MEDIA_BUS_FMT_SRGGB10_1X10, 10 },
+ { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16 },
+ { MEDIA_BUS_FMT_SBGGR12_1X12, 12 },
+ { MEDIA_BUS_FMT_SGBRG12_1X12, 12 },
+ { MEDIA_BUS_FMT_SGRBG12_1X12, 12 },
+ { MEDIA_BUS_FMT_SRGGB12_1X12, 12 },
+ { MEDIA_BUS_FMT_SBGGR14_1X14, 14 },
+ { MEDIA_BUS_FMT_SGBRG14_1X14, 14 },
+ { MEDIA_BUS_FMT_SGRBG14_1X14, 14 },
+ { MEDIA_BUS_FMT_SRGGB14_1X14, 14 },
+ { MEDIA_BUS_FMT_Y10_1X10, 10 },
+ { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16 },
+};
+
+static const struct vfe_format formats_pix_8x96[] = {
+ { MEDIA_BUS_FMT_UYVY8_2X8, 8 },
+ { MEDIA_BUS_FMT_VYUY8_2X8, 8 },
+ { MEDIA_BUS_FMT_YUYV8_2X8, 8 },
+ { MEDIA_BUS_FMT_YVYU8_2X8, 8 },
};
/*
* vfe_get_bpp - map media bus format to bits per pixel
+ * @formats: supported media bus formats array
+ * @nformats: size of @formats array
* @code: media bus format code
*
* Return number of bits per pixel
*/
-static u8 vfe_get_bpp(u32 code)
+static u8 vfe_get_bpp(const struct vfe_format *formats,
+ unsigned int nformats, u32 code)
{
unsigned int i;
- for (i = 0; i < ARRAY_SIZE(vfe_formats); i++)
- if (code == vfe_formats[i].code)
- return vfe_formats[i].bpp;
+ for (i = 0; i < nformats; i++)
+ if (code == formats[i].code)
+ return formats[i].bpp;
WARN(1, "Unknown format\n");
- return vfe_formats[0].bpp;
-}
-
-static inline void vfe_reg_clr(struct vfe_device *vfe, u32 reg, u32 clr_bits)
-{
- u32 bits = readl_relaxed(vfe->base + reg);
-
- writel_relaxed(bits & ~clr_bits, vfe->base + reg);
-}
-
-static inline void vfe_reg_set(struct vfe_device *vfe, u32 reg, u32 set_bits)
-{
- u32 bits = readl_relaxed(vfe->base + reg);
-
- writel_relaxed(bits | set_bits, vfe->base + reg);
-}
-
-static void vfe_global_reset(struct vfe_device *vfe)
-{
- u32 reset_bits = VFE_0_GLOBAL_RESET_CMD_TESTGEN |
- VFE_0_GLOBAL_RESET_CMD_BUS_MISR |
- VFE_0_GLOBAL_RESET_CMD_PM |
- VFE_0_GLOBAL_RESET_CMD_TIMER |
- VFE_0_GLOBAL_RESET_CMD_REGISTER |
- VFE_0_GLOBAL_RESET_CMD_BUS_BDG |
- VFE_0_GLOBAL_RESET_CMD_BUS |
- VFE_0_GLOBAL_RESET_CMD_CAMIF |
- VFE_0_GLOBAL_RESET_CMD_CORE;
-
- writel_relaxed(reset_bits, vfe->base + VFE_0_GLOBAL_RESET_CMD);
-}
-
-static void vfe_wm_enable(struct vfe_device *vfe, u8 wm, u8 enable)
-{
- if (enable)
- vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm),
- 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT);
- else
- vfe_reg_clr(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm),
- 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_WR_PATH_SHIFT);
+ return formats[0].bpp;
}
-static void vfe_wm_frame_based(struct vfe_device *vfe, u8 wm, u8 enable)
-{
- if (enable)
- vfe_reg_set(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm),
- 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT);
- else
- vfe_reg_clr(vfe, VFE_0_BUS_IMAGE_MASTER_n_WR_CFG(wm),
- 1 << VFE_0_BUS_IMAGE_MASTER_n_WR_CFG_FRM_BASED_SHIFT);
-}
-
-#define CALC_WORD(width, M, N) (((width) * (M) + (N) - 1) / (N))
-
-static int vfe_word_per_line(uint32_t format, uint32_t pixel_per_line)
+static u32 vfe_find_code(u32 *code, unsigned int n_code,
+ unsigned int index, u32 req_code)
{
- int val = 0;
-
- switch (format) {
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV21:
- case V4L2_PIX_FMT_NV16:
- case V4L2_PIX_FMT_NV61:
- val = CALC_WORD(pixel_per_line, 1, 8);
- break;
- case V4L2_PIX_FMT_YUYV:
- case V4L2_PIX_FMT_YVYU:
- case V4L2_PIX_FMT_UYVY:
- case V4L2_PIX_FMT_VYUY:
- val = CALC_WORD(pixel_per_line, 2, 8);
- break;
- }
-
- return val;
-}
-
-static void vfe_get_wm_sizes(struct v4l2_pix_format_mplane *pix, u8 plane,
- u16 *width, u16 *height, u16 *bytesperline)
-{
- switch (pix->pixelformat) {
- case V4L2_PIX_FMT_NV12:
- case V4L2_PIX_FMT_NV21:
- *width = pix->width;
- *height = pix->height;
- *bytesperline = pix->plane_fmt[0].bytesperline;
- if (plane == 1)
- *height /= 2;
- break;
- case V4L2_PIX_FMT_NV16:
- case V4L2_PIX_FMT_NV61:
- *width = pix->width;
- *height = pix->height;
- *bytesperline = pix->plane_fmt[0].bytesperline;
- break;
- }
-}
-
-static void vfe_wm_line_based(struct vfe_device *vfe, u32 wm,
- struct v4l2_pix_format_mplane *pix,
- u8 plane, u32 enable)
-{
- u32 reg;
-
- if (enable) {
- u16 width = 0, height = 0, bytesperline = 0, wpl;
-
- vfe_get_wm_sizes(pix, plane, &width, &height, &bytesperline);
-
- wpl = vfe_word_per_line(pix->pixelformat, width);
-
- reg = height - 1;
- reg |= ((wpl + 1) / 2 - 1) << 16;
-
- writel_relaxed(reg, vfe->base +
- VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm));
-
- wpl = vfe_word_per_line(pix->pixelformat, bytesperline);
-
- reg = 0x3;
- reg |= (height - 1) << 4;
- reg |= wpl << 16;
-
- writel_relaxed(reg, vfe->base +
- VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm));
- } else {
- writel_relaxed(0, vfe->base +
- VFE_0_BUS_IMAGE_MASTER_n_WR_IMAGE_SIZE(wm));
- writel_relaxed(0, vfe->base +
- VFE_0_BUS_IMAGE_MASTER_n_WR_BUFFER_CFG(wm));
- }
-}
-
-static void vfe_wm_set_framedrop_period(struct vfe_device *vfe, u8 wm, u8 per)
-{
- u32 reg;
-
- reg = readl_relaxed(vfe->base +
- VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm));
-
- reg &= ~(VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK);
-
- reg |= (per << VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_SHIFT)
- & VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG_FRM_DROP_PER_MASK;
-
- writel_relaxed(reg,
- vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_ADDR_CFG(wm));
-}
-
-static void vfe_wm_set_framedrop_pattern(struct vfe_device *vfe, u8 wm,
- u32 pattern)
-{
- writel_relaxed(pattern,
- vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_FRAMEDROP_PATTERN(wm));
-}
-
-static void vfe_wm_set_ub_cfg(struct vfe_device *vfe, u8 wm, u16 offset,
- u16 depth)
-{
- u32 reg;
-
- reg = (offset << VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG_OFFSET_SHIFT) |
- depth;
- writel_relaxed(reg, vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_UB_CFG(wm));
-}
-
-static void vfe_bus_reload_wm(struct vfe_device *vfe, u8 wm)
-{
- wmb();
- writel_relaxed(VFE_0_BUS_CMD_Mx_RLD_CMD(wm), vfe->base + VFE_0_BUS_CMD);
- wmb();
-}
-
-static void vfe_wm_set_ping_addr(struct vfe_device *vfe, u8 wm, u32 addr)
-{
- writel_relaxed(addr,
- vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PING_ADDR(wm));
-}
-
-static void vfe_wm_set_pong_addr(struct vfe_device *vfe, u8 wm, u32 addr)
-{
- writel_relaxed(addr,
- vfe->base + VFE_0_BUS_IMAGE_MASTER_n_WR_PONG_ADDR(wm));
-}
-
-static int vfe_wm_get_ping_pong_status(struct vfe_device *vfe, u8 wm)
-{
- u32 reg;
-
- reg = readl_relaxed(vfe->base + VFE_0_BUS_PING_PONG_STATUS);
-
- return (reg >> wm) & 0x1;
-}
-
-static void vfe_bus_enable_wr_if(struct vfe_device *vfe, u8 enable)
-{
- if (enable)
- writel_relaxed(0x10000009, vfe->base + VFE_0_BUS_CFG);
- else
- writel_relaxed(0, vfe->base + VFE_0_BUS_CFG);
-}
-
-static void vfe_bus_connect_wm_to_rdi(struct vfe_device *vfe, u8 wm,
- enum vfe_line_id id)
-{
- u32 reg;
-
- reg = VFE_0_RDI_CFG_x_MIPI_EN_BITS;
- reg |= VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(id);
- vfe_reg_set(vfe, VFE_0_RDI_CFG_x(0), reg);
-
- reg = VFE_0_RDI_CFG_x_RDI_EN_BIT;
- reg |= ((3 * id) << VFE_0_RDI_CFG_x_RDI_STREAM_SEL_SHIFT) &
- VFE_0_RDI_CFG_x_RDI_STREAM_SEL_MASK;
- vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id), reg);
-
- switch (id) {
- case VFE_LINE_RDI0:
- default:
- reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 <<
- VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
- break;
- case VFE_LINE_RDI1:
- reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 <<
- VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
- break;
- case VFE_LINE_RDI2:
- reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 <<
- VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
- break;
- }
-
- if (wm % 2 == 1)
- reg <<= 16;
-
- vfe_reg_set(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg);
-}
-
-static void vfe_wm_set_subsample(struct vfe_device *vfe, u8 wm)
-{
- writel_relaxed(VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN_DEF,
- vfe->base +
- VFE_0_BUS_IMAGE_MASTER_n_WR_IRQ_SUBSAMPLE_PATTERN(wm));
-}
-
-static void vfe_bus_disconnect_wm_from_rdi(struct vfe_device *vfe, u8 wm,
- enum vfe_line_id id)
-{
- u32 reg;
-
- reg = VFE_0_RDI_CFG_x_RDI_Mr_FRAME_BASED_EN(id);
- vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(0), reg);
-
- reg = VFE_0_RDI_CFG_x_RDI_EN_BIT;
- vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id), reg);
-
- switch (id) {
- case VFE_LINE_RDI0:
- default:
- reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI0 <<
- VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
- break;
- case VFE_LINE_RDI1:
- reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI1 <<
- VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
- break;
- case VFE_LINE_RDI2:
- reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_VAL_RDI2 <<
- VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
- break;
- }
-
- if (wm % 2 == 1)
- reg <<= 16;
-
- vfe_reg_clr(vfe, VFE_0_BUS_XBAR_CFG_x(wm), reg);
-}
+ int i;
-static void vfe_set_xbar_cfg(struct vfe_device *vfe, struct vfe_output *output,
- u8 enable)
-{
- struct vfe_line *line = container_of(output, struct vfe_line, output);
- u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
- u32 reg;
- unsigned int i;
+ if (!req_code && (index >= n_code))
+ return 0;
- for (i = 0; i < output->wm_num; i++) {
- if (i == 0) {
- reg = VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_LUMA <<
- VFE_0_BUS_XBAR_CFG_x_M_SINGLE_STREAM_SEL_SHIFT;
- } else if (i == 1) {
- reg = VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_EN;
- if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV16)
- reg |= VFE_0_BUS_XBAR_CFG_x_M_PAIR_STREAM_SWAP_INTER_INTRA;
+ for (i = 0; i < n_code; i++)
+ if (req_code) {
+ if (req_code == code[i])
+ return req_code;
} else {
- /* On current devices output->wm_num is always <= 2 */
- break;
+ if (i == index)
+ return code[i];
}
- if (output->wm_idx[i] % 2 == 1)
- reg <<= 16;
-
- if (enable)
- vfe_reg_set(vfe,
- VFE_0_BUS_XBAR_CFG_x(output->wm_idx[i]),
- reg);
- else
- vfe_reg_clr(vfe,
- VFE_0_BUS_XBAR_CFG_x(output->wm_idx[i]),
- reg);
- }
-}
-
-static void vfe_set_rdi_cid(struct vfe_device *vfe, enum vfe_line_id id, u8 cid)
-{
- vfe_reg_clr(vfe, VFE_0_RDI_CFG_x(id),
- VFE_0_RDI_CFG_x_RDI_M0_SEL_MASK);
-
- vfe_reg_set(vfe, VFE_0_RDI_CFG_x(id),
- cid << VFE_0_RDI_CFG_x_RDI_M0_SEL_SHIFT);
+ return code[0];
}
-static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
+static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code,
+ unsigned int index, u32 src_req_code)
{
- vfe->reg_update |= VFE_0_REG_UPDATE_line_n(line_id);
- wmb();
- writel_relaxed(vfe->reg_update, vfe->base + VFE_0_REG_UPDATE);
- wmb();
-}
-
-static void vfe_enable_irq_wm_line(struct vfe_device *vfe, u8 wm,
- enum vfe_line_id line_id, u8 enable)
-{
- u32 irq_en0 = VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(wm) |
- VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id);
- u32 irq_en1 = VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(wm) |
- VFE_0_IRQ_MASK_1_RDIn_SOF(line_id);
-
- if (enable) {
- vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0);
- vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1);
- } else {
- vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0);
- vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1);
- }
-}
-
-static void vfe_enable_irq_pix_line(struct vfe_device *vfe, u8 comp,
- enum vfe_line_id line_id, u8 enable)
-{
- struct vfe_output *output = &vfe->line[line_id].output;
- unsigned int i;
- u32 irq_en0;
- u32 irq_en1;
- u32 comp_mask = 0;
-
- irq_en0 = VFE_0_IRQ_MASK_0_CAMIF_SOF;
- irq_en0 |= VFE_0_IRQ_MASK_0_CAMIF_EOF;
- irq_en0 |= VFE_0_IRQ_MASK_0_IMAGE_COMPOSITE_DONE_n(comp);
- irq_en0 |= VFE_0_IRQ_MASK_0_line_n_REG_UPDATE(line_id);
- irq_en1 = VFE_0_IRQ_MASK_1_CAMIF_ERROR;
- for (i = 0; i < output->wm_num; i++) {
- irq_en1 |= VFE_0_IRQ_MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(
- output->wm_idx[i]);
- comp_mask |= (1 << output->wm_idx[i]) << comp * 8;
- }
-
- if (enable) {
- vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0);
- vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1);
- vfe_reg_set(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask);
- } else {
- vfe_reg_clr(vfe, VFE_0_IRQ_MASK_0, irq_en0);
- vfe_reg_clr(vfe, VFE_0_IRQ_MASK_1, irq_en1);
- vfe_reg_clr(vfe, VFE_0_IRQ_COMPOSITE_MASK_0, comp_mask);
- }
-}
-
-static void vfe_enable_irq_common(struct vfe_device *vfe)
-{
- u32 irq_en0 = VFE_0_IRQ_MASK_0_RESET_ACK;
- u32 irq_en1 = VFE_0_IRQ_MASK_1_VIOLATION |
- VFE_0_IRQ_MASK_1_BUS_BDG_HALT_ACK;
-
- vfe_reg_set(vfe, VFE_0_IRQ_MASK_0, irq_en0);
- vfe_reg_set(vfe, VFE_0_IRQ_MASK_1, irq_en1);
-}
-
-static void vfe_set_demux_cfg(struct vfe_device *vfe, struct vfe_line *line)
-{
- u32 val, even_cfg, odd_cfg;
-
- writel_relaxed(VFE_0_DEMUX_CFG_PERIOD, vfe->base + VFE_0_DEMUX_CFG);
-
- val = VFE_0_DEMUX_GAIN_0_CH0_EVEN | VFE_0_DEMUX_GAIN_0_CH0_ODD;
- writel_relaxed(val, vfe->base + VFE_0_DEMUX_GAIN_0);
-
- val = VFE_0_DEMUX_GAIN_1_CH1 | VFE_0_DEMUX_GAIN_1_CH2;
- writel_relaxed(val, vfe->base + VFE_0_DEMUX_GAIN_1);
+ struct vfe_device *vfe = to_vfe(line);
- switch (line->fmt[MSM_VFE_PAD_SINK].code) {
- case MEDIA_BUS_FMT_YUYV8_2X8:
- even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_YUYV;
- odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_YUYV;
- break;
- case MEDIA_BUS_FMT_YVYU8_2X8:
- even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_YVYU;
- odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_YVYU;
- break;
- case MEDIA_BUS_FMT_UYVY8_2X8:
- default:
- even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_UYVY;
- odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_UYVY;
- break;
- case MEDIA_BUS_FMT_VYUY8_2X8:
- even_cfg = VFE_0_DEMUX_EVEN_CFG_PATTERN_VYUY;
- odd_cfg = VFE_0_DEMUX_ODD_CFG_PATTERN_VYUY;
- break;
- }
+ if (vfe->camss->version == CAMSS_8x16)
+ switch (sink_code) {
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ {
+ u32 src_code[] = {
+ MEDIA_BUS_FMT_YUYV8_2X8,
+ MEDIA_BUS_FMT_YUYV8_1_5X8,
+ };
+
+ return vfe_find_code(src_code, ARRAY_SIZE(src_code),
+ index, src_req_code);
+ }
+ case MEDIA_BUS_FMT_YVYU8_2X8:
+ {
+ u32 src_code[] = {
+ MEDIA_BUS_FMT_YVYU8_2X8,
+ MEDIA_BUS_FMT_YVYU8_1_5X8,
+ };
+
+ return vfe_find_code(src_code, ARRAY_SIZE(src_code),
+ index, src_req_code);
+ }
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ {
+ u32 src_code[] = {
+ MEDIA_BUS_FMT_UYVY8_2X8,
+ MEDIA_BUS_FMT_UYVY8_1_5X8,
+ };
+
+ return vfe_find_code(src_code, ARRAY_SIZE(src_code),
+ index, src_req_code);
+ }
+ case MEDIA_BUS_FMT_VYUY8_2X8:
+ {
+ u32 src_code[] = {
+ MEDIA_BUS_FMT_VYUY8_2X8,
+ MEDIA_BUS_FMT_VYUY8_1_5X8,
+ };
+
+ return vfe_find_code(src_code, ARRAY_SIZE(src_code),
+ index, src_req_code);
+ }
+ default:
+ if (index > 0)
+ return 0;
- writel_relaxed(even_cfg, vfe->base + VFE_0_DEMUX_EVEN_CFG);
- writel_relaxed(odd_cfg, vfe->base + VFE_0_DEMUX_ODD_CFG);
-}
+ return sink_code;
+ }
+ else if (vfe->camss->version == CAMSS_8x96)
+ switch (sink_code) {
+ case MEDIA_BUS_FMT_YUYV8_2X8:
+ {
+ u32 src_code[] = {
+ MEDIA_BUS_FMT_YUYV8_2X8,
+ MEDIA_BUS_FMT_YVYU8_2X8,
+ MEDIA_BUS_FMT_UYVY8_2X8,
+ MEDIA_BUS_FMT_VYUY8_2X8,
+ MEDIA_BUS_FMT_YUYV8_1_5X8,
+ };
+
+ return vfe_find_code(src_code, ARRAY_SIZE(src_code),
+ index, src_req_code);
+ }
+ case MEDIA_BUS_FMT_YVYU8_2X8:
+ {
+ u32 src_code[] = {
+ MEDIA_BUS_FMT_YVYU8_2X8,
+ MEDIA_BUS_FMT_YUYV8_2X8,
+ MEDIA_BUS_FMT_UYVY8_2X8,
+ MEDIA_BUS_FMT_VYUY8_2X8,
+ MEDIA_BUS_FMT_YVYU8_1_5X8,
+ };
+
+ return vfe_find_code(src_code, ARRAY_SIZE(src_code),
+ index, src_req_code);
+ }
+ case MEDIA_BUS_FMT_UYVY8_2X8:
+ {
+ u32 src_code[] = {
+ MEDIA_BUS_FMT_UYVY8_2X8,
+ MEDIA_BUS_FMT_YUYV8_2X8,
+ MEDIA_BUS_FMT_YVYU8_2X8,
+ MEDIA_BUS_FMT_VYUY8_2X8,
+ MEDIA_BUS_FMT_UYVY8_1_5X8,
+ };
+
+ return vfe_find_code(src_code, ARRAY_SIZE(src_code),
+ index, src_req_code);
+ }
+ case MEDIA_BUS_FMT_VYUY8_2X8:
+ {
+ u32 src_code[] = {
+ MEDIA_BUS_FMT_VYUY8_2X8,
+ MEDIA_BUS_FMT_YUYV8_2X8,
+ MEDIA_BUS_FMT_YVYU8_2X8,
+ MEDIA_BUS_FMT_UYVY8_2X8,
+ MEDIA_BUS_FMT_VYUY8_1_5X8,
+ };
+
+ return vfe_find_code(src_code, ARRAY_SIZE(src_code),
+ index, src_req_code);
+ }
+ default:
+ if (index > 0)
+ return 0;
-static inline u8 vfe_calc_interp_reso(u16 input, u16 output)
-{
- if (input / output >= 16)
+ return sink_code;
+ }
+ else
return 0;
-
- if (input / output >= 8)
- return 1;
-
- if (input / output >= 4)
- return 2;
-
- return 3;
-}
-
-static void vfe_set_scale_cfg(struct vfe_device *vfe, struct vfe_line *line)
-{
- u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
- u32 reg;
- u16 input, output;
- u8 interp_reso;
- u32 phase_mult;
-
- writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_Y_CFG);
-
- input = line->fmt[MSM_VFE_PAD_SINK].width;
- output = line->compose.width;
- reg = (output << 16) | input;
- writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_H_IMAGE_SIZE);
-
- interp_reso = vfe_calc_interp_reso(input, output);
- phase_mult = input * (1 << (13 + interp_reso)) / output;
- reg = (interp_reso << 20) | phase_mult;
- writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_H_PHASE);
-
- input = line->fmt[MSM_VFE_PAD_SINK].height;
- output = line->compose.height;
- reg = (output << 16) | input;
- writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_V_IMAGE_SIZE);
-
- interp_reso = vfe_calc_interp_reso(input, output);
- phase_mult = input * (1 << (13 + interp_reso)) / output;
- reg = (interp_reso << 20) | phase_mult;
- writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_Y_V_PHASE);
-
- writel_relaxed(0x3, vfe->base + VFE_0_SCALE_ENC_CBCR_CFG);
-
- input = line->fmt[MSM_VFE_PAD_SINK].width;
- output = line->compose.width / 2;
- reg = (output << 16) | input;
- writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_IMAGE_SIZE);
-
- interp_reso = vfe_calc_interp_reso(input, output);
- phase_mult = input * (1 << (13 + interp_reso)) / output;
- reg = (interp_reso << 20) | phase_mult;
- writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_H_PHASE);
-
- input = line->fmt[MSM_VFE_PAD_SINK].height;
- output = line->compose.height;
- if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21)
- output = line->compose.height / 2;
- reg = (output << 16) | input;
- writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_IMAGE_SIZE);
-
- interp_reso = vfe_calc_interp_reso(input, output);
- phase_mult = input * (1 << (13 + interp_reso)) / output;
- reg = (interp_reso << 20) | phase_mult;
- writel_relaxed(reg, vfe->base + VFE_0_SCALE_ENC_CBCR_V_PHASE);
-}
-
-static void vfe_set_crop_cfg(struct vfe_device *vfe, struct vfe_line *line)
-{
- u32 p = line->video_out.active_fmt.fmt.pix_mp.pixelformat;
- u32 reg;
- u16 first, last;
-
- first = line->crop.left;
- last = line->crop.left + line->crop.width - 1;
- reg = (first << 16) | last;
- writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_Y_WIDTH);
-
- first = line->crop.top;
- last = line->crop.top + line->crop.height - 1;
- reg = (first << 16) | last;
- writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_Y_HEIGHT);
-
- first = line->crop.left / 2;
- last = line->crop.left / 2 + line->crop.width / 2 - 1;
- reg = (first << 16) | last;
- writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_CBCR_WIDTH);
-
- first = line->crop.top;
- last = line->crop.top + line->crop.height - 1;
- if (p == V4L2_PIX_FMT_NV12 || p == V4L2_PIX_FMT_NV21) {
- first = line->crop.top / 2;
- last = line->crop.top / 2 + line->crop.height / 2 - 1;
- }
- reg = (first << 16) | last;
- writel_relaxed(reg, vfe->base + VFE_0_CROP_ENC_CBCR_HEIGHT);
-}
-
-static void vfe_set_clamp_cfg(struct vfe_device *vfe)
-{
- u32 val = VFE_0_CLAMP_ENC_MAX_CFG_CH0 |
- VFE_0_CLAMP_ENC_MAX_CFG_CH1 |
- VFE_0_CLAMP_ENC_MAX_CFG_CH2;
-
- writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MAX_CFG);
-
- val = VFE_0_CLAMP_ENC_MIN_CFG_CH0 |
- VFE_0_CLAMP_ENC_MIN_CFG_CH1 |
- VFE_0_CLAMP_ENC_MIN_CFG_CH2;
-
- writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MIN_CFG);
}
/*
@@ -879,12 +281,12 @@ static int vfe_reset(struct vfe_device *vfe)
reinit_completion(&vfe->reset_complete);
- vfe_global_reset(vfe);
+ vfe->ops->global_reset(vfe);
time = wait_for_completion_timeout(&vfe->reset_complete,
msecs_to_jiffies(VFE_RESET_TIMEOUT_MS));
if (!time) {
- dev_err(to_device(vfe), "VFE reset timeout\n");
+ dev_err(vfe->camss->dev, "VFE reset timeout\n");
return -EIO;
}
@@ -903,13 +305,12 @@ static int vfe_halt(struct vfe_device *vfe)
reinit_completion(&vfe->halt_complete);
- writel_relaxed(VFE_0_BUS_BDG_CMD_HALT_REQ,
- vfe->base + VFE_0_BUS_BDG_CMD);
+ vfe->ops->halt_request(vfe);
time = wait_for_completion_timeout(&vfe->halt_complete,
msecs_to_jiffies(VFE_HALT_TIMEOUT_MS));
if (!time) {
- dev_err(to_device(vfe), "VFE halt timeout\n");
+ dev_err(vfe->camss->dev, "VFE halt timeout\n");
return -EIO;
}
@@ -927,10 +328,6 @@ static void vfe_init_outputs(struct vfe_device *vfe)
output->buf[0] = NULL;
output->buf[1] = NULL;
INIT_LIST_HEAD(&output->pending_bufs);
-
- output->wm_num = 1;
- if (vfe->line[i].id == VFE_LINE_PIX)
- output->wm_num = 2;
}
}
@@ -942,115 +339,6 @@ static void vfe_reset_output_maps(struct vfe_device *vfe)
vfe->wm_output_map[i] = VFE_LINE_NONE;
}
-static void vfe_set_qos(struct vfe_device *vfe)
-{
- u32 val = VFE_0_BUS_BDG_QOS_CFG_0_CFG;
- u32 val7 = VFE_0_BUS_BDG_QOS_CFG_7_CFG;
-
- writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_0);
- writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_1);
- writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_2);
- writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_3);
- writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_4);
- writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_5);
- writel_relaxed(val, vfe->base + VFE_0_BUS_BDG_QOS_CFG_6);
- writel_relaxed(val7, vfe->base + VFE_0_BUS_BDG_QOS_CFG_7);
-}
-
-static void vfe_set_cgc_override(struct vfe_device *vfe, u8 wm, u8 enable)
-{
- u32 val = VFE_0_CGC_OVERRIDE_1_IMAGE_Mx_CGC_OVERRIDE(wm);
-
- if (enable)
- vfe_reg_set(vfe, VFE_0_CGC_OVERRIDE_1, val);
- else
- vfe_reg_clr(vfe, VFE_0_CGC_OVERRIDE_1, val);
-
- wmb();
-}
-
-static void vfe_set_module_cfg(struct vfe_device *vfe, u8 enable)
-{
- u32 val = VFE_0_MODULE_CFG_DEMUX |
- VFE_0_MODULE_CFG_CHROMA_UPSAMPLE |
- VFE_0_MODULE_CFG_SCALE_ENC |
- VFE_0_MODULE_CFG_CROP_ENC;
-
- if (enable)
- writel_relaxed(val, vfe->base + VFE_0_MODULE_CFG);
- else
- writel_relaxed(0x0, vfe->base + VFE_0_MODULE_CFG);
-}
-
-static void vfe_set_camif_cfg(struct vfe_device *vfe, struct vfe_line *line)
-{
- u32 val;
-
- switch (line->fmt[MSM_VFE_PAD_SINK].code) {
- case MEDIA_BUS_FMT_YUYV8_2X8:
- val = VFE_0_CORE_CFG_PIXEL_PATTERN_YCBYCR;
- break;
- case MEDIA_BUS_FMT_YVYU8_2X8:
- val = VFE_0_CORE_CFG_PIXEL_PATTERN_YCRYCB;
- break;
- case MEDIA_BUS_FMT_UYVY8_2X8:
- default:
- val = VFE_0_CORE_CFG_PIXEL_PATTERN_CBYCRY;
- break;
- case MEDIA_BUS_FMT_VYUY8_2X8:
- val = VFE_0_CORE_CFG_PIXEL_PATTERN_CRYCBY;
- break;
- }
-
- writel_relaxed(val, vfe->base + VFE_0_CORE_CFG);
-
- val = line->fmt[MSM_VFE_PAD_SINK].width * 2;
- val |= line->fmt[MSM_VFE_PAD_SINK].height << 16;
- writel_relaxed(val, vfe->base + VFE_0_CAMIF_FRAME_CFG);
-
- val = line->fmt[MSM_VFE_PAD_SINK].width * 2 - 1;
- writel_relaxed(val, vfe->base + VFE_0_CAMIF_WINDOW_WIDTH_CFG);
-
- val = line->fmt[MSM_VFE_PAD_SINK].height - 1;
- writel_relaxed(val, vfe->base + VFE_0_CAMIF_WINDOW_HEIGHT_CFG);
-
- val = 0xffffffff;
- writel_relaxed(val, vfe->base + VFE_0_CAMIF_SUBSAMPLE_CFG_0);
-
- val = 0xffffffff;
- writel_relaxed(val, vfe->base + VFE_0_CAMIF_IRQ_SUBSAMPLE_PATTERN);
-
- val = VFE_0_RDI_CFG_x_MIPI_EN_BITS;
- vfe_reg_set(vfe, VFE_0_RDI_CFG_x(0), val);
-
- val = VFE_0_CAMIF_CFG_VFE_OUTPUT_EN;
- writel_relaxed(val, vfe->base + VFE_0_CAMIF_CFG);
-}
-
-static void vfe_set_camif_cmd(struct vfe_device *vfe, u32 cmd)
-{
- writel_relaxed(VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS,
- vfe->base + VFE_0_CAMIF_CMD);
-
- writel_relaxed(cmd, vfe->base + VFE_0_CAMIF_CMD);
-}
-
-static int vfe_camif_wait_for_stop(struct vfe_device *vfe)
-{
- u32 val;
- int ret;
-
- ret = readl_poll_timeout(vfe->base + VFE_0_CAMIF_STATUS,
- val,
- (val & VFE_0_CAMIF_STATUS_HALT),
- CAMIF_TIMEOUT_SLEEP_US,
- CAMIF_TIMEOUT_ALL_US);
- if (ret < 0)
- dev_err(to_device(vfe), "%s: camif stop timeout\n", __func__);
-
- return ret;
-}
-
static void vfe_output_init_addrs(struct vfe_device *vfe,
struct vfe_output *output, u8 sync)
{
@@ -1071,10 +359,10 @@ static void vfe_output_init_addrs(struct vfe_device *vfe,
else
pong_addr = ping_addr;
- vfe_wm_set_ping_addr(vfe, output->wm_idx[i], ping_addr);
- vfe_wm_set_pong_addr(vfe, output->wm_idx[i], pong_addr);
+ vfe->ops->wm_set_ping_addr(vfe, output->wm_idx[i], ping_addr);
+ vfe->ops->wm_set_pong_addr(vfe, output->wm_idx[i], pong_addr);
if (sync)
- vfe_bus_reload_wm(vfe, output->wm_idx[i]);
+ vfe->ops->bus_reload_wm(vfe, output->wm_idx[i]);
}
}
@@ -1090,9 +378,9 @@ static void vfe_output_update_ping_addr(struct vfe_device *vfe,
else
addr = 0;
- vfe_wm_set_ping_addr(vfe, output->wm_idx[i], addr);
+ vfe->ops->wm_set_ping_addr(vfe, output->wm_idx[i], addr);
if (sync)
- vfe_bus_reload_wm(vfe, output->wm_idx[i]);
+ vfe->ops->bus_reload_wm(vfe, output->wm_idx[i]);
}
}
@@ -1108,9 +396,9 @@ static void vfe_output_update_pong_addr(struct vfe_device *vfe,
else
addr = 0;
- vfe_wm_set_pong_addr(vfe, output->wm_idx[i], addr);
+ vfe->ops->wm_set_pong_addr(vfe, output->wm_idx[i], addr);
if (sync)
- vfe_bus_reload_wm(vfe, output->wm_idx[i]);
+ vfe->ops->bus_reload_wm(vfe, output->wm_idx[i]);
}
}
@@ -1154,12 +442,13 @@ static void vfe_output_frame_drop(struct vfe_device *vfe,
drop_period = VFE_FRAME_DROP_VAL + output->drop_update_idx;
for (i = 0; i < output->wm_num; i++) {
- vfe_wm_set_framedrop_period(vfe, output->wm_idx[i],
- drop_period);
- vfe_wm_set_framedrop_pattern(vfe, output->wm_idx[i],
- drop_pattern);
+ vfe->ops->wm_set_framedrop_period(vfe, output->wm_idx[i],
+ drop_period);
+ vfe->ops->wm_set_framedrop_pattern(vfe, output->wm_idx[i],
+ drop_pattern);
}
- vfe_reg_update(vfe, container_of(output, struct vfe_line, output)->id);
+ vfe->ops->reg_update(vfe,
+ container_of(output, struct vfe_line, output)->id);
}
static struct camss_buffer *vfe_buf_get_pending(struct vfe_output *output)
@@ -1214,7 +503,7 @@ static void vfe_buf_update_wm_on_next(struct vfe_device *vfe,
break;
case VFE_OUTPUT_SINGLE:
default:
- dev_err_ratelimited(to_device(vfe),
+ dev_err_ratelimited(vfe->camss->dev,
"Next buf in wrong state! %d\n",
output->state);
break;
@@ -1234,7 +523,7 @@ static void vfe_buf_update_wm_on_last(struct vfe_device *vfe,
vfe_output_frame_drop(vfe, output, 0);
break;
default:
- dev_err_ratelimited(to_device(vfe),
+ dev_err_ratelimited(vfe->camss->dev,
"Last buff in wrong state! %d\n",
output->state);
break;
@@ -1263,7 +552,7 @@ static void vfe_buf_update_wm_on_new(struct vfe_device *vfe,
output->state = VFE_OUTPUT_CONTINUOUS;
} else {
vfe_buf_add_pending(output, new_buf);
- dev_err_ratelimited(to_device(vfe),
+ dev_err_ratelimited(vfe->camss->dev,
"Inactive buffer is busy\n");
}
break;
@@ -1278,7 +567,7 @@ static void vfe_buf_update_wm_on_new(struct vfe_device *vfe,
output->state = VFE_OUTPUT_SINGLE;
} else {
vfe_buf_add_pending(output, new_buf);
- dev_err_ratelimited(to_device(vfe),
+ dev_err_ratelimited(vfe->camss->dev,
"Output idle with buffer set!\n");
}
break;
@@ -1294,6 +583,7 @@ static int vfe_get_output(struct vfe_line *line)
{
struct vfe_device *vfe = to_vfe(line);
struct vfe_output *output;
+ struct v4l2_format *f = &line->video_out.active_fmt;
unsigned long flags;
int i;
int wm_idx;
@@ -1302,17 +592,29 @@ static int vfe_get_output(struct vfe_line *line)
output = &line->output;
if (output->state != VFE_OUTPUT_OFF) {
- dev_err(to_device(vfe), "Output is running\n");
+ dev_err(vfe->camss->dev, "Output is running\n");
goto error;
}
output->state = VFE_OUTPUT_RESERVED;
output->active_buf = 0;
+ switch (f->fmt.pix_mp.pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV16:
+ case V4L2_PIX_FMT_NV61:
+ output->wm_num = 2;
+ break;
+ default:
+ output->wm_num = 1;
+ break;
+ }
+
for (i = 0; i < output->wm_num; i++) {
wm_idx = vfe_reserve_wm(vfe, line->id);
if (wm_idx < 0) {
- dev_err(to_device(vfe), "Can not reserve wm\n");
+ dev_err(vfe->camss->dev, "Can not reserve wm\n");
goto error_get_wm;
}
output->wm_idx[i] = wm_idx;
@@ -1356,27 +658,21 @@ static int vfe_enable_output(struct vfe_line *line)
{
struct vfe_device *vfe = to_vfe(line);
struct vfe_output *output = &line->output;
+ const struct vfe_hw_ops *ops = vfe->ops;
unsigned long flags;
unsigned int i;
u16 ub_size;
- switch (vfe->id) {
- case 0:
- ub_size = MSM_VFE_VFE0_UB_SIZE_RDI;
- break;
- case 1:
- ub_size = MSM_VFE_VFE1_UB_SIZE_RDI;
- break;
- default:
+ ub_size = ops->get_ub_size(vfe->id);
+ if (!ub_size)
return -EINVAL;
- }
spin_lock_irqsave(&vfe->output_lock, flags);
- vfe->reg_update &= ~VFE_0_REG_UPDATE_line_n(line->id);
+ ops->reg_update_clear(vfe, line->id);
if (output->state != VFE_OUTPUT_RESERVED) {
- dev_err(to_device(vfe), "Output is not in reserved state %d\n",
+ dev_err(vfe->camss->dev, "Output is not in reserved state %d\n",
output->state);
spin_unlock_irqrestore(&vfe->output_lock, flags);
return -EINVAL;
@@ -1418,42 +714,43 @@ static int vfe_enable_output(struct vfe_line *line)
vfe_output_init_addrs(vfe, output, 0);
if (line->id != VFE_LINE_PIX) {
- vfe_set_cgc_override(vfe, output->wm_idx[0], 1);
- vfe_enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 1);
- vfe_bus_connect_wm_to_rdi(vfe, output->wm_idx[0], line->id);
- vfe_wm_set_subsample(vfe, output->wm_idx[0]);
- vfe_set_rdi_cid(vfe, line->id, 0);
- vfe_wm_set_ub_cfg(vfe, output->wm_idx[0],
- (ub_size + 1) * output->wm_idx[0], ub_size);
- vfe_wm_frame_based(vfe, output->wm_idx[0], 1);
- vfe_wm_enable(vfe, output->wm_idx[0], 1);
- vfe_bus_reload_wm(vfe, output->wm_idx[0]);
+ ops->set_cgc_override(vfe, output->wm_idx[0], 1);
+ ops->enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 1);
+ ops->bus_connect_wm_to_rdi(vfe, output->wm_idx[0], line->id);
+ ops->wm_set_subsample(vfe, output->wm_idx[0]);
+ ops->set_rdi_cid(vfe, line->id, 0);
+ ops->wm_set_ub_cfg(vfe, output->wm_idx[0],
+ (ub_size + 1) * output->wm_idx[0], ub_size);
+ ops->wm_frame_based(vfe, output->wm_idx[0], 1);
+ ops->wm_enable(vfe, output->wm_idx[0], 1);
+ ops->bus_reload_wm(vfe, output->wm_idx[0]);
} else {
ub_size /= output->wm_num;
for (i = 0; i < output->wm_num; i++) {
- vfe_set_cgc_override(vfe, output->wm_idx[i], 1);
- vfe_wm_set_subsample(vfe, output->wm_idx[i]);
- vfe_wm_set_ub_cfg(vfe, output->wm_idx[i],
- (ub_size + 1) * output->wm_idx[i],
- ub_size);
- vfe_wm_line_based(vfe, output->wm_idx[i],
+ ops->set_cgc_override(vfe, output->wm_idx[i], 1);
+ ops->wm_set_subsample(vfe, output->wm_idx[i]);
+ ops->wm_set_ub_cfg(vfe, output->wm_idx[i],
+ (ub_size + 1) * output->wm_idx[i],
+ ub_size);
+ ops->wm_line_based(vfe, output->wm_idx[i],
&line->video_out.active_fmt.fmt.pix_mp,
i, 1);
- vfe_wm_enable(vfe, output->wm_idx[i], 1);
- vfe_bus_reload_wm(vfe, output->wm_idx[i]);
+ ops->wm_enable(vfe, output->wm_idx[i], 1);
+ ops->bus_reload_wm(vfe, output->wm_idx[i]);
}
- vfe_enable_irq_pix_line(vfe, 0, line->id, 1);
- vfe_set_module_cfg(vfe, 1);
- vfe_set_camif_cfg(vfe, line);
- vfe_set_xbar_cfg(vfe, output, 1);
- vfe_set_demux_cfg(vfe, line);
- vfe_set_scale_cfg(vfe, line);
- vfe_set_crop_cfg(vfe, line);
- vfe_set_clamp_cfg(vfe);
- vfe_set_camif_cmd(vfe, VFE_0_CAMIF_CMD_ENABLE_FRAME_BOUNDARY);
+ ops->enable_irq_pix_line(vfe, 0, line->id, 1);
+ ops->set_module_cfg(vfe, 1);
+ ops->set_camif_cfg(vfe, line);
+ ops->set_realign_cfg(vfe, line, 1);
+ ops->set_xbar_cfg(vfe, output, 1);
+ ops->set_demux_cfg(vfe, line);
+ ops->set_scale_cfg(vfe, line);
+ ops->set_crop_cfg(vfe, line);
+ ops->set_clamp_cfg(vfe);
+ ops->set_camif_cmd(vfe, 1);
}
- vfe_reg_update(vfe, line->id);
+ ops->reg_update(vfe, line->id);
spin_unlock_irqrestore(&vfe->output_lock, flags);
@@ -1464,6 +761,7 @@ static int vfe_disable_output(struct vfe_line *line)
{
struct vfe_device *vfe = to_vfe(line);
struct vfe_output *output = &line->output;
+ const struct vfe_hw_ops *ops = vfe->ops;
unsigned long flags;
unsigned long time;
unsigned int i;
@@ -1476,43 +774,45 @@ static int vfe_disable_output(struct vfe_line *line)
time = wait_for_completion_timeout(&output->sof,
msecs_to_jiffies(VFE_NEXT_SOF_MS));
if (!time)
- dev_err(to_device(vfe), "VFE sof timeout\n");
+ dev_err(vfe->camss->dev, "VFE sof timeout\n");
spin_lock_irqsave(&vfe->output_lock, flags);
for (i = 0; i < output->wm_num; i++)
- vfe_wm_enable(vfe, output->wm_idx[i], 0);
+ ops->wm_enable(vfe, output->wm_idx[i], 0);
- vfe_reg_update(vfe, line->id);
+ ops->reg_update(vfe, line->id);
output->wait_reg_update = 1;
spin_unlock_irqrestore(&vfe->output_lock, flags);
time = wait_for_completion_timeout(&output->reg_update,
msecs_to_jiffies(VFE_NEXT_SOF_MS));
if (!time)
- dev_err(to_device(vfe), "VFE reg update timeout\n");
+ dev_err(vfe->camss->dev, "VFE reg update timeout\n");
spin_lock_irqsave(&vfe->output_lock, flags);
if (line->id != VFE_LINE_PIX) {
- vfe_wm_frame_based(vfe, output->wm_idx[0], 0);
- vfe_bus_disconnect_wm_from_rdi(vfe, output->wm_idx[0], line->id);
- vfe_enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 0);
- vfe_set_cgc_override(vfe, output->wm_idx[0], 0);
+ ops->wm_frame_based(vfe, output->wm_idx[0], 0);
+ ops->bus_disconnect_wm_from_rdi(vfe, output->wm_idx[0],
+ line->id);
+ ops->enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 0);
+ ops->set_cgc_override(vfe, output->wm_idx[0], 0);
spin_unlock_irqrestore(&vfe->output_lock, flags);
} else {
for (i = 0; i < output->wm_num; i++) {
- vfe_wm_line_based(vfe, output->wm_idx[i], NULL, i, 0);
- vfe_set_cgc_override(vfe, output->wm_idx[i], 0);
+ ops->wm_line_based(vfe, output->wm_idx[i], NULL, i, 0);
+ ops->set_cgc_override(vfe, output->wm_idx[i], 0);
}
- vfe_enable_irq_pix_line(vfe, 0, line->id, 0);
- vfe_set_module_cfg(vfe, 0);
- vfe_set_xbar_cfg(vfe, output, 0);
+ ops->enable_irq_pix_line(vfe, 0, line->id, 0);
+ ops->set_module_cfg(vfe, 0);
+ ops->set_realign_cfg(vfe, line, 0);
+ ops->set_xbar_cfg(vfe, output, 0);
- vfe_set_camif_cmd(vfe, VFE_0_CAMIF_CMD_DISABLE_FRAME_BOUNDARY);
+ ops->set_camif_cmd(vfe, 0);
spin_unlock_irqrestore(&vfe->output_lock, flags);
- vfe_camif_wait_for_stop(vfe);
+ ops->camif_wait_for_stop(vfe, vfe->camss->dev);
}
return 0;
@@ -1532,11 +832,13 @@ static int vfe_enable(struct vfe_line *line)
mutex_lock(&vfe->stream_lock);
if (!vfe->stream_count) {
- vfe_enable_irq_common(vfe);
+ vfe->ops->enable_irq_common(vfe);
+
+ vfe->ops->bus_enable_wr_if(vfe, 1);
- vfe_bus_enable_wr_if(vfe, 1);
+ vfe->ops->set_qos(vfe);
- vfe_set_qos(vfe);
+ vfe->ops->set_ds(vfe);
}
vfe->stream_count++;
@@ -1563,7 +865,7 @@ error_get_output:
mutex_lock(&vfe->stream_lock);
if (vfe->stream_count == 1)
- vfe_bus_enable_wr_if(vfe, 0);
+ vfe->ops->bus_enable_wr_if(vfe, 0);
vfe->stream_count--;
@@ -1589,7 +891,7 @@ static int vfe_disable(struct vfe_line *line)
mutex_lock(&vfe->stream_lock);
if (vfe->stream_count == 1)
- vfe_bus_enable_wr_if(vfe, 0);
+ vfe->ops->bus_enable_wr_if(vfe, 0);
vfe->stream_count--;
@@ -1628,7 +930,7 @@ static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id)
unsigned long flags;
spin_lock_irqsave(&vfe->output_lock, flags);
- vfe->reg_update &= ~VFE_0_REG_UPDATE_line_n(line_id);
+ vfe->ops->reg_update_clear(vfe, line_id);
output = &vfe->line[line_id].output;
@@ -1698,19 +1000,19 @@ static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm)
u64 ts = ktime_get_ns();
unsigned int i;
- active_index = vfe_wm_get_ping_pong_status(vfe, wm);
+ active_index = vfe->ops->wm_get_ping_pong_status(vfe, wm);
spin_lock_irqsave(&vfe->output_lock, flags);
if (vfe->wm_output_map[wm] == VFE_LINE_NONE) {
- dev_err_ratelimited(to_device(vfe),
+ dev_err_ratelimited(vfe->camss->dev,
"Received wm done for unmapped index\n");
goto out_unlock;
}
output = &vfe->line[vfe->wm_output_map[wm]].output;
if (output->active_buf == active_index) {
- dev_err_ratelimited(to_device(vfe),
+ dev_err_ratelimited(vfe->camss->dev,
"Active buffer mismatch!\n");
goto out_unlock;
}
@@ -1718,7 +1020,7 @@ static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm)
ready_buf = output->buf[!active_index];
if (!ready_buf) {
- dev_err_ratelimited(to_device(vfe),
+ dev_err_ratelimited(vfe->camss->dev,
"Missing ready buf %d %d!\n",
!active_index, output->state);
goto out_unlock;
@@ -1740,12 +1042,12 @@ static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm)
if (active_index)
for (i = 0; i < output->wm_num; i++)
- vfe_wm_set_ping_addr(vfe, output->wm_idx[i],
- new_addr[i]);
+ vfe->ops->wm_set_ping_addr(vfe, output->wm_idx[i],
+ new_addr[i]);
else
for (i = 0; i < output->wm_num; i++)
- vfe_wm_set_pong_addr(vfe, output->wm_idx[i],
- new_addr[i]);
+ vfe->ops->wm_set_pong_addr(vfe, output->wm_idx[i],
+ new_addr[i]);
spin_unlock_irqrestore(&vfe->output_lock, flags);
@@ -1776,67 +1078,15 @@ static void vfe_isr_comp_done(struct vfe_device *vfe, u8 comp)
}
}
-/*
- * vfe_isr - ISPIF module interrupt handler
- * @irq: Interrupt line
- * @dev: VFE device
- *
- * Return IRQ_HANDLED on success
- */
-static irqreturn_t vfe_isr(int irq, void *dev)
+static inline void vfe_isr_reset_ack(struct vfe_device *vfe)
{
- struct vfe_device *vfe = dev;
- u32 value0, value1;
- u32 violation;
- int i, j;
-
- value0 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_0);
- value1 = readl_relaxed(vfe->base + VFE_0_IRQ_STATUS_1);
-
- writel_relaxed(value0, vfe->base + VFE_0_IRQ_CLEAR_0);
- writel_relaxed(value1, vfe->base + VFE_0_IRQ_CLEAR_1);
-
- wmb();
- writel_relaxed(VFE_0_IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_0_IRQ_CMD);
-
- if (value0 & VFE_0_IRQ_STATUS_0_RESET_ACK)
- complete(&vfe->reset_complete);
-
- if (value1 & VFE_0_IRQ_STATUS_1_VIOLATION) {
- violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS);
- dev_err_ratelimited(to_device(vfe),
- "VFE: violation = 0x%08x\n", violation);
- }
-
- if (value1 & VFE_0_IRQ_STATUS_1_BUS_BDG_HALT_ACK) {
- complete(&vfe->halt_complete);
- writel_relaxed(0x0, vfe->base + VFE_0_BUS_BDG_CMD);
- }
-
- for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++)
- if (value0 & VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(i))
- vfe_isr_reg_update(vfe, i);
-
- if (value0 & VFE_0_IRQ_STATUS_0_CAMIF_SOF)
- vfe_isr_sof(vfe, VFE_LINE_PIX);
-
- for (i = VFE_LINE_RDI0; i <= VFE_LINE_RDI2; i++)
- if (value1 & VFE_0_IRQ_STATUS_1_RDIn_SOF(i))
- vfe_isr_sof(vfe, i);
-
- for (i = 0; i < MSM_VFE_COMPOSITE_IRQ_NUM; i++)
- if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_COMPOSITE_DONE_n(i)) {
- vfe_isr_comp_done(vfe, i);
- for (j = 0; j < ARRAY_SIZE(vfe->wm_output_map); j++)
- if (vfe->wm_output_map[j] == VFE_LINE_PIX)
- value0 &= ~VFE_0_IRQ_MASK_0_IMAGE_MASTER_n_PING_PONG(j);
- }
-
- for (i = 0; i < MSM_VFE_IMAGE_MASTERS_NUM; i++)
- if (value0 & VFE_0_IRQ_STATUS_0_IMAGE_MASTER_n_PING_PONG(i))
- vfe_isr_wm_done(vfe, i);
+ complete(&vfe->reset_complete);
+}
- return IRQ_HANDLED;
+static inline void vfe_isr_halt_ack(struct vfe_device *vfe)
+{
+ complete(&vfe->halt_complete);
+ vfe->ops->halt_clear(vfe);
}
/*
@@ -1847,7 +1097,7 @@ static irqreturn_t vfe_isr(int irq, void *dev)
*/
static int vfe_set_clock_rates(struct vfe_device *vfe)
{
- struct device *dev = to_device(vfe);
+ struct device *dev = vfe->camss->dev;
u32 pixel_clock[MSM_VFE_LINE_NUM];
int i, j;
int ret;
@@ -1862,7 +1112,8 @@ static int vfe_set_clock_rates(struct vfe_device *vfe)
for (i = 0; i < vfe->nclocks; i++) {
struct camss_clock *clock = &vfe->clock[i];
- if (!strcmp(clock->name, "camss_vfe_vfe")) {
+ if (!strcmp(clock->name, "vfe0") ||
+ !strcmp(clock->name, "vfe1")) {
u64 min_rate = 0;
long rate;
@@ -1873,8 +1124,11 @@ static int vfe_set_clock_rates(struct vfe_device *vfe)
if (j == VFE_LINE_PIX) {
tmp = pixel_clock[j];
} else {
- bpp = vfe_get_bpp(vfe->line[j].
- fmt[MSM_VFE_PAD_SINK].code);
+ struct vfe_line *l = &vfe->line[j];
+
+ bpp = vfe_get_bpp(l->formats,
+ l->nformats,
+ l->fmt[MSM_VFE_PAD_SINK].code);
tmp = pixel_clock[j] * bpp / 64;
}
@@ -1940,7 +1194,8 @@ static int vfe_check_clock_rates(struct vfe_device *vfe)
for (i = 0; i < vfe->nclocks; i++) {
struct camss_clock *clock = &vfe->clock[i];
- if (!strcmp(clock->name, "camss_vfe_vfe")) {
+ if (!strcmp(clock->name, "vfe0") ||
+ !strcmp(clock->name, "vfe1")) {
u64 min_rate = 0;
unsigned long rate;
@@ -1951,8 +1206,11 @@ static int vfe_check_clock_rates(struct vfe_device *vfe)
if (j == VFE_LINE_PIX) {
tmp = pixel_clock[j];
} else {
- bpp = vfe_get_bpp(vfe->line[j].
- fmt[MSM_VFE_PAD_SINK].code);
+ struct vfe_line *l = &vfe->line[j];
+
+ bpp = vfe_get_bpp(l->formats,
+ l->nformats,
+ l->fmt[MSM_VFE_PAD_SINK].code);
tmp = pixel_clock[j] * bpp / 64;
}
@@ -1984,12 +1242,20 @@ static int vfe_get(struct vfe_device *vfe)
mutex_lock(&vfe->power_lock);
if (vfe->power_count == 0) {
+ ret = camss_pm_domain_on(vfe->camss, vfe->id);
+ if (ret < 0)
+ goto error_pm_domain;
+
+ ret = pm_runtime_get_sync(vfe->camss->dev);
+ if (ret < 0)
+ goto error_pm_runtime_get;
+
ret = vfe_set_clock_rates(vfe);
if (ret < 0)
goto error_clocks;
ret = camss_enable_clocks(vfe->nclocks, vfe->clock,
- to_device(vfe));
+ vfe->camss->dev);
if (ret < 0)
goto error_clocks;
@@ -2015,6 +1281,12 @@ error_reset:
camss_disable_clocks(vfe->nclocks, vfe->clock);
error_clocks:
+ pm_runtime_put_sync(vfe->camss->dev);
+
+error_pm_runtime_get:
+ camss_pm_domain_off(vfe->camss, vfe->id);
+
+error_pm_domain:
mutex_unlock(&vfe->power_lock);
return ret;
@@ -2029,7 +1301,7 @@ static void vfe_put(struct vfe_device *vfe)
mutex_lock(&vfe->power_lock);
if (vfe->power_count == 0) {
- dev_err(to_device(vfe), "vfe power off on power_count == 0\n");
+ dev_err(vfe->camss->dev, "vfe power off on power_count == 0\n");
goto exit;
} else if (vfe->power_count == 1) {
if (vfe->was_streaming) {
@@ -2037,6 +1309,8 @@ static void vfe_put(struct vfe_device *vfe)
vfe_halt(vfe);
}
camss_disable_clocks(vfe->nclocks, vfe->clock);
+ pm_runtime_put_sync(vfe->camss->dev);
+ camss_pm_domain_off(vfe->camss, vfe->id);
}
vfe->power_count--;
@@ -2046,26 +1320,6 @@ exit:
}
/*
- * vfe_video_pad_to_line - Get pointer to VFE line by media pad
- * @pad: Media pad
- *
- * Return pointer to vfe line structure
- */
-static struct vfe_line *vfe_video_pad_to_line(struct media_pad *pad)
-{
- struct media_pad *vfe_pad;
- struct v4l2_subdev *subdev;
-
- vfe_pad = media_entity_remote_pad(pad);
- if (vfe_pad == NULL)
- return NULL;
-
- subdev = media_entity_to_v4l2_subdev(vfe_pad->entity);
-
- return container_of(subdev, struct vfe_line, subdev);
-}
-
-/*
* vfe_queue_buffer - Add empty buffer
* @vid: Video device structure
* @buf: Buffer to be enqueued
@@ -2078,16 +1332,11 @@ static struct vfe_line *vfe_video_pad_to_line(struct media_pad *pad)
static int vfe_queue_buffer(struct camss_video *vid,
struct camss_buffer *buf)
{
- struct vfe_device *vfe = &vid->camss->vfe;
- struct vfe_line *line;
+ struct vfe_line *line = container_of(vid, struct vfe_line, video_out);
+ struct vfe_device *vfe = to_vfe(line);
struct vfe_output *output;
unsigned long flags;
- line = vfe_video_pad_to_line(&vid->pad);
- if (!line) {
- dev_err(to_device(vfe), "Can not queue buffer\n");
- return -1;
- }
output = &line->output;
spin_lock_irqsave(&vfe->output_lock, flags);
@@ -2112,16 +1361,11 @@ static int vfe_queue_buffer(struct camss_video *vid,
static int vfe_flush_buffers(struct camss_video *vid,
enum vb2_buffer_state state)
{
- struct vfe_device *vfe = &vid->camss->vfe;
- struct vfe_line *line;
+ struct vfe_line *line = container_of(vid, struct vfe_line, video_out);
+ struct vfe_device *vfe = to_vfe(line);
struct vfe_output *output;
unsigned long flags;
- line = vfe_video_pad_to_line(&vid->pad);
- if (!line) {
- dev_err(to_device(vfe), "Can not flush buffers\n");
- return -1;
- }
output = &line->output;
spin_lock_irqsave(&vfe->output_lock, flags);
@@ -2158,15 +1402,11 @@ static int vfe_set_power(struct v4l2_subdev *sd, int on)
int ret;
if (on) {
- u32 hw_version;
-
ret = vfe_get(vfe);
if (ret < 0)
return ret;
- hw_version = readl_relaxed(vfe->base + VFE_0_HW_VERSION);
- dev_dbg(to_device(vfe),
- "VFE HW Version = 0x%08x\n", hw_version);
+ vfe->ops->hw_version_read(vfe, vfe->camss->dev);
} else {
vfe_put(vfe);
}
@@ -2192,12 +1432,12 @@ static int vfe_set_stream(struct v4l2_subdev *sd, int enable)
if (enable) {
ret = vfe_enable(line);
if (ret < 0)
- dev_err(to_device(vfe),
+ dev_err(vfe->camss->dev,
"Failed to enable vfe outputs\n");
} else {
ret = vfe_disable(line);
if (ret < 0)
- dev_err(to_device(vfe),
+ dev_err(vfe->camss->dev,
"Failed to disable vfe outputs\n");
}
@@ -2286,12 +1526,12 @@ static void vfe_try_format(struct vfe_line *line,
case MSM_VFE_PAD_SINK:
/* Set format on sink pad */
- for (i = 0; i < ARRAY_SIZE(vfe_formats); i++)
- if (fmt->code == vfe_formats[i].code)
+ for (i = 0; i < line->nformats; i++)
+ if (fmt->code == line->formats[i].code)
break;
/* If not found, use UYVY as default */
- if (i >= ARRAY_SIZE(vfe_formats))
+ if (i >= line->nformats)
fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
fmt->width = clamp_t(u32, fmt->width, 1, 8191);
@@ -2304,11 +1544,11 @@ static void vfe_try_format(struct vfe_line *line,
case MSM_VFE_PAD_SRC:
/* Set and return a format same as sink pad */
-
code = fmt->code;
- *fmt = *__vfe_get_format(line, cfg, MSM_VFE_PAD_SINK,
- which);
+ *fmt = *__vfe_get_format(line, cfg, MSM_VFE_PAD_SINK, which);
+
+ fmt->code = vfe_src_pad_code(line, fmt->code, 0, code);
if (line->id == VFE_LINE_PIX) {
struct v4l2_rect *rect;
@@ -2317,34 +1557,6 @@ static void vfe_try_format(struct vfe_line *line,
fmt->width = rect->width;
fmt->height = rect->height;
-
- switch (fmt->code) {
- case MEDIA_BUS_FMT_YUYV8_2X8:
- if (code == MEDIA_BUS_FMT_YUYV8_1_5X8)
- fmt->code = MEDIA_BUS_FMT_YUYV8_1_5X8;
- else
- fmt->code = MEDIA_BUS_FMT_YUYV8_2X8;
- break;
- case MEDIA_BUS_FMT_YVYU8_2X8:
- if (code == MEDIA_BUS_FMT_YVYU8_1_5X8)
- fmt->code = MEDIA_BUS_FMT_YVYU8_1_5X8;
- else
- fmt->code = MEDIA_BUS_FMT_YVYU8_2X8;
- break;
- case MEDIA_BUS_FMT_UYVY8_2X8:
- default:
- if (code == MEDIA_BUS_FMT_UYVY8_1_5X8)
- fmt->code = MEDIA_BUS_FMT_UYVY8_1_5X8;
- else
- fmt->code = MEDIA_BUS_FMT_UYVY8_2X8;
- break;
- case MEDIA_BUS_FMT_VYUY8_2X8:
- if (code == MEDIA_BUS_FMT_VYUY8_1_5X8)
- fmt->code = MEDIA_BUS_FMT_VYUY8_1_5X8;
- else
- fmt->code = MEDIA_BUS_FMT_VYUY8_2X8;
- break;
- }
}
break;
@@ -2448,21 +1660,22 @@ static int vfe_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_mbus_code_enum *code)
{
struct vfe_line *line = v4l2_get_subdevdata(sd);
- struct v4l2_mbus_framefmt *format;
if (code->pad == MSM_VFE_PAD_SINK) {
- if (code->index >= ARRAY_SIZE(vfe_formats))
+ if (code->index >= line->nformats)
return -EINVAL;
- code->code = vfe_formats[code->index].code;
+ code->code = line->formats[code->index].code;
} else {
- if (code->index > 0)
- return -EINVAL;
+ struct v4l2_mbus_framefmt *sink_fmt;
- format = __vfe_get_format(line, cfg, MSM_VFE_PAD_SINK,
- code->which);
+ sink_fmt = __vfe_get_format(line, cfg, MSM_VFE_PAD_SINK,
+ code->which);
- code->code = format->code;
+ code->code = vfe_src_pad_code(line, sink_fmt->code,
+ code->index, 0);
+ if (!code->code)
+ return -EINVAL;
}
return 0;
@@ -2751,15 +1964,29 @@ static int vfe_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
*
* Return 0 on success or a negative error code otherwise
*/
-int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res)
+int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe,
+ const struct resources *res, u8 id)
{
- struct device *dev = to_device(vfe);
+ struct device *dev = camss->dev;
struct platform_device *pdev = to_platform_device(dev);
struct resource *r;
- struct camss *camss = to_camss(vfe);
int i, j;
int ret;
+ vfe->isr_ops.reset_ack = vfe_isr_reset_ack;
+ vfe->isr_ops.halt_ack = vfe_isr_halt_ack;
+ vfe->isr_ops.reg_update = vfe_isr_reg_update;
+ vfe->isr_ops.sof = vfe_isr_sof;
+ vfe->isr_ops.comp_done = vfe_isr_comp_done;
+ vfe->isr_ops.wm_done = vfe_isr_wm_done;
+
+ if (camss->version == CAMSS_8x16)
+ vfe->ops = &vfe_ops_4_1;
+ else if (camss->version == CAMSS_8x96)
+ vfe->ops = &vfe_ops_4_7;
+ else
+ return -EINVAL;
+
/* Memory */
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]);
@@ -2781,7 +2008,7 @@ int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res)
vfe->irq = r->start;
snprintf(vfe->irq_name, sizeof(vfe->irq_name), "%s_%s%d",
dev_name(dev), MSM_VFE_NAME, vfe->id);
- ret = devm_request_irq(dev, vfe->irq, vfe_isr,
+ ret = devm_request_irq(dev, vfe->irq, vfe->ops->isr,
IRQF_TRIGGER_RISING, vfe->irq_name, vfe);
if (ret < 0) {
dev_err(dev, "request_irq failed: %d\n", ret);
@@ -2836,16 +2063,38 @@ int msm_vfe_subdev_init(struct vfe_device *vfe, const struct resources *res)
spin_lock_init(&vfe->output_lock);
- vfe->id = 0;
+ vfe->camss = camss;
+ vfe->id = id;
vfe->reg_update = 0;
for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) {
- vfe->line[i].video_out.type =
- V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
- vfe->line[i].video_out.camss = camss;
- vfe->line[i].id = i;
- init_completion(&vfe->line[i].output.sof);
- init_completion(&vfe->line[i].output.reg_update);
+ struct vfe_line *l = &vfe->line[i];
+
+ l->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ l->video_out.camss = camss;
+ l->id = i;
+ init_completion(&l->output.sof);
+ init_completion(&l->output.reg_update);
+
+ if (camss->version == CAMSS_8x16) {
+ if (i == VFE_LINE_PIX) {
+ l->formats = formats_pix_8x16;
+ l->nformats = ARRAY_SIZE(formats_pix_8x16);
+ } else {
+ l->formats = formats_rdi_8x16;
+ l->nformats = ARRAY_SIZE(formats_rdi_8x16);
+ }
+ } else if (camss->version == CAMSS_8x96) {
+ if (i == VFE_LINE_PIX) {
+ l->formats = formats_pix_8x96;
+ l->nformats = ARRAY_SIZE(formats_pix_8x96);
+ } else {
+ l->formats = formats_rdi_8x96;
+ l->nformats = ARRAY_SIZE(formats_rdi_8x96);
+ }
+ } else {
+ return -EINVAL;
+ }
}
init_completion(&vfe->reset_complete);
@@ -2968,7 +2217,7 @@ void msm_vfe_stop_streaming(struct vfe_device *vfe)
int msm_vfe_register_entities(struct vfe_device *vfe,
struct v4l2_device *v4l2_dev)
{
- struct device *dev = to_device(vfe);
+ struct device *dev = vfe->camss->dev;
struct v4l2_subdev *sd;
struct media_pad *pads;
struct camss_video *video_out;
diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h
new file mode 100644
index 000000000000..0d10071ae881
--- /dev/null
+++ b/drivers/media/platform/qcom/camss/camss-vfe.h
@@ -0,0 +1,186 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * camss-vfe.h
+ *
+ * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module
+ *
+ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2015-2018 Linaro Ltd.
+ */
+#ifndef QC_MSM_CAMSS_VFE_H
+#define QC_MSM_CAMSS_VFE_H
+
+#include <linux/clk.h>
+#include <linux/spinlock_types.h>
+#include <media/media-entity.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+
+#include "camss-video.h"
+
+#define MSM_VFE_PAD_SINK 0
+#define MSM_VFE_PAD_SRC 1
+#define MSM_VFE_PADS_NUM 2
+
+#define MSM_VFE_LINE_NUM 4
+#define MSM_VFE_IMAGE_MASTERS_NUM 7
+#define MSM_VFE_COMPOSITE_IRQ_NUM 4
+
+enum vfe_output_state {
+ VFE_OUTPUT_OFF,
+ VFE_OUTPUT_RESERVED,
+ VFE_OUTPUT_SINGLE,
+ VFE_OUTPUT_CONTINUOUS,
+ VFE_OUTPUT_IDLE,
+ VFE_OUTPUT_STOPPING
+};
+
+enum vfe_line_id {
+ VFE_LINE_NONE = -1,
+ VFE_LINE_RDI0 = 0,
+ VFE_LINE_RDI1 = 1,
+ VFE_LINE_RDI2 = 2,
+ VFE_LINE_PIX = 3
+};
+
+struct vfe_output {
+ u8 wm_num;
+ u8 wm_idx[3];
+
+ int active_buf;
+ struct camss_buffer *buf[2];
+ struct camss_buffer *last_buffer;
+ struct list_head pending_bufs;
+
+ unsigned int drop_update_idx;
+
+ enum vfe_output_state state;
+ unsigned int sequence;
+ int wait_sof;
+ int wait_reg_update;
+ struct completion sof;
+ struct completion reg_update;
+};
+
+struct vfe_line {
+ enum vfe_line_id id;
+ struct v4l2_subdev subdev;
+ struct media_pad pads[MSM_VFE_PADS_NUM];
+ struct v4l2_mbus_framefmt fmt[MSM_VFE_PADS_NUM];
+ struct v4l2_rect compose;
+ struct v4l2_rect crop;
+ struct camss_video video_out;
+ struct vfe_output output;
+ const struct vfe_format *formats;
+ unsigned int nformats;
+};
+
+struct vfe_device;
+
+struct vfe_hw_ops {
+ void (*hw_version_read)(struct vfe_device *vfe, struct device *dev);
+ u16 (*get_ub_size)(u8 vfe_id);
+ void (*global_reset)(struct vfe_device *vfe);
+ void (*halt_request)(struct vfe_device *vfe);
+ void (*halt_clear)(struct vfe_device *vfe);
+ void (*wm_enable)(struct vfe_device *vfe, u8 wm, u8 enable);
+ void (*wm_frame_based)(struct vfe_device *vfe, u8 wm, u8 enable);
+ void (*wm_line_based)(struct vfe_device *vfe, u32 wm,
+ struct v4l2_pix_format_mplane *pix,
+ u8 plane, u32 enable);
+ void (*wm_set_framedrop_period)(struct vfe_device *vfe, u8 wm, u8 per);
+ void (*wm_set_framedrop_pattern)(struct vfe_device *vfe, u8 wm,
+ u32 pattern);
+ void (*wm_set_ub_cfg)(struct vfe_device *vfe, u8 wm, u16 offset,
+ u16 depth);
+ void (*bus_reload_wm)(struct vfe_device *vfe, u8 wm);
+ void (*wm_set_ping_addr)(struct vfe_device *vfe, u8 wm, u32 addr);
+ void (*wm_set_pong_addr)(struct vfe_device *vfe, u8 wm, u32 addr);
+ int (*wm_get_ping_pong_status)(struct vfe_device *vfe, u8 wm);
+ void (*bus_enable_wr_if)(struct vfe_device *vfe, u8 enable);
+ void (*bus_connect_wm_to_rdi)(struct vfe_device *vfe, u8 wm,
+ enum vfe_line_id id);
+ void (*wm_set_subsample)(struct vfe_device *vfe, u8 wm);
+ void (*bus_disconnect_wm_from_rdi)(struct vfe_device *vfe, u8 wm,
+ enum vfe_line_id id);
+ void (*set_xbar_cfg)(struct vfe_device *vfe, struct vfe_output *output,
+ u8 enable);
+ void (*set_rdi_cid)(struct vfe_device *vfe, enum vfe_line_id id,
+ u8 cid);
+ void (*set_realign_cfg)(struct vfe_device *vfe, struct vfe_line *line,
+ u8 enable);
+ void (*reg_update)(struct vfe_device *vfe, enum vfe_line_id line_id);
+ void (*reg_update_clear)(struct vfe_device *vfe,
+ enum vfe_line_id line_id);
+ void (*enable_irq_wm_line)(struct vfe_device *vfe, u8 wm,
+ enum vfe_line_id line_id, u8 enable);
+ void (*enable_irq_pix_line)(struct vfe_device *vfe, u8 comp,
+ enum vfe_line_id line_id, u8 enable);
+ void (*enable_irq_common)(struct vfe_device *vfe);
+ void (*set_demux_cfg)(struct vfe_device *vfe, struct vfe_line *line);
+ void (*set_scale_cfg)(struct vfe_device *vfe, struct vfe_line *line);
+ void (*set_crop_cfg)(struct vfe_device *vfe, struct vfe_line *line);
+ void (*set_clamp_cfg)(struct vfe_device *vfe);
+ void (*set_qos)(struct vfe_device *vfe);
+ void (*set_ds)(struct vfe_device *vfe);
+ void (*set_cgc_override)(struct vfe_device *vfe, u8 wm, u8 enable);
+ void (*set_camif_cfg)(struct vfe_device *vfe, struct vfe_line *line);
+ void (*set_camif_cmd)(struct vfe_device *vfe, u8 enable);
+ void (*set_module_cfg)(struct vfe_device *vfe, u8 enable);
+ int (*camif_wait_for_stop)(struct vfe_device *vfe, struct device *dev);
+ void (*isr_read)(struct vfe_device *vfe, u32 *value0, u32 *value1);
+ void (*violation_read)(struct vfe_device *vfe);
+ irqreturn_t (*isr)(int irq, void *dev);
+};
+
+struct vfe_isr_ops {
+ void (*reset_ack)(struct vfe_device *vfe);
+ void (*halt_ack)(struct vfe_device *vfe);
+ void (*reg_update)(struct vfe_device *vfe, enum vfe_line_id line_id);
+ void (*sof)(struct vfe_device *vfe, enum vfe_line_id line_id);
+ void (*comp_done)(struct vfe_device *vfe, u8 comp);
+ void (*wm_done)(struct vfe_device *vfe, u8 wm);
+};
+
+struct vfe_device {
+ struct camss *camss;
+ u8 id;
+ void __iomem *base;
+ u32 irq;
+ char irq_name[30];
+ struct camss_clock *clock;
+ int nclocks;
+ struct completion reset_complete;
+ struct completion halt_complete;
+ struct mutex power_lock;
+ int power_count;
+ struct mutex stream_lock;
+ int stream_count;
+ spinlock_t output_lock;
+ enum vfe_line_id wm_output_map[MSM_VFE_IMAGE_MASTERS_NUM];
+ struct vfe_line line[MSM_VFE_LINE_NUM];
+ u32 reg_update;
+ u8 was_streaming;
+ const struct vfe_hw_ops *ops;
+ struct vfe_isr_ops isr_ops;
+};
+
+struct resources;
+
+int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe,
+ const struct resources *res, u8 id);
+
+int msm_vfe_register_entities(struct vfe_device *vfe,
+ struct v4l2_device *v4l2_dev);
+
+void msm_vfe_unregister_entities(struct vfe_device *vfe);
+
+void msm_vfe_get_vfe_id(struct media_entity *entity, u8 *id);
+void msm_vfe_get_vfe_line_id(struct media_entity *entity, enum vfe_line_id *id);
+
+void msm_vfe_stop_streaming(struct vfe_device *vfe);
+
+extern const struct vfe_hw_ops vfe_ops_4_1;
+extern const struct vfe_hw_ops vfe_ops_4_7;
+
+#endif /* QC_MSM_CAMSS_VFE_H */
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c
index ffaa2849e0c1..c9bb0d023db4 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-video.c
+++ b/drivers/media/platform/qcom/camss/camss-video.c
@@ -1,19 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* camss-video.c
*
* Qualcomm MSM Camera Subsystem - V4L2 device node
*
* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
- * Copyright (C) 2015-2017 Linaro Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * Copyright (C) 2015-2018 Linaro Ltd.
*/
#include <linux/slab.h>
#include <media/media-entity.h>
@@ -49,7 +41,7 @@ struct camss_format_info {
unsigned int bpp[3];
};
-static const struct camss_format_info formats_rdi[] = {
+static const struct camss_format_info formats_rdi_8x16[] = {
{ MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_UYVY, 1,
{ { 1, 1 } }, { { 1, 1 } }, { 16 } },
{ MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_VYUY, 1,
@@ -82,9 +74,60 @@ static const struct camss_format_info formats_rdi[] = {
{ { 1, 1 } }, { { 1, 1 } }, { 12 } },
{ MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1,
{ { 1, 1 } }, { { 1, 1 } }, { 12 } },
+ { MEDIA_BUS_FMT_Y10_1X10, V4L2_PIX_FMT_Y10P, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 10 } },
};
-static const struct camss_format_info formats_pix[] = {
+static const struct camss_format_info formats_rdi_8x96[] = {
+ { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_UYVY, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 16 } },
+ { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_VYUY, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 16 } },
+ { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_YUYV, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 16 } },
+ { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_YVYU, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 16 } },
+ { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR8, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 8 } },
+ { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_PIX_FMT_SGBRG8, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 8 } },
+ { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_PIX_FMT_SGRBG8, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 8 } },
+ { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 8 } },
+ { MEDIA_BUS_FMT_SBGGR10_1X10, V4L2_PIX_FMT_SBGGR10P, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 10 } },
+ { MEDIA_BUS_FMT_SGBRG10_1X10, V4L2_PIX_FMT_SGBRG10P, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 10 } },
+ { MEDIA_BUS_FMT_SGRBG10_1X10, V4L2_PIX_FMT_SGRBG10P, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 10 } },
+ { MEDIA_BUS_FMT_SRGGB10_1X10, V4L2_PIX_FMT_SRGGB10P, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 10 } },
+ { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, V4L2_PIX_FMT_SBGGR10, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 16 } },
+ { MEDIA_BUS_FMT_SBGGR12_1X12, V4L2_PIX_FMT_SBGGR12P, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 12 } },
+ { MEDIA_BUS_FMT_SGBRG12_1X12, V4L2_PIX_FMT_SGBRG12P, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 12 } },
+ { MEDIA_BUS_FMT_SGRBG12_1X12, V4L2_PIX_FMT_SGRBG12P, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 12 } },
+ { MEDIA_BUS_FMT_SRGGB12_1X12, V4L2_PIX_FMT_SRGGB12P, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 12 } },
+ { MEDIA_BUS_FMT_SBGGR14_1X14, V4L2_PIX_FMT_SBGGR14P, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 14 } },
+ { MEDIA_BUS_FMT_SGBRG14_1X14, V4L2_PIX_FMT_SGBRG14P, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 14 } },
+ { MEDIA_BUS_FMT_SGRBG14_1X14, V4L2_PIX_FMT_SGRBG14P, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 14 } },
+ { MEDIA_BUS_FMT_SRGGB14_1X14, V4L2_PIX_FMT_SRGGB14P, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 14 } },
+ { MEDIA_BUS_FMT_Y10_1X10, V4L2_PIX_FMT_Y10P, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 10 } },
+ { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, V4L2_PIX_FMT_Y10, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 16 } },
+};
+
+static const struct camss_format_info formats_pix_8x16[] = {
{ MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV12, 1,
{ { 1, 1 } }, { { 2, 3 } }, { 8 } },
{ MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV12, 1,
@@ -119,6 +162,49 @@ static const struct camss_format_info formats_pix[] = {
{ { 1, 1 } }, { { 1, 2 } }, { 8 } },
};
+static const struct camss_format_info formats_pix_8x96[] = {
+ { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV12, 1,
+ { { 1, 1 } }, { { 2, 3 } }, { 8 } },
+ { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV12, 1,
+ { { 1, 1 } }, { { 2, 3 } }, { 8 } },
+ { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV12, 1,
+ { { 1, 1 } }, { { 2, 3 } }, { 8 } },
+ { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV12, 1,
+ { { 1, 1 } }, { { 2, 3 } }, { 8 } },
+ { MEDIA_BUS_FMT_YUYV8_1_5X8, V4L2_PIX_FMT_NV21, 1,
+ { { 1, 1 } }, { { 2, 3 } }, { 8 } },
+ { MEDIA_BUS_FMT_YVYU8_1_5X8, V4L2_PIX_FMT_NV21, 1,
+ { { 1, 1 } }, { { 2, 3 } }, { 8 } },
+ { MEDIA_BUS_FMT_UYVY8_1_5X8, V4L2_PIX_FMT_NV21, 1,
+ { { 1, 1 } }, { { 2, 3 } }, { 8 } },
+ { MEDIA_BUS_FMT_VYUY8_1_5X8, V4L2_PIX_FMT_NV21, 1,
+ { { 1, 1 } }, { { 2, 3 } }, { 8 } },
+ { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV16, 1,
+ { { 1, 1 } }, { { 1, 2 } }, { 8 } },
+ { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV16, 1,
+ { { 1, 1 } }, { { 1, 2 } }, { 8 } },
+ { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV16, 1,
+ { { 1, 1 } }, { { 1, 2 } }, { 8 } },
+ { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV16, 1,
+ { { 1, 1 } }, { { 1, 2 } }, { 8 } },
+ { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_NV61, 1,
+ { { 1, 1 } }, { { 1, 2 } }, { 8 } },
+ { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_NV61, 1,
+ { { 1, 1 } }, { { 1, 2 } }, { 8 } },
+ { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_NV61, 1,
+ { { 1, 1 } }, { { 1, 2 } }, { 8 } },
+ { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_NV61, 1,
+ { { 1, 1 } }, { { 1, 2 } }, { 8 } },
+ { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_PIX_FMT_UYVY, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 16 } },
+ { MEDIA_BUS_FMT_VYUY8_2X8, V4L2_PIX_FMT_VYUY, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 16 } },
+ { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_PIX_FMT_YUYV, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 16 } },
+ { MEDIA_BUS_FMT_YVYU8_2X8, V4L2_PIX_FMT_YVYU, 1,
+ { { 1, 1 } }, { { 1, 1 } }, { 16 } },
+};
+
/* -----------------------------------------------------------------------------
* Helper functions
*/
@@ -798,11 +884,24 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev,
mutex_init(&video->lock);
- video->formats = formats_rdi;
- video->nformats = ARRAY_SIZE(formats_rdi);
- if (is_pix) {
- video->formats = formats_pix;
- video->nformats = ARRAY_SIZE(formats_pix);
+ if (video->camss->version == CAMSS_8x16) {
+ if (is_pix) {
+ video->formats = formats_pix_8x16;
+ video->nformats = ARRAY_SIZE(formats_pix_8x16);
+ } else {
+ video->formats = formats_rdi_8x16;
+ video->nformats = ARRAY_SIZE(formats_rdi_8x16);
+ }
+ } else if (video->camss->version == CAMSS_8x96) {
+ if (is_pix) {
+ video->formats = formats_pix_8x96;
+ video->nformats = ARRAY_SIZE(formats_pix_8x96);
+ } else {
+ video->formats = formats_rdi_8x96;
+ video->nformats = ARRAY_SIZE(formats_rdi_8x96);
+ }
+ } else {
+ goto error_video_register;
}
ret = msm_video_init_format(video);
diff --git a/drivers/media/platform/qcom/camss-8x16/camss-video.h b/drivers/media/platform/qcom/camss/camss-video.h
index 38bd1f2eec54..aa35e8cc6fd5 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss-video.h
+++ b/drivers/media/platform/qcom/camss/camss-video.h
@@ -1,19 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* camss-video.h
*
* Qualcomm MSM Camera Subsystem - V4L2 device node
*
* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
- * Copyright (C) 2015-2017 Linaro Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * Copyright (C) 2015-2018 Linaro Ltd.
*/
#ifndef QC_MSM_CAMSS_VIDEO_H
#define QC_MSM_CAMSS_VIDEO_H
diff --git a/drivers/media/platform/qcom/camss-8x16/camss.c b/drivers/media/platform/qcom/camss/camss.c
index 23fda6207a23..dcc0c30ef1b1 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss.c
+++ b/drivers/media/platform/qcom/camss/camss.c
@@ -1,19 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* camss.c
*
* Qualcomm MSM Camera Subsystem - Core
*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
- * Copyright (C) 2015-2017 Linaro Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * Copyright (C) 2015-2018 Linaro Ltd.
*/
#include <linux/clk.h>
#include <linux/media-bus-format.h>
@@ -22,6 +14,8 @@
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_graph.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_domain.h>
#include <linux/slab.h>
#include <linux/videodev2.h>
@@ -36,12 +30,11 @@
#define CAMSS_CLOCK_MARGIN_NUMERATOR 105
#define CAMSS_CLOCK_MARGIN_DENOMINATOR 100
-static const struct resources csiphy_res[] = {
+static const struct resources csiphy_res_8x16[] = {
/* CSIPHY0 */
{
.regulator = { NULL },
- .clock = { "camss_top_ahb", "ispif_ahb",
- "camss_ahb", "csiphy0_timer" },
+ .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy0_timer" },
.clock_rate = { { 0 },
{ 0 },
{ 0 },
@@ -53,8 +46,7 @@ static const struct resources csiphy_res[] = {
/* CSIPHY1 */
{
.regulator = { NULL },
- .clock = { "camss_top_ahb", "ispif_ahb",
- "camss_ahb", "csiphy1_timer" },
+ .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy1_timer" },
.clock_rate = { { 0 },
{ 0 },
{ 0 },
@@ -64,12 +56,11 @@ static const struct resources csiphy_res[] = {
}
};
-static const struct resources csid_res[] = {
+static const struct resources csid_res_8x16[] = {
/* CSID0 */
{
.regulator = { "vdda" },
- .clock = { "camss_top_ahb", "ispif_ahb",
- "csi0_ahb", "camss_ahb",
+ .clock = { "top_ahb", "ispif_ahb", "csi0_ahb", "ahb",
"csi0", "csi0_phy", "csi0_pix", "csi0_rdi" },
.clock_rate = { { 0 },
{ 0 },
@@ -86,8 +77,7 @@ static const struct resources csid_res[] = {
/* CSID1 */
{
.regulator = { "vdda" },
- .clock = { "camss_top_ahb", "ispif_ahb",
- "csi1_ahb", "camss_ahb",
+ .clock = { "top_ahb", "ispif_ahb", "csi1_ahb", "ahb",
"csi1", "csi1_phy", "csi1_pix", "csi1_rdi" },
.clock_rate = { { 0 },
{ 0 },
@@ -102,35 +92,195 @@ static const struct resources csid_res[] = {
},
};
-static const struct resources_ispif ispif_res = {
+static const struct resources_ispif ispif_res_8x16 = {
/* ISPIF */
- .clock = { "camss_top_ahb", "camss_ahb", "ispif_ahb",
+ .clock = { "top_ahb", "ahb", "ispif_ahb",
"csi0", "csi0_pix", "csi0_rdi",
"csi1", "csi1_pix", "csi1_rdi" },
- .clock_for_reset = { "camss_vfe_vfe", "camss_csi_vfe" },
+ .clock_for_reset = { "vfe0", "csi_vfe0" },
.reg = { "ispif", "csi_clk_mux" },
.interrupt = "ispif"
};
-static const struct resources vfe_res = {
+static const struct resources vfe_res_8x16[] = {
+ /* VFE0 */
+ {
+ .regulator = { NULL },
+ .clock = { "top_ahb", "vfe0", "csi_vfe0",
+ "vfe_ahb", "vfe_axi", "ahb" },
+ .clock_rate = { { 0 },
+ { 50000000, 80000000, 100000000, 160000000,
+ 177780000, 200000000, 266670000, 320000000,
+ 400000000, 465000000 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 } },
+ .reg = { "vfe0" },
+ .interrupt = { "vfe0" }
+ }
+};
+
+static const struct resources csiphy_res_8x96[] = {
+ /* CSIPHY0 */
+ {
+ .regulator = { NULL },
+ .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy0_timer" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 100000000, 200000000, 266666667 } },
+ .reg = { "csiphy0", "csiphy0_clk_mux" },
+ .interrupt = { "csiphy0" }
+ },
+
+ /* CSIPHY1 */
+ {
+ .regulator = { NULL },
+ .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy1_timer" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 100000000, 200000000, 266666667 } },
+ .reg = { "csiphy1", "csiphy1_clk_mux" },
+ .interrupt = { "csiphy1" }
+ },
+
+ /* CSIPHY2 */
+ {
+ .regulator = { NULL },
+ .clock = { "top_ahb", "ispif_ahb", "ahb", "csiphy2_timer" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 100000000, 200000000, 266666667 } },
+ .reg = { "csiphy2", "csiphy2_clk_mux" },
+ .interrupt = { "csiphy2" }
+ }
+};
+
+static const struct resources csid_res_8x96[] = {
+ /* CSID0 */
+ {
+ .regulator = { "vdda" },
+ .clock = { "top_ahb", "ispif_ahb", "csi0_ahb", "ahb",
+ "csi0", "csi0_phy", "csi0_pix", "csi0_rdi" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 100000000, 200000000, 266666667 },
+ { 0 },
+ { 0 },
+ { 0 } },
+ .reg = { "csid0" },
+ .interrupt = { "csid0" }
+ },
+
+ /* CSID1 */
+ {
+ .regulator = { "vdda" },
+ .clock = { "top_ahb", "ispif_ahb", "csi1_ahb", "ahb",
+ "csi1", "csi1_phy", "csi1_pix", "csi1_rdi" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 100000000, 200000000, 266666667 },
+ { 0 },
+ { 0 },
+ { 0 } },
+ .reg = { "csid1" },
+ .interrupt = { "csid1" }
+ },
+
+ /* CSID2 */
+ {
+ .regulator = { "vdda" },
+ .clock = { "top_ahb", "ispif_ahb", "csi2_ahb", "ahb",
+ "csi2", "csi2_phy", "csi2_pix", "csi2_rdi" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 100000000, 200000000, 266666667 },
+ { 0 },
+ { 0 },
+ { 0 } },
+ .reg = { "csid2" },
+ .interrupt = { "csid2" }
+ },
+
+ /* CSID3 */
+ {
+ .regulator = { "vdda" },
+ .clock = { "top_ahb", "ispif_ahb", "csi3_ahb", "ahb",
+ "csi3", "csi3_phy", "csi3_pix", "csi3_rdi" },
+ .clock_rate = { { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 100000000, 200000000, 266666667 },
+ { 0 },
+ { 0 },
+ { 0 } },
+ .reg = { "csid3" },
+ .interrupt = { "csid3" }
+ }
+};
+
+static const struct resources_ispif ispif_res_8x96 = {
+ /* ISPIF */
+ .clock = { "top_ahb", "ahb", "ispif_ahb",
+ "csi0", "csi0_pix", "csi0_rdi",
+ "csi1", "csi1_pix", "csi1_rdi",
+ "csi2", "csi2_pix", "csi2_rdi",
+ "csi3", "csi3_pix", "csi3_rdi" },
+ .clock_for_reset = { "vfe0", "csi_vfe0", "vfe1", "csi_vfe1" },
+ .reg = { "ispif", "csi_clk_mux" },
+ .interrupt = "ispif"
+};
+
+static const struct resources vfe_res_8x96[] = {
/* VFE0 */
- .regulator = { NULL },
- .clock = { "camss_top_ahb", "camss_vfe_vfe", "camss_csi_vfe",
- "iface", "bus", "camss_ahb" },
- .clock_rate = { { 0 },
- { 50000000, 80000000, 100000000, 160000000,
- 177780000, 200000000, 266670000, 320000000,
- 400000000, 465000000 },
- { 0 },
- { 0 },
- { 0 },
- { 0 },
- { 0 },
- { 0 },
- { 0 } },
- .reg = { "vfe0" },
- .interrupt = { "vfe0" }
+ {
+ .regulator = { NULL },
+ .clock = { "top_ahb", "ahb", "vfe0", "csi_vfe0", "vfe_ahb",
+ "vfe0_ahb", "vfe_axi", "vfe0_stream"},
+ .clock_rate = { { 0 },
+ { 0 },
+ { 75000000, 100000000, 300000000,
+ 320000000, 480000000, 600000000 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 } },
+ .reg = { "vfe0" },
+ .interrupt = { "vfe0" }
+ },
+
+ /* VFE1 */
+ {
+ .regulator = { NULL },
+ .clock = { "top_ahb", "ahb", "vfe1", "csi_vfe1", "vfe_ahb",
+ "vfe1_ahb", "vfe_axi", "vfe1_stream"},
+ .clock_rate = { { 0 },
+ { 0 },
+ { 75000000, 100000000, 300000000,
+ 320000000, 480000000, 600000000 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 },
+ { 0 } },
+ .reg = { "vfe1" },
+ .interrupt = { "vfe1" }
+ }
};
/*
@@ -245,6 +395,26 @@ int camss_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock)
return 0;
}
+int camss_pm_domain_on(struct camss *camss, int id)
+{
+ if (camss->version == CAMSS_8x96) {
+ camss->genpd_link[id] = device_link_add(camss->dev,
+ camss->genpd[id], DL_FLAG_STATELESS |
+ DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE);
+
+ if (!camss->genpd_link[id])
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+void camss_pm_domain_off(struct camss *camss, int id)
+{
+ if (camss->version == CAMSS_8x96)
+ device_link_del(camss->genpd_link[id]);
+}
+
/*
* camss_of_parse_endpoint_node - Parse port endpoint node
* @dev: Device
@@ -304,6 +474,7 @@ static int camss_of_parse_ports(struct device *dev,
if (of_device_is_available(node))
notifier->num_subdevs++;
+ of_node_put(node);
size = sizeof(*notifier->subdevs) * notifier->num_subdevs;
notifier->subdevs = devm_kzalloc(dev, size, GFP_KERNEL);
if (!notifier->subdevs) {
@@ -334,16 +505,16 @@ static int camss_of_parse_ports(struct device *dev,
}
remote = of_graph_get_remote_port_parent(node);
- of_node_put(node);
-
if (!remote) {
dev_err(dev, "Cannot get remote parent\n");
+ of_node_put(node);
return -EINVAL;
}
csd->asd.match_type = V4L2_ASYNC_MATCH_FWNODE;
csd->asd.match.fwnode = of_fwnode_handle(remote);
}
+ of_node_put(node);
return notifier->num_subdevs;
}
@@ -356,11 +527,29 @@ static int camss_of_parse_ports(struct device *dev,
*/
static int camss_init_subdevices(struct camss *camss)
{
+ const struct resources *csiphy_res;
+ const struct resources *csid_res;
+ const struct resources_ispif *ispif_res;
+ const struct resources *vfe_res;
unsigned int i;
int ret;
- for (i = 0; i < ARRAY_SIZE(camss->csiphy); i++) {
- ret = msm_csiphy_subdev_init(&camss->csiphy[i],
+ if (camss->version == CAMSS_8x16) {
+ csiphy_res = csiphy_res_8x16;
+ csid_res = csid_res_8x16;
+ ispif_res = &ispif_res_8x16;
+ vfe_res = vfe_res_8x16;
+ } else if (camss->version == CAMSS_8x96) {
+ csiphy_res = csiphy_res_8x96;
+ csid_res = csid_res_8x96;
+ ispif_res = &ispif_res_8x96;
+ vfe_res = vfe_res_8x96;
+ } else {
+ return -EINVAL;
+ }
+
+ for (i = 0; i < camss->csiphy_num; i++) {
+ ret = msm_csiphy_subdev_init(camss, &camss->csiphy[i],
&csiphy_res[i], i);
if (ret < 0) {
dev_err(camss->dev,
@@ -370,8 +559,8 @@ static int camss_init_subdevices(struct camss *camss)
}
}
- for (i = 0; i < ARRAY_SIZE(camss->csid); i++) {
- ret = msm_csid_subdev_init(&camss->csid[i],
+ for (i = 0; i < camss->csid_num; i++) {
+ ret = msm_csid_subdev_init(camss, &camss->csid[i],
&csid_res[i], i);
if (ret < 0) {
dev_err(camss->dev,
@@ -381,17 +570,21 @@ static int camss_init_subdevices(struct camss *camss)
}
}
- ret = msm_ispif_subdev_init(&camss->ispif, &ispif_res);
+ ret = msm_ispif_subdev_init(&camss->ispif, ispif_res);
if (ret < 0) {
dev_err(camss->dev, "Failed to init ispif sub-device: %d\n",
ret);
return ret;
}
- ret = msm_vfe_subdev_init(&camss->vfe, &vfe_res);
- if (ret < 0) {
- dev_err(camss->dev, "Fail to init vfe sub-device: %d\n", ret);
- return ret;
+ for (i = 0; i < camss->vfe_num; i++) {
+ ret = msm_vfe_subdev_init(camss, &camss->vfe[i],
+ &vfe_res[i], i);
+ if (ret < 0) {
+ dev_err(camss->dev,
+ "Fail to init vfe%d sub-device: %d\n", i, ret);
+ return ret;
+ }
}
return 0;
@@ -405,10 +598,10 @@ static int camss_init_subdevices(struct camss *camss)
*/
static int camss_register_entities(struct camss *camss)
{
- int i, j;
+ int i, j, k;
int ret;
- for (i = 0; i < ARRAY_SIZE(camss->csiphy); i++) {
+ for (i = 0; i < camss->csiphy_num; i++) {
ret = msm_csiphy_register_entity(&camss->csiphy[i],
&camss->v4l2_dev);
if (ret < 0) {
@@ -419,7 +612,7 @@ static int camss_register_entities(struct camss *camss)
}
}
- for (i = 0; i < ARRAY_SIZE(camss->csid); i++) {
+ for (i = 0; i < camss->csid_num; i++) {
ret = msm_csid_register_entity(&camss->csid[i],
&camss->v4l2_dev);
if (ret < 0) {
@@ -437,15 +630,19 @@ static int camss_register_entities(struct camss *camss)
goto err_reg_ispif;
}
- ret = msm_vfe_register_entities(&camss->vfe, &camss->v4l2_dev);
- if (ret < 0) {
- dev_err(camss->dev, "Failed to register vfe entities: %d\n",
- ret);
- goto err_reg_vfe;
+ for (i = 0; i < camss->vfe_num; i++) {
+ ret = msm_vfe_register_entities(&camss->vfe[i],
+ &camss->v4l2_dev);
+ if (ret < 0) {
+ dev_err(camss->dev,
+ "Failed to register vfe%d entities: %d\n",
+ i, ret);
+ goto err_reg_vfe;
+ }
}
- for (i = 0; i < ARRAY_SIZE(camss->csiphy); i++) {
- for (j = 0; j < ARRAY_SIZE(camss->csid); j++) {
+ for (i = 0; i < camss->csiphy_num; i++) {
+ for (j = 0; j < camss->csid_num; j++) {
ret = media_create_pad_link(
&camss->csiphy[i].subdev.entity,
MSM_CSIPHY_PAD_SRC,
@@ -463,8 +660,8 @@ static int camss_register_entities(struct camss *camss)
}
}
- for (i = 0; i < ARRAY_SIZE(camss->csid); i++) {
- for (j = 0; j < ARRAY_SIZE(camss->ispif.line); j++) {
+ for (i = 0; i < camss->csid_num; i++) {
+ for (j = 0; j < camss->ispif.line_num; j++) {
ret = media_create_pad_link(
&camss->csid[i].subdev.entity,
MSM_CSID_PAD_SRC,
@@ -482,39 +679,42 @@ static int camss_register_entities(struct camss *camss)
}
}
- for (i = 0; i < ARRAY_SIZE(camss->ispif.line); i++) {
- for (j = 0; j < ARRAY_SIZE(camss->vfe.line); j++) {
- ret = media_create_pad_link(
- &camss->ispif.line[i].subdev.entity,
- MSM_ISPIF_PAD_SRC,
- &camss->vfe.line[j].subdev.entity,
- MSM_VFE_PAD_SINK,
- 0);
- if (ret < 0) {
- dev_err(camss->dev,
- "Failed to link %s->%s entities: %d\n",
- camss->ispif.line[i].subdev.entity.name,
- camss->vfe.line[j].subdev.entity.name,
- ret);
- goto err_link;
+ for (i = 0; i < camss->ispif.line_num; i++)
+ for (k = 0; k < camss->vfe_num; k++)
+ for (j = 0; j < ARRAY_SIZE(camss->vfe[k].line); j++) {
+ ret = media_create_pad_link(
+ &camss->ispif.line[i].subdev.entity,
+ MSM_ISPIF_PAD_SRC,
+ &camss->vfe[k].line[j].subdev.entity,
+ MSM_VFE_PAD_SINK,
+ 0);
+ if (ret < 0) {
+ dev_err(camss->dev,
+ "Failed to link %s->%s entities: %d\n",
+ camss->ispif.line[i].subdev.entity.name,
+ camss->vfe[k].line[j].subdev.entity.name,
+ ret);
+ goto err_link;
+ }
}
- }
- }
return 0;
err_link:
- msm_vfe_unregister_entities(&camss->vfe);
+ i = camss->vfe_num;
err_reg_vfe:
+ for (i--; i >= 0; i--)
+ msm_vfe_unregister_entities(&camss->vfe[i]);
+
msm_ispif_unregister_entities(&camss->ispif);
err_reg_ispif:
- i = ARRAY_SIZE(camss->csid);
+ i = camss->csid_num;
err_reg_csid:
for (i--; i >= 0; i--)
msm_csid_unregister_entity(&camss->csid[i]);
- i = ARRAY_SIZE(camss->csiphy);
+ i = camss->csiphy_num;
err_reg_csiphy:
for (i--; i >= 0; i--)
msm_csiphy_unregister_entity(&camss->csiphy[i]);
@@ -532,14 +732,16 @@ static void camss_unregister_entities(struct camss *camss)
{
unsigned int i;
- for (i = 0; i < ARRAY_SIZE(camss->csiphy); i++)
+ for (i = 0; i < camss->csiphy_num; i++)
msm_csiphy_unregister_entity(&camss->csiphy[i]);
- for (i = 0; i < ARRAY_SIZE(camss->csid); i++)
+ for (i = 0; i < camss->csid_num; i++)
msm_csid_unregister_entity(&camss->csid[i]);
msm_ispif_unregister_entities(&camss->ispif);
- msm_vfe_unregister_entities(&camss->vfe);
+
+ for (i = 0; i < camss->vfe_num; i++)
+ msm_vfe_unregister_entities(&camss->vfe[i]);
}
static int camss_subdev_notifier_bound(struct v4l2_async_notifier *async,
@@ -631,6 +833,35 @@ static int camss_probe(struct platform_device *pdev)
camss->dev = dev;
platform_set_drvdata(pdev, camss);
+ if (of_device_is_compatible(dev->of_node, "qcom,msm8916-camss")) {
+ camss->version = CAMSS_8x16;
+ camss->csiphy_num = 2;
+ camss->csid_num = 2;
+ camss->vfe_num = 1;
+ } else if (of_device_is_compatible(dev->of_node,
+ "qcom,msm8996-camss")) {
+ camss->version = CAMSS_8x96;
+ camss->csiphy_num = 3;
+ camss->csid_num = 4;
+ camss->vfe_num = 2;
+ } else {
+ return -EINVAL;
+ }
+
+ camss->csiphy = kcalloc(camss->csiphy_num, sizeof(*camss->csiphy),
+ GFP_KERNEL);
+ if (!camss->csiphy)
+ return -ENOMEM;
+
+ camss->csid = kcalloc(camss->csid_num, sizeof(*camss->csid),
+ GFP_KERNEL);
+ if (!camss->csid)
+ return -ENOMEM;
+
+ camss->vfe = kcalloc(camss->vfe_num, sizeof(*camss->vfe), GFP_KERNEL);
+ if (!camss->vfe)
+ return -ENOMEM;
+
ret = camss_of_parse_ports(dev, &camss->notifier);
if (ret < 0)
return ret;
@@ -687,6 +918,23 @@ static int camss_probe(struct platform_device *pdev)
}
}
+ if (camss->version == CAMSS_8x96) {
+ camss->genpd[PM_DOMAIN_VFE0] = dev_pm_domain_attach_by_id(
+ camss->dev, PM_DOMAIN_VFE0);
+ if (IS_ERR(camss->genpd[PM_DOMAIN_VFE0]))
+ return PTR_ERR(camss->genpd[PM_DOMAIN_VFE0]);
+
+ camss->genpd[PM_DOMAIN_VFE1] = dev_pm_domain_attach_by_id(
+ camss->dev, PM_DOMAIN_VFE1);
+ if (IS_ERR(camss->genpd[PM_DOMAIN_VFE1])) {
+ dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE0],
+ true);
+ return PTR_ERR(camss->genpd[PM_DOMAIN_VFE1]);
+ }
+ }
+
+ pm_runtime_enable(dev);
+
return 0;
err_register_subdevs:
@@ -703,6 +951,13 @@ void camss_delete(struct camss *camss)
media_device_unregister(&camss->media_dev);
media_device_cleanup(&camss->media_dev);
+ pm_runtime_disable(camss->dev);
+
+ if (camss->version == CAMSS_8x96) {
+ dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE0], true);
+ dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE1], true);
+ }
+
kfree(camss);
}
@@ -714,9 +969,12 @@ void camss_delete(struct camss *camss)
*/
static int camss_remove(struct platform_device *pdev)
{
+ unsigned int i;
+
struct camss *camss = platform_get_drvdata(pdev);
- msm_vfe_stop_streaming(&camss->vfe);
+ for (i = 0; i < camss->vfe_num; i++)
+ msm_vfe_stop_streaming(&camss->vfe[i]);
v4l2_async_notifier_unregister(&camss->notifier);
camss_unregister_entities(camss);
@@ -729,17 +987,35 @@ static int camss_remove(struct platform_device *pdev)
static const struct of_device_id camss_dt_match[] = {
{ .compatible = "qcom,msm8916-camss" },
+ { .compatible = "qcom,msm8996-camss" },
{ }
};
MODULE_DEVICE_TABLE(of, camss_dt_match);
+static int camss_runtime_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int camss_runtime_resume(struct device *dev)
+{
+ return 0;
+}
+
+static const struct dev_pm_ops camss_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(camss_runtime_suspend, camss_runtime_resume, NULL)
+};
+
static struct platform_driver qcom_camss_driver = {
.probe = camss_probe,
.remove = camss_remove,
.driver = {
.name = "qcom-camss",
.of_match_table = camss_dt_match,
+ .pm = &camss_pm_ops,
},
};
diff --git a/drivers/media/platform/qcom/camss-8x16/camss.h b/drivers/media/platform/qcom/camss/camss.h
index 4ad223443e4b..418996d8dad8 100644
--- a/drivers/media/platform/qcom/camss-8x16/camss.h
+++ b/drivers/media/platform/qcom/camss/camss.h
@@ -1,23 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* camss.h
*
* Qualcomm MSM Camera Subsystem - Core
*
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
- * Copyright (C) 2015-2017 Linaro Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
+ * Copyright (C) 2015-2018 Linaro Ltd.
*/
#ifndef QC_MSM_CAMSS_H
#define QC_MSM_CAMSS_H
+#include <linux/device.h>
#include <linux/types.h>
#include <media/v4l2-async.h>
#include <media/v4l2-device.h>
@@ -31,9 +24,6 @@
#include "camss-ispif.h"
#include "camss-vfe.h"
-#define CAMSS_CSID_NUM 2
-#define CAMSS_CSIPHY_NUM 2
-
#define to_camss(ptr_module) \
container_of(ptr_module, struct camss, ptr_module)
@@ -50,7 +40,7 @@
#define to_device_index(ptr_module, index) \
(to_camss_index(ptr_module, index)->dev)
-#define CAMSS_RES_MAX 15
+#define CAMSS_RES_MAX 17
struct resources {
char *regulator[CAMSS_RES_MAX];
@@ -67,16 +57,33 @@ struct resources_ispif {
char *interrupt;
};
+enum pm_domain {
+ PM_DOMAIN_VFE0,
+ PM_DOMAIN_VFE1,
+ PM_DOMAIN_COUNT
+};
+
+enum camss_version {
+ CAMSS_8x16,
+ CAMSS_8x96,
+};
+
struct camss {
+ enum camss_version version;
struct v4l2_device v4l2_dev;
struct v4l2_async_notifier notifier;
struct media_device media_dev;
struct device *dev;
- struct csiphy_device csiphy[CAMSS_CSIPHY_NUM];
- struct csid_device csid[CAMSS_CSID_NUM];
+ int csiphy_num;
+ struct csiphy_device *csiphy;
+ int csid_num;
+ struct csid_device *csid;
struct ispif_device ispif;
- struct vfe_device vfe;
+ int vfe_num;
+ struct vfe_device *vfe;
atomic_t ref_count;
+ struct device *genpd[PM_DOMAIN_COUNT];
+ struct device_link *genpd_link[PM_DOMAIN_COUNT];
};
struct camss_camera_interface {
@@ -101,6 +108,8 @@ int camss_enable_clocks(int nclocks, struct camss_clock *clock,
struct device *dev);
void camss_disable_clocks(int nclocks, struct camss_clock *clock);
int camss_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock);
+int camss_pm_domain_on(struct camss *camss, int id);
+void camss_pm_domain_off(struct camss *camss, int id);
void camss_delete(struct camss *camss);
#endif /* QC_MSM_CAMSS_H */
diff --git a/drivers/media/platform/qcom/venus/Makefile b/drivers/media/platform/qcom/venus/Makefile
index bfd4edf7c83f..b44b11b03e12 100644
--- a/drivers/media/platform/qcom/venus/Makefile
+++ b/drivers/media/platform/qcom/venus/Makefile
@@ -2,7 +2,8 @@
# Makefile for Qualcomm Venus driver
venus-core-objs += core.o helpers.o firmware.o \
- hfi_venus.o hfi_msgs.o hfi_cmds.o hfi.o
+ hfi_venus.o hfi_msgs.o hfi_cmds.o hfi.o \
+ hfi_parser.o
venus-dec-objs += vdec.o vdec_ctrls.o
venus-enc-objs += venc.o venc_ctrls.o
diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c
index 41eef376eb2d..bb6add9d340e 100644
--- a/drivers/media/platform/qcom/venus/core.c
+++ b/drivers/media/platform/qcom/venus/core.c
@@ -152,6 +152,83 @@ static void venus_clks_disable(struct venus_core *core)
clk_disable_unprepare(core->clks[i]);
}
+static u32 to_v4l2_codec_type(u32 codec)
+{
+ switch (codec) {
+ case HFI_VIDEO_CODEC_H264:
+ return V4L2_PIX_FMT_H264;
+ case HFI_VIDEO_CODEC_H263:
+ return V4L2_PIX_FMT_H263;
+ case HFI_VIDEO_CODEC_MPEG1:
+ return V4L2_PIX_FMT_MPEG1;
+ case HFI_VIDEO_CODEC_MPEG2:
+ return V4L2_PIX_FMT_MPEG2;
+ case HFI_VIDEO_CODEC_MPEG4:
+ return V4L2_PIX_FMT_MPEG4;
+ case HFI_VIDEO_CODEC_VC1:
+ return V4L2_PIX_FMT_VC1_ANNEX_G;
+ case HFI_VIDEO_CODEC_VP8:
+ return V4L2_PIX_FMT_VP8;
+ case HFI_VIDEO_CODEC_VP9:
+ return V4L2_PIX_FMT_VP9;
+ case HFI_VIDEO_CODEC_DIVX:
+ case HFI_VIDEO_CODEC_DIVX_311:
+ return V4L2_PIX_FMT_XVID;
+ default:
+ return 0;
+ }
+}
+
+static int venus_enumerate_codecs(struct venus_core *core, u32 type)
+{
+ const struct hfi_inst_ops dummy_ops = {};
+ struct venus_inst *inst;
+ u32 codec, codecs;
+ unsigned int i;
+ int ret;
+
+ if (core->res->hfi_version != HFI_VERSION_1XX)
+ return 0;
+
+ inst = kzalloc(sizeof(*inst), GFP_KERNEL);
+ if (!inst)
+ return -ENOMEM;
+
+ mutex_init(&inst->lock);
+ inst->core = core;
+ inst->session_type = type;
+ if (type == VIDC_SESSION_TYPE_DEC)
+ codecs = core->dec_codecs;
+ else
+ codecs = core->enc_codecs;
+
+ ret = hfi_session_create(inst, &dummy_ops);
+ if (ret)
+ goto err;
+
+ for (i = 0; i < MAX_CODEC_NUM; i++) {
+ codec = (1 << i) & codecs;
+ if (!codec)
+ continue;
+
+ ret = hfi_session_init(inst, to_v4l2_codec_type(codec));
+ if (ret)
+ goto done;
+
+ ret = hfi_session_deinit(inst);
+ if (ret)
+ goto done;
+ }
+
+done:
+ hfi_session_destroy(inst);
+err:
+ mutex_destroy(&inst->lock);
+ kfree(inst);
+
+ return ret;
+}
+
static int venus_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -219,6 +296,14 @@ static int venus_probe(struct platform_device *pdev)
if (ret)
goto err_venus_shutdown;
+ ret = venus_enumerate_codecs(core, VIDC_SESSION_TYPE_DEC);
+ if (ret)
+ goto err_venus_shutdown;
+
+ ret = venus_enumerate_codecs(core, VIDC_SESSION_TYPE_ENC);
+ if (ret)
+ goto err_venus_shutdown;
+
ret = v4l2_device_register(dev, &core->v4l2_dev);
if (ret)
goto err_core_deinit;
@@ -365,9 +450,31 @@ static const struct venus_resources msm8996_res = {
.fwname = "qcom/venus-4.2/venus.mdt",
};
+static const struct freq_tbl sdm845_freq_table[] = {
+ { 1944000, 380000000 }, /* 4k UHD @ 60 */
+ { 972000, 320000000 }, /* 4k UHD @ 30 */
+ { 489600, 200000000 }, /* 1080p @ 60 */
+ { 244800, 100000000 }, /* 1080p @ 30 */
+};
+
+static const struct venus_resources sdm845_res = {
+ .freq_tbl = sdm845_freq_table,
+ .freq_tbl_size = ARRAY_SIZE(sdm845_freq_table),
+ .clks = {"core", "iface", "bus" },
+ .clks_num = 3,
+ .max_load = 2563200,
+ .hfi_version = HFI_VERSION_4XX,
+ .vmem_id = VIDC_RESOURCE_NONE,
+ .vmem_size = 0,
+ .vmem_addr = 0,
+ .dma_mask = 0xe0000000 - 1,
+ .fwname = "qcom/venus-5.2/venus.mdt",
+};
+
static const struct of_device_id venus_dt_match[] = {
{ .compatible = "qcom,msm8916-venus", .data = &msm8916_res, },
{ .compatible = "qcom,msm8996-venus", .data = &msm8996_res, },
+ { .compatible = "qcom,sdm845-venus", .data = &sdm845_res, },
{ }
};
MODULE_DEVICE_TABLE(of, venus_dt_match);
diff --git a/drivers/media/platform/qcom/venus/core.h b/drivers/media/platform/qcom/venus/core.h
index 0360d295f4c8..2f02365f4818 100644
--- a/drivers/media/platform/qcom/venus/core.h
+++ b/drivers/media/platform/qcom/venus/core.h
@@ -57,6 +57,30 @@ struct venus_format {
u32 type;
};
+#define MAX_PLANES 4
+#define MAX_FMT_ENTRIES 32
+#define MAX_CAP_ENTRIES 32
+#define MAX_ALLOC_MODE_ENTRIES 16
+#define MAX_CODEC_NUM 32
+
+struct raw_formats {
+ u32 buftype;
+ u32 fmt;
+};
+
+struct venus_caps {
+ u32 codec;
+ u32 domain;
+ bool cap_bufs_mode_dynamic;
+ unsigned int num_caps;
+ struct hfi_capability caps[MAX_CAP_ENTRIES];
+ unsigned int num_pl;
+ struct hfi_profile_level pl[HFI_MAX_PROFILE_COUNT];
+ unsigned int num_fmts;
+ struct raw_formats fmts[MAX_FMT_ENTRIES];
+ bool valid; /* used only for Venus v1xx */
+};
+
/**
* struct venus_core - holds core parameters valid for all instances
*
@@ -65,6 +89,8 @@ struct venus_format {
* @clks: an array of struct clk pointers
* @core0_clk: a struct clk pointer for core0
* @core1_clk: a struct clk pointer for core1
+ * @core0_bus_clk: a struct clk pointer for core0 bus clock
+ * @core1_bus_clk: a struct clk pointer for core1 bus clock
* @vdev_dec: a reference to video device structure for decoder instances
* @vdev_enc: a reference to video device structure for encoder instances
* @v4l2_dev: a holder for v4l2 device structure
@@ -94,6 +120,8 @@ struct venus_core {
struct clk *clks[VIDC_CLKS_NUM_MAX];
struct clk *core0_clk;
struct clk *core1_clk;
+ struct clk *core0_bus_clk;
+ struct clk *core1_bus_clk;
struct video_device *vdev_dec;
struct video_device *vdev_enc;
struct v4l2_device v4l2_dev;
@@ -109,8 +137,8 @@ struct venus_core {
unsigned int error;
bool sys_error;
const struct hfi_core_ops *core_ops;
- u32 enc_codecs;
- u32 dec_codecs;
+ unsigned long enc_codecs;
+ unsigned long dec_codecs;
unsigned int max_sessions_supported;
#define ENC_ROTATION_CAPABILITY 0x1
#define ENC_SCALING_CAPABILITY 0x2
@@ -120,6 +148,8 @@ struct venus_core {
void *priv;
const struct hfi_ops *ops;
struct delayed_work work;
+ struct venus_caps caps[MAX_CODEC_NUM];
+ unsigned int codecs_count;
};
struct vdec_controls {
@@ -160,10 +190,12 @@ struct venc_controls {
u32 mpeg4;
u32 h264;
u32 vpx;
+ u32 hevc;
} profile;
struct {
u32 mpeg4;
u32 h264;
+ u32 hevc;
} level;
};
@@ -185,6 +217,7 @@ struct venus_buffer {
* @list: used for attach an instance to the core
* @lock: instance lock
* @core: a reference to the core struct
+ * @dpbbufs: a list of decoded picture buffers
* @internalbufs: a list of internal bufferes
* @registeredbufs: a list of registered capture bufferes
* @delayed_process a list of delayed buffers
@@ -209,9 +242,15 @@ struct venus_buffer {
* @num_output_bufs: holds number of output buffers
* @input_buf_size holds input buffer size
* @output_buf_size: holds output buffer size
+ * @output2_buf_size: holds secondary decoder output buffer size
+ * @dpb_buftype: decoded picture buffer type
+ * @dpb_fmt: decoded picture buffer raw format
+ * @opb_buftype: output picture buffer type
+ * @opb_fmt: output picture buffer raw format
* @reconfig: a flag raised by decoder when the stream resolution changed
* @reconfig_width: holds the new width
* @reconfig_height: holds the new height
+ * @hfi_codec: current codec for this instance in HFI space
* @sequence_cap: a sequence counter for capture queue
* @sequence_out: a sequence counter for output queue
* @m2m_dev: a reference to m2m device structure
@@ -224,27 +263,12 @@ struct venus_buffer {
* @priv: a private for HFI operations callbacks
* @session_type: the type of the session (decoder or encoder)
* @hprop: a union used as a holder by get property
- * @cap_width: width capability
- * @cap_height: height capability
- * @cap_mbs_per_frame: macroblocks per frame capability
- * @cap_mbs_per_sec: macroblocks per second capability
- * @cap_framerate: framerate capability
- * @cap_scale_x: horizontal scaling capability
- * @cap_scale_y: vertical scaling capability
- * @cap_bitrate: bitrate capability
- * @cap_hier_p: hier capability
- * @cap_ltr_count: LTR count capability
- * @cap_secure_output2_threshold: secure OUTPUT2 threshold capability
- * @cap_bufs_mode_static: buffers allocation mode capability
- * @cap_bufs_mode_dynamic: buffers allocation mode capability
- * @pl_count: count of supported profiles/levels
- * @pl: supported profiles/levels
- * @bufreq: holds buffer requirements
*/
struct venus_inst {
struct list_head list;
struct mutex lock;
struct venus_core *core;
+ struct list_head dpbbufs;
struct list_head internalbufs;
struct list_head registeredbufs;
struct list_head delayed_process;
@@ -273,9 +297,15 @@ struct venus_inst {
unsigned int num_output_bufs;
unsigned int input_buf_size;
unsigned int output_buf_size;
+ unsigned int output2_buf_size;
+ u32 dpb_buftype;
+ u32 dpb_fmt;
+ u32 opb_buftype;
+ u32 opb_fmt;
bool reconfig;
u32 reconfig_width;
u32 reconfig_height;
+ u32 hfi_codec;
u32 sequence_cap;
u32 sequence_out;
struct v4l2_m2m_dev *m2m_dev;
@@ -287,24 +317,12 @@ struct venus_inst {
const struct hfi_inst_ops *ops;
u32 session_type;
union hfi_get_property hprop;
- struct hfi_capability cap_width;
- struct hfi_capability cap_height;
- struct hfi_capability cap_mbs_per_frame;
- struct hfi_capability cap_mbs_per_sec;
- struct hfi_capability cap_framerate;
- struct hfi_capability cap_scale_x;
- struct hfi_capability cap_scale_y;
- struct hfi_capability cap_bitrate;
- struct hfi_capability cap_hier_p;
- struct hfi_capability cap_ltr_count;
- struct hfi_capability cap_secure_output2_threshold;
- bool cap_bufs_mode_static;
- bool cap_bufs_mode_dynamic;
- unsigned int pl_count;
- struct hfi_profile_level pl[HFI_MAX_PROFILE_COUNT];
- struct hfi_buffer_requirements bufreq[HFI_BUFFER_TYPE_MAX];
};
+#define IS_V1(core) ((core)->res->hfi_version == HFI_VERSION_1XX)
+#define IS_V3(core) ((core)->res->hfi_version == HFI_VERSION_3XX)
+#define IS_V4(core) ((core)->res->hfi_version == HFI_VERSION_4XX)
+
#define ctrl_to_inst(ctrl) \
container_of((ctrl)->handler, struct venus_inst, ctrl_handler)
@@ -318,4 +336,18 @@ static inline void *to_hfi_priv(struct venus_core *core)
return core->priv;
}
+static inline struct venus_caps *
+venus_caps_by_codec(struct venus_core *core, u32 codec, u32 domain)
+{
+ unsigned int c;
+
+ for (c = 0; c < core->codecs_count; c++) {
+ if (core->caps[c].codec == codec &&
+ core->caps[c].domain == domain)
+ return &core->caps[c];
+ }
+
+ return NULL;
+}
+
#endif
diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c
index 0ce9559a2924..cd3b96e6f24b 100644
--- a/drivers/media/platform/qcom/venus/helpers.c
+++ b/drivers/media/platform/qcom/venus/helpers.c
@@ -13,6 +13,7 @@
*
*/
#include <linux/clk.h>
+#include <linux/iopoll.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/pm_runtime.h>
@@ -24,6 +25,7 @@
#include "core.h"
#include "helpers.h"
#include "hfi_helper.h"
+#include "hfi_venus_io.h"
struct intbuf {
struct list_head list;
@@ -69,6 +71,9 @@ bool venus_helper_check_codec(struct venus_inst *inst, u32 v4l2_pixfmt)
case V4L2_PIX_FMT_XVID:
codec = HFI_VIDEO_CODEC_DIVX;
break;
+ case V4L2_PIX_FMT_HEVC:
+ codec = HFI_VIDEO_CODEC_HEVC;
+ break;
default:
return false;
}
@@ -83,6 +88,106 @@ bool venus_helper_check_codec(struct venus_inst *inst, u32 v4l2_pixfmt)
}
EXPORT_SYMBOL_GPL(venus_helper_check_codec);
+static int venus_helper_queue_dpb_bufs(struct venus_inst *inst)
+{
+ struct intbuf *buf;
+ int ret = 0;
+
+ list_for_each_entry(buf, &inst->dpbbufs, list) {
+ struct hfi_frame_data fdata;
+
+ memset(&fdata, 0, sizeof(fdata));
+ fdata.alloc_len = buf->size;
+ fdata.device_addr = buf->da;
+ fdata.buffer_type = buf->type;
+
+ ret = hfi_session_process_buf(inst, &fdata);
+ if (ret)
+ goto fail;
+ }
+
+fail:
+ return ret;
+}
+
+int venus_helper_free_dpb_bufs(struct venus_inst *inst)
+{
+ struct intbuf *buf, *n;
+
+ list_for_each_entry_safe(buf, n, &inst->dpbbufs, list) {
+ list_del_init(&buf->list);
+ dma_free_attrs(inst->core->dev, buf->size, buf->va, buf->da,
+ buf->attrs);
+ kfree(buf);
+ }
+
+ INIT_LIST_HEAD(&inst->dpbbufs);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(venus_helper_free_dpb_bufs);
+
+int venus_helper_alloc_dpb_bufs(struct venus_inst *inst)
+{
+ struct venus_core *core = inst->core;
+ struct device *dev = core->dev;
+ enum hfi_version ver = core->res->hfi_version;
+ struct hfi_buffer_requirements bufreq;
+ u32 buftype = inst->dpb_buftype;
+ unsigned int dpb_size = 0;
+ struct intbuf *buf;
+ unsigned int i;
+ u32 count;
+ int ret;
+
+ /* no need to allocate dpb buffers */
+ if (!inst->dpb_fmt)
+ return 0;
+
+ if (inst->dpb_buftype == HFI_BUFFER_OUTPUT)
+ dpb_size = inst->output_buf_size;
+ else if (inst->dpb_buftype == HFI_BUFFER_OUTPUT2)
+ dpb_size = inst->output2_buf_size;
+
+ if (!dpb_size)
+ return 0;
+
+ ret = venus_helper_get_bufreq(inst, buftype, &bufreq);
+ if (ret)
+ return ret;
+
+ count = HFI_BUFREQ_COUNT_MIN(&bufreq, ver);
+
+ for (i = 0; i < count; i++) {
+ buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ buf->type = buftype;
+ buf->size = dpb_size;
+ buf->attrs = DMA_ATTR_WRITE_COMBINE |
+ DMA_ATTR_NO_KERNEL_MAPPING;
+ buf->va = dma_alloc_attrs(dev, buf->size, &buf->da, GFP_KERNEL,
+ buf->attrs);
+ if (!buf->va) {
+ kfree(buf);
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ list_add_tail(&buf->list, &inst->dpbbufs);
+ }
+
+ return 0;
+
+fail:
+ venus_helper_free_dpb_bufs(inst);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(venus_helper_alloc_dpb_bufs);
+
static int intbufs_set_buffer(struct venus_inst *inst, u32 type)
{
struct venus_core *core = inst->core;
@@ -166,21 +271,38 @@ static int intbufs_unset_buffers(struct venus_inst *inst)
return ret;
}
-static const unsigned int intbuf_types[] = {
- HFI_BUFFER_INTERNAL_SCRATCH,
- HFI_BUFFER_INTERNAL_SCRATCH_1,
- HFI_BUFFER_INTERNAL_SCRATCH_2,
+static const unsigned int intbuf_types_1xx[] = {
+ HFI_BUFFER_INTERNAL_SCRATCH(HFI_VERSION_1XX),
+ HFI_BUFFER_INTERNAL_SCRATCH_1(HFI_VERSION_1XX),
+ HFI_BUFFER_INTERNAL_SCRATCH_2(HFI_VERSION_1XX),
+ HFI_BUFFER_INTERNAL_PERSIST,
+ HFI_BUFFER_INTERNAL_PERSIST_1,
+};
+
+static const unsigned int intbuf_types_4xx[] = {
+ HFI_BUFFER_INTERNAL_SCRATCH(HFI_VERSION_4XX),
+ HFI_BUFFER_INTERNAL_SCRATCH_1(HFI_VERSION_4XX),
+ HFI_BUFFER_INTERNAL_SCRATCH_2(HFI_VERSION_4XX),
HFI_BUFFER_INTERNAL_PERSIST,
HFI_BUFFER_INTERNAL_PERSIST_1,
};
static int intbufs_alloc(struct venus_inst *inst)
{
- unsigned int i;
+ const unsigned int *intbuf;
+ size_t arr_sz, i;
int ret;
- for (i = 0; i < ARRAY_SIZE(intbuf_types); i++) {
- ret = intbufs_set_buffer(inst, intbuf_types[i]);
+ if (IS_V4(inst->core)) {
+ arr_sz = ARRAY_SIZE(intbuf_types_4xx);
+ intbuf = intbuf_types_4xx;
+ } else {
+ arr_sz = ARRAY_SIZE(intbuf_types_1xx);
+ intbuf = intbuf_types_1xx;
+ }
+
+ for (i = 0; i < arr_sz; i++) {
+ ret = intbufs_set_buffer(inst, intbuf[i]);
if (ret)
goto error;
}
@@ -257,20 +379,23 @@ static int load_scale_clocks(struct venus_core *core)
set_freq:
- if (core->res->hfi_version == HFI_VERSION_3XX) {
- ret = clk_set_rate(clk, freq);
- ret |= clk_set_rate(core->core0_clk, freq);
- ret |= clk_set_rate(core->core1_clk, freq);
- } else {
- ret = clk_set_rate(clk, freq);
- }
+ ret = clk_set_rate(clk, freq);
+ if (ret)
+ goto err;
- if (ret) {
- dev_err(dev, "failed to set clock rate %lu (%d)\n", freq, ret);
- return ret;
- }
+ ret = clk_set_rate(core->core0_clk, freq);
+ if (ret)
+ goto err;
+
+ ret = clk_set_rate(core->core1_clk, freq);
+ if (ret)
+ goto err;
return 0;
+
+err:
+ dev_err(dev, "failed to set clock rate %lu (%d)\n", freq, ret);
+ return ret;
}
static void fill_buffer_desc(const struct venus_buffer *buf,
@@ -325,7 +450,10 @@ session_process_buf(struct venus_inst *inst, struct vb2_v4l2_buffer *vbuf)
if (vbuf->flags & V4L2_BUF_FLAG_LAST || !fdata.filled_len)
fdata.flags |= HFI_BUFFERFLAG_EOS;
} else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
- fdata.buffer_type = HFI_BUFFER_OUTPUT;
+ if (inst->session_type == VIDC_SESSION_TYPE_ENC)
+ fdata.buffer_type = HFI_BUFFER_OUTPUT;
+ else
+ fdata.buffer_type = inst->opb_buftype;
fdata.filled_len = 0;
fdata.offset = 0;
}
@@ -337,18 +465,16 @@ session_process_buf(struct venus_inst *inst, struct vb2_v4l2_buffer *vbuf)
return 0;
}
-static inline int is_reg_unreg_needed(struct venus_inst *inst)
+static bool is_dynamic_bufmode(struct venus_inst *inst)
{
- if (inst->session_type == VIDC_SESSION_TYPE_DEC &&
- inst->core->res->hfi_version == HFI_VERSION_3XX)
- return 0;
+ struct venus_core *core = inst->core;
+ struct venus_caps *caps;
- if (inst->session_type == VIDC_SESSION_TYPE_DEC &&
- inst->cap_bufs_mode_dynamic &&
- inst->core->res->hfi_version == HFI_VERSION_1XX)
+ caps = venus_caps_by_codec(core, inst->hfi_codec, inst->session_type);
+ if (!caps)
return 0;
- return 1;
+ return caps->cap_bufs_mode_dynamic;
}
static int session_unregister_bufs(struct venus_inst *inst)
@@ -357,7 +483,7 @@ static int session_unregister_bufs(struct venus_inst *inst)
struct hfi_buffer_desc bd;
int ret = 0;
- if (!is_reg_unreg_needed(inst))
+ if (is_dynamic_bufmode(inst))
return 0;
list_for_each_entry_safe(buf, n, &inst->registeredbufs, reg_list) {
@@ -377,7 +503,7 @@ static int session_register_bufs(struct venus_inst *inst)
struct venus_buffer *buf;
int ret = 0;
- if (!is_reg_unreg_needed(inst))
+ if (is_dynamic_bufmode(inst))
return 0;
list_for_each_entry(buf, &inst->registeredbufs, reg_list) {
@@ -392,6 +518,20 @@ static int session_register_bufs(struct venus_inst *inst)
return ret;
}
+static u32 to_hfi_raw_fmt(u32 v4l2_fmt)
+{
+ switch (v4l2_fmt) {
+ case V4L2_PIX_FMT_NV12:
+ return HFI_COLOR_FORMAT_NV12;
+ case V4L2_PIX_FMT_NV21:
+ return HFI_COLOR_FORMAT_NV21;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
int venus_helper_get_bufreq(struct venus_inst *inst, u32 type,
struct hfi_buffer_requirements *req)
{
@@ -423,6 +563,104 @@ int venus_helper_get_bufreq(struct venus_inst *inst, u32 type,
}
EXPORT_SYMBOL_GPL(venus_helper_get_bufreq);
+static u32 get_framesize_raw_nv12(u32 width, u32 height)
+{
+ u32 y_stride, uv_stride, y_plane;
+ u32 y_sclines, uv_sclines, uv_plane;
+ u32 size;
+
+ y_stride = ALIGN(width, 128);
+ uv_stride = ALIGN(width, 128);
+ y_sclines = ALIGN(height, 32);
+ uv_sclines = ALIGN(((height + 1) >> 1), 16);
+
+ y_plane = y_stride * y_sclines;
+ uv_plane = uv_stride * uv_sclines + SZ_4K;
+ size = y_plane + uv_plane + SZ_8K;
+
+ return ALIGN(size, SZ_4K);
+}
+
+static u32 get_framesize_raw_nv12_ubwc(u32 width, u32 height)
+{
+ u32 y_meta_stride, y_meta_plane;
+ u32 y_stride, y_plane;
+ u32 uv_meta_stride, uv_meta_plane;
+ u32 uv_stride, uv_plane;
+ u32 extradata = SZ_16K;
+
+ y_meta_stride = ALIGN(DIV_ROUND_UP(width, 32), 64);
+ y_meta_plane = y_meta_stride * ALIGN(DIV_ROUND_UP(height, 8), 16);
+ y_meta_plane = ALIGN(y_meta_plane, SZ_4K);
+
+ y_stride = ALIGN(width, 128);
+ y_plane = ALIGN(y_stride * ALIGN(height, 32), SZ_4K);
+
+ uv_meta_stride = ALIGN(DIV_ROUND_UP(width / 2, 16), 64);
+ uv_meta_plane = uv_meta_stride * ALIGN(DIV_ROUND_UP(height / 2, 8), 16);
+ uv_meta_plane = ALIGN(uv_meta_plane, SZ_4K);
+
+ uv_stride = ALIGN(width, 128);
+ uv_plane = ALIGN(uv_stride * ALIGN(height / 2, 32), SZ_4K);
+
+ return ALIGN(y_meta_plane + y_plane + uv_meta_plane + uv_plane +
+ max(extradata, y_stride * 48), SZ_4K);
+}
+
+u32 venus_helper_get_framesz_raw(u32 hfi_fmt, u32 width, u32 height)
+{
+ switch (hfi_fmt) {
+ case HFI_COLOR_FORMAT_NV12:
+ case HFI_COLOR_FORMAT_NV21:
+ return get_framesize_raw_nv12(width, height);
+ case HFI_COLOR_FORMAT_NV12_UBWC:
+ return get_framesize_raw_nv12_ubwc(width, height);
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL_GPL(venus_helper_get_framesz_raw);
+
+u32 venus_helper_get_framesz(u32 v4l2_fmt, u32 width, u32 height)
+{
+ u32 hfi_fmt, sz;
+ bool compressed;
+
+ switch (v4l2_fmt) {
+ case V4L2_PIX_FMT_MPEG:
+ case V4L2_PIX_FMT_H264:
+ case V4L2_PIX_FMT_H264_NO_SC:
+ case V4L2_PIX_FMT_H264_MVC:
+ case V4L2_PIX_FMT_H263:
+ case V4L2_PIX_FMT_MPEG1:
+ case V4L2_PIX_FMT_MPEG2:
+ case V4L2_PIX_FMT_MPEG4:
+ case V4L2_PIX_FMT_XVID:
+ case V4L2_PIX_FMT_VC1_ANNEX_G:
+ case V4L2_PIX_FMT_VC1_ANNEX_L:
+ case V4L2_PIX_FMT_VP8:
+ case V4L2_PIX_FMT_VP9:
+ case V4L2_PIX_FMT_HEVC:
+ compressed = true;
+ break;
+ default:
+ compressed = false;
+ break;
+ }
+
+ if (compressed) {
+ sz = ALIGN(height, 32) * ALIGN(width, 32) * 3 / 2 / 2;
+ return ALIGN(sz, SZ_4K);
+ }
+
+ hfi_fmt = to_hfi_raw_fmt(v4l2_fmt);
+ if (!hfi_fmt)
+ return 0;
+
+ return venus_helper_get_framesz_raw(hfi_fmt, width, height);
+}
+EXPORT_SYMBOL_GPL(venus_helper_get_framesz);
+
int venus_helper_set_input_resolution(struct venus_inst *inst,
unsigned int width, unsigned int height)
{
@@ -438,12 +676,13 @@ int venus_helper_set_input_resolution(struct venus_inst *inst,
EXPORT_SYMBOL_GPL(venus_helper_set_input_resolution);
int venus_helper_set_output_resolution(struct venus_inst *inst,
- unsigned int width, unsigned int height)
+ unsigned int width, unsigned int height,
+ u32 buftype)
{
u32 ptype = HFI_PROPERTY_PARAM_FRAME_SIZE;
struct hfi_framesize fs;
- fs.buffer_type = HFI_BUFFER_OUTPUT;
+ fs.buffer_type = buftype;
fs.width = width;
fs.height = height;
@@ -451,8 +690,37 @@ int venus_helper_set_output_resolution(struct venus_inst *inst,
}
EXPORT_SYMBOL_GPL(venus_helper_set_output_resolution);
+int venus_helper_set_work_mode(struct venus_inst *inst, u32 mode)
+{
+ const u32 ptype = HFI_PROPERTY_PARAM_WORK_MODE;
+ struct hfi_video_work_mode wm;
+
+ if (!IS_V4(inst->core))
+ return 0;
+
+ wm.video_work_mode = mode;
+
+ return hfi_session_set_property(inst, ptype, &wm);
+}
+EXPORT_SYMBOL_GPL(venus_helper_set_work_mode);
+
+int venus_helper_set_core_usage(struct venus_inst *inst, u32 usage)
+{
+ const u32 ptype = HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE;
+ struct hfi_videocores_usage_type cu;
+
+ if (!IS_V4(inst->core))
+ return 0;
+
+ cu.video_core_enable_mask = usage;
+
+ return hfi_session_set_property(inst, ptype, &cu);
+}
+EXPORT_SYMBOL_GPL(venus_helper_set_core_usage);
+
int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs,
- unsigned int output_bufs)
+ unsigned int output_bufs,
+ unsigned int output2_bufs)
{
u32 ptype = HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL;
struct hfi_buffer_count_actual buf_count;
@@ -468,41 +736,122 @@ int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs,
buf_count.type = HFI_BUFFER_OUTPUT;
buf_count.count_actual = output_bufs;
- return hfi_session_set_property(inst, ptype, &buf_count);
+ ret = hfi_session_set_property(inst, ptype, &buf_count);
+ if (ret)
+ return ret;
+
+ if (output2_bufs) {
+ buf_count.type = HFI_BUFFER_OUTPUT2;
+ buf_count.count_actual = output2_bufs;
+
+ ret = hfi_session_set_property(inst, ptype, &buf_count);
+ }
+
+ return ret;
}
EXPORT_SYMBOL_GPL(venus_helper_set_num_bufs);
-int venus_helper_set_color_format(struct venus_inst *inst, u32 pixfmt)
+int venus_helper_set_raw_format(struct venus_inst *inst, u32 hfi_format,
+ u32 buftype)
{
+ const u32 ptype = HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT;
struct hfi_uncompressed_format_select fmt;
- u32 ptype = HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT;
- int ret;
+
+ fmt.buffer_type = buftype;
+ fmt.format = hfi_format;
+
+ return hfi_session_set_property(inst, ptype, &fmt);
+}
+EXPORT_SYMBOL_GPL(venus_helper_set_raw_format);
+
+int venus_helper_set_color_format(struct venus_inst *inst, u32 pixfmt)
+{
+ u32 hfi_format, buftype;
if (inst->session_type == VIDC_SESSION_TYPE_DEC)
- fmt.buffer_type = HFI_BUFFER_OUTPUT;
+ buftype = HFI_BUFFER_OUTPUT;
else if (inst->session_type == VIDC_SESSION_TYPE_ENC)
- fmt.buffer_type = HFI_BUFFER_INPUT;
+ buftype = HFI_BUFFER_INPUT;
else
return -EINVAL;
- switch (pixfmt) {
- case V4L2_PIX_FMT_NV12:
- fmt.format = HFI_COLOR_FORMAT_NV12;
- break;
- case V4L2_PIX_FMT_NV21:
- fmt.format = HFI_COLOR_FORMAT_NV21;
- break;
- default:
+ hfi_format = to_hfi_raw_fmt(pixfmt);
+ if (!hfi_format)
return -EINVAL;
- }
- ret = hfi_session_set_property(inst, ptype, &fmt);
+ return venus_helper_set_raw_format(inst, hfi_format, buftype);
+}
+EXPORT_SYMBOL_GPL(venus_helper_set_color_format);
+
+int venus_helper_set_multistream(struct venus_inst *inst, bool out_en,
+ bool out2_en)
+{
+ struct hfi_multi_stream multi = {0};
+ u32 ptype = HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM;
+ int ret;
+
+ multi.buffer_type = HFI_BUFFER_OUTPUT;
+ multi.enable = out_en;
+
+ ret = hfi_session_set_property(inst, ptype, &multi);
+ if (ret)
+ return ret;
+
+ multi.buffer_type = HFI_BUFFER_OUTPUT2;
+ multi.enable = out2_en;
+
+ return hfi_session_set_property(inst, ptype, &multi);
+}
+EXPORT_SYMBOL_GPL(venus_helper_set_multistream);
+
+int venus_helper_set_dyn_bufmode(struct venus_inst *inst)
+{
+ const u32 ptype = HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE;
+ struct hfi_buffer_alloc_mode mode;
+ int ret;
+
+ if (!is_dynamic_bufmode(inst))
+ return 0;
+
+ mode.type = HFI_BUFFER_OUTPUT;
+ mode.mode = HFI_BUFFER_MODE_DYNAMIC;
+
+ ret = hfi_session_set_property(inst, ptype, &mode);
if (ret)
return ret;
+ mode.type = HFI_BUFFER_OUTPUT2;
+
+ return hfi_session_set_property(inst, ptype, &mode);
+}
+EXPORT_SYMBOL_GPL(venus_helper_set_dyn_bufmode);
+
+int venus_helper_set_bufsize(struct venus_inst *inst, u32 bufsize, u32 buftype)
+{
+ const u32 ptype = HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL;
+ struct hfi_buffer_size_actual bufsz;
+
+ bufsz.type = buftype;
+ bufsz.size = bufsize;
+
+ return hfi_session_set_property(inst, ptype, &bufsz);
+}
+EXPORT_SYMBOL_GPL(venus_helper_set_bufsize);
+
+unsigned int venus_helper_get_opb_size(struct venus_inst *inst)
+{
+ /* the encoder has only one output */
+ if (inst->session_type == VIDC_SESSION_TYPE_ENC)
+ return inst->output_buf_size;
+
+ if (inst->opb_buftype == HFI_BUFFER_OUTPUT)
+ return inst->output_buf_size;
+ else if (inst->opb_buftype == HFI_BUFFER_OUTPUT2)
+ return inst->output2_buf_size;
+
return 0;
}
-EXPORT_SYMBOL_GPL(venus_helper_set_color_format);
+EXPORT_SYMBOL_GPL(venus_helper_get_opb_size);
static void delayed_process_buf_func(struct work_struct *work)
{
@@ -602,9 +951,10 @@ EXPORT_SYMBOL_GPL(venus_helper_vb2_buf_init);
int venus_helper_vb2_buf_prepare(struct vb2_buffer *vb)
{
struct venus_inst *inst = vb2_get_drv_priv(vb->vb2_queue);
+ unsigned int out_buf_size = venus_helper_get_opb_size(inst);
if (vb->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
- vb2_plane_size(vb, 0) < inst->output_buf_size)
+ vb2_plane_size(vb, 0) < out_buf_size)
return -EINVAL;
if (vb->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
vb2_plane_size(vb, 0) < inst->input_buf_size)
@@ -674,6 +1024,8 @@ void venus_helper_vb2_stop_streaming(struct vb2_queue *q)
if (ret)
hfi_session_abort(inst);
+ venus_helper_free_dpb_bufs(inst);
+
load_scale_clocks(core);
INIT_LIST_HEAD(&inst->registeredbufs);
}
@@ -712,8 +1064,14 @@ int venus_helper_vb2_start_streaming(struct venus_inst *inst)
if (ret)
goto err_unload_res;
+ ret = venus_helper_queue_dpb_bufs(inst);
+ if (ret)
+ goto err_session_stop;
+
return 0;
+err_session_stop:
+ hfi_session_stop(inst);
err_unload_res:
hfi_session_unload_res(inst);
err_unreg_bufs:
@@ -766,3 +1124,113 @@ void venus_helper_init_instance(struct venus_inst *inst)
}
}
EXPORT_SYMBOL_GPL(venus_helper_init_instance);
+
+static bool find_fmt_from_caps(struct venus_caps *caps, u32 buftype, u32 fmt)
+{
+ unsigned int i;
+
+ for (i = 0; i < caps->num_fmts; i++) {
+ if (caps->fmts[i].buftype == buftype &&
+ caps->fmts[i].fmt == fmt)
+ return true;
+ }
+
+ return false;
+}
+
+int venus_helper_get_out_fmts(struct venus_inst *inst, u32 v4l2_fmt,
+ u32 *out_fmt, u32 *out2_fmt, bool ubwc)
+{
+ struct venus_core *core = inst->core;
+ struct venus_caps *caps;
+ u32 ubwc_fmt, fmt = to_hfi_raw_fmt(v4l2_fmt);
+ bool found, found_ubwc;
+
+ *out_fmt = *out2_fmt = 0;
+
+ if (!fmt)
+ return -EINVAL;
+
+ caps = venus_caps_by_codec(core, inst->hfi_codec, inst->session_type);
+ if (!caps)
+ return -EINVAL;
+
+ if (ubwc) {
+ ubwc_fmt = fmt | HFI_COLOR_FORMAT_UBWC_BASE;
+ found_ubwc = find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT,
+ ubwc_fmt);
+ found = find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT2, fmt);
+
+ if (found_ubwc && found) {
+ *out_fmt = ubwc_fmt;
+ *out2_fmt = fmt;
+ return 0;
+ }
+ }
+
+ found = find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT, fmt);
+ if (found) {
+ *out_fmt = fmt;
+ *out2_fmt = 0;
+ return 0;
+ }
+
+ found = find_fmt_from_caps(caps, HFI_BUFFER_OUTPUT2, fmt);
+ if (found) {
+ *out_fmt = 0;
+ *out2_fmt = fmt;
+ return 0;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(venus_helper_get_out_fmts);
+
+int venus_helper_power_enable(struct venus_core *core, u32 session_type,
+ bool enable)
+{
+ void __iomem *ctrl, *stat;
+ u32 val;
+ int ret;
+
+ if (!IS_V3(core) && !IS_V4(core))
+ return 0;
+
+ if (IS_V3(core)) {
+ if (session_type == VIDC_SESSION_TYPE_DEC)
+ ctrl = core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL;
+ else
+ ctrl = core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL;
+ if (enable)
+ writel(0, ctrl);
+ else
+ writel(1, ctrl);
+
+ return 0;
+ }
+
+ if (session_type == VIDC_SESSION_TYPE_DEC) {
+ ctrl = core->base + WRAPPER_VCODEC0_MMCC_POWER_CONTROL;
+ stat = core->base + WRAPPER_VCODEC0_MMCC_POWER_STATUS;
+ } else {
+ ctrl = core->base + WRAPPER_VCODEC1_MMCC_POWER_CONTROL;
+ stat = core->base + WRAPPER_VCODEC1_MMCC_POWER_STATUS;
+ }
+
+ if (enable) {
+ writel(0, ctrl);
+
+ ret = readl_poll_timeout(stat, val, val & BIT(1), 1, 100);
+ if (ret)
+ return ret;
+ } else {
+ writel(1, ctrl);
+
+ ret = readl_poll_timeout(stat, val, !(val & BIT(1)), 1, 100);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(venus_helper_power_enable);
diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h
index 971392be5df5..2475f284f396 100644
--- a/drivers/media/platform/qcom/venus/helpers.h
+++ b/drivers/media/platform/qcom/venus/helpers.h
@@ -33,14 +33,33 @@ void venus_helper_m2m_device_run(void *priv);
void venus_helper_m2m_job_abort(void *priv);
int venus_helper_get_bufreq(struct venus_inst *inst, u32 type,
struct hfi_buffer_requirements *req);
+u32 venus_helper_get_framesz_raw(u32 hfi_fmt, u32 width, u32 height);
+u32 venus_helper_get_framesz(u32 v4l2_fmt, u32 width, u32 height);
int venus_helper_set_input_resolution(struct venus_inst *inst,
unsigned int width, unsigned int height);
int venus_helper_set_output_resolution(struct venus_inst *inst,
- unsigned int width, unsigned int height);
+ unsigned int width, unsigned int height,
+ u32 buftype);
+int venus_helper_set_work_mode(struct venus_inst *inst, u32 mode);
+int venus_helper_set_core_usage(struct venus_inst *inst, u32 usage);
int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs,
- unsigned int output_bufs);
+ unsigned int output_bufs,
+ unsigned int output2_bufs);
+int venus_helper_set_raw_format(struct venus_inst *inst, u32 hfi_format,
+ u32 buftype);
int venus_helper_set_color_format(struct venus_inst *inst, u32 fmt);
+int venus_helper_set_dyn_bufmode(struct venus_inst *inst);
+int venus_helper_set_bufsize(struct venus_inst *inst, u32 bufsize, u32 buftype);
+int venus_helper_set_multistream(struct venus_inst *inst, bool out_en,
+ bool out2_en);
+unsigned int venus_helper_get_opb_size(struct venus_inst *inst);
void venus_helper_acquire_buf_ref(struct vb2_v4l2_buffer *vbuf);
void venus_helper_release_buf_ref(struct venus_inst *inst, unsigned int idx);
void venus_helper_init_instance(struct venus_inst *inst);
+int venus_helper_get_out_fmts(struct venus_inst *inst, u32 fmt, u32 *out_fmt,
+ u32 *out2_fmt, bool ubwc);
+int venus_helper_alloc_dpb_bufs(struct venus_inst *inst);
+int venus_helper_free_dpb_bufs(struct venus_inst *inst);
+int venus_helper_power_enable(struct venus_core *core, u32 session_type,
+ bool enable);
#endif
diff --git a/drivers/media/platform/qcom/venus/hfi.c b/drivers/media/platform/qcom/venus/hfi.c
index bca894a00c07..24207829982f 100644
--- a/drivers/media/platform/qcom/venus/hfi.c
+++ b/drivers/media/platform/qcom/venus/hfi.c
@@ -49,6 +49,8 @@ static u32 to_codec_type(u32 pixfmt)
return HFI_VIDEO_CODEC_VP9;
case V4L2_PIX_FMT_XVID:
return HFI_VIDEO_CODEC_DIVX;
+ case V4L2_PIX_FMT_HEVC:
+ return HFI_VIDEO_CODEC_HEVC;
default:
return 0;
}
@@ -203,13 +205,12 @@ int hfi_session_init(struct venus_inst *inst, u32 pixfmt)
{
struct venus_core *core = inst->core;
const struct hfi_ops *ops = core->ops;
- u32 codec;
int ret;
- codec = to_codec_type(pixfmt);
+ inst->hfi_codec = to_codec_type(pixfmt);
reinit_completion(&inst->done);
- ret = ops->session_init(inst, inst->session_type, codec);
+ ret = ops->session_init(inst, inst->session_type, inst->hfi_codec);
if (ret)
return ret;
@@ -312,7 +313,7 @@ int hfi_session_continue(struct venus_inst *inst)
{
struct venus_core *core = inst->core;
- if (core->res->hfi_version != HFI_VERSION_3XX)
+ if (core->res->hfi_version == HFI_VERSION_1XX)
return 0;
return core->ops->session_continue(inst);
@@ -473,7 +474,8 @@ int hfi_session_process_buf(struct venus_inst *inst, struct hfi_frame_data *fd)
if (fd->buffer_type == HFI_BUFFER_INPUT)
return ops->session_etb(inst, fd);
- else if (fd->buffer_type == HFI_BUFFER_OUTPUT)
+ else if (fd->buffer_type == HFI_BUFFER_OUTPUT ||
+ fd->buffer_type == HFI_BUFFER_OUTPUT2)
return ops->session_ftb(inst, fd);
return -EINVAL;
diff --git a/drivers/media/platform/qcom/venus/hfi.h b/drivers/media/platform/qcom/venus/hfi.h
index 5466b7d60dd0..6038d8e0ab22 100644
--- a/drivers/media/platform/qcom/venus/hfi.h
+++ b/drivers/media/platform/qcom/venus/hfi.h
@@ -74,6 +74,16 @@ struct hfi_event_data {
u32 tag;
u32 profile;
u32 level;
+ /* the following properties start appear from v4 onwards */
+ u32 bit_depth;
+ u32 pic_struct;
+ u32 colour_space;
+ u32 entropy_mode;
+ u32 buf_count;
+ struct {
+ u32 left, top;
+ u32 width, height;
+ } input_crop;
};
/* define core states */
diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.c b/drivers/media/platform/qcom/venus/hfi_cmds.c
index 1cfeb7743041..e8389d8d8c48 100644
--- a/drivers/media/platform/qcom/venus/hfi_cmds.c
+++ b/drivers/media/platform/qcom/venus/hfi_cmds.c
@@ -1166,6 +1166,63 @@ pkt_session_set_property_3xx(struct hfi_session_set_property_pkt *pkt,
return ret;
}
+static int
+pkt_session_set_property_4xx(struct hfi_session_set_property_pkt *pkt,
+ void *cookie, u32 ptype, void *pdata)
+{
+ void *prop_data;
+
+ if (!pkt || !cookie || !pdata)
+ return -EINVAL;
+
+ prop_data = &pkt->data[1];
+
+ pkt->shdr.hdr.size = sizeof(*pkt);
+ pkt->shdr.hdr.pkt_type = HFI_CMD_SESSION_SET_PROPERTY;
+ pkt->shdr.session_id = hash32_ptr(cookie);
+ pkt->num_properties = 1;
+ pkt->data[0] = ptype;
+
+ /*
+ * Any session set property which is different in 3XX packetization
+ * should be added as a new case below. All unchanged session set
+ * properties will be handled in the default case.
+ */
+ switch (ptype) {
+ case HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL: {
+ struct hfi_buffer_count_actual *in = pdata;
+ struct hfi_buffer_count_actual_4xx *count = prop_data;
+
+ count->count_actual = in->count_actual;
+ count->type = in->type;
+ count->count_min_host = in->count_actual;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*count);
+ break;
+ }
+ case HFI_PROPERTY_PARAM_WORK_MODE: {
+ struct hfi_video_work_mode *in = pdata, *wm = prop_data;
+
+ wm->video_work_mode = in->video_work_mode;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*wm);
+ break;
+ }
+ case HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE: {
+ struct hfi_videocores_usage_type *in = pdata, *cu = prop_data;
+
+ cu->video_core_enable_mask = in->video_core_enable_mask;
+ pkt->shdr.hdr.size += sizeof(u32) + sizeof(*cu);
+ break;
+ }
+ case HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE:
+ /* not implemented on Venus 4xx */
+ break;
+ default:
+ return pkt_session_set_property_3xx(pkt, cookie, ptype, pdata);
+ }
+
+ return 0;
+}
+
int pkt_session_get_property(struct hfi_session_get_property_pkt *pkt,
void *cookie, u32 ptype)
{
@@ -1181,7 +1238,10 @@ int pkt_session_set_property(struct hfi_session_set_property_pkt *pkt,
if (hfi_ver == HFI_VERSION_1XX)
return pkt_session_set_property_1x(pkt, cookie, ptype, pdata);
- return pkt_session_set_property_3xx(pkt, cookie, ptype, pdata);
+ if (hfi_ver == HFI_VERSION_3XX)
+ return pkt_session_set_property_3xx(pkt, cookie, ptype, pdata);
+
+ return pkt_session_set_property_4xx(pkt, cookie, ptype, pdata);
}
void pkt_set_version(enum hfi_version version)
diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h
index 55d8eb21403a..15804ad7e65d 100644
--- a/drivers/media/platform/qcom/venus/hfi_helper.h
+++ b/drivers/media/platform/qcom/venus/hfi_helper.h
@@ -121,6 +121,7 @@
#define HFI_EXTRADATA_METADATA_FILLER 0x7fe00002
#define HFI_INDEX_EXTRADATA_INPUT_CROP 0x0700000e
+#define HFI_INDEX_EXTRADATA_OUTPUT_CROP 0x0700000f
#define HFI_INDEX_EXTRADATA_DIGITAL_ZOOM 0x07000010
#define HFI_INDEX_EXTRADATA_ASPECT_RATIO 0x7f100003
@@ -376,13 +377,18 @@
#define HFI_BUFFER_OUTPUT2 0x3
#define HFI_BUFFER_INTERNAL_PERSIST 0x4
#define HFI_BUFFER_INTERNAL_PERSIST_1 0x5
-#define HFI_BUFFER_INTERNAL_SCRATCH 0x1000001
-#define HFI_BUFFER_EXTRADATA_INPUT 0x1000002
-#define HFI_BUFFER_EXTRADATA_OUTPUT 0x1000003
-#define HFI_BUFFER_EXTRADATA_OUTPUT2 0x1000004
-#define HFI_BUFFER_INTERNAL_SCRATCH_1 0x1000005
-#define HFI_BUFFER_INTERNAL_SCRATCH_2 0x1000006
-
+#define HFI_BUFFER_INTERNAL_SCRATCH(ver) \
+ (((ver) == HFI_VERSION_4XX) ? 0x6 : 0x1000001)
+#define HFI_BUFFER_INTERNAL_SCRATCH_1(ver) \
+ (((ver) == HFI_VERSION_4XX) ? 0x7 : 0x1000005)
+#define HFI_BUFFER_INTERNAL_SCRATCH_2(ver) \
+ (((ver) == HFI_VERSION_4XX) ? 0x8 : 0x1000006)
+#define HFI_BUFFER_EXTRADATA_INPUT(ver) \
+ (((ver) == HFI_VERSION_4XX) ? 0xc : 0x1000002)
+#define HFI_BUFFER_EXTRADATA_OUTPUT(ver) \
+ (((ver) == HFI_VERSION_4XX) ? 0xa : 0x1000003)
+#define HFI_BUFFER_EXTRADATA_OUTPUT2(ver) \
+ (((ver) == HFI_VERSION_4XX) ? 0xb : 0x1000004)
#define HFI_BUFFER_TYPE_MAX 11
#define HFI_BUFFER_MODE_STATIC 0x1000001
@@ -424,12 +430,14 @@
#define HFI_PROPERTY_PARAM_CODEC_MASK_SUPPORTED 0x100e
#define HFI_PROPERTY_PARAM_MVC_BUFFER_LAYOUT 0x100f
#define HFI_PROPERTY_PARAM_MAX_SESSIONS_SUPPORTED 0x1010
+#define HFI_PROPERTY_PARAM_WORK_MODE 0x1015
/*
* HFI_PROPERTY_CONFIG_COMMON_START
* HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + 0x2000
*/
#define HFI_PROPERTY_CONFIG_FRAME_RATE 0x2001
+#define HFI_PROPERTY_CONFIG_VIDEOCORES_USAGE 0x2002
/*
* HFI_PROPERTY_PARAM_VDEC_COMMON_START
@@ -438,6 +446,9 @@
#define HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM 0x1003001
#define HFI_PROPERTY_PARAM_VDEC_CONCEAL_COLOR 0x1003002
#define HFI_PROPERTY_PARAM_VDEC_NONCP_OUTPUT2 0x1003003
+#define HFI_PROPERTY_PARAM_VDEC_PIXEL_BITDEPTH 0x1003007
+#define HFI_PROPERTY_PARAM_VDEC_PIC_STRUCT 0x1003009
+#define HFI_PROPERTY_PARAM_VDEC_COLOUR_SPACE 0x100300a
/*
* HFI_PROPERTY_CONFIG_VDEC_COMMON_START
@@ -518,6 +529,7 @@
enum hfi_version {
HFI_VERSION_1XX,
HFI_VERSION_3XX,
+ HFI_VERSION_4XX
};
struct hfi_buffer_info {
@@ -767,12 +779,56 @@ struct hfi_framesize {
u32 height;
};
+#define VIDC_CORE_ID_DEFAULT 0
+#define VIDC_CORE_ID_1 1
+#define VIDC_CORE_ID_2 2
+#define VIDC_CORE_ID_3 3
+
+struct hfi_videocores_usage_type {
+ u32 video_core_enable_mask;
+};
+
+#define VIDC_WORK_MODE_1 1
+#define VIDC_WORK_MODE_2 2
+
+struct hfi_video_work_mode {
+ u32 video_work_mode;
+};
+
struct hfi_h264_vui_timing_info {
u32 enable;
u32 fixed_framerate;
u32 time_scale;
};
+struct hfi_bit_depth {
+ u32 buffer_type;
+ u32 bit_depth;
+};
+
+struct hfi_picture_type {
+ u32 is_sync_frame;
+ u32 picture_type;
+};
+
+struct hfi_pic_struct {
+ u32 progressive_only;
+};
+
+struct hfi_colour_space {
+ u32 colour_space;
+};
+
+struct hfi_extradata_input_crop {
+ u32 size;
+ u32 version;
+ u32 port_index;
+ u32 left;
+ u32 top;
+ u32 width;
+ u32 height;
+};
+
#define HFI_COLOR_FORMAT_MONOCHROME 0x01
#define HFI_COLOR_FORMAT_NV12 0x02
#define HFI_COLOR_FORMAT_NV21 0x03
@@ -802,10 +858,23 @@ struct hfi_uncompressed_format_select {
u32 format;
};
+struct hfi_uncompressed_plane_constraints {
+ u32 stride_multiples;
+ u32 max_stride;
+ u32 min_plane_buffer_height_multiple;
+ u32 buffer_alignment;
+};
+
+struct hfi_uncompressed_plane_info {
+ u32 format;
+ u32 num_planes;
+ struct hfi_uncompressed_plane_constraints plane_constraints[1];
+};
+
struct hfi_uncompressed_format_supported {
u32 buffer_type;
u32 format_entries;
- u32 format_info[1];
+ struct hfi_uncompressed_plane_info plane_info[1];
};
struct hfi_uncompressed_plane_actual {
@@ -819,19 +888,6 @@ struct hfi_uncompressed_plane_actual_info {
struct hfi_uncompressed_plane_actual plane_format[1];
};
-struct hfi_uncompressed_plane_constraints {
- u32 stride_multiples;
- u32 max_stride;
- u32 min_plane_buffer_height_multiple;
- u32 buffer_alignment;
-};
-
-struct hfi_uncompressed_plane_info {
- u32 format;
- u32 num_planes;
- struct hfi_uncompressed_plane_constraints plane_format[1];
-};
-
struct hfi_uncompressed_plane_actual_constraints_info {
u32 buffer_type;
u32 num_planes;
@@ -961,6 +1017,12 @@ struct hfi_buffer_count_actual {
u32 count_actual;
};
+struct hfi_buffer_count_actual_4xx {
+ u32 type;
+ u32 count_actual;
+ u32 count_min_host;
+};
+
struct hfi_buffer_size_actual {
u32 type;
u32 size;
@@ -971,6 +1033,14 @@ struct hfi_buffer_display_hold_count_actual {
u32 hold_count;
};
+/* HFI 4XX reorder the fields, use these macros */
+#define HFI_BUFREQ_HOLD_COUNT(bufreq, ver) \
+ ((ver) == HFI_VERSION_4XX ? 0 : (bufreq)->hold_count)
+#define HFI_BUFREQ_COUNT_MIN(bufreq, ver) \
+ ((ver) == HFI_VERSION_4XX ? (bufreq)->hold_count : (bufreq)->count_min)
+#define HFI_BUFREQ_COUNT_MIN_HOST(bufreq, ver) \
+ ((ver) == HFI_VERSION_4XX ? (bufreq)->count_min : 0)
+
struct hfi_buffer_requirements {
u32 type;
u32 size;
diff --git a/drivers/media/platform/qcom/venus/hfi_msgs.c b/drivers/media/platform/qcom/venus/hfi_msgs.c
index 90c93d9603dc..0ecdaa15c296 100644
--- a/drivers/media/platform/qcom/venus/hfi_msgs.c
+++ b/drivers/media/platform/qcom/venus/hfi_msgs.c
@@ -21,14 +21,21 @@
#include "hfi.h"
#include "hfi_helper.h"
#include "hfi_msgs.h"
+#include "hfi_parser.h"
static void event_seq_changed(struct venus_core *core, struct venus_inst *inst,
struct hfi_msg_event_notify_pkt *pkt)
{
+ enum hfi_version ver = core->res->hfi_version;
struct hfi_event_data event = {0};
int num_properties_changed;
struct hfi_framesize *frame_sz;
struct hfi_profile_level *profile_level;
+ struct hfi_bit_depth *pixel_depth;
+ struct hfi_pic_struct *pic_struct;
+ struct hfi_colour_space *colour_info;
+ struct hfi_buffer_requirements *bufreq;
+ struct hfi_extradata_input_crop *crop;
u8 *data_ptr;
u32 ptype;
@@ -60,14 +67,52 @@ static void event_seq_changed(struct venus_core *core, struct venus_inst *inst,
frame_sz = (struct hfi_framesize *)data_ptr;
event.width = frame_sz->width;
event.height = frame_sz->height;
- data_ptr += sizeof(frame_sz);
+ data_ptr += sizeof(*frame_sz);
break;
case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT:
data_ptr += sizeof(u32);
profile_level = (struct hfi_profile_level *)data_ptr;
event.profile = profile_level->profile;
event.level = profile_level->level;
- data_ptr += sizeof(profile_level);
+ data_ptr += sizeof(*profile_level);
+ break;
+ case HFI_PROPERTY_PARAM_VDEC_PIXEL_BITDEPTH:
+ data_ptr += sizeof(u32);
+ pixel_depth = (struct hfi_bit_depth *)data_ptr;
+ event.bit_depth = pixel_depth->bit_depth;
+ data_ptr += sizeof(*pixel_depth);
+ break;
+ case HFI_PROPERTY_PARAM_VDEC_PIC_STRUCT:
+ data_ptr += sizeof(u32);
+ pic_struct = (struct hfi_pic_struct *)data_ptr;
+ event.pic_struct = pic_struct->progressive_only;
+ data_ptr += sizeof(*pic_struct);
+ break;
+ case HFI_PROPERTY_PARAM_VDEC_COLOUR_SPACE:
+ data_ptr += sizeof(u32);
+ colour_info = (struct hfi_colour_space *)data_ptr;
+ event.colour_space = colour_info->colour_space;
+ data_ptr += sizeof(*colour_info);
+ break;
+ case HFI_PROPERTY_CONFIG_VDEC_ENTROPY:
+ data_ptr += sizeof(u32);
+ event.entropy_mode = *(u32 *)data_ptr;
+ data_ptr += sizeof(u32);
+ break;
+ case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS:
+ data_ptr += sizeof(u32);
+ bufreq = (struct hfi_buffer_requirements *)data_ptr;
+ event.buf_count = HFI_BUFREQ_COUNT_MIN(bufreq, ver);
+ data_ptr += sizeof(*bufreq);
+ break;
+ case HFI_INDEX_EXTRADATA_INPUT_CROP:
+ data_ptr += sizeof(u32);
+ crop = (struct hfi_extradata_input_crop *)data_ptr;
+ event.input_crop.left = crop->left;
+ event.input_crop.top = crop->top;
+ event.input_crop.width = crop->width;
+ event.input_crop.height = crop->height;
+ data_ptr += sizeof(*crop);
break;
default:
break;
@@ -173,81 +218,28 @@ static void hfi_sys_init_done(struct venus_core *core, struct venus_inst *inst,
void *packet)
{
struct hfi_msg_sys_init_done_pkt *pkt = packet;
- u32 rem_bytes, read_bytes = 0, num_properties;
- u32 error, ptype;
- u8 *data;
+ int rem_bytes;
+ u32 error;
error = pkt->error_type;
if (error != HFI_ERR_NONE)
- goto err_no_prop;
-
- num_properties = pkt->num_properties;
+ goto done;
- if (!num_properties) {
+ if (!pkt->num_properties) {
error = HFI_ERR_SYS_INVALID_PARAMETER;
- goto err_no_prop;
+ goto done;
}
rem_bytes = pkt->hdr.size - sizeof(*pkt) + sizeof(u32);
-
- if (!rem_bytes) {
+ if (rem_bytes <= 0) {
/* missing property data */
error = HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
- goto err_no_prop;
+ goto done;
}
- data = (u8 *)&pkt->data[0];
-
- if (core->res->hfi_version == HFI_VERSION_3XX)
- goto err_no_prop;
-
- while (num_properties && rem_bytes >= sizeof(u32)) {
- ptype = *((u32 *)data);
- data += sizeof(u32);
+ error = hfi_parser(core, inst, pkt->data, rem_bytes);
- switch (ptype) {
- case HFI_PROPERTY_PARAM_CODEC_SUPPORTED: {
- struct hfi_codec_supported *prop;
-
- prop = (struct hfi_codec_supported *)data;
-
- if (rem_bytes < sizeof(*prop)) {
- error = HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
- break;
- }
-
- read_bytes += sizeof(*prop) + sizeof(u32);
- core->dec_codecs = prop->dec_codecs;
- core->enc_codecs = prop->enc_codecs;
- break;
- }
- case HFI_PROPERTY_PARAM_MAX_SESSIONS_SUPPORTED: {
- struct hfi_max_sessions_supported *prop;
-
- if (rem_bytes < sizeof(*prop)) {
- error = HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
- break;
- }
-
- prop = (struct hfi_max_sessions_supported *)data;
- read_bytes += sizeof(*prop) + sizeof(u32);
- core->max_sessions_supported = prop->max_sessions;
- break;
- }
- default:
- error = HFI_ERR_SYS_INVALID_PARAMETER;
- break;
- }
-
- if (error)
- break;
-
- rem_bytes -= read_bytes;
- data += read_bytes;
- num_properties--;
- }
-
-err_no_prop:
+done:
core->error = error;
complete(&core->done);
}
@@ -325,51 +317,6 @@ static void hfi_sys_pc_prepare_done(struct venus_core *core,
dev_dbg(core->dev, "pc prepare done (error %x)\n", pkt->error_type);
}
-static void
-hfi_copy_cap_prop(struct hfi_capability *in, struct venus_inst *inst)
-{
- if (!in || !inst)
- return;
-
- switch (in->capability_type) {
- case HFI_CAPABILITY_FRAME_WIDTH:
- inst->cap_width = *in;
- break;
- case HFI_CAPABILITY_FRAME_HEIGHT:
- inst->cap_height = *in;
- break;
- case HFI_CAPABILITY_MBS_PER_FRAME:
- inst->cap_mbs_per_frame = *in;
- break;
- case HFI_CAPABILITY_MBS_PER_SECOND:
- inst->cap_mbs_per_sec = *in;
- break;
- case HFI_CAPABILITY_FRAMERATE:
- inst->cap_framerate = *in;
- break;
- case HFI_CAPABILITY_SCALE_X:
- inst->cap_scale_x = *in;
- break;
- case HFI_CAPABILITY_SCALE_Y:
- inst->cap_scale_y = *in;
- break;
- case HFI_CAPABILITY_BITRATE:
- inst->cap_bitrate = *in;
- break;
- case HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS:
- inst->cap_hier_p = *in;
- break;
- case HFI_CAPABILITY_ENC_LTR_COUNT:
- inst->cap_ltr_count = *in;
- break;
- case HFI_CAPABILITY_CP_OUTPUT2_THRESH:
- inst->cap_secure_output2_threshold = *in;
- break;
- default:
- break;
- }
-}
-
static unsigned int
session_get_prop_profile_level(struct hfi_msg_session_property_info_pkt *pkt,
struct hfi_profile_level *profile_level)
@@ -459,248 +406,27 @@ done:
complete(&inst->done);
}
-static u32 init_done_read_prop(struct venus_core *core, struct venus_inst *inst,
- struct hfi_msg_session_init_done_pkt *pkt)
-{
- struct device *dev = core->dev;
- u32 rem_bytes, num_props;
- u32 ptype, next_offset = 0;
- u32 err;
- u8 *data;
-
- rem_bytes = pkt->shdr.hdr.size - sizeof(*pkt) + sizeof(u32);
- if (!rem_bytes) {
- dev_err(dev, "%s: missing property info\n", __func__);
- return HFI_ERR_SESSION_INSUFFICIENT_RESOURCES;
- }
-
- err = pkt->error_type;
- if (err)
- return err;
-
- data = (u8 *)&pkt->data[0];
- num_props = pkt->num_properties;
-
- while (err == HFI_ERR_NONE && num_props && rem_bytes >= sizeof(u32)) {
- ptype = *((u32 *)data);
- next_offset = sizeof(u32);
-
- switch (ptype) {
- case HFI_PROPERTY_PARAM_CODEC_MASK_SUPPORTED: {
- struct hfi_codec_mask_supported *masks =
- (struct hfi_codec_mask_supported *)
- (data + next_offset);
-
- next_offset += sizeof(*masks);
- num_props--;
- break;
- }
- case HFI_PROPERTY_PARAM_CAPABILITY_SUPPORTED: {
- struct hfi_capabilities *caps;
- struct hfi_capability *cap;
- u32 num_caps;
-
- if ((rem_bytes - next_offset) < sizeof(*cap)) {
- err = HFI_ERR_SESSION_INVALID_PARAMETER;
- break;
- }
-
- caps = (struct hfi_capabilities *)(data + next_offset);
-
- num_caps = caps->num_capabilities;
- cap = &caps->data[0];
- next_offset += sizeof(u32);
-
- while (num_caps &&
- (rem_bytes - next_offset) >= sizeof(u32)) {
- hfi_copy_cap_prop(cap, inst);
- cap++;
- next_offset += sizeof(*cap);
- num_caps--;
- }
- num_props--;
- break;
- }
- case HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED: {
- struct hfi_uncompressed_format_supported *prop =
- (struct hfi_uncompressed_format_supported *)
- (data + next_offset);
- u32 num_fmt_entries;
- u8 *fmt;
- struct hfi_uncompressed_plane_info *inf;
-
- if ((rem_bytes - next_offset) < sizeof(*prop)) {
- err = HFI_ERR_SESSION_INVALID_PARAMETER;
- break;
- }
-
- num_fmt_entries = prop->format_entries;
- next_offset = sizeof(*prop) - sizeof(u32);
- fmt = (u8 *)&prop->format_info[0];
-
- dev_dbg(dev, "uncomm format support num entries:%u\n",
- num_fmt_entries);
-
- while (num_fmt_entries) {
- struct hfi_uncompressed_plane_constraints *cnts;
- u32 bytes_to_skip;
-
- inf = (struct hfi_uncompressed_plane_info *)fmt;
-
- if ((rem_bytes - next_offset) < sizeof(*inf)) {
- err = HFI_ERR_SESSION_INVALID_PARAMETER;
- break;
- }
-
- dev_dbg(dev, "plane info: fmt:%x, planes:%x\n",
- inf->format, inf->num_planes);
-
- cnts = &inf->plane_format[0];
- dev_dbg(dev, "%u %u %u %u\n",
- cnts->stride_multiples,
- cnts->max_stride,
- cnts->min_plane_buffer_height_multiple,
- cnts->buffer_alignment);
-
- bytes_to_skip = sizeof(*inf) - sizeof(*cnts) +
- inf->num_planes * sizeof(*cnts);
-
- fmt += bytes_to_skip;
- next_offset += bytes_to_skip;
- num_fmt_entries--;
- }
- num_props--;
- break;
- }
- case HFI_PROPERTY_PARAM_PROPERTIES_SUPPORTED: {
- struct hfi_properties_supported *prop =
- (struct hfi_properties_supported *)
- (data + next_offset);
-
- next_offset += sizeof(*prop) - sizeof(u32)
- + prop->num_properties * sizeof(u32);
- num_props--;
- break;
- }
- case HFI_PROPERTY_PARAM_PROFILE_LEVEL_SUPPORTED: {
- struct hfi_profile_level_supported *prop =
- (struct hfi_profile_level_supported *)
- (data + next_offset);
- struct hfi_profile_level *pl;
- unsigned int prop_count = 0;
- unsigned int count = 0;
- u8 *ptr;
-
- ptr = (u8 *)&prop->profile_level[0];
- prop_count = prop->profile_count;
-
- if (prop_count > HFI_MAX_PROFILE_COUNT)
- prop_count = HFI_MAX_PROFILE_COUNT;
-
- while (prop_count) {
- ptr++;
- pl = (struct hfi_profile_level *)ptr;
-
- inst->pl[count].profile = pl->profile;
- inst->pl[count].level = pl->level;
- prop_count--;
- count++;
- ptr += sizeof(*pl) / sizeof(u32);
- }
-
- inst->pl_count = count;
- next_offset += sizeof(*prop) - sizeof(*pl) +
- prop->profile_count * sizeof(*pl);
-
- num_props--;
- break;
- }
- case HFI_PROPERTY_PARAM_INTERLACE_FORMAT_SUPPORTED: {
- next_offset +=
- sizeof(struct hfi_interlace_format_supported);
- num_props--;
- break;
- }
- case HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SUPPORTED: {
- struct hfi_nal_stream_format *nal =
- (struct hfi_nal_stream_format *)
- (data + next_offset);
- dev_dbg(dev, "NAL format: %x\n", nal->format);
- next_offset += sizeof(*nal);
- num_props--;
- break;
- }
- case HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SELECT: {
- next_offset += sizeof(u32);
- num_props--;
- break;
- }
- case HFI_PROPERTY_PARAM_MAX_SEQUENCE_HEADER_SIZE: {
- u32 *max_seq_sz = (u32 *)(data + next_offset);
-
- dev_dbg(dev, "max seq header sz: %x\n", *max_seq_sz);
- next_offset += sizeof(u32);
- num_props--;
- break;
- }
- case HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH: {
- next_offset += sizeof(struct hfi_intra_refresh);
- num_props--;
- break;
- }
- case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE_SUPPORTED: {
- struct hfi_buffer_alloc_mode_supported *prop =
- (struct hfi_buffer_alloc_mode_supported *)
- (data + next_offset);
- unsigned int i;
-
- for (i = 0; i < prop->num_entries; i++) {
- if (prop->buffer_type == HFI_BUFFER_OUTPUT ||
- prop->buffer_type == HFI_BUFFER_OUTPUT2) {
- switch (prop->data[i]) {
- case HFI_BUFFER_MODE_STATIC:
- inst->cap_bufs_mode_static = true;
- break;
- case HFI_BUFFER_MODE_DYNAMIC:
- inst->cap_bufs_mode_dynamic = true;
- break;
- default:
- break;
- }
- }
- }
- next_offset += sizeof(*prop) -
- sizeof(u32) + prop->num_entries * sizeof(u32);
- num_props--;
- break;
- }
- default:
- dev_dbg(dev, "%s: default case %#x\n", __func__, ptype);
- break;
- }
-
- rem_bytes -= next_offset;
- data += next_offset;
- }
-
- return err;
-}
-
static void hfi_session_init_done(struct venus_core *core,
struct venus_inst *inst, void *packet)
{
struct hfi_msg_session_init_done_pkt *pkt = packet;
- unsigned int error;
+ int rem_bytes;
+ u32 error;
error = pkt->error_type;
if (error != HFI_ERR_NONE)
goto done;
- if (core->res->hfi_version != HFI_VERSION_1XX)
+ if (!IS_V1(core))
goto done;
- error = init_done_read_prop(core, inst, pkt);
+ rem_bytes = pkt->shdr.hdr.size - sizeof(*pkt) + sizeof(u32);
+ if (rem_bytes <= 0) {
+ error = HFI_ERR_SESSION_INSUFFICIENT_RESOURCES;
+ goto done;
+ }
+ error = hfi_parser(core, inst, pkt->data, rem_bytes);
done:
inst->error = error;
complete(&inst->done);
@@ -779,7 +505,8 @@ static void hfi_session_ftb_done(struct venus_core *core,
error = HFI_ERR_SESSION_INVALID_PARAMETER;
}
- if (buffer_type != HFI_BUFFER_OUTPUT)
+ if (buffer_type != HFI_BUFFER_OUTPUT &&
+ buffer_type != HFI_BUFFER_OUTPUT2)
goto done;
if (hfi_flags & HFI_BUFFERFLAG_EOS)
diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c
new file mode 100644
index 000000000000..2293d936e49c
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/hfi_parser.c
@@ -0,0 +1,278 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Linaro Ltd.
+ *
+ * Author: Stanimir Varbanov <stanimir.varbanov@linaro.org>
+ */
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+
+#include "core.h"
+#include "hfi_helper.h"
+#include "hfi_parser.h"
+
+typedef void (*func)(struct venus_caps *cap, const void *data,
+ unsigned int size);
+
+static void init_codecs(struct venus_core *core)
+{
+ struct venus_caps *caps = core->caps, *cap;
+ unsigned long bit;
+
+ for_each_set_bit(bit, &core->dec_codecs, MAX_CODEC_NUM) {
+ cap = &caps[core->codecs_count++];
+ cap->codec = BIT(bit);
+ cap->domain = VIDC_SESSION_TYPE_DEC;
+ cap->valid = false;
+ }
+
+ for_each_set_bit(bit, &core->enc_codecs, MAX_CODEC_NUM) {
+ cap = &caps[core->codecs_count++];
+ cap->codec = BIT(bit);
+ cap->domain = VIDC_SESSION_TYPE_ENC;
+ cap->valid = false;
+ }
+}
+
+static void for_each_codec(struct venus_caps *caps, unsigned int caps_num,
+ u32 codecs, u32 domain, func cb, void *data,
+ unsigned int size)
+{
+ struct venus_caps *cap;
+ unsigned int i;
+
+ for (i = 0; i < caps_num; i++) {
+ cap = &caps[i];
+ if (cap->valid && cap->domain == domain)
+ continue;
+ if (cap->codec & codecs && cap->domain == domain)
+ cb(cap, data, size);
+ }
+}
+
+static void
+fill_buf_mode(struct venus_caps *cap, const void *data, unsigned int num)
+{
+ const u32 *type = data;
+
+ if (*type == HFI_BUFFER_MODE_DYNAMIC)
+ cap->cap_bufs_mode_dynamic = true;
+}
+
+static void
+parse_alloc_mode(struct venus_core *core, u32 codecs, u32 domain, void *data)
+{
+ struct hfi_buffer_alloc_mode_supported *mode = data;
+ u32 num_entries = mode->num_entries;
+ u32 *type;
+
+ if (num_entries > MAX_ALLOC_MODE_ENTRIES)
+ return;
+
+ type = mode->data;
+
+ while (num_entries--) {
+ if (mode->buffer_type == HFI_BUFFER_OUTPUT ||
+ mode->buffer_type == HFI_BUFFER_OUTPUT2)
+ for_each_codec(core->caps, ARRAY_SIZE(core->caps),
+ codecs, domain, fill_buf_mode, type, 1);
+
+ type++;
+ }
+}
+
+static void fill_profile_level(struct venus_caps *cap, const void *data,
+ unsigned int num)
+{
+ const struct hfi_profile_level *pl = data;
+
+ memcpy(&cap->pl[cap->num_pl], pl, num * sizeof(*pl));
+ cap->num_pl += num;
+}
+
+static void
+parse_profile_level(struct venus_core *core, u32 codecs, u32 domain, void *data)
+{
+ struct hfi_profile_level_supported *pl = data;
+ struct hfi_profile_level *proflevel = pl->profile_level;
+ struct hfi_profile_level pl_arr[HFI_MAX_PROFILE_COUNT] = {};
+
+ if (pl->profile_count > HFI_MAX_PROFILE_COUNT)
+ return;
+
+ memcpy(pl_arr, proflevel, pl->profile_count * sizeof(*proflevel));
+
+ for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain,
+ fill_profile_level, pl_arr, pl->profile_count);
+}
+
+static void
+fill_caps(struct venus_caps *cap, const void *data, unsigned int num)
+{
+ const struct hfi_capability *caps = data;
+
+ memcpy(&cap->caps[cap->num_caps], caps, num * sizeof(*caps));
+ cap->num_caps += num;
+}
+
+static void
+parse_caps(struct venus_core *core, u32 codecs, u32 domain, void *data)
+{
+ struct hfi_capabilities *caps = data;
+ struct hfi_capability *cap = caps->data;
+ u32 num_caps = caps->num_capabilities;
+ struct hfi_capability caps_arr[MAX_CAP_ENTRIES] = {};
+
+ if (num_caps > MAX_CAP_ENTRIES)
+ return;
+
+ memcpy(caps_arr, cap, num_caps * sizeof(*cap));
+
+ for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain,
+ fill_caps, caps_arr, num_caps);
+}
+
+static void fill_raw_fmts(struct venus_caps *cap, const void *fmts,
+ unsigned int num_fmts)
+{
+ const struct raw_formats *formats = fmts;
+
+ memcpy(&cap->fmts[cap->num_fmts], formats, num_fmts * sizeof(*formats));
+ cap->num_fmts += num_fmts;
+}
+
+static void
+parse_raw_formats(struct venus_core *core, u32 codecs, u32 domain, void *data)
+{
+ struct hfi_uncompressed_format_supported *fmt = data;
+ struct hfi_uncompressed_plane_info *pinfo = fmt->plane_info;
+ struct hfi_uncompressed_plane_constraints *constr;
+ struct raw_formats rawfmts[MAX_FMT_ENTRIES] = {};
+ u32 entries = fmt->format_entries;
+ unsigned int i = 0;
+ u32 num_planes;
+
+ while (entries) {
+ num_planes = pinfo->num_planes;
+
+ rawfmts[i].fmt = pinfo->format;
+ rawfmts[i].buftype = fmt->buffer_type;
+ i++;
+
+ if (pinfo->num_planes > MAX_PLANES)
+ break;
+
+ pinfo = (void *)pinfo + sizeof(*constr) * num_planes +
+ 2 * sizeof(u32);
+ entries--;
+ }
+
+ for_each_codec(core->caps, ARRAY_SIZE(core->caps), codecs, domain,
+ fill_raw_fmts, rawfmts, i);
+}
+
+static void parse_codecs(struct venus_core *core, void *data)
+{
+ struct hfi_codec_supported *codecs = data;
+
+ core->dec_codecs = codecs->dec_codecs;
+ core->enc_codecs = codecs->enc_codecs;
+
+ if (IS_V1(core)) {
+ core->dec_codecs &= ~HFI_VIDEO_CODEC_HEVC;
+ core->dec_codecs &= ~HFI_VIDEO_CODEC_SPARK;
+ }
+}
+
+static void parse_max_sessions(struct venus_core *core, const void *data)
+{
+ const struct hfi_max_sessions_supported *sessions = data;
+
+ core->max_sessions_supported = sessions->max_sessions;
+}
+
+static void parse_codecs_mask(u32 *codecs, u32 *domain, void *data)
+{
+ struct hfi_codec_mask_supported *mask = data;
+
+ *codecs = mask->codecs;
+ *domain = mask->video_domains;
+}
+
+static void parser_init(struct venus_inst *inst, u32 *codecs, u32 *domain)
+{
+ if (!inst || !IS_V1(inst->core))
+ return;
+
+ *codecs = inst->hfi_codec;
+ *domain = inst->session_type;
+}
+
+static void parser_fini(struct venus_inst *inst, u32 codecs, u32 domain)
+{
+ struct venus_caps *caps, *cap;
+ unsigned int i;
+ u32 dom;
+
+ if (!inst || !IS_V1(inst->core))
+ return;
+
+ caps = inst->core->caps;
+ dom = inst->session_type;
+
+ for (i = 0; i < MAX_CODEC_NUM; i++) {
+ cap = &caps[i];
+ if (cap->codec & codecs && cap->domain == dom)
+ cap->valid = true;
+ }
+}
+
+u32 hfi_parser(struct venus_core *core, struct venus_inst *inst, void *buf,
+ u32 size)
+{
+ unsigned int words_count = size >> 2;
+ u32 *word = buf, *data, codecs = 0, domain = 0;
+
+ if (size % 4)
+ return HFI_ERR_SYS_INSUFFICIENT_RESOURCES;
+
+ parser_init(inst, &codecs, &domain);
+
+ while (words_count) {
+ data = word + 1;
+
+ switch (*word) {
+ case HFI_PROPERTY_PARAM_CODEC_SUPPORTED:
+ parse_codecs(core, data);
+ init_codecs(core);
+ break;
+ case HFI_PROPERTY_PARAM_MAX_SESSIONS_SUPPORTED:
+ parse_max_sessions(core, data);
+ break;
+ case HFI_PROPERTY_PARAM_CODEC_MASK_SUPPORTED:
+ parse_codecs_mask(&codecs, &domain, data);
+ break;
+ case HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED:
+ parse_raw_formats(core, codecs, domain, data);
+ break;
+ case HFI_PROPERTY_PARAM_CAPABILITY_SUPPORTED:
+ parse_caps(core, codecs, domain, data);
+ break;
+ case HFI_PROPERTY_PARAM_PROFILE_LEVEL_SUPPORTED:
+ parse_profile_level(core, codecs, domain, data);
+ break;
+ case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE_SUPPORTED:
+ parse_alloc_mode(core, codecs, domain, data);
+ break;
+ default:
+ break;
+ }
+
+ word++;
+ words_count--;
+ }
+
+ parser_fini(inst, codecs, domain);
+
+ return HFI_ERR_NONE;
+}
diff --git a/drivers/media/platform/qcom/venus/hfi_parser.h b/drivers/media/platform/qcom/venus/hfi_parser.h
new file mode 100644
index 000000000000..3e931c747e19
--- /dev/null
+++ b/drivers/media/platform/qcom/venus/hfi_parser.h
@@ -0,0 +1,110 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2018 Linaro Ltd. */
+#ifndef __VENUS_HFI_PARSER_H__
+#define __VENUS_HFI_PARSER_H__
+
+#include "core.h"
+
+u32 hfi_parser(struct venus_core *core, struct venus_inst *inst,
+ void *buf, u32 size);
+
+#define WHICH_CAP_MIN 0
+#define WHICH_CAP_MAX 1
+#define WHICH_CAP_STEP 2
+
+static inline u32 get_cap(struct venus_inst *inst, u32 type, u32 which)
+{
+ struct venus_core *core = inst->core;
+ struct hfi_capability *cap = NULL;
+ struct venus_caps *caps;
+ unsigned int i;
+
+ caps = venus_caps_by_codec(core, inst->hfi_codec, inst->session_type);
+ if (!caps)
+ return 0;
+
+ for (i = 0; i < caps->num_caps; i++) {
+ if (caps->caps[i].capability_type == type) {
+ cap = &caps->caps[i];
+ break;
+ }
+ }
+
+ if (!cap)
+ return 0;
+
+ switch (which) {
+ case WHICH_CAP_MIN:
+ return cap->min;
+ case WHICH_CAP_MAX:
+ return cap->max;
+ case WHICH_CAP_STEP:
+ return cap->step_size;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static inline u32 cap_min(struct venus_inst *inst, u32 type)
+{
+ return get_cap(inst, type, WHICH_CAP_MIN);
+}
+
+static inline u32 cap_max(struct venus_inst *inst, u32 type)
+{
+ return get_cap(inst, type, WHICH_CAP_MAX);
+}
+
+static inline u32 cap_step(struct venus_inst *inst, u32 type)
+{
+ return get_cap(inst, type, WHICH_CAP_STEP);
+}
+
+static inline u32 frame_width_min(struct venus_inst *inst)
+{
+ return cap_min(inst, HFI_CAPABILITY_FRAME_WIDTH);
+}
+
+static inline u32 frame_width_max(struct venus_inst *inst)
+{
+ return cap_max(inst, HFI_CAPABILITY_FRAME_WIDTH);
+}
+
+static inline u32 frame_width_step(struct venus_inst *inst)
+{
+ return cap_step(inst, HFI_CAPABILITY_FRAME_WIDTH);
+}
+
+static inline u32 frame_height_min(struct venus_inst *inst)
+{
+ return cap_min(inst, HFI_CAPABILITY_FRAME_HEIGHT);
+}
+
+static inline u32 frame_height_max(struct venus_inst *inst)
+{
+ return cap_max(inst, HFI_CAPABILITY_FRAME_HEIGHT);
+}
+
+static inline u32 frame_height_step(struct venus_inst *inst)
+{
+ return cap_step(inst, HFI_CAPABILITY_FRAME_HEIGHT);
+}
+
+static inline u32 frate_min(struct venus_inst *inst)
+{
+ return cap_min(inst, HFI_CAPABILITY_FRAMERATE);
+}
+
+static inline u32 frate_max(struct venus_inst *inst)
+{
+ return cap_max(inst, HFI_CAPABILITY_FRAMERATE);
+}
+
+static inline u32 frate_step(struct venus_inst *inst)
+{
+ return cap_step(inst, HFI_CAPABILITY_FRAMERATE);
+}
+
+#endif
diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c
index 734ce11b0ed0..124085556b94 100644
--- a/drivers/media/platform/qcom/venus/hfi_venus.c
+++ b/drivers/media/platform/qcom/venus/hfi_venus.c
@@ -532,6 +532,24 @@ static int venus_halt_axi(struct venus_hfi_device *hdev)
u32 val;
int ret;
+ if (IS_V4(hdev->core)) {
+ val = venus_readl(hdev, WRAPPER_CPU_AXI_HALT);
+ val |= WRAPPER_CPU_AXI_HALT_HALT;
+ venus_writel(hdev, WRAPPER_CPU_AXI_HALT, val);
+
+ ret = readl_poll_timeout(base + WRAPPER_CPU_AXI_HALT_STATUS,
+ val,
+ val & WRAPPER_CPU_AXI_HALT_STATUS_IDLE,
+ POLL_INTERVAL_US,
+ VBIF_AXI_HALT_ACK_TIMEOUT_US);
+ if (ret) {
+ dev_err(dev, "AXI bus port halt timeout\n");
+ return ret;
+ }
+
+ return 0;
+ }
+
/* Halt AXI and AXI IMEM VBIF Access */
val = venus_readl(hdev, VBIF_AXI_HALT_CTRL0);
val |= VBIF_AXI_HALT_CTRL0_HALT_REQ;
@@ -861,6 +879,14 @@ static int venus_sys_set_default_properties(struct venus_hfi_device *hdev)
if (ret)
dev_warn(dev, "setting fw debug msg ON failed (%d)\n", ret);
+ /*
+ * Idle indicator is disabled by default on some 4xx firmware versions,
+ * enable it explicitly in order to make suspend functional by checking
+ * WFI (wait-for-interrupt) bit.
+ */
+ if (IS_V4(hdev->core))
+ venus_sys_idle_indicator = true;
+
ret = venus_sys_set_idle_message(hdev, venus_sys_idle_indicator);
if (ret)
dev_warn(dev, "setting idle response ON failed (%d)\n", ret);
@@ -1073,6 +1099,10 @@ static int venus_core_init(struct venus_core *core)
if (ret)
dev_warn(dev, "failed to send image version pkt to fw\n");
+ ret = venus_sys_set_default_properties(hdev);
+ if (ret)
+ return ret;
+
return 0;
}
@@ -1117,10 +1147,6 @@ static int venus_session_init(struct venus_inst *inst, u32 session_type,
struct hfi_session_init_pkt pkt;
int ret;
- ret = venus_sys_set_default_properties(hdev);
- if (ret)
- return ret;
-
ret = pkt_session_init(&pkt, inst, session_type, codec);
if (ret)
goto err;
@@ -1426,13 +1452,40 @@ static int venus_suspend_1xx(struct venus_core *core)
return 0;
}
+static bool venus_cpu_and_video_core_idle(struct venus_hfi_device *hdev)
+{
+ u32 ctrl_status, cpu_status;
+
+ cpu_status = venus_readl(hdev, WRAPPER_CPU_STATUS);
+ ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0);
+
+ if (cpu_status & WRAPPER_CPU_STATUS_WFI &&
+ ctrl_status & CPU_CS_SCIACMDARG0_INIT_IDLE_MSG_MASK)
+ return true;
+
+ return false;
+}
+
+static bool venus_cpu_idle_and_pc_ready(struct venus_hfi_device *hdev)
+{
+ u32 ctrl_status, cpu_status;
+
+ cpu_status = venus_readl(hdev, WRAPPER_CPU_STATUS);
+ ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0);
+
+ if (cpu_status & WRAPPER_CPU_STATUS_WFI &&
+ ctrl_status & CPU_CS_SCIACMDARG0_PC_READY)
+ return true;
+
+ return false;
+}
+
static int venus_suspend_3xx(struct venus_core *core)
{
struct venus_hfi_device *hdev = to_hfi_priv(core);
struct device *dev = core->dev;
- u32 ctrl_status, wfi_status;
+ bool val;
int ret;
- int cnt = 100;
if (!hdev->power_enabled || hdev->suspended)
return 0;
@@ -1446,29 +1499,30 @@ static int venus_suspend_3xx(struct venus_core *core)
return -EINVAL;
}
- ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0);
- if (!(ctrl_status & CPU_CS_SCIACMDARG0_PC_READY)) {
- wfi_status = venus_readl(hdev, WRAPPER_CPU_STATUS);
- ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0);
-
- ret = venus_prepare_power_collapse(hdev, false);
- if (ret) {
- dev_err(dev, "prepare for power collapse fail (%d)\n",
- ret);
- return ret;
- }
+ /*
+ * Power collapse sequence for Venus 3xx and 4xx versions:
+ * 1. Check for ARM9 and video core to be idle by checking WFI bit
+ * (bit 0) in CPU status register and by checking Idle (bit 30) in
+ * Control status register for video core.
+ * 2. Send a command to prepare for power collapse.
+ * 3. Check for WFI and PC_READY bits.
+ */
+ ret = readx_poll_timeout(venus_cpu_and_video_core_idle, hdev, val, val,
+ 1500, 100 * 1500);
+ if (ret)
+ return ret;
- cnt = 100;
- while (cnt--) {
- wfi_status = venus_readl(hdev, WRAPPER_CPU_STATUS);
- ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0);
- if (ctrl_status & CPU_CS_SCIACMDARG0_PC_READY &&
- wfi_status & BIT(0))
- break;
- usleep_range(1000, 1500);
- }
+ ret = venus_prepare_power_collapse(hdev, false);
+ if (ret) {
+ dev_err(dev, "prepare for power collapse fail (%d)\n", ret);
+ return ret;
}
+ ret = readx_poll_timeout(venus_cpu_idle_and_pc_ready, hdev, val, val,
+ 1500, 100 * 1500);
+ if (ret)
+ return ret;
+
mutex_lock(&hdev->lock);
ret = venus_power_off(hdev);
@@ -1487,7 +1541,7 @@ static int venus_suspend_3xx(struct venus_core *core)
static int venus_suspend(struct venus_core *core)
{
- if (core->res->hfi_version == HFI_VERSION_3XX)
+ if (IS_V3(core) || IS_V4(core))
return venus_suspend_3xx(core);
return venus_suspend_1xx(core);
diff --git a/drivers/media/platform/qcom/venus/hfi_venus_io.h b/drivers/media/platform/qcom/venus/hfi_venus_io.h
index 98cc350113ab..def0926a6dee 100644
--- a/drivers/media/platform/qcom/venus/hfi_venus_io.h
+++ b/drivers/media/platform/qcom/venus/hfi_venus_io.h
@@ -104,10 +104,20 @@
#define WRAPPER_CPU_CLOCK_CONFIG (WRAPPER_BASE + 0x2000)
#define WRAPPER_CPU_AXI_HALT (WRAPPER_BASE + 0x2008)
+#define WRAPPER_CPU_AXI_HALT_HALT BIT(16)
#define WRAPPER_CPU_AXI_HALT_STATUS (WRAPPER_BASE + 0x200c)
+#define WRAPPER_CPU_AXI_HALT_STATUS_IDLE BIT(24)
#define WRAPPER_CPU_CGC_DIS (WRAPPER_BASE + 0x2010)
#define WRAPPER_CPU_STATUS (WRAPPER_BASE + 0x2014)
+#define WRAPPER_CPU_STATUS_WFI BIT(0)
#define WRAPPER_SW_RESET (WRAPPER_BASE + 0x3000)
+/* Venus 4xx */
+#define WRAPPER_VCODEC0_MMCC_POWER_STATUS (WRAPPER_BASE + 0x90)
+#define WRAPPER_VCODEC0_MMCC_POWER_CONTROL (WRAPPER_BASE + 0x94)
+
+#define WRAPPER_VCODEC1_MMCC_POWER_STATUS (WRAPPER_BASE + 0x110)
+#define WRAPPER_VCODEC1_MMCC_POWER_CONTROL (WRAPPER_BASE + 0x114)
+
#endif
diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c
index 49bbd1861d3a..dfbbbf0f746f 100644
--- a/drivers/media/platform/qcom/venus/vdec.c
+++ b/drivers/media/platform/qcom/venus/vdec.c
@@ -14,6 +14,7 @@
*/
#include <linux/clk.h>
#include <linux/module.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
@@ -24,33 +25,11 @@
#include <media/videobuf2-dma-sg.h>
#include "hfi_venus_io.h"
+#include "hfi_parser.h"
#include "core.h"
#include "helpers.h"
#include "vdec.h"
-static u32 get_framesize_uncompressed(unsigned int plane, u32 width, u32 height)
-{
- u32 y_stride, uv_stride, y_plane;
- u32 y_sclines, uv_sclines, uv_plane;
- u32 size;
-
- y_stride = ALIGN(width, 128);
- uv_stride = ALIGN(width, 128);
- y_sclines = ALIGN(height, 32);
- uv_sclines = ALIGN(((height + 1) >> 1), 16);
-
- y_plane = y_stride * y_sclines;
- uv_plane = uv_stride * uv_sclines + SZ_4K;
- size = y_plane + uv_plane + SZ_8K;
-
- return ALIGN(size, SZ_4K);
-}
-
-static u32 get_framesize_compressed(unsigned int width, unsigned int height)
-{
- return ((width * height * 3 / 2) / 2) + 128;
-}
-
/*
* Three resons to keep MPLANE formats (despite that the number of planes
* currently is one):
@@ -99,6 +78,10 @@ static const struct venus_format vdec_formats[] = {
.pixfmt = V4L2_PIX_FMT_XVID,
.num_planes = 1,
.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ }, {
+ .pixfmt = V4L2_PIX_FMT_HEVC,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
},
};
@@ -159,7 +142,6 @@ vdec_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f)
struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp;
struct v4l2_plane_pix_format *pfmt = pixmp->plane_fmt;
const struct venus_format *fmt;
- unsigned int p;
memset(pfmt[0].reserved, 0, sizeof(pfmt[0].reserved));
memset(pixmp->reserved, 0, sizeof(pixmp->reserved));
@@ -173,14 +155,12 @@ vdec_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f)
else
return NULL;
fmt = find_format(inst, pixmp->pixelformat, f->type);
- pixmp->width = 1280;
- pixmp->height = 720;
}
- pixmp->width = clamp(pixmp->width, inst->cap_width.min,
- inst->cap_width.max);
- pixmp->height = clamp(pixmp->height, inst->cap_height.min,
- inst->cap_height.max);
+ pixmp->width = clamp(pixmp->width, frame_width_min(inst),
+ frame_width_max(inst));
+ pixmp->height = clamp(pixmp->height, frame_height_min(inst),
+ frame_height_max(inst));
if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
pixmp->height = ALIGN(pixmp->height, 32);
@@ -190,18 +170,14 @@ vdec_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f)
pixmp->num_planes = fmt->num_planes;
pixmp->flags = 0;
- if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
- for (p = 0; p < pixmp->num_planes; p++) {
- pfmt[p].sizeimage =
- get_framesize_uncompressed(p, pixmp->width,
- pixmp->height);
- pfmt[p].bytesperline = ALIGN(pixmp->width, 128);
- }
- } else {
- pfmt[0].sizeimage = get_framesize_compressed(pixmp->width,
- pixmp->height);
+ pfmt[0].sizeimage = venus_helper_get_framesz(pixmp->pixelformat,
+ pixmp->width,
+ pixmp->height);
+
+ if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+ pfmt[0].bytesperline = ALIGN(pixmp->width, 128);
+ else
pfmt[0].bytesperline = 0;
- }
return fmt;
}
@@ -442,12 +418,12 @@ static int vdec_enum_framesizes(struct file *file, void *fh,
fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
- fsize->stepwise.min_width = inst->cap_width.min;
- fsize->stepwise.max_width = inst->cap_width.max;
- fsize->stepwise.step_width = inst->cap_width.step_size;
- fsize->stepwise.min_height = inst->cap_height.min;
- fsize->stepwise.max_height = inst->cap_height.max;
- fsize->stepwise.step_height = inst->cap_height.step_size;
+ fsize->stepwise.min_width = frame_width_min(inst);
+ fsize->stepwise.max_width = frame_width_max(inst);
+ fsize->stepwise.step_width = frame_width_step(inst);
+ fsize->stepwise.min_height = frame_height_min(inst);
+ fsize->stepwise.max_height = frame_height_max(inst);
+ fsize->stepwise.step_height = frame_height_step(inst);
return 0;
}
@@ -544,11 +520,41 @@ static const struct v4l2_ioctl_ops vdec_ioctl_ops = {
static int vdec_set_properties(struct venus_inst *inst)
{
struct vdec_controls *ctr = &inst->controls.dec;
+ struct hfi_enable en = { .enable = 1 };
+ u32 ptype;
+ int ret;
+
+ if (ctr->post_loop_deb_mode) {
+ ptype = HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER;
+ ret = hfi_session_set_property(inst, ptype, &en);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+#define is_ubwc_fmt(fmt) (!!((fmt) & HFI_COLOR_FORMAT_UBWC_BASE))
+
+static int vdec_output_conf(struct venus_inst *inst)
+{
struct venus_core *core = inst->core;
struct hfi_enable en = { .enable = 1 };
+ u32 width = inst->out_width;
+ u32 height = inst->out_height;
+ u32 out_fmt, out2_fmt;
+ bool ubwc = false;
u32 ptype;
int ret;
+ ret = venus_helper_set_work_mode(inst, VIDC_WORK_MODE_2);
+ if (ret)
+ return ret;
+
+ ret = venus_helper_set_core_usage(inst, VIDC_CORE_ID_1);
+ if (ret)
+ return ret;
+
if (core->res->hfi_version == HFI_VERSION_1XX) {
ptype = HFI_PROPERTY_PARAM_VDEC_CONTINUE_DATA_TRANSFER;
ret = hfi_session_set_property(inst, ptype, &en);
@@ -556,27 +562,84 @@ static int vdec_set_properties(struct venus_inst *inst)
return ret;
}
- if (core->res->hfi_version == HFI_VERSION_3XX ||
- inst->cap_bufs_mode_dynamic) {
- struct hfi_buffer_alloc_mode mode;
+ /* Force searching UBWC formats for bigger then HD resolutions */
+ if (width > 1920 && height > ALIGN(1080, 32))
+ ubwc = true;
+
+ /* For Venus v4 UBWC format is mandatory */
+ if (IS_V4(core))
+ ubwc = true;
+
+ ret = venus_helper_get_out_fmts(inst, inst->fmt_cap->pixfmt, &out_fmt,
+ &out2_fmt, ubwc);
+ if (ret)
+ return ret;
+
+ inst->output_buf_size =
+ venus_helper_get_framesz_raw(out_fmt, width, height);
+ inst->output2_buf_size =
+ venus_helper_get_framesz_raw(out2_fmt, width, height);
+
+ if (is_ubwc_fmt(out_fmt)) {
+ inst->opb_buftype = HFI_BUFFER_OUTPUT2;
+ inst->opb_fmt = out2_fmt;
+ inst->dpb_buftype = HFI_BUFFER_OUTPUT;
+ inst->dpb_fmt = out_fmt;
+ } else if (is_ubwc_fmt(out2_fmt)) {
+ inst->opb_buftype = HFI_BUFFER_OUTPUT;
+ inst->opb_fmt = out_fmt;
+ inst->dpb_buftype = HFI_BUFFER_OUTPUT2;
+ inst->dpb_fmt = out2_fmt;
+ } else {
+ inst->opb_buftype = HFI_BUFFER_OUTPUT;
+ inst->opb_fmt = out_fmt;
+ inst->dpb_buftype = 0;
+ inst->dpb_fmt = 0;
+ }
+
+ ret = venus_helper_set_raw_format(inst, inst->opb_fmt,
+ inst->opb_buftype);
+ if (ret)
+ return ret;
- ptype = HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE;
- mode.type = HFI_BUFFER_OUTPUT;
- mode.mode = HFI_BUFFER_MODE_DYNAMIC;
+ if (inst->dpb_fmt) {
+ ret = venus_helper_set_multistream(inst, false, true);
+ if (ret)
+ return ret;
- ret = hfi_session_set_property(inst, ptype, &mode);
+ ret = venus_helper_set_raw_format(inst, inst->dpb_fmt,
+ inst->dpb_buftype);
if (ret)
return ret;
- }
- if (ctr->post_loop_deb_mode) {
- ptype = HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER;
- en.enable = 1;
- ret = hfi_session_set_property(inst, ptype, &en);
+ ret = venus_helper_set_output_resolution(inst, width, height,
+ HFI_BUFFER_OUTPUT2);
if (ret)
return ret;
}
+ if (IS_V3(core) || IS_V4(core)) {
+ if (inst->output2_buf_size) {
+ ret = venus_helper_set_bufsize(inst,
+ inst->output2_buf_size,
+ HFI_BUFFER_OUTPUT2);
+ if (ret)
+ return ret;
+ }
+
+ if (inst->output_buf_size) {
+ ret = venus_helper_set_bufsize(inst,
+ inst->output_buf_size,
+ HFI_BUFFER_OUTPUT);
+ if (ret)
+ return ret;
+ }
+ }
+
+ ret = venus_helper_set_dyn_bufmode(inst);
+ if (ret)
+ return ret;
+
return 0;
}
@@ -603,19 +666,32 @@ deinit:
return ret;
}
-static int vdec_cap_num_buffers(struct venus_inst *inst, unsigned int *num)
+static int vdec_num_buffers(struct venus_inst *inst, unsigned int *in_num,
+ unsigned int *out_num)
{
+ enum hfi_version ver = inst->core->res->hfi_version;
struct hfi_buffer_requirements bufreq;
int ret;
+ *in_num = *out_num = 0;
+
ret = vdec_init_session(inst);
if (ret)
return ret;
+ ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq);
+ if (ret)
+ goto deinit;
+
+ *in_num = HFI_BUFREQ_COUNT_MIN(&bufreq, ver);
+
ret = venus_helper_get_bufreq(inst, HFI_BUFFER_OUTPUT, &bufreq);
+ if (ret)
+ goto deinit;
- *num = bufreq.count_actual;
+ *out_num = HFI_BUFREQ_COUNT_MIN(&bufreq, ver);
+deinit:
hfi_session_deinit(inst);
return ret;
@@ -626,10 +702,12 @@ static int vdec_queue_setup(struct vb2_queue *q,
unsigned int sizes[], struct device *alloc_devs[])
{
struct venus_inst *inst = vb2_get_drv_priv(q);
- unsigned int p, num;
+ unsigned int in_num, out_num;
int ret = 0;
if (*num_planes) {
+ unsigned int output_buf_size = venus_helper_get_opb_size(inst);
+
if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
*num_planes != inst->fmt_out->num_planes)
return -EINVAL;
@@ -643,41 +721,35 @@ static int vdec_queue_setup(struct vb2_queue *q,
return -EINVAL;
if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
- sizes[0] < inst->output_buf_size)
+ sizes[0] < output_buf_size)
return -EINVAL;
return 0;
}
+ ret = vdec_num_buffers(inst, &in_num, &out_num);
+ if (ret)
+ return ret;
+
switch (q->type) {
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
*num_planes = inst->fmt_out->num_planes;
- sizes[0] = get_framesize_compressed(inst->out_width,
+ sizes[0] = venus_helper_get_framesz(inst->fmt_out->pixfmt,
+ inst->out_width,
inst->out_height);
inst->input_buf_size = sizes[0];
+ *num_buffers = max(*num_buffers, in_num);
inst->num_input_bufs = *num_buffers;
-
- ret = vdec_cap_num_buffers(inst, &num);
- if (ret)
- break;
-
- inst->num_output_bufs = num;
+ inst->num_output_bufs = out_num;
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
*num_planes = inst->fmt_cap->num_planes;
-
- ret = vdec_cap_num_buffers(inst, &num);
- if (ret)
- break;
-
- *num_buffers = max(*num_buffers, num);
-
- for (p = 0; p < *num_planes; p++)
- sizes[p] = get_framesize_uncompressed(p, inst->width,
- inst->height);
-
- inst->num_output_bufs = *num_buffers;
+ sizes[0] = venus_helper_get_framesz(inst->fmt_cap->pixfmt,
+ inst->width,
+ inst->height);
inst->output_buf_size = sizes[0];
+ *num_buffers = max(*num_buffers, out_num);
+ inst->num_output_bufs = *num_buffers;
break;
default:
ret = -EINVAL;
@@ -689,6 +761,7 @@ static int vdec_queue_setup(struct vb2_queue *q,
static int vdec_verify_conf(struct venus_inst *inst)
{
+ enum hfi_version ver = inst->core->res->hfi_version;
struct hfi_buffer_requirements bufreq;
int ret;
@@ -700,14 +773,14 @@ static int vdec_verify_conf(struct venus_inst *inst)
return ret;
if (inst->num_output_bufs < bufreq.count_actual ||
- inst->num_output_bufs < bufreq.count_min)
+ inst->num_output_bufs < HFI_BUFREQ_COUNT_MIN(&bufreq, ver))
return -EINVAL;
ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq);
if (ret)
return ret;
- if (inst->num_input_bufs < bufreq.count_min)
+ if (inst->num_input_bufs < HFI_BUFREQ_COUNT_MIN(&bufreq, ver))
return -EINVAL;
return 0;
@@ -716,8 +789,6 @@ static int vdec_verify_conf(struct venus_inst *inst)
static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
{
struct venus_inst *inst = vb2_get_drv_priv(q);
- struct venus_core *core = inst->core;
- u32 ptype;
int ret;
mutex_lock(&inst->lock);
@@ -746,24 +817,20 @@ static int vdec_start_streaming(struct vb2_queue *q, unsigned int count)
if (ret)
goto deinit_sess;
- if (core->res->hfi_version == HFI_VERSION_3XX) {
- struct hfi_buffer_size_actual buf_sz;
-
- ptype = HFI_PROPERTY_PARAM_BUFFER_SIZE_ACTUAL;
- buf_sz.type = HFI_BUFFER_OUTPUT;
- buf_sz.size = inst->output_buf_size;
-
- ret = hfi_session_set_property(inst, ptype, &buf_sz);
- if (ret)
- goto deinit_sess;
- }
+ ret = vdec_output_conf(inst);
+ if (ret)
+ goto deinit_sess;
ret = vdec_verify_conf(inst);
if (ret)
goto deinit_sess;
ret = venus_helper_set_num_bufs(inst, inst->num_input_bufs,
- VB2_MAX_FRAME);
+ VB2_MAX_FRAME, VB2_MAX_FRAME);
+ if (ret)
+ goto deinit_sess;
+
+ ret = venus_helper_alloc_dpb_bufs(inst);
if (ret)
goto deinit_sess;
@@ -818,9 +885,11 @@ static void vdec_buf_done(struct venus_inst *inst, unsigned int buf_type,
vbuf->field = V4L2_FIELD_NONE;
if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ unsigned int opb_sz = venus_helper_get_opb_size(inst);
+
vb = &vbuf->vb2_buf;
vb->planes[0].bytesused =
- max_t(unsigned int, inst->output_buf_size, bytesused);
+ max_t(unsigned int, opb_sz, bytesused);
vb->planes[0].data_offset = data_offset;
vb->timestamp = timestamp_us * NSEC_PER_USEC;
vbuf->sequence = inst->sequence_cap++;
@@ -901,22 +970,7 @@ static void vdec_inst_init(struct venus_inst *inst)
inst->fps = 30;
inst->timeperframe.numerator = 1;
inst->timeperframe.denominator = 30;
-
- inst->cap_width.min = 64;
- inst->cap_width.max = 1920;
- if (inst->core->res->hfi_version == HFI_VERSION_3XX)
- inst->cap_width.max = 3840;
- inst->cap_width.step_size = 1;
- inst->cap_height.min = 64;
- inst->cap_height.max = ALIGN(1080, 32);
- if (inst->core->res->hfi_version == HFI_VERSION_3XX)
- inst->cap_height.max = ALIGN(2160, 32);
- inst->cap_height.step_size = 1;
- inst->cap_framerate.min = 1;
- inst->cap_framerate.max = 30;
- inst->cap_framerate.step_size = 1;
- inst->cap_mbs_per_frame.min = 16;
- inst->cap_mbs_per_frame.max = 8160;
+ inst->hfi_codec = HFI_VIDEO_CODEC_H264;
}
static const struct v4l2_m2m_ops vdec_m2m_ops = {
@@ -973,6 +1027,7 @@ static int vdec_open(struct file *file)
if (!inst)
return -ENOMEM;
+ INIT_LIST_HEAD(&inst->dpbbufs);
INIT_LIST_HEAD(&inst->registeredbufs);
INIT_LIST_HEAD(&inst->internalbufs);
INIT_LIST_HEAD(&inst->list);
@@ -1080,12 +1135,18 @@ static int vdec_probe(struct platform_device *pdev)
if (!core)
return -EPROBE_DEFER;
- if (core->res->hfi_version == HFI_VERSION_3XX) {
+ if (IS_V3(core) || IS_V4(core)) {
core->core0_clk = devm_clk_get(dev, "core");
if (IS_ERR(core->core0_clk))
return PTR_ERR(core->core0_clk);
}
+ if (IS_V4(core)) {
+ core->core0_bus_clk = devm_clk_get(dev, "bus");
+ if (IS_ERR(core->core0_bus_clk))
+ return PTR_ERR(core->core0_bus_clk);
+ }
+
platform_set_drvdata(pdev, core);
vdev = video_device_alloc();
@@ -1130,15 +1191,21 @@ static int vdec_remove(struct platform_device *pdev)
static __maybe_unused int vdec_runtime_suspend(struct device *dev)
{
struct venus_core *core = dev_get_drvdata(dev);
+ int ret;
- if (core->res->hfi_version == HFI_VERSION_1XX)
+ if (IS_V1(core))
return 0;
- writel(0, core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL);
+ ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, true);
+ if (ret)
+ return ret;
+
+ if (IS_V4(core))
+ clk_disable_unprepare(core->core0_bus_clk);
+
clk_disable_unprepare(core->core0_clk);
- writel(1, core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL);
- return 0;
+ return venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, false);
}
static __maybe_unused int vdec_runtime_resume(struct device *dev)
@@ -1146,13 +1213,29 @@ static __maybe_unused int vdec_runtime_resume(struct device *dev)
struct venus_core *core = dev_get_drvdata(dev);
int ret;
- if (core->res->hfi_version == HFI_VERSION_1XX)
+ if (IS_V1(core))
return 0;
- writel(0, core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL);
+ ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, true);
+ if (ret)
+ return ret;
+
ret = clk_prepare_enable(core->core0_clk);
- writel(1, core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL);
+ if (ret)
+ goto err_power_disable;
+ if (IS_V4(core))
+ ret = clk_prepare_enable(core->core0_bus_clk);
+
+ if (ret)
+ goto err_unprepare_core0;
+
+ return venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, false);
+
+err_unprepare_core0:
+ clk_disable_unprepare(core->core0_clk);
+err_power_disable:
+ venus_helper_power_enable(core, VIDC_SESSION_TYPE_DEC, false);
return ret;
}
diff --git a/drivers/media/platform/qcom/venus/vdec_ctrls.c b/drivers/media/platform/qcom/venus/vdec_ctrls.c
index 032839bbc967..f4604b0cd57e 100644
--- a/drivers/media/platform/qcom/venus/vdec_ctrls.c
+++ b/drivers/media/platform/qcom/venus/vdec_ctrls.c
@@ -29,7 +29,7 @@ static int vdec_op_s_ctrl(struct v4l2_ctrl *ctrl)
break;
case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
- case V4L2_CID_MPEG_VIDEO_VPX_PROFILE:
+ case V4L2_CID_MPEG_VIDEO_VP8_PROFILE:
ctr->profile = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
@@ -54,7 +54,7 @@ static int vdec_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
switch (ctrl->id) {
case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
- case V4L2_CID_MPEG_VIDEO_VPX_PROFILE:
+ case V4L2_CID_MPEG_VIDEO_VP8_PROFILE:
ret = hfi_session_get_property(inst, ptype, &hprop);
if (!ret)
ctr->profile = hprop.profile_level.profile;
@@ -130,8 +130,10 @@ int vdec_ctrl_init(struct venus_inst *inst)
if (ctrl)
ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
- ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler, &vdec_ctrl_ops,
- V4L2_CID_MPEG_VIDEO_VPX_PROFILE, 0, 3, 1, 0);
+ ctrl = v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &vdec_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_VP8_PROFILE,
+ V4L2_MPEG_VIDEO_VP8_PROFILE_3,
+ 0, V4L2_MPEG_VIDEO_VP8_PROFILE_0);
if (ctrl)
ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c
index 6b2ce479584e..41249d1443fa 100644
--- a/drivers/media/platform/qcom/venus/venc.c
+++ b/drivers/media/platform/qcom/venus/venc.c
@@ -14,6 +14,7 @@
*/
#include <linux/clk.h>
#include <linux/module.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
@@ -24,38 +25,13 @@
#include <media/v4l2-ctrls.h>
#include "hfi_venus_io.h"
+#include "hfi_parser.h"
#include "core.h"
#include "helpers.h"
#include "venc.h"
#define NUM_B_FRAMES_MAX 4
-static u32 get_framesize_uncompressed(unsigned int plane, u32 width, u32 height)
-{
- u32 y_stride, uv_stride, y_plane;
- u32 y_sclines, uv_sclines, uv_plane;
- u32 size;
-
- y_stride = ALIGN(width, 128);
- uv_stride = ALIGN(width, 128);
- y_sclines = ALIGN(height, 32);
- uv_sclines = ALIGN(((height + 1) >> 1), 16);
-
- y_plane = y_stride * y_sclines;
- uv_plane = uv_stride * uv_sclines + SZ_4K;
- size = y_plane + uv_plane + SZ_8K;
- size = ALIGN(size, SZ_4K);
-
- return size;
-}
-
-static u32 get_framesize_compressed(u32 width, u32 height)
-{
- u32 sz = ALIGN(height, 32) * ALIGN(width, 32) * 3 / 2 / 2;
-
- return ALIGN(sz, SZ_4K);
-}
-
/*
* Three resons to keep MPLANE formats (despite that the number of planes
* currently is one):
@@ -84,6 +60,10 @@ static const struct venus_format venc_formats[] = {
.pixfmt = V4L2_PIX_FMT_VP8,
.num_planes = 1,
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ }, {
+ .pixfmt = V4L2_PIX_FMT_HEVC,
+ .num_planes = 1,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
},
};
@@ -223,7 +203,7 @@ static int venc_v4l2_to_hfi(int id, int value)
case V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC:
return HFI_H264_ENTROPY_CABAC;
}
- case V4L2_CID_MPEG_VIDEO_VPX_PROFILE:
+ case V4L2_CID_MPEG_VIDEO_VP8_PROFILE:
switch (value) {
case 0:
default:
@@ -245,6 +225,46 @@ static int venc_v4l2_to_hfi(int id, int value)
case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY:
return HFI_H264_DB_MODE_SKIP_SLICE_BOUNDARY;
}
+ case V4L2_CID_MPEG_VIDEO_HEVC_PROFILE:
+ switch (value) {
+ case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN:
+ default:
+ return HFI_HEVC_PROFILE_MAIN;
+ case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_STILL_PICTURE:
+ return HFI_HEVC_PROFILE_MAIN_STILL_PIC;
+ case V4L2_MPEG_VIDEO_HEVC_PROFILE_MAIN_10:
+ return HFI_HEVC_PROFILE_MAIN10;
+ }
+ case V4L2_CID_MPEG_VIDEO_HEVC_LEVEL:
+ switch (value) {
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_1:
+ default:
+ return HFI_HEVC_LEVEL_1;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_2:
+ return HFI_HEVC_LEVEL_2;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_2_1:
+ return HFI_HEVC_LEVEL_21;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_3:
+ return HFI_HEVC_LEVEL_3;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_3_1:
+ return HFI_HEVC_LEVEL_31;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_4:
+ return HFI_HEVC_LEVEL_4;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_4_1:
+ return HFI_HEVC_LEVEL_41;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_5:
+ return HFI_HEVC_LEVEL_5;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_1:
+ return HFI_HEVC_LEVEL_51;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_5_2:
+ return HFI_HEVC_LEVEL_52;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_6:
+ return HFI_HEVC_LEVEL_6;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_6_1:
+ return HFI_HEVC_LEVEL_61;
+ case V4L2_MPEG_VIDEO_HEVC_LEVEL_6_2:
+ return HFI_HEVC_LEVEL_62;
+ }
}
return 0;
@@ -283,7 +303,6 @@ venc_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f)
struct v4l2_pix_format_mplane *pixmp = &f->fmt.pix_mp;
struct v4l2_plane_pix_format *pfmt = pixmp->plane_fmt;
const struct venus_format *fmt;
- unsigned int p;
memset(pfmt[0].reserved, 0, sizeof(pfmt[0].reserved));
memset(pixmp->reserved, 0, sizeof(pixmp->reserved));
@@ -297,14 +316,12 @@ venc_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f)
else
return NULL;
fmt = find_format(inst, pixmp->pixelformat, f->type);
- pixmp->width = 1280;
- pixmp->height = 720;
}
- pixmp->width = clamp(pixmp->width, inst->cap_width.min,
- inst->cap_width.max);
- pixmp->height = clamp(pixmp->height, inst->cap_height.min,
- inst->cap_height.max);
+ pixmp->width = clamp(pixmp->width, frame_width_min(inst),
+ frame_width_max(inst));
+ pixmp->height = clamp(pixmp->height, frame_height_min(inst),
+ frame_height_max(inst));
if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
pixmp->height = ALIGN(pixmp->height, 32);
@@ -317,19 +334,14 @@ venc_try_fmt_common(struct venus_inst *inst, struct v4l2_format *f)
pixmp->num_planes = fmt->num_planes;
pixmp->flags = 0;
- if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
- for (p = 0; p < pixmp->num_planes; p++) {
- pfmt[p].sizeimage =
- get_framesize_uncompressed(p, pixmp->width,
- pixmp->height);
+ pfmt[0].sizeimage = venus_helper_get_framesz(pixmp->pixelformat,
+ pixmp->width,
+ pixmp->height);
- pfmt[p].bytesperline = ALIGN(pixmp->width, 128);
- }
- } else {
- pfmt[0].sizeimage = get_framesize_compressed(pixmp->width,
- pixmp->height);
+ if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+ pfmt[0].bytesperline = ALIGN(pixmp->width, 128);
+ else
pfmt[0].bytesperline = 0;
- }
return fmt;
}
@@ -553,12 +565,12 @@ static int venc_enum_framesizes(struct file *file, void *fh,
if (fsize->index)
return -EINVAL;
- fsize->stepwise.min_width = inst->cap_width.min;
- fsize->stepwise.max_width = inst->cap_width.max;
- fsize->stepwise.step_width = inst->cap_width.step_size;
- fsize->stepwise.min_height = inst->cap_height.min;
- fsize->stepwise.max_height = inst->cap_height.max;
- fsize->stepwise.step_height = inst->cap_height.step_size;
+ fsize->stepwise.min_width = frame_width_min(inst);
+ fsize->stepwise.max_width = frame_width_max(inst);
+ fsize->stepwise.step_width = frame_width_step(inst);
+ fsize->stepwise.min_height = frame_height_min(inst);
+ fsize->stepwise.max_height = frame_height_max(inst);
+ fsize->stepwise.step_height = frame_height_step(inst);
return 0;
}
@@ -586,18 +598,18 @@ static int venc_enum_frameintervals(struct file *file, void *fh,
if (!fival->width || !fival->height)
return -EINVAL;
- if (fival->width > inst->cap_width.max ||
- fival->width < inst->cap_width.min ||
- fival->height > inst->cap_height.max ||
- fival->height < inst->cap_height.min)
+ if (fival->width > frame_width_max(inst) ||
+ fival->width < frame_width_min(inst) ||
+ fival->height > frame_height_max(inst) ||
+ fival->height < frame_height_min(inst))
return -EINVAL;
fival->stepwise.min.numerator = 1;
- fival->stepwise.min.denominator = inst->cap_framerate.max;
+ fival->stepwise.min.denominator = frate_max(inst);
fival->stepwise.max.numerator = 1;
- fival->stepwise.max.denominator = inst->cap_framerate.min;
+ fival->stepwise.max.denominator = frate_min(inst);
fival->stepwise.step.numerator = 1;
- fival->stepwise.step.denominator = inst->cap_framerate.max;
+ fival->stepwise.step.denominator = frate_max(inst);
return 0;
}
@@ -642,6 +654,14 @@ static int venc_set_properties(struct venus_inst *inst)
u32 ptype, rate_control, bitrate, profile = 0, level = 0;
int ret;
+ ret = venus_helper_set_work_mode(inst, VIDC_WORK_MODE_2);
+ if (ret)
+ return ret;
+
+ ret = venus_helper_set_core_usage(inst, VIDC_CORE_ID_2);
+ if (ret)
+ return ret;
+
ptype = HFI_PROPERTY_CONFIG_FRAME_RATE;
frate.buffer_type = HFI_BUFFER_OUTPUT;
frate.framerate = inst->fps * (1 << 16);
@@ -756,7 +776,7 @@ static int venc_set_properties(struct venus_inst *inst)
level = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_H264_LEVEL,
ctr->level.h264);
} else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_VP8) {
- profile = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_VPX_PROFILE,
+ profile = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_VP8_PROFILE,
ctr->profile.vpx);
level = 0;
} else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_MPEG4) {
@@ -767,6 +787,11 @@ static int venc_set_properties(struct venus_inst *inst)
} else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H263) {
profile = 0;
level = 0;
+ } else if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) {
+ profile = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_HEVC_PROFILE,
+ ctr->profile.hevc);
+ level = venc_v4l2_to_hfi(V4L2_CID_MPEG_VIDEO_HEVC_LEVEL,
+ ctr->level.hevc);
}
ptype = HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT;
@@ -794,7 +819,8 @@ static int venc_init_session(struct venus_inst *inst)
goto deinit;
ret = venus_helper_set_output_resolution(inst, inst->width,
- inst->height);
+ inst->height,
+ HFI_BUFFER_OUTPUT);
if (ret)
goto deinit;
@@ -835,7 +861,7 @@ static int venc_queue_setup(struct vb2_queue *q,
unsigned int sizes[], struct device *alloc_devs[])
{
struct venus_inst *inst = vb2_get_drv_priv(q);
- unsigned int p, num, min = 4;
+ unsigned int num, min = 4;
int ret = 0;
if (*num_planes) {
@@ -870,16 +896,18 @@ static int venc_queue_setup(struct vb2_queue *q,
*num_buffers = max(*num_buffers, num);
inst->num_input_bufs = *num_buffers;
- for (p = 0; p < *num_planes; ++p)
- sizes[p] = get_framesize_uncompressed(p, inst->width,
- inst->height);
+ sizes[0] = venus_helper_get_framesz(inst->fmt_out->pixfmt,
+ inst->width,
+ inst->height);
inst->input_buf_size = sizes[0];
break;
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
*num_planes = inst->fmt_cap->num_planes;
*num_buffers = max(*num_buffers, min);
inst->num_output_bufs = *num_buffers;
- sizes[0] = get_framesize_compressed(inst->width, inst->height);
+ sizes[0] = venus_helper_get_framesz(inst->fmt_cap->pixfmt,
+ inst->width,
+ inst->height);
inst->output_buf_size = sizes[0];
break;
default:
@@ -892,6 +920,7 @@ static int venc_queue_setup(struct vb2_queue *q,
static int venc_verify_conf(struct venus_inst *inst)
{
+ enum hfi_version ver = inst->core->res->hfi_version;
struct hfi_buffer_requirements bufreq;
int ret;
@@ -903,7 +932,7 @@ static int venc_verify_conf(struct venus_inst *inst)
return ret;
if (inst->num_output_bufs < bufreq.count_actual ||
- inst->num_output_bufs < bufreq.count_min)
+ inst->num_output_bufs < HFI_BUFREQ_COUNT_MIN(&bufreq, ver))
return -EINVAL;
ret = venus_helper_get_bufreq(inst, HFI_BUFFER_INPUT, &bufreq);
@@ -911,7 +940,7 @@ static int venc_verify_conf(struct venus_inst *inst)
return ret;
if (inst->num_input_bufs < bufreq.count_actual ||
- inst->num_input_bufs < bufreq.count_min)
+ inst->num_input_bufs < HFI_BUFREQ_COUNT_MIN(&bufreq, ver))
return -EINVAL;
return 0;
@@ -952,7 +981,7 @@ static int venc_start_streaming(struct vb2_queue *q, unsigned int count)
goto deinit_sess;
ret = venus_helper_set_num_bufs(inst, inst->num_input_bufs,
- inst->num_output_bufs);
+ inst->num_output_bufs, 0);
if (ret)
goto deinit_sess;
@@ -1090,22 +1119,7 @@ static void venc_inst_init(struct venus_inst *inst)
inst->fps = 15;
inst->timeperframe.numerator = 1;
inst->timeperframe.denominator = 15;
-
- inst->cap_width.min = 96;
- inst->cap_width.max = 1920;
- if (inst->core->res->hfi_version == HFI_VERSION_3XX)
- inst->cap_width.max = 3840;
- inst->cap_width.step_size = 2;
- inst->cap_height.min = 64;
- inst->cap_height.max = ALIGN(1080, 32);
- if (inst->core->res->hfi_version == HFI_VERSION_3XX)
- inst->cap_height.max = ALIGN(2160, 32);
- inst->cap_height.step_size = 2;
- inst->cap_framerate.min = 1;
- inst->cap_framerate.max = 30;
- inst->cap_framerate.step_size = 1;
- inst->cap_mbs_per_frame.min = 24;
- inst->cap_mbs_per_frame.max = 8160;
+ inst->hfi_codec = HFI_VIDEO_CODEC_H264;
}
static int venc_open(struct file *file)
@@ -1118,6 +1132,7 @@ static int venc_open(struct file *file)
if (!inst)
return -ENOMEM;
+ INIT_LIST_HEAD(&inst->dpbbufs);
INIT_LIST_HEAD(&inst->registeredbufs);
INIT_LIST_HEAD(&inst->internalbufs);
INIT_LIST_HEAD(&inst->list);
@@ -1224,12 +1239,18 @@ static int venc_probe(struct platform_device *pdev)
if (!core)
return -EPROBE_DEFER;
- if (core->res->hfi_version == HFI_VERSION_3XX) {
+ if (IS_V3(core) || IS_V4(core)) {
core->core1_clk = devm_clk_get(dev, "core");
if (IS_ERR(core->core1_clk))
return PTR_ERR(core->core1_clk);
}
+ if (IS_V4(core)) {
+ core->core1_bus_clk = devm_clk_get(dev, "bus");
+ if (IS_ERR(core->core1_bus_clk))
+ return PTR_ERR(core->core1_bus_clk);
+ }
+
platform_set_drvdata(pdev, core);
vdev = video_device_alloc();
@@ -1274,15 +1295,21 @@ static int venc_remove(struct platform_device *pdev)
static __maybe_unused int venc_runtime_suspend(struct device *dev)
{
struct venus_core *core = dev_get_drvdata(dev);
+ int ret;
- if (core->res->hfi_version == HFI_VERSION_1XX)
+ if (IS_V1(core))
return 0;
- writel(0, core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL);
+ ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, true);
+ if (ret)
+ return ret;
+
+ if (IS_V4(core))
+ clk_disable_unprepare(core->core1_bus_clk);
+
clk_disable_unprepare(core->core1_clk);
- writel(1, core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL);
- return 0;
+ return venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, false);
}
static __maybe_unused int venc_runtime_resume(struct device *dev)
@@ -1290,13 +1317,29 @@ static __maybe_unused int venc_runtime_resume(struct device *dev)
struct venus_core *core = dev_get_drvdata(dev);
int ret;
- if (core->res->hfi_version == HFI_VERSION_1XX)
+ if (IS_V1(core))
return 0;
- writel(0, core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL);
+ ret = venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, true);
+ if (ret)
+ return ret;
+
ret = clk_prepare_enable(core->core1_clk);
- writel(1, core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL);
+ if (ret)
+ goto err_power_disable;
+
+ if (IS_V4(core))
+ ret = clk_prepare_enable(core->core1_bus_clk);
+
+ if (ret)
+ goto err_unprepare_core1;
+
+ return venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, false);
+err_unprepare_core1:
+ clk_disable_unprepare(core->core1_clk);
+err_power_disable:
+ venus_helper_power_enable(core, VIDC_SESSION_TYPE_ENC, false);
return ret;
}
diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c
index 21e938a28662..459101728d26 100644
--- a/drivers/media/platform/qcom/venus/venc_ctrls.c
+++ b/drivers/media/platform/qcom/venus/venc_ctrls.c
@@ -101,7 +101,7 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl)
case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
ctr->profile.h264 = ctrl->val;
break;
- case V4L2_CID_MPEG_VIDEO_VPX_PROFILE:
+ case V4L2_CID_MPEG_VIDEO_VP8_PROFILE:
ctr->profile.vpx = ctrl->val;
break;
case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
@@ -248,6 +248,11 @@ int venc_ctrl_init(struct venus_inst *inst)
V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES,
0, V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE);
+ v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_VP8_PROFILE,
+ V4L2_MPEG_VIDEO_VP8_PROFILE_3,
+ 0, V4L2_MPEG_VIDEO_VP8_PROFILE_0);
+
v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
V4L2_CID_MPEG_VIDEO_BITRATE, BITRATE_MIN, BITRATE_MAX,
BITRATE_STEP, BITRATE_DEFAULT);
@@ -257,9 +262,6 @@ int venc_ctrl_init(struct venus_inst *inst)
BITRATE_STEP, BITRATE_DEFAULT_PEAK);
v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
- V4L2_CID_MPEG_VIDEO_VPX_PROFILE, 0, 3, 1, 0);
-
- v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,
V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP, 1, 51, 1, 26);
v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops,