diff options
Diffstat (limited to 'drivers/media/platform/qcom')
44 files changed, 6461 insertions, 2152 deletions
diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile index 63c1b1b2943c..0752c46ea37b 100644 --- a/drivers/media/platform/qcom/camss/Makefile +++ b/drivers/media/platform/qcom/camss/Makefile @@ -4,12 +4,18 @@ qcom-camss-objs += \ camss.o \ camss-csid.o \ + camss-csid-4-1.o \ + camss-csid-4-7.o \ + camss-csid-170.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-4-8.o \ + camss-vfe-170.o \ + camss-vfe-gen1.o \ camss-vfe.o \ camss-video.o \ diff --git a/drivers/media/platform/qcom/camss/camss-csid-170.c b/drivers/media/platform/qcom/camss/camss-csid-170.c new file mode 100644 index 000000000000..ac22ff29d2a9 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-csid-170.c @@ -0,0 +1,599 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * camss-csid-4-7.c + * + * Qualcomm MSM Camera Subsystem - CSID (CSI Decoder) Module + * + * Copyright (C) 2020 Linaro Ltd. + */ +#include <linux/completion.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/of.h> + +#include "camss-csid.h" +#include "camss-csid-gen2.h" +#include "camss.h" + +/* The CSID 2 IP-block is different from the others, + * and is of a bare-bones Lite version, with no PIX + * interface support. As a result of that it has an + * alternate register layout. + */ +#define IS_LITE (csid->id == 2 ? 1 : 0) + +#define CSID_HW_VERSION 0x0 +#define HW_VERSION_STEPPING 0 +#define HW_VERSION_REVISION 16 +#define HW_VERSION_GENERATION 28 + +#define CSID_RST_STROBES 0x10 +#define RST_STROBES 0 + +#define CSID_CSI2_RX_IRQ_STATUS 0x20 +#define CSID_CSI2_RX_IRQ_MASK 0x24 +#define CSID_CSI2_RX_IRQ_CLEAR 0x28 + +#define CSID_CSI2_RDIN_IRQ_STATUS(rdi) ((IS_LITE ? 0x30 : 0x40) \ + + 0x10 * (rdi)) +#define CSID_CSI2_RDIN_IRQ_MASK(rdi) ((IS_LITE ? 0x34 : 0x44) \ + + 0x10 * (rdi)) +#define CSID_CSI2_RDIN_IRQ_CLEAR(rdi) ((IS_LITE ? 0x38 : 0x48) \ + + 0x10 * (rdi)) +#define CSID_CSI2_RDIN_IRQ_SET(rdi) ((IS_LITE ? 0x3C : 0x4C) \ + + 0x10 * (rdi)) + +#define CSID_TOP_IRQ_STATUS 0x70 +#define TOP_IRQ_STATUS_RESET_DONE 0 +#define CSID_TOP_IRQ_MASK 0x74 +#define CSID_TOP_IRQ_CLEAR 0x78 +#define CSID_TOP_IRQ_SET 0x7C +#define CSID_IRQ_CMD 0x80 +#define IRQ_CMD_CLEAR 0 +#define IRQ_CMD_SET 4 + +#define CSID_CSI2_RX_CFG0 0x100 +#define CSI2_RX_CFG0_NUM_ACTIVE_LANES 0 +#define CSI2_RX_CFG0_DL0_INPUT_SEL 4 +#define CSI2_RX_CFG0_DL1_INPUT_SEL 8 +#define CSI2_RX_CFG0_DL2_INPUT_SEL 12 +#define CSI2_RX_CFG0_DL3_INPUT_SEL 16 +#define CSI2_RX_CFG0_PHY_NUM_SEL 20 +#define CSI2_RX_CFG0_PHY_TYPE_SEL 24 + +#define CSID_CSI2_RX_CFG1 0x104 +#define CSI2_RX_CFG1_PACKET_ECC_CORRECTION_EN 0 +#define CSI2_RX_CFG1_DE_SCRAMBLE_EN 1 +#define CSI2_RX_CFG1_VC_MODE 2 +#define CSI2_RX_CFG1_COMPLETE_STREAM_EN 4 +#define CSI2_RX_CFG1_COMPLETE_STREAM_FRAME_TIMING 5 +#define CSI2_RX_CFG1_MISR_EN 6 +#define CSI2_RX_CFG1_CGC_MODE 7 +#define CGC_MODE_DYNAMIC_GATING 0 +#define CGC_MODE_ALWAYS_ON 1 + +#define CSID_RDI_CFG0(rdi) ((IS_LITE ? 0x200 : 0x300) \ + + 0x100 * (rdi)) +#define RDI_CFG0_BYTE_CNTR_EN 0 +#define RDI_CFG0_FORMAT_MEASURE_EN 1 +#define RDI_CFG0_TIMESTAMP_EN 2 +#define RDI_CFG0_DROP_H_EN 3 +#define RDI_CFG0_DROP_V_EN 4 +#define RDI_CFG0_CROP_H_EN 5 +#define RDI_CFG0_CROP_V_EN 6 +#define RDI_CFG0_MISR_EN 7 +#define RDI_CFG0_CGC_MODE 8 +#define CGC_MODE_DYNAMIC 0 +#define CGC_MODE_ALWAYS_ON 1 +#define RDI_CFG0_PLAIN_ALIGNMENT 9 +#define PLAIN_ALIGNMENT_LSB 0 +#define PLAIN_ALIGNMENT_MSB 1 +#define RDI_CFG0_PLAIN_FORMAT 10 +#define RDI_CFG0_DECODE_FORMAT 12 +#define RDI_CFG0_DATA_TYPE 16 +#define RDI_CFG0_VIRTUAL_CHANNEL 22 +#define RDI_CFG0_DT_ID 27 +#define RDI_CFG0_EARLY_EOF_EN 29 +#define RDI_CFG0_PACKING_FORMAT 30 +#define RDI_CFG0_ENABLE 31 + +#define CSID_RDI_CFG1(rdi) ((IS_LITE ? 0x204 : 0x304)\ + + 0x100 * (rdi)) +#define RDI_CFG1_TIMESTAMP_STB_SEL 0 + +#define CSID_RDI_CTRL(rdi) ((IS_LITE ? 0x208 : 0x308)\ + + 0x100 * (rdi)) +#define RDI_CTRL_HALT_CMD 0 +#define ALT_CMD_RESUME_AT_FRAME_BOUNDARY 1 +#define RDI_CTRL_HALT_MODE 2 + +#define CSID_RDI_FRM_DROP_PATTERN(rdi) ((IS_LITE ? 0x20C : 0x30C)\ + + 0x100 * (rdi)) +#define CSID_RDI_FRM_DROP_PERIOD(rdi) ((IS_LITE ? 0x210 : 0x310)\ + + 0x100 * (rdi)) +#define CSID_RDI_IRQ_SUBSAMPLE_PATTERN(rdi) ((IS_LITE ? 0x214 : 0x314)\ + + 0x100 * (rdi)) +#define CSID_RDI_IRQ_SUBSAMPLE_PERIOD(rdi) ((IS_LITE ? 0x218 : 0x318)\ + + 0x100 * (rdi)) +#define CSID_RDI_RPP_PIX_DROP_PATTERN(rdi) ((IS_LITE ? 0x224 : 0x324)\ + + 0x100 * (rdi)) +#define CSID_RDI_RPP_PIX_DROP_PERIOD(rdi) ((IS_LITE ? 0x228 : 0x328)\ + + 0x100 * (rdi)) +#define CSID_RDI_RPP_LINE_DROP_PATTERN(rdi) ((IS_LITE ? 0x22C : 0x32C)\ + + 0x100 * (rdi)) +#define CSID_RDI_RPP_LINE_DROP_PERIOD(rdi) ((IS_LITE ? 0x230 : 0x330)\ + + 0x100 * (rdi)) + +#define CSID_TPG_CTRL 0x600 +#define TPG_CTRL_TEST_EN 0 +#define TPG_CTRL_FS_PKT_EN 1 +#define TPG_CTRL_FE_PKT_EN 2 +#define TPG_CTRL_NUM_ACTIVE_LANES 4 +#define TPG_CTRL_CYCLES_BETWEEN_PKTS 8 +#define TPG_CTRL_NUM_TRAIL_BYTES 20 + +#define CSID_TPG_VC_CFG0 0x604 +#define TPG_VC_CFG0_VC_NUM 0 +#define TPG_VC_CFG0_NUM_ACTIVE_SLOTS 8 +#define NUM_ACTIVE_SLOTS_0_ENABLED 0 +#define NUM_ACTIVE_SLOTS_0_1_ENABLED 1 +#define NUM_ACTIVE_SLOTS_0_1_2_ENABLED 2 +#define NUM_ACTIVE_SLOTS_0_1_3_ENABLED 3 +#define TPG_VC_CFG0_LINE_INTERLEAVING_MODE 10 +#define INTELEAVING_MODE_INTERLEAVED 0 +#define INTELEAVING_MODE_ONE_SHOT 1 +#define TPG_VC_CFG0_NUM_FRAMES 16 + +#define CSID_TPG_VC_CFG1 0x608 +#define TPG_VC_CFG1_H_BLANKING_COUNT 0 +#define TPG_VC_CFG1_V_BLANKING_COUNT 12 +#define TPG_VC_CFG1_V_BLANK_FRAME_WIDTH_SEL 24 + +#define CSID_TPG_LFSR_SEED 0x60C + +#define CSID_TPG_DT_n_CFG_0(n) (0x610 + (n) * 0xC) +#define TPG_DT_n_CFG_0_FRAME_HEIGHT 0 +#define TPG_DT_n_CFG_0_FRAME_WIDTH 16 + +#define CSID_TPG_DT_n_CFG_1(n) (0x614 + (n) * 0xC) +#define TPG_DT_n_CFG_1_DATA_TYPE 0 +#define TPG_DT_n_CFG_1_ECC_XOR_MASK 8 +#define TPG_DT_n_CFG_1_CRC_XOR_MASK 16 + +#define CSID_TPG_DT_n_CFG_2(n) (0x618 + (n) * 0xC) +#define TPG_DT_n_CFG_2_PAYLOAD_MODE 0 +#define TPG_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD 4 +#define TPG_DT_n_CFG_2_ENCODE_FORMAT 16 + +#define CSID_TPG_COLOR_BARS_CFG 0x640 +#define TPG_COLOR_BARS_CFG_UNICOLOR_BAR_EN 0 +#define TPG_COLOR_BARS_CFG_UNICOLOR_BAR_SEL 4 +#define TPG_COLOR_BARS_CFG_SPLIT_EN 5 +#define TPG_COLOR_BARS_CFG_ROTATE_PERIOD 8 + +#define CSID_TPG_COLOR_BOX_CFG 0x644 +#define TPG_COLOR_BOX_CFG_MODE 0 +#define TPG_COLOR_BOX_PATTERN_SEL 2 + +static const struct csid_format csid_formats[] = { + { + 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_Y10_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, + }, +}; + +static void csid_configure_stream(struct csid_device *csid, u8 enable) +{ + struct csid_testgen_config *tg = &csid->testgen; + u32 val; + u32 phy_sel = 0; + u8 lane_cnt = csid->phy.lane_cnt; + struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_SRC]; + const struct csid_format *format = csid_get_fmt_entry(csid->formats, csid->nformats, + input_format->code); + + if (!lane_cnt) + lane_cnt = 4; + + if (!tg->enabled) + phy_sel = csid->phy.csiphy_id; + + if (enable) { + u8 vc = 0; /* Virtual Channel 0 */ + u8 dt_id = vc * 4; + + if (tg->enabled) { + /* Config Test Generator */ + vc = 0xa; + + /* configure one DT, infinite frames */ + val = vc << TPG_VC_CFG0_VC_NUM; + val |= INTELEAVING_MODE_ONE_SHOT << TPG_VC_CFG0_LINE_INTERLEAVING_MODE; + val |= 0 << TPG_VC_CFG0_NUM_FRAMES; + writel_relaxed(val, csid->base + CSID_TPG_VC_CFG0); + + val = 0x740 << TPG_VC_CFG1_H_BLANKING_COUNT; + val |= 0x3ff << TPG_VC_CFG1_V_BLANKING_COUNT; + writel_relaxed(val, csid->base + CSID_TPG_VC_CFG1); + + writel_relaxed(0x12345678, csid->base + CSID_TPG_LFSR_SEED); + + val = input_format->height & 0x1fff << TPG_DT_n_CFG_0_FRAME_HEIGHT; + val |= input_format->width & 0x1fff << TPG_DT_n_CFG_0_FRAME_WIDTH; + writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_0(0)); + + val = DATA_TYPE_RAW_10BIT << TPG_DT_n_CFG_1_DATA_TYPE; + writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_1(0)); + + val = tg->mode << TPG_DT_n_CFG_2_PAYLOAD_MODE; + val |= 0xBE << TPG_DT_n_CFG_2_USER_SPECIFIED_PAYLOAD; + val |= format->decode_format << TPG_DT_n_CFG_2_ENCODE_FORMAT; + writel_relaxed(val, csid->base + CSID_TPG_DT_n_CFG_2(0)); + + writel_relaxed(0, csid->base + CSID_TPG_COLOR_BARS_CFG); + + writel_relaxed(0, csid->base + CSID_TPG_COLOR_BOX_CFG); + } + + val = 1 << RDI_CFG0_BYTE_CNTR_EN; + val |= 1 << RDI_CFG0_FORMAT_MEASURE_EN; + val |= 1 << RDI_CFG0_TIMESTAMP_EN; + val |= DECODE_FORMAT_PAYLOAD_ONLY << RDI_CFG0_DECODE_FORMAT; + val |= DATA_TYPE_RAW_10BIT << RDI_CFG0_DATA_TYPE; + val |= vc << RDI_CFG0_VIRTUAL_CHANNEL; + val |= dt_id << RDI_CFG0_DT_ID; + writel_relaxed(val, csid->base + CSID_RDI_CFG0(0)); + + /* CSID_TIMESTAMP_STB_POST_IRQ */ + val = 2 << RDI_CFG1_TIMESTAMP_STB_SEL; + writel_relaxed(val, csid->base + CSID_RDI_CFG1(0)); + + val = 1; + writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(0)); + + val = 0; + writel_relaxed(0, csid->base + CSID_RDI_FRM_DROP_PATTERN(0)); + + val = 1; + writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(0)); + + val = 0; + writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(0)); + + val = 1; + writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PERIOD(0)); + + val = 0; + writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PATTERN(0)); + + val = 1; + writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PERIOD(0)); + + val = 0; + writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PATTERN(0)); + + val = 0; + writel_relaxed(val, csid->base + CSID_RDI_CTRL(0)); + + val = readl_relaxed(csid->base + CSID_RDI_CFG0(0)); + val |= 1 << RDI_CFG0_ENABLE; + writel_relaxed(val, csid->base + CSID_RDI_CFG0(0)); + } + + if (tg->enabled) { + val = enable << TPG_CTRL_TEST_EN; + val |= 1 << TPG_CTRL_FS_PKT_EN; + val |= 1 << TPG_CTRL_FE_PKT_EN; + val |= (lane_cnt - 1) << TPG_CTRL_NUM_ACTIVE_LANES; + val |= 0x64 << TPG_CTRL_CYCLES_BETWEEN_PKTS; + val |= 0xA << TPG_CTRL_NUM_TRAIL_BYTES; + writel_relaxed(val, csid->base + CSID_TPG_CTRL); + } + + val = (lane_cnt - 1) << CSI2_RX_CFG0_NUM_ACTIVE_LANES; + val |= csid->phy.lane_assign << CSI2_RX_CFG0_DL0_INPUT_SEL; + val |= phy_sel << CSI2_RX_CFG0_PHY_NUM_SEL; + writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG0); + + val = 1 << CSI2_RX_CFG1_PACKET_ECC_CORRECTION_EN; + val |= 1 << CSI2_RX_CFG1_MISR_EN; + writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG1); // csi2_vc_mode_shift_val ? + + /* error irqs start at BIT(11) */ + writel_relaxed(~0u, csid->base + CSID_CSI2_RX_IRQ_MASK); + + /* RDI irq */ + writel_relaxed(~0u, csid->base + CSID_TOP_IRQ_MASK); + + val = 1 << RDI_CTRL_HALT_CMD; + writel_relaxed(val, csid->base + CSID_RDI_CTRL(0)); +} + +static int csid_configure_testgen_pattern(struct csid_device *csid, s32 val) +{ + if (val > 0 && val <= csid->testgen.nmodes) + csid->testgen.mode = val; + + return 0; +} + +/* + * csid_hw_version - CSID hardware version query + * @csid: CSID device + * + * Return HW version or error + */ +static u32 csid_hw_version(struct csid_device *csid) +{ + u32 hw_version; + u32 hw_gen; + u32 hw_rev; + u32 hw_step; + + hw_version = readl_relaxed(csid->base + CSID_HW_VERSION); + hw_gen = (hw_version >> HW_VERSION_GENERATION) & 0xF; + hw_rev = (hw_version >> HW_VERSION_REVISION) & 0xFFF; + hw_step = (hw_version >> HW_VERSION_STEPPING) & 0xFFFF; + dev_dbg(csid->camss->dev, "CSID HW Version = %u.%u.%u\n", + hw_gen, hw_rev, hw_step); + + return hw_version; +} + +/* + * csid_isr - CSID module interrupt service routine + * @irq: Interrupt line + * @dev: CSID device + * + * Return IRQ_HANDLED on success + */ +static irqreturn_t csid_isr(int irq, void *dev) +{ + struct csid_device *csid = dev; + u32 val; + u8 reset_done; + + val = readl_relaxed(csid->base + CSID_TOP_IRQ_STATUS); + writel_relaxed(val, csid->base + CSID_TOP_IRQ_CLEAR); + reset_done = val & BIT(TOP_IRQ_STATUS_RESET_DONE); + + val = readl_relaxed(csid->base + CSID_CSI2_RX_IRQ_STATUS); + writel_relaxed(val, csid->base + CSID_CSI2_RX_IRQ_CLEAR); + + val = readl_relaxed(csid->base + CSID_CSI2_RDIN_IRQ_STATUS(0)); + writel_relaxed(val, csid->base + CSID_CSI2_RDIN_IRQ_CLEAR(0)); + + val = 1 << IRQ_CMD_CLEAR; + writel_relaxed(val, csid->base + CSID_IRQ_CMD); + + if (reset_done) + complete(&csid->reset_complete); + + return IRQ_HANDLED; +} + +/* + * csid_reset - Trigger reset on CSID module and wait to complete + * @csid: CSID device + * + * Return 0 on success or a negative error code otherwise + */ +static int csid_reset(struct csid_device *csid) +{ + unsigned long time; + u32 val; + + reinit_completion(&csid->reset_complete); + + writel_relaxed(1, csid->base + CSID_TOP_IRQ_CLEAR); + writel_relaxed(1, csid->base + CSID_IRQ_CMD); + writel_relaxed(1, csid->base + CSID_TOP_IRQ_MASK); + writel_relaxed(1, csid->base + CSID_IRQ_CMD); + + /* preserve registers */ + val = 0x1e << RST_STROBES; + writel_relaxed(val, csid->base + CSID_RST_STROBES); + + time = wait_for_completion_timeout(&csid->reset_complete, + msecs_to_jiffies(CSID_RESET_TIMEOUT_MS)); + if (!time) { + dev_err(csid->camss->dev, "CSID reset timeout\n"); + return -EIO; + } + + return 0; +} + +static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code, + unsigned int match_format_idx, u32 match_code) +{ + 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), + match_format_idx, match_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), + match_format_idx, match_code); + } + default: + if (match_format_idx > 0) + return 0; + + return sink_code; + } +} + +static void csid_subdev_init(struct csid_device *csid) +{ + csid->formats = csid_formats; + csid->nformats = ARRAY_SIZE(csid_formats); + csid->testgen.modes = csid_testgen_modes; + csid->testgen.nmodes = CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN2; +} + +const struct csid_hw_ops csid_ops_170 = { + .configure_stream = csid_configure_stream, + .configure_testgen_pattern = csid_configure_testgen_pattern, + .hw_version = csid_hw_version, + .isr = csid_isr, + .reset = csid_reset, + .src_pad_code = csid_src_pad_code, + .subdev_init = csid_subdev_init, +}; diff --git a/drivers/media/platform/qcom/camss/camss-csid-4-1.c b/drivers/media/platform/qcom/camss/camss-csid-4-1.c new file mode 100644 index 000000000000..d2aec0679dfc --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-csid-4-1.c @@ -0,0 +1,328 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * camss-csid-4-1.c + * + * Qualcomm MSM Camera Subsystem - CSID (CSI Decoder) Module + * + * Copyright (C) 2020 Linaro Ltd. + */ + +#include <linux/completion.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/of.h> + +#include "camss-csid.h" +#include "camss-csid-gen1.h" +#include "camss.h" + +#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_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 (PLAIN_FORMAT_PLAIN8 << 8) +#define CAMSS_CSID_CID_n_CFG_PLAIN_FORMAT_16 (PLAIN_FORMAT_PLAIN16 << 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 0x060 +#define CAMSS_CSID_IRQ_MASK 0x064 +#define CAMSS_CSID_IRQ_STATUS 0x068 +#define CAMSS_CSID_TG_CTRL 0x0a0 +#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_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)) + +static const struct csid_format csid_formats[] = { + { + 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_Y10_1X10, + DATA_TYPE_RAW_10BIT, + DECODE_FORMAT_UNCOMPRESSED_10_BIT, + 10, + 1, + }, +}; + +static void csid_configure_stream(struct csid_device *csid, u8 enable) +{ + struct csid_testgen_config *tg = &csid->testgen; + u32 val; + + if (enable) { + struct v4l2_mbus_framefmt *input_format; + const struct csid_format *format; + u8 vc = 0; /* Virtual Channel 0 */ + u8 cid = vc * 4; /* id of Virtual Channel and Data Type set */ + u8 dt_shift; + + if (tg->enabled) { + /* Config Test Generator */ + u32 num_lines, num_bytes_per_line; + + input_format = &csid->fmt[MSM_CSID_PAD_SRC]; + format = csid_get_fmt_entry(csid->formats, csid->nformats, + input_format->code); + num_bytes_per_line = input_format->width * format->bpp * format->spp / 8; + num_lines = input_format->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); + + /* 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)); + + /* 5:0 data type */ + val = format->data_type; + writel_relaxed(val, csid->base + CAMSS_CSID_TG_DT_n_CGG_1(0)); + + /* 2:0 output test pattern */ + val = tg->mode - 1; + writel_relaxed(val, csid->base + CAMSS_CSID_TG_DT_n_CGG_2(0)); + } else { + struct csid_phy_config *phy = &csid->phy; + + input_format = &csid->fmt[MSM_CSID_PAD_SINK]; + format = csid_get_fmt_entry(csid->formats, csid->nformats, + input_format->code); + + val = phy->lane_cnt - 1; + val |= phy->lane_assign << 4; + + writel_relaxed(val, csid->base + CAMSS_CSID_CORE_CTRL_0); + + val = phy->csiphy_id << 17; + val |= 0x9; + + writel_relaxed(val, csid->base + CAMSS_CSID_CORE_CTRL_1); + } + + /* Config LUT */ + + dt_shift = (cid % 4) * 8; + val = readl_relaxed(csid->base + CAMSS_CSID_CID_LUT_VC_n(vc)); + val &= ~(0xff << dt_shift); + val |= format->data_type << dt_shift; + writel_relaxed(val, csid->base + CAMSS_CSID_CID_LUT_VC_n(vc)); + + val = CAMSS_CSID_CID_n_CFG_ISPIF_EN; + val |= CAMSS_CSID_CID_n_CFG_RDI_EN; + val |= format->decode_format << CAMSS_CSID_CID_n_CFG_DECODE_FORMAT_SHIFT; + val |= CAMSS_CSID_CID_n_CFG_RDI_MODE_RAW_DUMP; + writel_relaxed(val, csid->base + CAMSS_CSID_CID_n_CFG(cid)); + + if (tg->enabled) { + val = CAMSS_CSID_TG_CTRL_ENABLE; + writel_relaxed(val, csid->base + CAMSS_CSID_TG_CTRL); + } + } else { + if (tg->enabled) { + val = CAMSS_CSID_TG_CTRL_DISABLE; + writel_relaxed(val, csid->base + CAMSS_CSID_TG_CTRL); + } + } +} + +static int csid_configure_testgen_pattern(struct csid_device *csid, s32 val) +{ + if (val > 0 && val <= csid->testgen.nmodes) + csid->testgen.mode = val; + + return 0; +} + +static u32 csid_hw_version(struct csid_device *csid) +{ + u32 hw_version = readl_relaxed(csid->base + CAMSS_CSID_HW_VERSION); + + dev_dbg(csid->camss->dev, "CSID HW Version = 0x%08x\n", hw_version); + + return hw_version; +} + +static irqreturn_t csid_isr(int irq, void *dev) +{ + struct csid_device *csid = dev; + u32 value; + + value = readl_relaxed(csid->base + CAMSS_CSID_IRQ_STATUS); + writel_relaxed(value, csid->base + CAMSS_CSID_IRQ_CLEAR_CMD); + + if ((value >> 11) & 0x1) + complete(&csid->reset_complete); + + return IRQ_HANDLED; +} + +static int csid_reset(struct csid_device *csid) +{ + unsigned long time; + + reinit_completion(&csid->reset_complete); + + writel_relaxed(0x7fff, csid->base + CAMSS_CSID_RST_CMD); + + time = wait_for_completion_timeout(&csid->reset_complete, + msecs_to_jiffies(CSID_RESET_TIMEOUT_MS)); + if (!time) { + dev_err(csid->camss->dev, "CSID reset timeout\n"); + return -EIO; + } + + return 0; +} + +static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code, + unsigned int match_format_idx, u32 match_code) +{ + if (match_format_idx > 0) + return 0; + + return sink_code; +} + +static void csid_subdev_init(struct csid_device *csid) +{ + csid->formats = csid_formats; + csid->nformats = ARRAY_SIZE(csid_formats); + csid->testgen.modes = csid_testgen_modes; + csid->testgen.nmodes = CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN1; +} + +const struct csid_hw_ops csid_ops_4_1 = { + .configure_stream = csid_configure_stream, + .configure_testgen_pattern = csid_configure_testgen_pattern, + .hw_version = csid_hw_version, + .isr = csid_isr, + .reset = csid_reset, + .src_pad_code = csid_src_pad_code, + .subdev_init = csid_subdev_init, +}; diff --git a/drivers/media/platform/qcom/camss/camss-csid-4-7.c b/drivers/media/platform/qcom/camss/camss-csid-4-7.c new file mode 100644 index 000000000000..e7436ec6d02b --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-csid-4-7.c @@ -0,0 +1,404 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * camss-csid-4-7.c + * + * Qualcomm MSM Camera Subsystem - CSID (CSI Decoder) Module + * + * Copyright (C) 2020 Linaro Ltd. + */ +#include <linux/completion.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/of.h> + +#include "camss-csid.h" +#include "camss-csid-gen1.h" +#include "camss.h" + +#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 0x010 +#define CAMSS_CSID_CID_LUT_VC_n(n) (0x014 + 0x4 * (n)) +#define CAMSS_CSID_CID_n_CFG(n) (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 (PLAIN_FORMAT_PLAIN8 << 8) +#define CAMSS_CSID_CID_n_CFG_PLAIN_FORMAT_16 (PLAIN_FORMAT_PLAIN16 << 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 0x064 +#define CAMSS_CSID_IRQ_MASK 0x068 +#define CAMSS_CSID_IRQ_STATUS 0x06c +#define CAMSS_CSID_TG_CTRL 0x0a8 +#define CAMSS_CSID_TG_CTRL_DISABLE 0xa06436 +#define CAMSS_CSID_TG_CTRL_ENABLE 0xa06437 +#define CAMSS_CSID_TG_VC_CFG 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) (0x0b4 + 0xc * (n)) +#define CAMSS_CSID_TG_DT_n_CGG_1(n) (0x0b8 + 0xc * (n)) +#define CAMSS_CSID_TG_DT_n_CGG_2(n) (0x0bc + 0xc * (n)) + +static const struct csid_format csid_formats[] = { + { + 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 void csid_configure_stream(struct csid_device *csid, u8 enable) +{ + struct csid_testgen_config *tg = &csid->testgen; + u32 sink_code = csid->fmt[MSM_CSID_PAD_SINK].code; + u32 src_code = csid->fmt[MSM_CSID_PAD_SRC].code; + u32 val; + + if (enable) { + struct v4l2_mbus_framefmt *input_format; + const struct csid_format *format; + u8 vc = 0; /* Virtual Channel 0 */ + u8 cid = vc * 4; /* id of Virtual Channel and Data Type set */ + u8 dt_shift; + + if (tg->enabled) { + /* Config Test Generator */ + u32 num_bytes_per_line, num_lines; + + input_format = &csid->fmt[MSM_CSID_PAD_SRC]; + format = csid_get_fmt_entry(csid->formats, csid->nformats, + input_format->code); + num_bytes_per_line = input_format->width * format->bpp * format->spp / 8; + num_lines = input_format->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); + + /* 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)); + + /* 5:0 data type */ + val = format->data_type; + writel_relaxed(val, csid->base + CAMSS_CSID_TG_DT_n_CGG_1(0)); + + /* 2:0 output test pattern */ + val = tg->mode - 1; + writel_relaxed(val, csid->base + CAMSS_CSID_TG_DT_n_CGG_2(0)); + } else { + struct csid_phy_config *phy = &csid->phy; + + input_format = &csid->fmt[MSM_CSID_PAD_SINK]; + format = csid_get_fmt_entry(csid->formats, csid->nformats, + input_format->code); + + val = phy->lane_cnt - 1; + val |= phy->lane_assign << 4; + + writel_relaxed(val, csid->base + CAMSS_CSID_CORE_CTRL_0); + + val = phy->csiphy_id << 17; + val |= 0x9; + + writel_relaxed(val, csid->base + CAMSS_CSID_CORE_CTRL_1); + } + + /* Config LUT */ + + dt_shift = (cid % 4) * 8; + + val = readl_relaxed(csid->base + CAMSS_CSID_CID_LUT_VC_n(vc)); + val &= ~(0xff << dt_shift); + val |= format->data_type << dt_shift; + writel_relaxed(val, csid->base + CAMSS_CSID_CID_LUT_VC_n(vc)); + + val = CAMSS_CSID_CID_n_CFG_ISPIF_EN; + val |= CAMSS_CSID_CID_n_CFG_RDI_EN; + val |= format->decode_format << CAMSS_CSID_CID_n_CFG_DECODE_FORMAT_SHIFT; + val |= CAMSS_CSID_CID_n_CFG_RDI_MODE_RAW_DUMP; + + 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; + } + + writel_relaxed(val, csid->base + CAMSS_CSID_CID_n_CFG(cid)); + + if (tg->enabled) { + val = CAMSS_CSID_TG_CTRL_ENABLE; + writel_relaxed(val, csid->base + CAMSS_CSID_TG_CTRL); + } + } else { + if (tg->enabled) { + val = CAMSS_CSID_TG_CTRL_DISABLE; + writel_relaxed(val, csid->base + CAMSS_CSID_TG_CTRL); + } + } +} + +static int csid_configure_testgen_pattern(struct csid_device *csid, s32 val) +{ + if (val > 0 && val <= csid->testgen.nmodes) + csid->testgen.mode = val; + + return 0; +} + +static u32 csid_hw_version(struct csid_device *csid) +{ + u32 hw_version = readl_relaxed(csid->base + CAMSS_CSID_HW_VERSION); + + dev_dbg(csid->camss->dev, "CSID HW Version = 0x%08x\n", hw_version); + + return hw_version; +} + +/* + * isr - CSID module interrupt service routine + * @irq: Interrupt line + * @dev: CSID device + * + * Return IRQ_HANDLED on success + */ +static irqreturn_t csid_isr(int irq, void *dev) +{ + struct csid_device *csid = dev; + u32 value; + + value = readl_relaxed(csid->base + CAMSS_CSID_IRQ_STATUS); + writel_relaxed(value, csid->base + CAMSS_CSID_IRQ_CLEAR_CMD); + + if ((value >> 11) & 0x1) + complete(&csid->reset_complete); + + return IRQ_HANDLED; +} + +/* + * csid_reset - Trigger reset on CSID module and wait to complete + * @csid: CSID device + * + * Return 0 on success or a negative error code otherwise + */ +static int csid_reset(struct csid_device *csid) +{ + unsigned long time; + + reinit_completion(&csid->reset_complete); + + writel_relaxed(0x7fff, csid->base + CAMSS_CSID_RST_CMD); + + time = wait_for_completion_timeout(&csid->reset_complete, + msecs_to_jiffies(CSID_RESET_TIMEOUT_MS)); + if (!time) { + dev_err(csid->camss->dev, "CSID reset timeout\n"); + return -EIO; + } + + return 0; +} + +static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code, + unsigned int match_format_idx, u32 match_code) +{ + 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), + match_format_idx, match_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), + match_format_idx, match_code); + } + default: + if (match_format_idx > 0) + return 0; + + return sink_code; + } +} + +static void csid_subdev_init(struct csid_device *csid) +{ + csid->formats = csid_formats; + csid->nformats = ARRAY_SIZE(csid_formats); + csid->testgen.modes = csid_testgen_modes; + csid->testgen.nmodes = CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN1; +} + +const struct csid_hw_ops csid_ops_4_7 = { + .configure_stream = csid_configure_stream, + .configure_testgen_pattern = csid_configure_testgen_pattern, + .hw_version = csid_hw_version, + .isr = csid_isr, + .reset = csid_reset, + .src_pad_code = csid_src_pad_code, + .subdev_init = csid_subdev_init, +}; diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen1.h b/drivers/media/platform/qcom/camss/camss-csid-gen1.h new file mode 100644 index 000000000000..80a2bc6efff6 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-csid-gen1.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * camss-csid-gen1.h + * + * Qualcomm MSM Camera Subsystem - CSID (CSI Decoder) Module Generation 1 + * + * Copyright (C) 2021 Linaro Ltd. + */ +#ifndef QC_MSM_CAMSS_CSID_GEN1_H +#define QC_MSM_CAMSS_CSID_GEN1_H + +#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_DPCM_10_6_10 0x4 +#define DECODE_FORMAT_DPCM_10_8_10 0x5 +#define DECODE_FORMAT_DPCM_12_6_12 0x6 +#define DECODE_FORMAT_DPCM_12_8_12 0x7 +#define DECODE_FORMAT_UNCOMPRESSED_14_BIT 0x8 +#define DECODE_FORMAT_DPCM_14_8_14 0x9 +#define DECODE_FORMAT_DPCM_14_10_14 0xa + +#define PLAIN_FORMAT_PLAIN8 0x0 /* supports DPCM, UNCOMPRESSED_6/8_BIT */ +#define PLAIN_FORMAT_PLAIN16 0x1 /* supports DPCM, UNCOMPRESSED_10/16_BIT */ + +#endif /* QC_MSM_CAMSS_CSID_GEN1_H */ diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen2.h b/drivers/media/platform/qcom/camss/camss-csid-gen2.h new file mode 100644 index 000000000000..3a8ad001b3e8 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-csid-gen2.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * camss-csid-gen1.h + * + * Qualcomm MSM Camera Subsystem - CSID (CSI Decoder) Module Generation 1 + * + * Copyright (C) 2021 Linaro Ltd. + */ +#ifndef QC_MSM_CAMSS_CSID_GEN2_H +#define QC_MSM_CAMSS_CSID_GEN2_H + +#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 0x4 +#define DECODE_FORMAT_UNCOMPRESSED_16_BIT 0x5 +#define DECODE_FORMAT_UNCOMPRESSED_20_BIT 0x6 +#define DECODE_FORMAT_DPCM_10_6_10 0x7 +#define DECODE_FORMAT_DPCM_10_8_10 0x8 +#define DECODE_FORMAT_DPCM_12_6_12 0x9 +#define DECODE_FORMAT_DPCM_12_8_12 0xa +#define DECODE_FORMAT_DPCM_14_8_14 0xb +#define DECODE_FORMAT_DPCM_14_10_14 0xc +#define DECODE_FORMAT_DPCM_12_10_12 0xd +#define DECODE_FORMAT_USER_DEFINED 0xe +#define DECODE_FORMAT_PAYLOAD_ONLY 0xf + +#define ENCODE_FORMAT_RAW_8_BIT 0x1 +#define ENCODE_FORMAT_RAW_10_BIT 0x2 +#define ENCODE_FORMAT_RAW_12_BIT 0x3 +#define ENCODE_FORMAT_RAW_14_BIT 0x4 +#define ENCODE_FORMAT_RAW_16_BIT 0x5 + +#define PLAIN_FORMAT_PLAIN8 0x0 /* supports DPCM, UNCOMPRESSED_6/8_BIT */ +#define PLAIN_FORMAT_PLAIN16 0x1 /* supports DPCM, UNCOMPRESSED_10/16_BIT */ +#define PLAIN_FORMAT_PLAIN32 0x2 /* supports UNCOMPRESSED_20_BIT */ + +#endif /* QC_MSM_CAMSS_CSID_GEN2_H */ diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c index be3fe76f3dc3..cc11fbfdae13 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.c +++ b/drivers/media/platform/qcom/camss/camss-csid.c @@ -22,409 +22,52 @@ #include <media/v4l2-subdev.h> #include "camss-csid.h" +#include "camss-csid-gen1.h" #include "camss.h" #define MSM_CSID_NAME "msm_csid" -#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(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(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(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 -#define DATA_TYPE_RAW_6BIT 0x28 -#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_format { - u32 code; - u8 data_type; - u8 decode_format; - u8 bpp; - u8 spp; /* bus samples per pixel */ -}; - -static const struct csid_format csid_formats_8x16[] = { - { - 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_Y10_1X10, - DATA_TYPE_RAW_10BIT, - DECODE_FORMAT_UNCOMPRESSED_10_BIT, - 10, - 1, - }, -}; - -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, - }, +const char * const csid_testgen_modes[] = { + "Disabled", + "Incrementing", + "Alternating 0x55/0xAA", + "All Zeros 0x00", + "All Ones 0xFF", + "Pseudo-random Data", + "User Specified", + "Complex pattern", + "Color box", + "Color bars", + NULL }; -static u32 csid_find_code(u32 *code, unsigned int n_code, - unsigned int index, u32 req_code) +u32 csid_find_code(u32 *codes, unsigned int ncodes, + unsigned int match_format_idx, u32 match_code) { int i; - if (!req_code && (index >= n_code)) + if (!match_code && (match_format_idx >= ncodes)) return 0; - for (i = 0; i < n_code; i++) - if (req_code) { - if (req_code == code[i]) - return req_code; + for (i = 0; i < ncodes; i++) + if (match_code) { + if (codes[i] == match_code) + return match_code; } else { - if (i == index) - return code[i]; + if (i == match_format_idx) + return codes[i]; } - return code[0]; + return codes[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 || - csid->camss->version == CAMSS_660) { - 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) +const struct csid_format *csid_get_fmt_entry(const struct csid_format *formats, + unsigned int nformats, + u32 code) { unsigned int i; - for (i = 0; i < nformat; i++) + for (i = 0; i < nformats; i++) if (code == formats[i].code) return &formats[i]; @@ -434,41 +77,23 @@ static const struct csid_format *csid_get_fmt_entry( } /* - * csid_isr - CSID module interrupt handler - * @irq: Interrupt line - * @dev: CSID device - * - * Return IRQ_HANDLED on success - */ -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(ver)); - writel_relaxed(value, csid->base + CAMSS_CSID_IRQ_CLEAR_CMD(ver)); - - if ((value >> 11) & 0x1) - complete(&csid->reset_complete); - - return IRQ_HANDLED; -} - -/* * csid_set_clock_rates - Calculate and set clock rates on CSID module * @csiphy: CSID device */ static int csid_set_clock_rates(struct csid_device *csid) { struct device *dev = csid->camss->dev; - u32 pixel_clock; + const struct csid_format *fmt; + s64 link_freq; int i, j; int ret; - ret = camss_get_pixel_clock(&csid->subdev.entity, &pixel_clock); - if (ret) - pixel_clock = 0; + fmt = csid_get_fmt_entry(csid->formats, csid->nformats, + csid->fmt[MSM_CSIPHY_PAD_SINK].code); + link_freq = camss_get_link_freq(&csid->subdev.entity, fmt->bpp, + csid->phy.lane_cnt); + if (link_freq < 0) + link_freq = 0; for (i = 0; i < csid->nclocks; i++) { struct camss_clock *clock = &csid->clock[i]; @@ -477,13 +102,7 @@ static int csid_set_clock_rates(struct csid_device *csid) !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 * f->bpp / - (2 * num_lanes * 4); + u64 min_rate = link_freq / 4; long rate; camss_add_clock_margin(&min_rate); @@ -515,6 +134,8 @@ static int csid_set_clock_rates(struct csid_device *csid) dev_err(dev, "clk set rate failed: %d\n", ret); return ret; } + } else if (clock->nfreqs) { + clk_set_rate(clock->clk, clock->freq[0]); } } @@ -522,31 +143,6 @@ static int csid_set_clock_rates(struct csid_device *csid) } /* - * csid_reset - Trigger reset on CSID module and wait to complete - * @csid: CSID device - * - * Return 0 on success or a negative error code otherwise - */ -static int csid_reset(struct csid_device *csid) -{ - unsigned long time; - - reinit_completion(&csid->reset_complete); - - 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(csid->camss->dev, "CSID reset timeout\n"); - return -EIO; - } - - return 0; -} - -/* * csid_set_power - Power on/off CSID module * @sd: CSID V4L2 subdevice * @on: Requested power state @@ -560,8 +156,6 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) int ret; if (on) { - u32 hw_version; - ret = pm_runtime_get_sync(dev); if (ret < 0) { pm_runtime_put_sync(dev); @@ -590,7 +184,7 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) enable_irq(csid->irq); - ret = csid_reset(csid); + ret = csid->ops->reset(csid); if (ret < 0) { disable_irq(csid->irq); camss_disable_clocks(csid->nclocks, csid->clock); @@ -599,8 +193,7 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) return ret; } - hw_version = readl_relaxed(csid->base + CAMSS_CSID_HW_VERSION); - dev_dbg(dev, "CSID HW Version = 0x%08x\n", hw_version); + csid->ops->hw_version(csid); } else { disable_irq(csid->irq); camss_disable_clocks(csid->nclocks, csid->clock); @@ -623,16 +216,9 @@ static int csid_set_power(struct v4l2_subdev *sd, int on) 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; + int ret; if (enable) { - u8 vc = 0; /* Virtual Channel 0 */ - u8 cid = vc * 4; /* id of Virtual Channel and Data Type set */ - u8 dt, dt_shift, df; - int ret; - ret = v4l2_ctrl_handler_setup(&csid->ctrls); if (ret < 0) { dev_err(csid->camss->dev, @@ -640,116 +226,13 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable) return ret; } - if (!tg->enabled && + if (!csid->testgen.enabled && !media_entity_remote_pad(&csid->pads[MSM_CSID_PAD_SINK])) return -ENOLINK; - - if (tg->enabled) { - /* Config Test Generator */ - struct v4l2_mbus_framefmt *f = - &csid->fmt[MSM_CSID_PAD_SRC]; - 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(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(ver, 0)); - - dt = format->data_type; - - /* 5:0 data type */ - val = dt; - writel_relaxed(val, csid->base + - 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(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; - val |= phy->lane_assign << 4; - - writel_relaxed(val, - csid->base + CAMSS_CSID_CORE_CTRL_0); - - val = phy->csiphy_id << 17; - val |= 0x9; - - 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; - - 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(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 || - csid->camss->version == CAMSS_660) { - 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; - } - } - - 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(ver)); - } - } else { - if (tg->enabled) { - val = CAMSS_CSID_TG_CTRL_DISABLE; - writel_relaxed(val, csid->base + - CAMSS_CSID_TG_CTRL(ver)); - } } + csid->ops->configure_stream(csid, enable); + return 0; } @@ -818,7 +301,7 @@ static void csid_try_format(struct csid_device *csid, *fmt = *__csid_get_format(csid, cfg, MSM_CSID_PAD_SINK, which); - fmt->code = csid_src_pad_code(csid, fmt->code, 0, code); + fmt->code = csid->ops->src_pad_code(csid, fmt->code, 0, code); } else { /* Test generator is enabled, set format on source */ /* pad to allow test generator usage */ @@ -868,7 +351,7 @@ static int csid_enum_mbus_code(struct v4l2_subdev *sd, MSM_CSID_PAD_SINK, code->which); - code->code = csid_src_pad_code(csid, sink_fmt->code, + code->code = csid->ops->src_pad_code(csid, sink_fmt->code, code->index, 0); if (!code->code) return -EINVAL; @@ -1004,15 +487,6 @@ static int csid_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) return csid_set_format(sd, fh ? fh->pad : NULL, &format); } -static const char * const csid_test_pattern_menu[] = { - "Disabled", - "Incrementing", - "Alternating 0x55/0xAA", - "All Zeros 0x00", - "All Ones 0xFF", - "Pseudo-random Data", -}; - /* * csid_set_test_pattern - Set test generator's pattern mode * @csid: CSID device @@ -1030,25 +504,7 @@ static int csid_set_test_pattern(struct csid_device *csid, s32 value) tg->enabled = !!value; - switch (value) { - case 1: - tg->payload_mode = CSID_PAYLOAD_MODE_INCREMENTING; - break; - case 2: - tg->payload_mode = CSID_PAYLOAD_MODE_ALTERNATING_55_AA; - break; - case 3: - tg->payload_mode = CSID_PAYLOAD_MODE_ALL_ZEROES; - break; - case 4: - tg->payload_mode = CSID_PAYLOAD_MODE_ALL_ONES; - break; - case 5: - tg->payload_mode = CSID_PAYLOAD_MODE_RANDOM; - break; - } - - return 0; + return csid->ops->configure_testgen_pattern(csid, value); } /* @@ -1097,26 +553,23 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, csid->id = id; if (camss->version == CAMSS_8x16) { - csid->formats = csid_formats_8x16; - csid->nformats = - ARRAY_SIZE(csid_formats_8x16); + csid->ops = &csid_ops_4_1; } else if (camss->version == CAMSS_8x96 || camss->version == CAMSS_660) { - csid->formats = csid_formats_8x96; - csid->nformats = - ARRAY_SIZE(csid_formats_8x96); + csid->ops = &csid_ops_4_7; + } else if (camss->version == CAMSS_845) { + csid->ops = &csid_ops_170; } else { return -EINVAL; } + csid->ops->subdev_init(csid); /* Memory */ r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]); csid->base = devm_ioremap_resource(dev, r); - if (IS_ERR(csid->base)) { - dev_err(dev, "could not map memory\n"); + if (IS_ERR(csid->base)) return PTR_ERR(csid->base); - } /* Interrupt */ @@ -1130,8 +583,8 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, csid->irq = r->start; snprintf(csid->irq_name, sizeof(csid->irq_name), "%s_%s%d", dev_name(dev), MSM_CSID_NAME, csid->id); - ret = devm_request_irq(dev, csid->irq, csid_isr, - IRQF_TRIGGER_RISING, csid->irq_name, csid); + ret = devm_request_irq(dev, csid->irq, csid->ops->isr, + IRQF_TRIGGER_RISING, csid->irq_name, csid); if (ret < 0) { dev_err(dev, "request_irq failed: %d\n", ret); return ret; @@ -1341,8 +794,8 @@ int msm_csid_register_entity(struct csid_device *csid, csid->testgen_mode = v4l2_ctrl_new_std_menu_items(&csid->ctrls, &csid_ctrl_ops, V4L2_CID_TEST_PATTERN, - ARRAY_SIZE(csid_test_pattern_menu) - 1, 0, 0, - csid_test_pattern_menu); + csid->testgen.nmodes, 0, 0, + csid->testgen.modes); if (csid->ctrls.error) { dev_err(dev, "Failed to init ctrl: %d\n", csid->ctrls.error); diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h index 1824b3745e10..814ebc7c29d6 100644 --- a/drivers/media/platform/qcom/camss/camss-csid.h +++ b/drivers/media/platform/qcom/camss/camss-csid.h @@ -11,6 +11,7 @@ #define QC_MSM_CAMSS_CSID_H #include <linux/clk.h> +#include <linux/interrupt.h> #include <media/media-entity.h> #include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> @@ -21,18 +22,59 @@ #define MSM_CSID_PAD_SRC 1 #define MSM_CSID_PADS_NUM 2 -enum csid_payload_mode { - CSID_PAYLOAD_MODE_INCREMENTING = 0, - CSID_PAYLOAD_MODE_ALTERNATING_55_AA = 1, - CSID_PAYLOAD_MODE_ALL_ZEROES = 2, - CSID_PAYLOAD_MODE_ALL_ONES = 3, - CSID_PAYLOAD_MODE_RANDOM = 4, - CSID_PAYLOAD_MODE_USER_SPECIFIED = 5, +#define DATA_TYPE_EMBEDDED_DATA_8BIT 0x12 +#define DATA_TYPE_YUV420_8BIT 0x18 +#define DATA_TYPE_YUV420_10BIT 0x19 +#define DATA_TYPE_YUV420_8BIT_LEGACY 0x1a +#define DATA_TYPE_YUV420_8BIT_SHIFTED 0x1c /* Chroma Shifted Pixel Sampling */ +#define DATA_TYPE_YUV420_10BIT_SHIFTED 0x1d /* Chroma Shifted Pixel Sampling */ +#define DATA_TYPE_YUV422_8BIT 0x1e +#define DATA_TYPE_YUV422_10BIT 0x1f +#define DATA_TYPE_RGB444 0x20 +#define DATA_TYPE_RGB555 0x21 +#define DATA_TYPE_RGB565 0x22 +#define DATA_TYPE_RGB666 0x23 +#define DATA_TYPE_RGB888 0x24 +#define DATA_TYPE_RAW_24BIT 0x27 +#define DATA_TYPE_RAW_6BIT 0x28 +#define DATA_TYPE_RAW_7BIT 0x29 +#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 DATA_TYPE_RAW_16BIT 0x2e +#define DATA_TYPE_RAW_20BIT 0x2f + +#define CSID_RESET_TIMEOUT_MS 500 + +enum csid_testgen_mode { + CSID_PAYLOAD_MODE_DISABLED = 0, + CSID_PAYLOAD_MODE_INCREMENTING = 1, + CSID_PAYLOAD_MODE_ALTERNATING_55_AA = 2, + CSID_PAYLOAD_MODE_ALL_ZEROES = 3, + CSID_PAYLOAD_MODE_ALL_ONES = 4, + CSID_PAYLOAD_MODE_RANDOM = 5, + CSID_PAYLOAD_MODE_USER_SPECIFIED = 6, + CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN1 = 6, /* excluding disabled */ + CSID_PAYLOAD_MODE_COMPLEX_PATTERN = 7, + CSID_PAYLOAD_MODE_COLOR_BOX = 8, + CSID_PAYLOAD_MODE_COLOR_BARS = 9, + CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN2 = 9, /* excluding disabled */ +}; + +struct csid_format { + u32 code; + u8 data_type; + u8 decode_format; + u8 bpp; + u8 spp; /* bus samples per pixel */ }; struct csid_testgen_config { + enum csid_testgen_mode mode; + const char * const*modes; + u8 nmodes; u8 enabled; - enum csid_payload_mode payload_mode; }; struct csid_phy_config { @@ -41,6 +83,65 @@ struct csid_phy_config { u32 lane_assign; }; +struct csid_device; + +struct csid_hw_ops { + /* + * configure_stream - Configures and starts CSID input stream + * @csid: CSID device + */ + void (*configure_stream)(struct csid_device *csid, u8 enable); + + /* + * configure_testgen_pattern - Validates and configures output pattern mode + * of test pattern generator + * @csid: CSID device + */ + int (*configure_testgen_pattern)(struct csid_device *csid, s32 val); + + /* + * hw_version - Read hardware version register from hardware + * @csid: CSID device + */ + u32 (*hw_version)(struct csid_device *csid); + + /* + * isr - CSID module interrupt service routine + * @irq: Interrupt line + * @dev: CSID device + * + * Return IRQ_HANDLED on success + */ + irqreturn_t (*isr)(int irq, void *dev); + + /* + * reset - Trigger reset on CSID module and wait to complete + * @csid: CSID device + * + * Return 0 on success or a negative error code otherwise + */ + int (*reset)(struct csid_device *csid); + + /* + * src_pad_code - Pick an output/src format based on the input/sink format + * @csid: CSID device + * @sink_code: The sink format of the input + * @match_format_idx: Request preferred index, as defined by subdevice csid_format. + * Set @match_code to 0 if used. + * @match_code: Request preferred code, set @match_format_idx to 0 if used + * + * Return 0 on failure or src format code otherwise + */ + u32 (*src_pad_code)(struct csid_device *csid, u32 sink_code, + unsigned int match_format_idx, u32 match_code); + + /* + * subdev_init - Initialize CSID device according for hardware revision + * @csid: CSID device + */ + void (*subdev_init)(struct csid_device *csid); +}; + struct csid_device { struct camss *camss; u8 id; @@ -60,10 +161,36 @@ struct csid_device { struct v4l2_ctrl *testgen_mode; const struct csid_format *formats; unsigned int nformats; + const struct csid_hw_ops *ops; }; struct resources; +/* + * csid_find_code - Find a format code in an array using array index or format code + * @codes: Array of format codes + * @ncodes: Length of @code array + * @req_format_idx: Request preferred index, as defined by subdevice csid_format. + * Set @match_code to 0 if used. + * @match_code: Request preferred code, set @req_format_idx to 0 if used + * + * Return 0 on failure or format code otherwise + */ +u32 csid_find_code(u32 *codes, unsigned int ncode, + unsigned int match_format_idx, u32 match_code); + +/* + * csid_get_fmt_entry - Find csid_format entry with matching format code + * @formats: Array of format csid_format entries + * @nformats: Length of @nformats array + * @code: Desired format code + * + * Return formats[0] on failure to find code + */ +const struct csid_format *csid_get_fmt_entry(const struct csid_format *formats, + unsigned int nformats, + u32 code); + int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, const struct resources *res, u8 id); @@ -74,4 +201,11 @@ void msm_csid_unregister_entity(struct csid_device *csid); void msm_csid_get_csid_id(struct media_entity *entity, u8 *id); +extern const char * const csid_testgen_modes[]; + +extern const struct csid_hw_ops csid_ops_4_1; +extern const struct csid_hw_ops csid_ops_4_7; +extern const struct csid_hw_ops csid_ops_170; + + #endif /* QC_MSM_CAMSS_CSID_H */ 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 index 12bce391d71f..30b454c369ab 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy-2ph-1-0.c @@ -51,16 +51,13 @@ static void csiphy_reset(struct csiphy_device *csiphy) * * 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. + * is calculated based on the CSI2 transmitter link frequency. * - * Return settle count value or 0 if the CSI2 pixel clock - * frequency is not available + * Return settle count value or 0 if the CSI2 link frequency + * is not available */ -static u8 csiphy_settle_cnt_calc(u32 pixel_clock, u8 bpp, u8 num_lanes, - u32 timer_clk_rate) +static u8 csiphy_settle_cnt_calc(s64 link_freq, u32 timer_clk_rate) { - u32 mipi_clock; /* Hz */ u32 ui; /* ps */ u32 timer_period; /* ps */ u32 t_hs_prepare_max; /* ps */ @@ -68,8 +65,10 @@ static u8 csiphy_settle_cnt_calc(u32 pixel_clock, u8 bpp, u8 num_lanes, u32 t_hs_settle; /* ps */ u8 settle_cnt; - mipi_clock = pixel_clock * bpp / (2 * num_lanes); - ui = div_u64(1000000000000LL, mipi_clock); + if (link_freq <= 0) + return 0; + + ui = div_u64(1000000000000LL, link_freq); ui /= 2; t_hs_prepare_max = 85000 + 6 * ui; t_hs_prepare_zero_min = 145000 + 10 * ui; @@ -83,15 +82,14 @@ static u8 csiphy_settle_cnt_calc(u32 pixel_clock, u8 bpp, u8 num_lanes, static void csiphy_lanes_enable(struct csiphy_device *csiphy, struct csiphy_config *cfg, - u32 pixel_clock, u8 bpp, u8 lane_mask) + s64 link_freq, 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); + settle_cnt = csiphy_settle_cnt_calc(link_freq, csiphy->timer_clk_rate); writel_relaxed(0x1, csiphy->base + CAMSS_CSI_PHY_GLBL_T_INIT_CFG0); 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 index 97cb9de85031..e318c822ab04 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy-3ph-1-0.c @@ -47,6 +47,105 @@ #define CSIPHY_3PH_CMN_CSI_COMMON_CTRL6_SHOW_REV_ID BIT(1) #define CSIPHY_3PH_CMN_CSI_COMMON_STATUSn(n) (0x8b0 + 0x4 * (n)) +#define CSIPHY_DEFAULT_PARAMS 0 +#define CSIPHY_LANE_ENABLE 1 +#define CSIPHY_SETTLE_CNT_LOWER_BYTE 2 +#define CSIPHY_SETTLE_CNT_HIGHER_BYTE 3 +#define CSIPHY_DNP_PARAMS 4 +#define CSIPHY_2PH_REGS 5 +#define CSIPHY_3PH_REGS 6 + +struct csiphy_reg_t { + s32 reg_addr; + s32 reg_data; + s32 delay; + u32 csiphy_param_type; +}; + +static const struct +csiphy_reg_t lane_regs_sdm845[5][14] = { + { + {0x0004, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x002C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0034, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x001C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0014, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0028, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x003C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0000, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0008, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x000c, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0010, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0038, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0060, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0064, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, + { + {0x0704, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x072C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0734, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x071C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0714, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0728, 0x04, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x073C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0700, 0x80, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0708, 0x14, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x070C, 0xA5, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0710, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0738, 0x1F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0760, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0764, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, + { + {0x0204, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x022C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0234, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x021C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0214, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0228, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x023C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0200, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0208, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x020C, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0210, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0238, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0260, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0264, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, + { + {0x0404, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x042C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0434, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x041C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0414, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0428, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x043C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0400, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0408, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x040C, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0410, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0438, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0460, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0464, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, + { + {0x0604, 0x0C, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x062C, 0x01, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0634, 0x0F, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x061C, 0x0A, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0614, 0x60, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0628, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x063C, 0xB8, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0600, 0x91, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0608, 0x00, 0x00, CSIPHY_SETTLE_CNT_LOWER_BYTE}, + {0x060C, 0x00, 0x00, CSIPHY_DNP_PARAMS}, + {0x0610, 0x52, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0638, 0xFE, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0660, 0x00, 0x00, CSIPHY_DEFAULT_PARAMS}, + {0x0664, 0x7F, 0x00, CSIPHY_DEFAULT_PARAMS}, + }, +}; + static void csiphy_hw_version_read(struct csiphy_device *csiphy, struct device *dev) { @@ -107,24 +206,23 @@ static irqreturn_t csiphy_isr(int irq, void *dev) * * 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. + * is calculated based on the CSI2 transmitter link frequency. * - * Return settle count value or 0 if the CSI2 pixel clock - * frequency is not available + * Return settle count value or 0 if the CSI2 link frequency + * is not available */ -static u8 csiphy_settle_cnt_calc(u32 pixel_clock, u8 bpp, u8 num_lanes, - u32 timer_clk_rate) +static u8 csiphy_settle_cnt_calc(s64 link_freq, 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); + if (link_freq <= 0) + return 0; + + ui = div_u64(1000000000000LL, link_freq); ui /= 2; t_hs_prepare_max = 85000 + 6 * ui; t_hs_settle = t_hs_prepare_max; @@ -135,26 +233,13 @@ static u8 csiphy_settle_cnt_calc(u32 pixel_clock, u8 bpp, u8 num_lanes, return settle_cnt; } -static void csiphy_lanes_enable(struct csiphy_device *csiphy, - struct csiphy_config *cfg, - u32 pixel_clock, u8 bpp, u8 lane_mask) +static void csiphy_gen1_config_lanes(struct csiphy_device *csiphy, + struct csiphy_config *cfg, + u8 settle_cnt) { 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)); + int i, l = 0; + u8 val; for (i = 0; i <= c->num_data; i++) { if (i == c->num_data) @@ -208,6 +293,64 @@ static void csiphy_lanes_enable(struct csiphy_device *csiphy, val = CSIPHY_3PH_LNn_MISC1_IS_CLKLANE; writel_relaxed(val, csiphy->base + CSIPHY_3PH_LNn_MISC1(l)); +} + +static void csiphy_gen2_config_lanes(struct csiphy_device *csiphy, + u8 settle_cnt) +{ + int i, l; + u32 val; + + for (l = 0; l < 5; l++) { + for (i = 0; i < 14; i++) { + const struct csiphy_reg_t *r = &lane_regs_sdm845[l][i]; + + switch (r->csiphy_param_type) { + case CSIPHY_SETTLE_CNT_LOWER_BYTE: + val = settle_cnt & 0xff; + break; + case CSIPHY_DNP_PARAMS: + continue; + default: + val = r->reg_data; + break; + } + writel_relaxed(val, csiphy->base + r->reg_addr); + } + } +} + +static void csiphy_lanes_enable(struct csiphy_device *csiphy, + struct csiphy_config *cfg, + s64 link_freq, u8 lane_mask) +{ + struct csiphy_lanes_cfg *c = &cfg->csi2->lane_cfg; + u8 settle_cnt; + u8 val; + int i; + + settle_cnt = csiphy_settle_cnt_calc(link_freq, 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)); + + val = 0x02; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(7)); + + val = 0x00; + writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(0)); + + if (csiphy->camss->version == CAMSS_8x16 || + csiphy->camss->version == CAMSS_8x96) + csiphy_gen1_config_lanes(csiphy, cfg, settle_cnt); + else if (csiphy->camss->version == CAMSS_845) + csiphy_gen2_config_lanes(csiphy, settle_cnt); val = 0xff; writel_relaxed(val, csiphy->base + CSIPHY_3PH_CMN_CSI_COMMON_CTRLn(11)); diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.c b/drivers/media/platform/qcom/camss/camss-csiphy.c index 509c9a59c09c..b3c3bf19e522 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.c +++ b/drivers/media/platform/qcom/camss/camss-csiphy.c @@ -73,6 +73,30 @@ static const struct csiphy_format csiphy_formats_8x96[] = { { MEDIA_BUS_FMT_Y10_1X10, 10 }, }; +static const struct csiphy_format csiphy_formats_sdm845[] = { + { 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 @@ -102,23 +126,23 @@ static u8 csiphy_get_bpp(const struct csiphy_format *formats, static int csiphy_set_clock_rates(struct csiphy_device *csiphy) { struct device *dev = csiphy->camss->dev; - u32 pixel_clock; + s64 link_freq; int i, j; int ret; - ret = camss_get_pixel_clock(&csiphy->subdev.entity, &pixel_clock); - if (ret) - pixel_clock = 0; + 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; + + link_freq = camss_get_link_freq(&csiphy->subdev.entity, bpp, num_lanes); + if (link_freq < 0) + link_freq = 0; for (i = 0; i < csiphy->nclocks; i++) { struct camss_clock *clock = &csiphy->clock[i]; if (csiphy->rate_set[i]) { - 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); + u64 min_rate = link_freq / 4; long round_rate; camss_add_clock_margin(&min_rate); @@ -238,37 +262,37 @@ static u8 csiphy_get_lane_mask(struct csiphy_lanes_cfg *lane_cfg) static int csiphy_stream_on(struct csiphy_device *csiphy) { struct csiphy_config *cfg = &csiphy->cfg; - u32 pixel_clock; + s64 link_freq; u8 lane_mask = csiphy_get_lane_mask(&cfg->csi2->lane_cfg); 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; u8 val; - int ret; - 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) { + link_freq = camss_get_link_freq(&csiphy->subdev.entity, bpp, num_lanes); + + if (link_freq < 0) { dev_err(csiphy->camss->dev, - "Got pixel clock == 0, cannot continue\n"); + "Cannot get CSI2 transmitter's link frequency\n"); return -EINVAL; } - val = readl_relaxed(csiphy->base_clk_mux); - if (cfg->combo_mode && (lane_mask & 0x18) == 0x18) { - val &= ~0xf0; - val |= cfg->csid_id << 4; - } else { - val &= ~0xf; - val |= cfg->csid_id; + if (csiphy->base_clk_mux) { + val = readl_relaxed(csiphy->base_clk_mux); + if (cfg->combo_mode && (lane_mask & 0x18) == 0x18) { + val &= ~0xf0; + val |= cfg->csid_id << 4; + } else { + val &= ~0xf; + val |= cfg->csid_id; + } + writel_relaxed(val, csiphy->base_clk_mux); + + /* Enforce reg write ordering between clk mux & lane enabling */ + wmb(); } - writel_relaxed(val, csiphy->base_clk_mux); - wmb(); - csiphy->ops->lanes_enable(csiphy, cfg, pixel_clock, bpp, lane_mask); + csiphy->ops->lanes_enable(csiphy, cfg, link_freq, lane_mask); return 0; } @@ -557,6 +581,10 @@ int msm_csiphy_subdev_init(struct camss *camss, csiphy->ops = &csiphy_ops_3ph_1_0; csiphy->formats = csiphy_formats_8x96; csiphy->nformats = ARRAY_SIZE(csiphy_formats_8x96); + } else if (camss->version == CAMSS_845) { + csiphy->ops = &csiphy_ops_3ph_1_0; + csiphy->formats = csiphy_formats_sdm845; + csiphy->nformats = ARRAY_SIZE(csiphy_formats_sdm845); } else { return -EINVAL; } @@ -565,16 +593,18 @@ int msm_csiphy_subdev_init(struct camss *camss, r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]); csiphy->base = devm_ioremap_resource(dev, r); - if (IS_ERR(csiphy->base)) { - dev_err(dev, "could not map memory\n"); + if (IS_ERR(csiphy->base)) return PTR_ERR(csiphy->base); - } - r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[1]); - csiphy->base_clk_mux = devm_ioremap_resource(dev, r); - if (IS_ERR(csiphy->base_clk_mux)) { - dev_err(dev, "could not map memory\n"); - return PTR_ERR(csiphy->base_clk_mux); + if (camss->version == CAMSS_8x16 || + camss->version == CAMSS_8x96) { + r = platform_get_resource_byname(pdev, IORESOURCE_MEM, + res->reg[1]); + csiphy->base_clk_mux = devm_ioremap_resource(dev, r); + if (IS_ERR(csiphy->base_clk_mux)) + return PTR_ERR(csiphy->base_clk_mux); + } else { + csiphy->base_clk_mux = NULL; } /* Interrupt */ diff --git a/drivers/media/platform/qcom/camss/camss-csiphy.h b/drivers/media/platform/qcom/camss/camss-csiphy.h index f7967ef836dc..d71b8bc6ec00 100644 --- a/drivers/media/platform/qcom/camss/camss-csiphy.h +++ b/drivers/media/platform/qcom/camss/camss-csiphy.h @@ -50,7 +50,7 @@ struct csiphy_hw_ops { 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); + s64 link_freq, u8 lane_mask); void (*lanes_disable)(struct csiphy_device *csiphy, struct csiphy_config *cfg); irqreturn_t (*isr)(int irq, void *dev); diff --git a/drivers/media/platform/qcom/camss/camss-ispif.c b/drivers/media/platform/qcom/camss/camss-ispif.c index adeb92808998..37611c8861da 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.c +++ b/drivers/media/platform/qcom/camss/camss-ispif.c @@ -161,6 +161,7 @@ static const u32 ispif_formats_8x96[] = { static irqreturn_t ispif_isr_8x96(int irq, void *dev) { struct ispif_device *ispif = dev; + struct camss *camss = ispif->camss; u32 value0, value1, value2, value3, value4, value5; value0 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_0(0)); @@ -186,34 +187,34 @@ static irqreturn_t ispif_isr_8x96(int irq, void *dev) complete(&ispif->reset_complete[1]); if (unlikely(value0 & ISPIF_VFE_m_IRQ_STATUS_0_PIX0_OVERFLOW)) - dev_err_ratelimited(to_device(ispif), "VFE0 pix0 overflow\n"); + dev_err_ratelimited(camss->dev, "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"); + dev_err_ratelimited(camss->dev, "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"); + dev_err_ratelimited(camss->dev, "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"); + dev_err_ratelimited(camss->dev, "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"); + dev_err_ratelimited(camss->dev, "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"); + dev_err_ratelimited(camss->dev, "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"); + dev_err_ratelimited(camss->dev, "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"); + dev_err_ratelimited(camss->dev, "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"); + dev_err_ratelimited(camss->dev, "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"); + dev_err_ratelimited(camss->dev, "VFE1 rdi2 overflow\n"); return IRQ_HANDLED; } @@ -228,6 +229,7 @@ static irqreturn_t ispif_isr_8x96(int irq, void *dev) static irqreturn_t ispif_isr_8x16(int irq, void *dev) { struct ispif_device *ispif = dev; + struct camss *camss = ispif->camss; u32 value0, value1, value2; value0 = readl_relaxed(ispif->base + ISPIF_VFE_m_IRQ_STATUS_0(0)); @@ -244,30 +246,32 @@ static irqreturn_t ispif_isr_8x16(int irq, void *dev) complete(&ispif->reset_complete[0]); if (unlikely(value0 & ISPIF_VFE_m_IRQ_STATUS_0_PIX0_OVERFLOW)) - dev_err_ratelimited(to_device(ispif), "VFE0 pix0 overflow\n"); + dev_err_ratelimited(camss->dev, "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"); + dev_err_ratelimited(camss->dev, "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"); + dev_err_ratelimited(camss->dev, "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"); + dev_err_ratelimited(camss->dev, "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"); + dev_err_ratelimited(camss->dev, "VFE0 rdi2 overflow\n"); return IRQ_HANDLED; } static int ispif_vfe_reset(struct ispif_device *ispif, u8 vfe_id) { + struct camss *camss = ispif->camss; + unsigned long time; u32 val; - if (vfe_id > (to_camss(ispif)->vfe_num - 1)) { - dev_err(to_device(ispif), + if (vfe_id > (camss->vfe_num - 1)) { + dev_err(camss->dev, "Error: asked reset for invalid VFE%d\n", vfe_id); return -ENOENT; } @@ -300,7 +304,7 @@ static int ispif_vfe_reset(struct ispif_device *ispif, u8 vfe_id) time = wait_for_completion_timeout(&ispif->reset_complete[vfe_id], msecs_to_jiffies(ISPIF_RESET_TIMEOUT_MS)); if (!time) { - dev_err(to_device(ispif), + dev_err(camss->dev, "ISPIF for VFE%d reset timeout\n", vfe_id); return -EIO; } @@ -316,30 +320,31 @@ static int ispif_vfe_reset(struct ispif_device *ispif, u8 vfe_id) */ static int ispif_reset(struct ispif_device *ispif, u8 vfe_id) { + struct camss *camss = ispif->camss; int ret; - ret = camss_pm_domain_on(to_camss(ispif), PM_DOMAIN_VFE0); + ret = camss_pm_domain_on(camss, PM_DOMAIN_VFE0); if (ret < 0) return ret; - ret = camss_pm_domain_on(to_camss(ispif), PM_DOMAIN_VFE1); + ret = camss_pm_domain_on(camss, PM_DOMAIN_VFE1); if (ret < 0) return ret; ret = camss_enable_clocks(ispif->nclocks_for_reset, ispif->clock_for_reset, - to_device(ispif)); + camss->dev); if (ret < 0) return ret; ret = ispif_vfe_reset(ispif, vfe_id); if (ret) - dev_dbg(to_device(ispif), "ISPIF Reset failed\n"); + dev_dbg(camss->dev, "ISPIF Reset failed\n"); camss_disable_clocks(ispif->nclocks_for_reset, ispif->clock_for_reset); - camss_pm_domain_off(to_camss(ispif), PM_DOMAIN_VFE0); - camss_pm_domain_off(to_camss(ispif), PM_DOMAIN_VFE1); + camss_pm_domain_off(camss, PM_DOMAIN_VFE0); + camss_pm_domain_off(camss, PM_DOMAIN_VFE1); return ret; } @@ -355,7 +360,7 @@ static int ispif_set_power(struct v4l2_subdev *sd, int on) { struct ispif_line *line = v4l2_get_subdevdata(sd); struct ispif_device *ispif = line->ispif; - struct device *dev = to_device(ispif); + struct device *dev = ispif->camss->dev; int ret = 0; mutex_lock(&ispif->power_lock); @@ -505,7 +510,7 @@ static int ispif_validate_intf_status(struct ispif_device *ispif, } if ((val & 0xf) != 0xf) { - dev_err(to_device(ispif), "%s: ispif is busy: 0x%x\n", + dev_err(ispif->camss->dev, "%s: ispif is busy: 0x%x\n", __func__, val); ret = -EBUSY; } @@ -552,7 +557,7 @@ static int ispif_wait_for_stop(struct ispif_device *ispif, ISPIF_TIMEOUT_SLEEP_US, ISPIF_TIMEOUT_ALL_US); if (ret < 0) - dev_err(to_device(ispif), "%s: ispif stop timeout\n", + dev_err(ispif->camss->dev, "%s: ispif stop timeout\n", __func__); return ret; @@ -800,6 +805,7 @@ static int ispif_set_stream(struct v4l2_subdev *sd, int enable) { struct ispif_line *line = v4l2_get_subdevdata(sd); struct ispif_device *ispif = line->ispif; + struct camss *camss = ispif->camss; enum ispif_intf intf = line->interface; u8 csid = line->csid_id; u8 vfe = line->vfe_id; @@ -825,8 +831,8 @@ 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 || - to_camss(ispif)->version == CAMSS_660) + if (camss->version == CAMSS_8x96 || + camss->version == CAMSS_660) ispif_config_pack(ispif, line->fmt[MSM_ISPIF_PAD_SINK].code, intf, cid, vfe, 1); @@ -843,8 +849,8 @@ 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 || - to_camss(ispif)->version == CAMSS_660) + if (camss->version == CAMSS_8x96 || + camss->version == CAMSS_660) ispif_config_pack(ispif, line->fmt[MSM_ISPIF_PAD_SINK].code, intf, cid, vfe, 0); @@ -1088,26 +1094,32 @@ static int ispif_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) * * Return 0 on success or a negative error code otherwise */ -int msm_ispif_subdev_init(struct ispif_device *ispif, +int msm_ispif_subdev_init(struct camss *camss, const struct resources_ispif *res) { - struct device *dev = to_device(ispif); + struct device *dev = camss->dev; + struct ispif_device *ispif = camss->ispif; struct platform_device *pdev = to_platform_device(dev); struct resource *r; int i; int ret; + if (!camss->ispif) + return 0; + + ispif->camss = camss; + /* Number of ISPIF lines - same as number of CSID hardware modules */ - if (to_camss(ispif)->version == CAMSS_8x16) + if (camss->version == CAMSS_8x16) ispif->line_num = 2; - else if (to_camss(ispif)->version == CAMSS_8x96 || - to_camss(ispif)->version == CAMSS_660) + else if (camss->version == CAMSS_8x96 || + camss->version == CAMSS_660) ispif->line_num = 4; else return -EINVAL; - ispif->line = devm_kcalloc(dev, ispif->line_num, sizeof(*ispif->line), - GFP_KERNEL); + ispif->line = devm_kcalloc(dev, ispif->line_num, + sizeof(*ispif->line), GFP_KERNEL); if (!ispif->line) return -ENOMEM; @@ -1115,12 +1127,12 @@ int msm_ispif_subdev_init(struct ispif_device *ispif, ispif->line[i].ispif = ispif; ispif->line[i].id = i; - if (to_camss(ispif)->version == CAMSS_8x16) { + if (camss->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 || - to_camss(ispif)->version == CAMSS_660) { + } else if (camss->version == CAMSS_8x96 || + camss->version == CAMSS_660) { ispif->line[i].formats = ispif_formats_8x96; ispif->line[i].nformats = ARRAY_SIZE(ispif_formats_8x96); @@ -1133,17 +1145,13 @@ int msm_ispif_subdev_init(struct ispif_device *ispif, r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]); ispif->base = devm_ioremap_resource(dev, r); - if (IS_ERR(ispif->base)) { - dev_err(dev, "could not map memory\n"); + if (IS_ERR(ispif->base)) return PTR_ERR(ispif->base); - } r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[1]); ispif->base_clk_mux = devm_ioremap_resource(dev, r); - if (IS_ERR(ispif->base_clk_mux)) { - dev_err(dev, "could not map memory\n"); + if (IS_ERR(ispif->base_clk_mux)) return PTR_ERR(ispif->base_clk_mux); - } /* Interrupt */ @@ -1157,15 +1165,16 @@ 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); - if (to_camss(ispif)->version == CAMSS_8x16) + if (camss->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 || - to_camss(ispif)->version == CAMSS_660) + else if (camss->version == CAMSS_8x96 || + camss->version == CAMSS_660) 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; @@ -1331,10 +1340,15 @@ static const struct media_entity_operations ispif_media_ops = { int msm_ispif_register_entities(struct ispif_device *ispif, struct v4l2_device *v4l2_dev) { - struct device *dev = to_device(ispif); + struct camss *camss; int ret; int i; + if (!ispif) + return 0; + + camss = ispif->camss; + for (i = 0; i < ispif->line_num; i++) { struct v4l2_subdev *sd = &ispif->line[i].subdev; struct media_pad *pads = ispif->line[i].pads; @@ -1348,7 +1362,7 @@ int msm_ispif_register_entities(struct ispif_device *ispif, ret = ispif_init_formats(sd, NULL); if (ret < 0) { - dev_err(dev, "Failed to init format: %d\n", ret); + dev_err(camss->dev, "Failed to init format: %d\n", ret); goto error; } @@ -1360,13 +1374,15 @@ int msm_ispif_register_entities(struct ispif_device *ispif, ret = media_entity_pads_init(&sd->entity, MSM_ISPIF_PADS_NUM, pads); if (ret < 0) { - dev_err(dev, "Failed to init media entity: %d\n", ret); + dev_err(camss->dev, "Failed to init media entity: %d\n", + ret); goto error; } ret = v4l2_device_register_subdev(v4l2_dev, sd); if (ret < 0) { - dev_err(dev, "Failed to register subdev: %d\n", ret); + dev_err(camss->dev, "Failed to register subdev: %d\n", + ret); media_entity_cleanup(&sd->entity); goto error; } @@ -1393,6 +1409,9 @@ void msm_ispif_unregister_entities(struct ispif_device *ispif) { int i; + if (!ispif) + return; + mutex_destroy(&ispif->power_lock); mutex_destroy(&ispif->config_lock); diff --git a/drivers/media/platform/qcom/camss/camss-ispif.h b/drivers/media/platform/qcom/camss/camss-ispif.h index 4132174f7ea1..fdf28e68cc7d 100644 --- a/drivers/media/platform/qcom/camss/camss-ispif.h +++ b/drivers/media/platform/qcom/camss/camss-ispif.h @@ -63,11 +63,12 @@ struct ispif_device { struct mutex config_lock; unsigned int line_num; struct ispif_line *line; + struct camss *camss; }; struct resources_ispif; -int msm_ispif_subdev_init(struct ispif_device *ispif, +int msm_ispif_subdev_init(struct camss *camss, const struct resources_ispif *res); int msm_ispif_register_entities(struct ispif_device *ispif, diff --git a/drivers/media/platform/qcom/camss/camss-vfe-170.c b/drivers/media/platform/qcom/camss/camss-vfe-170.c new file mode 100644 index 000000000000..8594d275b41d --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-vfe-170.c @@ -0,0 +1,786 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * camss-vfe-170.c + * + * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module v170 + * + * Copyright (C) 2020-2021 Linaro Ltd. + */ + +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/iopoll.h> + +#include "camss.h" +#include "camss-vfe.h" + +#define VFE_HW_VERSION (0x000) + +#define VFE_GLOBAL_RESET_CMD (0x018) +#define GLOBAL_RESET_CMD_CORE BIT(0) +#define GLOBAL_RESET_CMD_CAMIF BIT(1) +#define GLOBAL_RESET_CMD_BUS BIT(2) +#define GLOBAL_RESET_CMD_BUS_BDG BIT(3) +#define GLOBAL_RESET_CMD_REGISTER BIT(4) +#define GLOBAL_RESET_CMD_PM BIT(5) +#define GLOBAL_RESET_CMD_BUS_MISR BIT(6) +#define GLOBAL_RESET_CMD_TESTGEN BIT(7) +#define GLOBAL_RESET_CMD_DSP BIT(8) +#define GLOBAL_RESET_CMD_IDLE_CGC BIT(9) +#define GLOBAL_RESET_CMD_RDI0 BIT(10) +#define GLOBAL_RESET_CMD_RDI1 BIT(11) +#define GLOBAL_RESET_CMD_RDI2 BIT(12) +#define GLOBAL_RESET_CMD_RDI3 BIT(13) +#define GLOBAL_RESET_CMD_VFE_DOMAIN BIT(30) +#define GLOBAL_RESET_CMD_RESET_BYPASS BIT(31) + +#define VFE_CORE_CFG (0x050) +#define CFG_PIXEL_PATTERN_YCBYCR (0x4) +#define CFG_PIXEL_PATTERN_YCRYCB (0x5) +#define CFG_PIXEL_PATTERN_CBYCRY (0x6) +#define CFG_PIXEL_PATTERN_CRYCBY (0x7) +#define CFG_COMPOSITE_REG_UPDATE_EN BIT(4) + +#define VFE_IRQ_CMD (0x058) +#define CMD_GLOBAL_CLEAR BIT(0) + +#define VFE_IRQ_MASK_0 (0x05c) +#define MASK_0_CAMIF_SOF BIT(0) +#define MASK_0_CAMIF_EOF BIT(1) +#define MASK_0_RDI_REG_UPDATE(n) BIT((n) + 5) +#define MASK_0_IMAGE_MASTER_n_PING_PONG(n) BIT((n) + 8) +#define MASK_0_IMAGE_COMPOSITE_DONE_n(n) BIT((n) + 25) +#define MASK_0_RESET_ACK BIT(31) + +#define VFE_IRQ_MASK_1 (0x060) +#define MASK_1_CAMIF_ERROR BIT(0) +#define MASK_1_VIOLATION BIT(7) +#define MASK_1_BUS_BDG_HALT_ACK BIT(8) +#define MASK_1_IMAGE_MASTER_n_BUS_OVERFLOW(n) BIT((n) + 9) +#define MASK_1_RDI_SOF(n) BIT((n) + 29) + +#define VFE_IRQ_CLEAR_0 (0x064) +#define VFE_IRQ_CLEAR_1 (0x068) + +#define VFE_IRQ_STATUS_0 (0x06c) +#define STATUS_0_CAMIF_SOF BIT(0) +#define STATUS_0_RDI_REG_UPDATE(n) BIT((n) + 5) +#define STATUS_0_IMAGE_MASTER_PING_PONG(n) BIT((n) + 8) +#define STATUS_0_IMAGE_COMPOSITE_DONE(n) BIT((n) + 25) +#define STATUS_0_RESET_ACK BIT(31) + +#define VFE_IRQ_STATUS_1 (0x070) +#define STATUS_1_VIOLATION BIT(7) +#define STATUS_1_BUS_BDG_HALT_ACK BIT(8) +#define STATUS_1_RDI_SOF(n) BIT((n) + 27) + +#define VFE_VIOLATION_STATUS (0x07c) + +#define VFE_CAMIF_CMD (0x478) +#define CMD_CLEAR_CAMIF_STATUS BIT(2) + +#define VFE_CAMIF_CFG (0x47c) +#define CFG_VSYNC_SYNC_EDGE (0) +#define VSYNC_ACTIVE_HIGH (0) +#define VSYNC_ACTIVE_LOW (1) +#define CFG_HSYNC_SYNC_EDGE (1) +#define HSYNC_ACTIVE_HIGH (0) +#define HSYNC_ACTIVE_LOW (1) +#define CFG_VFE_SUBSAMPLE_ENABLE BIT(4) +#define CFG_BUS_SUBSAMPLE_ENABLE BIT(5) +#define CFG_VFE_OUTPUT_EN BIT(6) +#define CFG_BUS_OUTPUT_EN BIT(7) +#define CFG_BINNING_EN BIT(9) +#define CFG_FRAME_BASED_EN BIT(10) +#define CFG_RAW_CROP_EN BIT(22) + +#define VFE_REG_UPDATE_CMD (0x4ac) +#define REG_UPDATE_RDI(n) BIT(1 + (n)) + +#define VFE_BUS_IRQ_MASK(n) (0x2044 + (n) * 4) +#define VFE_BUS_IRQ_CLEAR(n) (0x2050 + (n) * 4) +#define VFE_BUS_IRQ_STATUS(n) (0x205c + (n) * 4) +#define STATUS0_COMP_RESET_DONE BIT(0) +#define STATUS0_COMP_REG_UPDATE0_DONE BIT(1) +#define STATUS0_COMP_REG_UPDATE1_DONE BIT(2) +#define STATUS0_COMP_REG_UPDATE2_DONE BIT(3) +#define STATUS0_COMP_REG_UPDATE3_DONE BIT(4) +#define STATUS0_COMP_REG_UPDATE_DONE(n) BIT((n) + 1) +#define STATUS0_COMP0_BUF_DONE BIT(5) +#define STATUS0_COMP1_BUF_DONE BIT(6) +#define STATUS0_COMP2_BUF_DONE BIT(7) +#define STATUS0_COMP3_BUF_DONE BIT(8) +#define STATUS0_COMP4_BUF_DONE BIT(9) +#define STATUS0_COMP5_BUF_DONE BIT(10) +#define STATUS0_COMP_BUF_DONE(n) BIT((n) + 5) +#define STATUS0_COMP_ERROR BIT(11) +#define STATUS0_COMP_OVERWRITE BIT(12) +#define STATUS0_OVERFLOW BIT(13) +#define STATUS0_VIOLATION BIT(14) +/* WM_CLIENT_BUF_DONE defined for buffers 0:19 */ +#define STATUS1_WM_CLIENT_BUF_DONE(n) BIT(n) +#define STATUS1_EARLY_DONE BIT(24) +#define STATUS2_DUAL_COMP0_BUF_DONE BIT(0) +#define STATUS2_DUAL_COMP1_BUF_DONE BIT(1) +#define STATUS2_DUAL_COMP2_BUF_DONE BIT(2) +#define STATUS2_DUAL_COMP3_BUF_DONE BIT(3) +#define STATUS2_DUAL_COMP4_BUF_DONE BIT(4) +#define STATUS2_DUAL_COMP5_BUF_DONE BIT(5) +#define STATUS2_DUAL_COMP_BUF_DONE(n) BIT(n) +#define STATUS2_DUAL_COMP_ERROR BIT(6) +#define STATUS2_DUAL_COMP_OVERWRITE BIT(7) + +#define VFE_BUS_IRQ_CLEAR_GLOBAL (0x2068) + +#define VFE_BUS_WM_DEBUG_STATUS_CFG (0x226c) +#define DEBUG_STATUS_CFG_STATUS0(n) BIT(n) +#define DEBUG_STATUS_CFG_STATUS1(n) BIT(8 + (n)) + +#define VFE_BUS_WM_ADDR_SYNC_FRAME_HEADER (0x2080) + +#define VFE_BUS_WM_ADDR_SYNC_NO_SYNC (0x2084) +#define BUS_VER2_MAX_CLIENTS (24) +#define WM_ADDR_NO_SYNC_DEFAULT_VAL \ + ((1 << BUS_VER2_MAX_CLIENTS) - 1) + +#define VFE_BUS_WM_CGC_OVERRIDE (0x200c) +#define WM_CGC_OVERRIDE_ALL (0xFFFFF) + +#define VFE_BUS_WM_TEST_BUS_CTRL (0x211c) + +#define VFE_BUS_WM_STATUS0(n) (0x2200 + (n) * 0x100) +#define VFE_BUS_WM_STATUS1(n) (0x2204 + (n) * 0x100) +#define VFE_BUS_WM_CFG(n) (0x2208 + (n) * 0x100) +#define WM_CFG_EN (0) +#define WM_CFG_MODE (1) +#define MODE_QCOM_PLAIN (0) +#define MODE_MIPI_RAW (1) +#define WM_CFG_VIRTUALFRAME (2) +#define VFE_BUS_WM_HEADER_ADDR(n) (0x220c + (n) * 0x100) +#define VFE_BUS_WM_HEADER_CFG(n) (0x2210 + (n) * 0x100) +#define VFE_BUS_WM_IMAGE_ADDR(n) (0x2214 + (n) * 0x100) +#define VFE_BUS_WM_IMAGE_ADDR_OFFSET(n) (0x2218 + (n) * 0x100) +#define VFE_BUS_WM_BUFFER_WIDTH_CFG(n) (0x221c + (n) * 0x100) +#define WM_BUFFER_DEFAULT_WIDTH (0xFF01) + +#define VFE_BUS_WM_BUFFER_HEIGHT_CFG(n) (0x2220 + (n) * 0x100) +#define VFE_BUS_WM_PACKER_CFG(n) (0x2224 + (n) * 0x100) + +#define VFE_BUS_WM_STRIDE(n) (0x2228 + (n) * 0x100) +#define WM_STRIDE_DEFAULT_STRIDE (0xFF01) + +#define VFE_BUS_WM_IRQ_SUBSAMPLE_PERIOD(n) (0x2248 + (n) * 0x100) +#define VFE_BUS_WM_IRQ_SUBSAMPLE_PATTERN(n) (0x224c + (n) * 0x100) +#define VFE_BUS_WM_FRAMEDROP_PERIOD(n) (0x2250 + (n) * 0x100) +#define VFE_BUS_WM_FRAMEDROP_PATTERN(n) (0x2254 + (n) * 0x100) +#define VFE_BUS_WM_FRAME_INC(n) (0x2258 + (n) * 0x100) +#define VFE_BUS_WM_BURST_LIMIT(n) (0x225c + (n) * 0x100) + +static void vfe_hw_version_read(struct vfe_device *vfe, struct device *dev) +{ + u32 hw_version = readl_relaxed(vfe->base + VFE_HW_VERSION); + + u32 gen = (hw_version >> 28) & 0xF; + u32 rev = (hw_version >> 16) & 0xFFF; + u32 step = hw_version & 0xFFFF; + + dev_err(dev, "VFE HW Version = %u.%u.%u\n", gen, rev, step); +} + +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 = GLOBAL_RESET_CMD_CORE | + GLOBAL_RESET_CMD_CAMIF | + GLOBAL_RESET_CMD_BUS | + GLOBAL_RESET_CMD_BUS_BDG | + GLOBAL_RESET_CMD_REGISTER | + GLOBAL_RESET_CMD_TESTGEN | + GLOBAL_RESET_CMD_DSP | + GLOBAL_RESET_CMD_IDLE_CGC | + GLOBAL_RESET_CMD_RDI0 | + GLOBAL_RESET_CMD_RDI1 | + GLOBAL_RESET_CMD_RDI2; + + writel_relaxed(BIT(31), vfe->base + VFE_IRQ_MASK_0); + + /* Make sure IRQ mask has been written before resetting */ + wmb(); + + writel_relaxed(reset_bits, vfe->base + VFE_GLOBAL_RESET_CMD); +} + +static void vfe_wm_start(struct vfe_device *vfe, u8 wm, struct vfe_line *line) +{ + u32 val; + + /*Set Debug Registers*/ + val = DEBUG_STATUS_CFG_STATUS0(1) | + DEBUG_STATUS_CFG_STATUS0(7); + writel_relaxed(val, vfe->base + VFE_BUS_WM_DEBUG_STATUS_CFG); + + /* BUS_WM_INPUT_IF_ADDR_SYNC_FRAME_HEADER */ + writel_relaxed(0, vfe->base + VFE_BUS_WM_ADDR_SYNC_FRAME_HEADER); + + /* no clock gating at bus input */ + val = WM_CGC_OVERRIDE_ALL; + writel_relaxed(val, vfe->base + VFE_BUS_WM_CGC_OVERRIDE); + + writel_relaxed(0x0, vfe->base + VFE_BUS_WM_TEST_BUS_CTRL); + + /* if addr_no_sync has default value then config the addr no sync reg */ + val = WM_ADDR_NO_SYNC_DEFAULT_VAL; + writel_relaxed(val, vfe->base + VFE_BUS_WM_ADDR_SYNC_NO_SYNC); + + writel_relaxed(0xf, vfe->base + VFE_BUS_WM_BURST_LIMIT(wm)); + + val = WM_BUFFER_DEFAULT_WIDTH; + writel_relaxed(val, vfe->base + VFE_BUS_WM_BUFFER_WIDTH_CFG(wm)); + + val = 0; + writel_relaxed(val, vfe->base + VFE_BUS_WM_BUFFER_HEIGHT_CFG(wm)); + + val = 0; + writel_relaxed(val, vfe->base + VFE_BUS_WM_PACKER_CFG(wm)); // XXX 1 for PLAIN8? + + /* Configure stride for RDIs */ + val = WM_STRIDE_DEFAULT_STRIDE; + writel_relaxed(val, vfe->base + VFE_BUS_WM_STRIDE(wm)); + + /* Enable WM */ + val = 1 << WM_CFG_EN | + MODE_MIPI_RAW << WM_CFG_MODE; + writel_relaxed(val, vfe->base + VFE_BUS_WM_CFG(wm)); +} + +static void vfe_wm_stop(struct vfe_device *vfe, u8 wm) +{ + /* Disable WM */ + writel_relaxed(0, vfe->base + VFE_BUS_WM_CFG(wm)); +} + +static void vfe_wm_update(struct vfe_device *vfe, u8 wm, u32 addr, + struct vfe_line *line) +{ + struct v4l2_pix_format_mplane *pix = + &line->video_out.active_fmt.fmt.pix_mp; + u32 stride = pix->plane_fmt[0].bytesperline; + + writel_relaxed(addr, vfe->base + VFE_BUS_WM_IMAGE_ADDR(wm)); + writel_relaxed(stride * pix->height, vfe->base + VFE_BUS_WM_FRAME_INC(wm)); +} + +static void vfe_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id) +{ + vfe->reg_update |= REG_UPDATE_RDI(line_id); + + /* Enforce ordering between previous reg writes and reg update */ + wmb(); + + writel_relaxed(vfe->reg_update, vfe->base + VFE_REG_UPDATE_CMD); + + /* Enforce ordering between reg update and subsequent reg writes */ + wmb(); +} + +static inline void vfe_reg_update_clear(struct vfe_device *vfe, + enum vfe_line_id line_id) +{ + vfe->reg_update &= ~REG_UPDATE_RDI(line_id); +} + +static void vfe_enable_irq_common(struct vfe_device *vfe) +{ + vfe_reg_set(vfe, VFE_IRQ_MASK_0, ~0u); + vfe_reg_set(vfe, VFE_IRQ_MASK_1, ~0u); + + writel_relaxed(~0u, vfe->base + VFE_BUS_IRQ_MASK(0)); + writel_relaxed(~0u, vfe->base + VFE_BUS_IRQ_MASK(1)); + writel_relaxed(~0u, vfe->base + VFE_BUS_IRQ_MASK(2)); +} + +static void vfe_isr_halt_ack(struct vfe_device *vfe) +{ + complete(&vfe->halt_complete); +} + +static void vfe_isr_read(struct vfe_device *vfe, u32 *status0, u32 *status1) +{ + *status0 = readl_relaxed(vfe->base + VFE_IRQ_STATUS_0); + *status1 = readl_relaxed(vfe->base + VFE_IRQ_STATUS_1); + + writel_relaxed(*status0, vfe->base + VFE_IRQ_CLEAR_0); + writel_relaxed(*status1, vfe->base + VFE_IRQ_CLEAR_1); + + /* Enforce ordering between IRQ Clear and Global IRQ Clear */ + wmb(); + writel_relaxed(CMD_GLOBAL_CLEAR, vfe->base + VFE_IRQ_CMD); +} + +static void vfe_violation_read(struct vfe_device *vfe) +{ + u32 violation = readl_relaxed(vfe->base + VFE_VIOLATION_STATUS); + + pr_err_ratelimited("VFE: violation = 0x%08x\n", violation); +} + +/* + * vfe_isr - VFE 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 status0, status1, vfe_bus_status[3]; + int i, wm; + + status0 = readl_relaxed(vfe->base + VFE_IRQ_STATUS_0); + status1 = readl_relaxed(vfe->base + VFE_IRQ_STATUS_1); + + writel_relaxed(status0, vfe->base + VFE_IRQ_CLEAR_0); + writel_relaxed(status1, vfe->base + VFE_IRQ_CLEAR_1); + + for (i = VFE_LINE_RDI0; i <= VFE_LINE_RDI2; i++) { + vfe_bus_status[i] = readl_relaxed(vfe->base + VFE_BUS_IRQ_STATUS(i)); + writel_relaxed(vfe_bus_status[i], vfe->base + VFE_BUS_IRQ_CLEAR(i)); + } + + /* Enforce ordering between IRQ reading and interpretation */ + wmb(); + + writel_relaxed(CMD_GLOBAL_CLEAR, vfe->base + VFE_IRQ_CMD); + writel_relaxed(1, vfe->base + VFE_BUS_IRQ_CLEAR_GLOBAL); + + if (status0 & STATUS_0_RESET_ACK) + vfe->isr_ops.reset_ack(vfe); + + for (i = VFE_LINE_RDI0; i <= VFE_LINE_RDI2; i++) + if (status0 & STATUS_0_RDI_REG_UPDATE(i)) + vfe->isr_ops.reg_update(vfe, i); + + for (i = VFE_LINE_RDI0; i <= VFE_LINE_RDI2; i++) + if (status0 & STATUS_1_RDI_SOF(i)) + vfe->isr_ops.sof(vfe, i); + + for (i = 0; i < MSM_VFE_COMPOSITE_IRQ_NUM; i++) + if (vfe_bus_status[0] & STATUS0_COMP_BUF_DONE(i)) + vfe->isr_ops.comp_done(vfe, i); + + for (wm = 0; wm < MSM_VFE_IMAGE_MASTERS_NUM; wm++) + if (status0 & BIT(9)) + if (vfe_bus_status[1] & STATUS1_WM_CLIENT_BUF_DONE(wm)) + vfe->isr_ops.wm_done(vfe, wm); + + return IRQ_HANDLED; +} + +/* + * vfe_halt - Trigger halt on VFE module and wait to complete + * @vfe: VFE device + * + * Return 0 on success or a negative error code otherwise + */ +static int vfe_halt(struct vfe_device *vfe) +{ + unsigned long time; + + reinit_completion(&vfe->halt_complete); + + time = wait_for_completion_timeout(&vfe->halt_complete, + msecs_to_jiffies(VFE_HALT_TIMEOUT_MS)); + if (!time) { + dev_err(vfe->camss->dev, "VFE halt timeout\n"); + return -EIO; + } + + return 0; +} + +static int vfe_get_output(struct vfe_line *line) +{ + struct vfe_device *vfe = to_vfe(line); + struct vfe_output *output; + unsigned long flags; + int wm_idx; + + spin_lock_irqsave(&vfe->output_lock, flags); + + output = &line->output; + if (output->state != VFE_OUTPUT_OFF) { + dev_err(vfe->camss->dev, "Output is running\n"); + goto error; + } + + output->wm_num = 1; + + wm_idx = vfe_reserve_wm(vfe, line->id); + if (wm_idx < 0) { + dev_err(vfe->camss->dev, "Can not reserve wm\n"); + goto error_get_wm; + } + output->wm_idx[0] = wm_idx; + + output->drop_update_idx = 0; + + spin_unlock_irqrestore(&vfe->output_lock, flags); + + return 0; + +error_get_wm: + vfe_release_wm(vfe, output->wm_idx[0]); + output->state = VFE_OUTPUT_OFF; +error: + spin_unlock_irqrestore(&vfe->output_lock, flags); + + return -EINVAL; +} + +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; + struct media_entity *sensor; + unsigned long flags; + unsigned int frame_skip = 0; + unsigned int i; + + sensor = camss_find_sensor(&line->subdev.entity); + if (sensor) { + struct v4l2_subdev *subdev = media_entity_to_v4l2_subdev(sensor); + + v4l2_subdev_call(subdev, sensor, g_skip_frames, &frame_skip); + /* Max frame skip is 29 frames */ + if (frame_skip > VFE_FRAME_DROP_VAL - 1) + frame_skip = VFE_FRAME_DROP_VAL - 1; + } + + spin_lock_irqsave(&vfe->output_lock, flags); + + ops->reg_update_clear(vfe, line->id); + + if (output->state != VFE_OUTPUT_OFF) { + dev_err(vfe->camss->dev, "Output is not in reserved state %d\n", + output->state); + spin_unlock_irqrestore(&vfe->output_lock, flags); + return -EINVAL; + } + + WARN_ON(output->gen2.active_num); + + output->state = VFE_OUTPUT_ON; + + output->sequence = 0; + output->wait_reg_update = 0; + reinit_completion(&output->reg_update); + + vfe_wm_start(vfe, output->wm_idx[0], line); + + for (i = 0; i < 2; i++) { + output->buf[i] = vfe_buf_get_pending(output); + if (!output->buf[i]) + break; + output->gen2.active_num++; + vfe_wm_update(vfe, output->wm_idx[0], output->buf[i]->addr[0], line); + } + + ops->reg_update(vfe, line->id); + + spin_unlock_irqrestore(&vfe->output_lock, flags); + + return 0; +} + +static int vfe_disable_output(struct vfe_line *line) +{ + struct vfe_device *vfe = to_vfe(line); + struct vfe_output *output = &line->output; + unsigned long flags; + unsigned int i; + bool done; + int timeout = 0; + + do { + spin_lock_irqsave(&vfe->output_lock, flags); + done = !output->gen2.active_num; + spin_unlock_irqrestore(&vfe->output_lock, flags); + usleep_range(10000, 20000); + + if (timeout++ == 100) { + dev_err(vfe->camss->dev, "VFE idle timeout - resetting\n"); + vfe_reset(vfe); + output->gen2.active_num = 0; + return 0; + } + } while (!done); + + spin_lock_irqsave(&vfe->output_lock, flags); + for (i = 0; i < output->wm_num; i++) + vfe_wm_stop(vfe, output->wm_idx[i]); + spin_unlock_irqrestore(&vfe->output_lock, flags); + + return 0; +} + +/* + * vfe_enable - Enable streaming on VFE line + * @line: VFE line + * + * Return 0 on success or a negative error code otherwise + */ +static int vfe_enable(struct vfe_line *line) +{ + struct vfe_device *vfe = to_vfe(line); + int ret; + + mutex_lock(&vfe->stream_lock); + + if (!vfe->stream_count) + vfe_enable_irq_common(vfe); + + vfe->stream_count++; + + mutex_unlock(&vfe->stream_lock); + + ret = vfe_get_output(line); + if (ret < 0) + goto error_get_output; + + ret = vfe_enable_output(line); + if (ret < 0) + goto error_enable_output; + + vfe->was_streaming = 1; + + return 0; + +error_enable_output: + vfe_put_output(line); + +error_get_output: + mutex_lock(&vfe->stream_lock); + + vfe->stream_count--; + + mutex_unlock(&vfe->stream_lock); + + return ret; +} + +/* + * vfe_disable - Disable streaming on VFE line + * @line: VFE line + * + * Return 0 on success or a negative error code otherwise + */ +static int vfe_disable(struct vfe_line *line) +{ + struct vfe_device *vfe = to_vfe(line); + + vfe_disable_output(line); + + vfe_put_output(line); + + mutex_lock(&vfe->stream_lock); + + vfe->stream_count--; + + mutex_unlock(&vfe->stream_lock); + + return 0; +} + +/* + * vfe_isr_sof - Process start of frame interrupt + * @vfe: VFE Device + * @line_id: VFE line + */ +static void vfe_isr_sof(struct vfe_device *vfe, enum vfe_line_id line_id) +{ + /* nop */ +} + +/* + * vfe_isr_reg_update - Process reg update interrupt + * @vfe: VFE Device + * @line_id: VFE line + */ +static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id) +{ + struct vfe_output *output; + unsigned long flags; + + spin_lock_irqsave(&vfe->output_lock, flags); + vfe->ops->reg_update_clear(vfe, line_id); + + output = &vfe->line[line_id].output; + + if (output->wait_reg_update) { + output->wait_reg_update = 0; + complete(&output->reg_update); + } + + spin_unlock_irqrestore(&vfe->output_lock, flags); +} + +/* + * vfe_isr_wm_done - Process write master done interrupt + * @vfe: VFE Device + * @wm: Write master id + */ +static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm) +{ + struct vfe_line *line = &vfe->line[vfe->wm_output_map[wm]]; + struct camss_buffer *ready_buf; + struct vfe_output *output; + unsigned long flags; + u32 index; + u64 ts = ktime_get_ns(); + + spin_lock_irqsave(&vfe->output_lock, flags); + + if (vfe->wm_output_map[wm] == VFE_LINE_NONE) { + 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; + + ready_buf = output->buf[0]; + if (!ready_buf) { + dev_err_ratelimited(vfe->camss->dev, + "Missing ready buf %d!\n", output->state); + goto out_unlock; + } + + ready_buf->vb.vb2_buf.timestamp = ts; + ready_buf->vb.sequence = output->sequence++; + + index = 0; + output->buf[0] = output->buf[1]; + if (output->buf[0]) + index = 1; + + output->buf[index] = vfe_buf_get_pending(output); + + if (output->buf[index]) + vfe_wm_update(vfe, output->wm_idx[0], output->buf[index]->addr[0], line); + else + output->gen2.active_num--; + + spin_unlock_irqrestore(&vfe->output_lock, flags); + + vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + + return; + +out_unlock: + spin_unlock_irqrestore(&vfe->output_lock, flags); +} + +/* + * vfe_pm_domain_off - Disable power domains specific to this VFE. + * @vfe: VFE Device + */ +static void vfe_pm_domain_off(struct vfe_device *vfe) +{ + /* nop */ +} + +/* + * vfe_pm_domain_on - Enable power domains specific to this VFE. + * @vfe: VFE Device + */ +static int vfe_pm_domain_on(struct vfe_device *vfe) +{ + return 0; +} + +/* + * vfe_queue_buffer - Add empty buffer + * @vid: Video device structure + * @buf: Buffer to be enqueued + * + * Add an empty buffer - depending on the current number of buffers it will be + * put in pending buffer queue or directly given to the hardware to be filled. + * + * Return 0 on success or a negative error code otherwise + */ +static int vfe_queue_buffer(struct camss_video *vid, + struct camss_buffer *buf) +{ + 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; + + output = &line->output; + + spin_lock_irqsave(&vfe->output_lock, flags); + + if (output->state == VFE_OUTPUT_ON && output->gen2.active_num < 2) { + output->buf[output->gen2.active_num++] = buf; + vfe_wm_update(vfe, output->wm_idx[0], buf->addr[0], line); + } else { + vfe_buf_add_pending(output, buf); + } + + spin_unlock_irqrestore(&vfe->output_lock, flags); + + return 0; +} + +static const struct vfe_isr_ops vfe_isr_ops_170 = { + .reset_ack = vfe_isr_reset_ack, + .halt_ack = vfe_isr_halt_ack, + .reg_update = vfe_isr_reg_update, + .sof = vfe_isr_sof, + .comp_done = vfe_isr_comp_done, + .wm_done = vfe_isr_wm_done, +}; + +static const struct camss_video_ops vfe_video_ops_170 = { + .queue_buffer = vfe_queue_buffer, + .flush_buffers = vfe_flush_buffers, +}; + +static void vfe_subdev_init(struct device *dev, struct vfe_device *vfe) +{ + vfe->isr_ops = vfe_isr_ops_170; + vfe->video_ops = vfe_video_ops_170; + + vfe->line_num = VFE_LINE_NUM_GEN2; +} + +const struct vfe_hw_ops vfe_ops_170 = { + .global_reset = vfe_global_reset, + .hw_version_read = vfe_hw_version_read, + .isr_read = vfe_isr_read, + .isr = vfe_isr, + .pm_domain_off = vfe_pm_domain_off, + .pm_domain_on = vfe_pm_domain_on, + .reg_update_clear = vfe_reg_update_clear, + .reg_update = vfe_reg_update, + .subdev_init = vfe_subdev_init, + .vfe_disable = vfe_disable, + .vfe_enable = vfe_enable, + .vfe_halt = vfe_halt, + .violation_read = vfe_violation_read, +}; diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c index 174a36be6f5d..53c56a8d4545 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c @@ -12,7 +12,9 @@ #include <linux/io.h> #include <linux/iopoll.h> +#include "camss.h" #include "camss-vfe.h" +#include "camss-vfe-gen1.h" #define VFE_0_HW_VERSION 0x000 @@ -283,30 +285,6 @@ static void vfe_wm_frame_based(struct vfe_device *vfe, u8 wm, u8 enable) 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) { @@ -665,20 +643,6 @@ static void vfe_set_demux_cfg(struct vfe_device *vfe, struct vfe_line *line) 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; @@ -922,7 +886,7 @@ static void vfe_violation_read(struct vfe_device *vfe) } /* - * vfe_isr - ISPIF module interrupt handler + * vfe_isr - VFE module interrupt handler * @irq: Interrupt line * @dev: VFE device * @@ -936,8 +900,8 @@ static irqreturn_t vfe_isr(int irq, void *dev) vfe->ops->isr_read(vfe, &value0, &value1); - trace_printk("VFE: status0 = 0x%08x, status1 = 0x%08x\n", - value0, value1); + dev_dbg(vfe->camss->dev, "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); @@ -974,46 +938,82 @@ static irqreturn_t vfe_isr(int irq, void *dev) return IRQ_HANDLED; } -const struct vfe_hw_ops vfe_ops_4_1 = { - .hw_version_read = vfe_hw_version_read, +/* + * vfe_pm_domain_off - Disable power domains specific to this VFE. + * @vfe: VFE Device + */ +static void vfe_pm_domain_off(struct vfe_device *vfe) +{ + /* nop */ +} + +/* + * vfe_pm_domain_on - Enable power domains specific to this VFE. + * @vfe: VFE Device + */ +static int vfe_pm_domain_on(struct vfe_device *vfe) +{ + return 0; +} + +static const struct vfe_hw_ops_gen1 vfe_ops_gen1_4_1 = { + .bus_connect_wm_to_rdi = vfe_bus_connect_wm_to_rdi, + .bus_disconnect_wm_from_rdi = vfe_bus_disconnect_wm_from_rdi, + .bus_enable_wr_if = vfe_bus_enable_wr_if, + .bus_reload_wm = vfe_bus_reload_wm, + .camif_wait_for_stop = vfe_camif_wait_for_stop, + .enable_irq_common = vfe_enable_irq_common, + .enable_irq_pix_line = vfe_enable_irq_pix_line, + .enable_irq_wm_line = vfe_enable_irq_wm_line, .get_ub_size = vfe_get_ub_size, - .global_reset = vfe_global_reset, - .halt_request = vfe_halt_request, .halt_clear = vfe_halt_clear, + .halt_request = vfe_halt_request, + .set_camif_cfg = vfe_set_camif_cfg, + .set_camif_cmd = vfe_set_camif_cmd, + .set_cgc_override = vfe_set_cgc_override, + .set_clamp_cfg = vfe_set_clamp_cfg, + .set_crop_cfg = vfe_set_crop_cfg, + .set_demux_cfg = vfe_set_demux_cfg, + .set_ds = vfe_set_ds, + .set_module_cfg = vfe_set_module_cfg, + .set_qos = vfe_set_qos, + .set_rdi_cid = vfe_set_rdi_cid, + .set_realign_cfg = vfe_set_realign_cfg, + .set_scale_cfg = vfe_set_scale_cfg, + .set_xbar_cfg = vfe_set_xbar_cfg, .wm_enable = vfe_wm_enable, .wm_frame_based = vfe_wm_frame_based, + .wm_get_ping_pong_status = vfe_wm_get_ping_pong_status, .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_framedrop_period = vfe_wm_set_framedrop_period, .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, + .wm_set_ub_cfg = vfe_wm_set_ub_cfg, +}; + +static void vfe_subdev_init(struct device *dev, struct vfe_device *vfe) +{ + vfe->isr_ops = vfe_isr_ops_gen1; + vfe->ops_gen1 = &vfe_ops_gen1_4_1; + vfe->video_ops = vfe_video_ops_gen1; + + vfe->line_num = VFE_LINE_NUM_GEN1; +} + +const struct vfe_hw_ops vfe_ops_4_1 = { + .global_reset = vfe_global_reset, + .hw_version_read = vfe_hw_version_read, .isr_read = vfe_isr_read, - .violation_read = vfe_violation_read, .isr = vfe_isr, + .pm_domain_off = vfe_pm_domain_off, + .pm_domain_on = vfe_pm_domain_on, + .reg_update_clear = vfe_reg_update_clear, + .reg_update = vfe_reg_update, + .subdev_init = vfe_subdev_init, + .vfe_disable = vfe_gen1_disable, + .vfe_enable = vfe_gen1_enable, + .vfe_halt = vfe_gen1_halt, + .violation_read = vfe_violation_read, }; diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-7.c b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c index b5704a2f119b..a59635217758 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe-4-7.c +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c @@ -8,11 +8,15 @@ * Copyright (C) 2015-2018 Linaro Ltd. */ +#include <linux/device.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/iopoll.h> +#include "camss.h" #include "camss-vfe.h" +#include "camss-vfe-gen1.h" + #define VFE_0_HW_VERSION 0x000 @@ -257,7 +261,7 @@ static void vfe_hw_version_read(struct vfe_device *vfe, struct device *dev) dev_err(dev, "VFE HW Version = 0x%08x\n", hw_version); } -static u16 vfe47_get_ub_size(u8 vfe_id) +static u16 vfe_get_ub_size(u8 vfe_id) { if (vfe_id == 0) return MSM_VFE_VFE0_UB_SIZE_RDI; @@ -295,6 +299,8 @@ static void vfe_global_reset(struct vfe_device *vfe) VFE_0_GLOBAL_RESET_CMD_CORE; writel_relaxed(BIT(31), vfe->base + VFE_0_IRQ_MASK_0); + + /* Enforce barrier between IRQ mask setup and global reset */ wmb(); writel_relaxed(reset_bits, vfe->base + VFE_0_GLOBAL_RESET_CMD); } @@ -310,7 +316,7 @@ static void vfe_halt_clear(struct vfe_device *vfe) writel_relaxed(0x0, vfe->base + VFE_0_BUS_BDG_CMD); } -static void vfe47_wm_enable(struct vfe_device *vfe, u8 wm, u8 enable) +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), @@ -459,8 +465,12 @@ static void vfe_wm_set_ub_cfg(struct vfe_device *vfe, u8 wm, static void vfe_bus_reload_wm(struct vfe_device *vfe, u8 wm) { + /* Enforce barrier between any outstanding register write */ wmb(); + writel_relaxed(VFE_0_BUS_CMD_Mx_RLD_CMD(wm), vfe->base + VFE_0_BUS_CMD); + + /* Use barrier to make sure bus reload is issued before anything else */ wmb(); } @@ -674,8 +684,12 @@ static void vfe_set_rdi_cid(struct vfe_device *vfe, enum vfe_line_id id, u8 cid) 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); + + /* Enforce barrier between line update and commit */ wmb(); writel_relaxed(vfe->reg_update, vfe->base + VFE_0_REG_UPDATE); + + /* Make sure register update is issued before further reg writes */ wmb(); } @@ -779,20 +793,6 @@ static void vfe_set_demux_cfg(struct vfe_device *vfe, struct vfe_line *line) 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; @@ -894,7 +894,7 @@ static void vfe_set_clamp_cfg(struct vfe_device *vfe) writel_relaxed(val, vfe->base + VFE_0_CLAMP_ENC_MIN_CFG); } -static void vfe47_set_qos(struct vfe_device *vfe) +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; @@ -909,7 +909,7 @@ static void vfe47_set_qos(struct vfe_device *vfe) writel_relaxed(val7, vfe->base + VFE_0_BUS_BDG_QOS_CFG_7); } -static void vfe47_set_ds(struct vfe_device *vfe) +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; @@ -993,6 +993,8 @@ static void vfe_set_camif_cmd(struct vfe_device *vfe, u8 enable) cmd = VFE_0_CAMIF_CMD_CLEAR_CAMIF_STATUS | VFE_0_CAMIF_CMD_NO_CHANGE; writel_relaxed(cmd, vfe->base + VFE_0_CAMIF_CMD); + + /* Make sure camif command is issued written before it is changed again */ wmb(); if (enable) @@ -1035,27 +1037,10 @@ static int vfe_camif_wait_for_stop(struct vfe_device *vfe, struct device *dev) 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 + * vfe_isr - VFE module interrupt handler * @irq: Interrupt line * @dev: VFE device * @@ -1069,8 +1054,8 @@ static irqreturn_t vfe_isr(int irq, void *dev) vfe->ops->isr_read(vfe, &value0, &value1); - trace_printk("VFE: status0 = 0x%08x, status1 = 0x%08x\n", - value0, value1); + dev_dbg(vfe->camss->dev, "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); @@ -1081,7 +1066,7 @@ static irqreturn_t vfe_isr(int irq, void *dev) 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++) + for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) if (value0 & VFE_0_IRQ_STATUS_0_line_n_REG_UPDATE(i)) vfe->isr_ops.reg_update(vfe, i); @@ -1107,150 +1092,120 @@ static irqreturn_t vfe_isr(int irq, void *dev) return IRQ_HANDLED; } -const struct vfe_hw_ops vfe_ops_4_7 = { - .hw_version_read = vfe_hw_version_read, - .get_ub_size = vfe47_get_ub_size, - .global_reset = vfe_global_reset, - .halt_request = vfe_halt_request, - .halt_clear = vfe_halt_clear, - .wm_enable = vfe47_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 = vfe47_set_qos, - .set_ds = vfe47_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, -}; - -static u16 vfe48_get_ub_size(u8 vfe_id) +static void vfe_isr_read(struct vfe_device *vfe, u32 *value0, u32 *value1) { - /* On VFE4.8 the ub-size is the same on both instances */ - return MSM_VFE_VFE0_UB_SIZE_RDI; + *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); + + /* Enforce barrier between local & global IRQ clear */ + wmb(); + writel_relaxed(VFE_0_IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_0_IRQ_CMD); } -static void vfe48_wm_enable(struct vfe_device *vfe, u8 wm, u8 enable) +/* + * vfe_pm_domain_off - Disable power domains specific to this VFE. + * @vfe: VFE Device + */ +static void vfe_pm_domain_off(struct vfe_device *vfe) { - if (enable) - writel_relaxed(2 << VFE48_0_BUS_IMAGE_MASTER_n_SHIFT(wm), - vfe->base + VFE48_0_BUS_IMAGE_MASTER_CMD); - else - writel_relaxed(1 << VFE48_0_BUS_IMAGE_MASTER_n_SHIFT(wm), - vfe->base + VFE48_0_BUS_IMAGE_MASTER_CMD); + struct camss *camss; - /* The WM must be enabled before sending other commands */ - wmb(); + if (!vfe) + return; + + camss = vfe->camss; + + device_link_del(camss->genpd_link[vfe->id]); } -static void vfe48_set_qos(struct vfe_device *vfe) +/* + * vfe_pm_domain_on - Enable power domains specific to this VFE. + * @vfe: VFE Device + */ +static int vfe_pm_domain_on(struct vfe_device *vfe) { - u32 val = VFE48_0_BUS_BDG_QOS_CFG_0_CFG; - u32 val3 = VFE48_0_BUS_BDG_QOS_CFG_3_CFG; - u32 val4 = VFE48_0_BUS_BDG_QOS_CFG_4_CFG; - u32 val7 = VFE48_0_BUS_BDG_QOS_CFG_7_CFG; + struct camss *camss = vfe->camss; + enum vfe_line_id id = vfe->id; - 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(val3, vfe->base + VFE_0_BUS_BDG_QOS_CFG_3); - writel_relaxed(val4, vfe->base + VFE_0_BUS_BDG_QOS_CFG_4); - writel_relaxed(val4, vfe->base + VFE_0_BUS_BDG_QOS_CFG_5); - writel_relaxed(val4, vfe->base + VFE_0_BUS_BDG_QOS_CFG_6); - writel_relaxed(val7, vfe->base + VFE_0_BUS_BDG_QOS_CFG_7); + 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]) { + dev_err(vfe->camss->dev, "Failed to add VFE#%d to power domain\n", id); + return -EINVAL; + } + + return 0; } -static void vfe48_set_ds(struct vfe_device *vfe) +static void vfe_violation_read(struct vfe_device *vfe) { - u32 val = VFE48_0_BUS_BDG_DS_CFG_0_CFG; - u32 val16 = VFE48_0_BUS_BDG_DS_CFG_16_CFG; + u32 violation = readl_relaxed(vfe->base + VFE_0_VIOLATION_STATUS); - 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); + pr_err_ratelimited("VFE: violation = 0x%08x\n", violation); } -const struct vfe_hw_ops vfe_ops_4_8 = { - .hw_version_read = vfe_hw_version_read, - .get_ub_size = vfe48_get_ub_size, - .global_reset = vfe_global_reset, - .halt_request = vfe_halt_request, +static const struct vfe_hw_ops_gen1 vfe_ops_gen1_4_7 = { + .bus_connect_wm_to_rdi = vfe_bus_connect_wm_to_rdi, + .bus_disconnect_wm_from_rdi = vfe_bus_disconnect_wm_from_rdi, + .bus_enable_wr_if = vfe_bus_enable_wr_if, + .bus_reload_wm = vfe_bus_reload_wm, + .camif_wait_for_stop = vfe_camif_wait_for_stop, + .enable_irq_common = vfe_enable_irq_common, + .enable_irq_pix_line = vfe_enable_irq_pix_line, + .enable_irq_wm_line = vfe_enable_irq_wm_line, + .get_ub_size = vfe_get_ub_size, .halt_clear = vfe_halt_clear, - .wm_enable = vfe48_wm_enable, + .halt_request = vfe_halt_request, + .set_camif_cfg = vfe_set_camif_cfg, + .set_camif_cmd = vfe_set_camif_cmd, + .set_cgc_override = vfe_set_cgc_override, + .set_clamp_cfg = vfe_set_clamp_cfg, + .set_crop_cfg = vfe_set_crop_cfg, + .set_demux_cfg = vfe_set_demux_cfg, + .set_ds = vfe_set_ds, + .set_module_cfg = vfe_set_module_cfg, + .set_qos = vfe_set_qos, + .set_rdi_cid = vfe_set_rdi_cid, + .set_realign_cfg = vfe_set_realign_cfg, + .set_scale_cfg = vfe_set_scale_cfg, + .set_xbar_cfg = vfe_set_xbar_cfg, + .wm_enable = vfe_wm_enable, .wm_frame_based = vfe_wm_frame_based, + .wm_get_ping_pong_status = vfe_wm_get_ping_pong_status, .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_framedrop_period = vfe_wm_set_framedrop_period, .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 = vfe48_set_qos, - .set_ds = vfe48_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, + .wm_set_ub_cfg = vfe_wm_set_ub_cfg, +}; + +static void vfe_subdev_init(struct device *dev, struct vfe_device *vfe) +{ + vfe->isr_ops = vfe_isr_ops_gen1; + vfe->ops_gen1 = &vfe_ops_gen1_4_7; + vfe->video_ops = vfe_video_ops_gen1; + + vfe->line_num = VFE_LINE_NUM_GEN1; +} + +const struct vfe_hw_ops vfe_ops_4_7 = { + .global_reset = vfe_global_reset, + .hw_version_read = vfe_hw_version_read, .isr_read = vfe_isr_read, - .violation_read = vfe_violation_read, .isr = vfe_isr, + .pm_domain_off = vfe_pm_domain_off, + .pm_domain_on = vfe_pm_domain_on, + .reg_update_clear = vfe_reg_update_clear, + .reg_update = vfe_reg_update, + .subdev_init = vfe_subdev_init, + .vfe_disable = vfe_gen1_disable, + .vfe_enable = vfe_gen1_enable, + .vfe_halt = vfe_gen1_halt, + .violation_read = vfe_violation_read, }; diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-8.c b/drivers/media/platform/qcom/camss/camss-vfe-4-8.c new file mode 100644 index 000000000000..998429dbb65c --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-8.c @@ -0,0 +1,1195 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * camss-vfe-4-8.c + * + * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module v4.8 + * + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. + * Copyright (C) 2015-2021 Linaro Ltd. + */ + +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/iopoll.h> + +#include "camss.h" +#include "camss-vfe.h" +#include "camss-vfe-gen1.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 0xaaa5aaa5 +#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_3_CFG 0xaa55aaa5 +#define VFE_0_BUS_BDG_QOS_CFG_4 0x414 +#define VFE_0_BUS_BDG_QOS_CFG_4_CFG 0xaa55aa55 +#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 0x0005aa55 + +#define VFE_0_BUS_BDG_DS_CFG_0 0x424 +#define VFE_0_BUS_BDG_DS_CFG_0_CFG 0xcccc1111 +#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 0x00000110 + +#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 VFE_0_BUS_IMAGE_MASTER_CMD 0xcec +#define VFE_0_BUS_IMAGE_MASTER_n_SHIFT(x) (2 * (x)) + +#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 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); + + /* Enforce barrier between IRQ mask setup and global reset */ + 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_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) +{ + /* Enforce barrier between any outstanding register write */ + wmb(); + + writel_relaxed(VFE_0_BUS_CMD_Mx_RLD_CMD(wm), vfe->base + VFE_0_BUS_CMD); + + /* Use barrier to make sure bus reload is issued before anything else */ + 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); + + /* Enforce barrier between line update and commit */ + wmb(); + + writel_relaxed(vfe->reg_update, vfe->base + VFE_0_REG_UPDATE); + + /* Make sure register update is issued before further reg writes */ + 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 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_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); + + /* Make sure camif command is issued written before it is changed again */ + 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; +} + +/* + * vfe_isr - VFE 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); + + dev_dbg(vfe->camss->dev, "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_num; 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; +} + +static u16 vfe_get_ub_size(u8 vfe_id) +{ + /* On VFE4.8 the ub-size is the same on both instances */ + return MSM_VFE_VFE0_UB_SIZE_RDI; +} + +static void vfe_wm_enable(struct vfe_device *vfe, u8 wm, u8 enable) +{ + if (enable) + writel_relaxed(2 << VFE_0_BUS_IMAGE_MASTER_n_SHIFT(wm), + vfe->base + VFE_0_BUS_IMAGE_MASTER_CMD); + else + writel_relaxed(1 << VFE_0_BUS_IMAGE_MASTER_n_SHIFT(wm), + vfe->base + VFE_0_BUS_IMAGE_MASTER_CMD); + + /* The WM must be enabled before sending other commands */ + wmb(); +} + +static void vfe_set_qos(struct vfe_device *vfe) +{ + u32 val = VFE_0_BUS_BDG_QOS_CFG_0_CFG; + u32 val3 = VFE_0_BUS_BDG_QOS_CFG_3_CFG; + u32 val4 = VFE_0_BUS_BDG_QOS_CFG_4_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(val3, vfe->base + VFE_0_BUS_BDG_QOS_CFG_3); + writel_relaxed(val4, vfe->base + VFE_0_BUS_BDG_QOS_CFG_4); + writel_relaxed(val4, vfe->base + VFE_0_BUS_BDG_QOS_CFG_5); + writel_relaxed(val4, 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_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); + + /* Enforce barrier between local & global IRQ clear */ + wmb(); + writel_relaxed(VFE_0_IRQ_CMD_GLOBAL_CLEAR, vfe->base + VFE_0_IRQ_CMD); +} + +/* + * vfe_pm_domain_off - Disable power domains specific to this VFE. + * @vfe: VFE Device + */ +static void vfe_pm_domain_off(struct vfe_device *vfe) +{ + struct camss *camss = vfe->camss; + + device_link_del(camss->genpd_link[vfe->id]); +} + +/* + * vfe_pm_domain_on - Enable power domains specific to this VFE. + * @vfe: VFE Device + */ +static int vfe_pm_domain_on(struct vfe_device *vfe) +{ + struct camss *camss = vfe->camss; + enum vfe_line_id id = vfe->id; + + 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]) { + dev_err(vfe->camss->dev, "Failed to add VFE#%d to power domain\n", id); + return -EINVAL; + } + + return 0; +} + +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); +} + +static const struct vfe_hw_ops_gen1 vfe_ops_gen1_4_8 = { + .bus_connect_wm_to_rdi = vfe_bus_connect_wm_to_rdi, + .bus_disconnect_wm_from_rdi = vfe_bus_disconnect_wm_from_rdi, + .bus_enable_wr_if = vfe_bus_enable_wr_if, + .bus_reload_wm = vfe_bus_reload_wm, + .camif_wait_for_stop = vfe_camif_wait_for_stop, + .enable_irq_common = vfe_enable_irq_common, + .enable_irq_pix_line = vfe_enable_irq_pix_line, + .enable_irq_wm_line = vfe_enable_irq_wm_line, + .get_ub_size = vfe_get_ub_size, + .halt_clear = vfe_halt_clear, + .halt_request = vfe_halt_request, + .set_camif_cfg = vfe_set_camif_cfg, + .set_camif_cmd = vfe_set_camif_cmd, + .set_cgc_override = vfe_set_cgc_override, + .set_clamp_cfg = vfe_set_clamp_cfg, + .set_crop_cfg = vfe_set_crop_cfg, + .set_demux_cfg = vfe_set_demux_cfg, + .set_ds = vfe_set_ds, + .set_module_cfg = vfe_set_module_cfg, + .set_qos = vfe_set_qos, + .set_rdi_cid = vfe_set_rdi_cid, + .set_realign_cfg = vfe_set_realign_cfg, + .set_scale_cfg = vfe_set_scale_cfg, + .set_xbar_cfg = vfe_set_xbar_cfg, + .wm_enable = vfe_wm_enable, + .wm_frame_based = vfe_wm_frame_based, + .wm_get_ping_pong_status = vfe_wm_get_ping_pong_status, + .wm_line_based = vfe_wm_line_based, + .wm_set_framedrop_pattern = vfe_wm_set_framedrop_pattern, + .wm_set_framedrop_period = vfe_wm_set_framedrop_period, + .wm_set_ping_addr = vfe_wm_set_ping_addr, + .wm_set_pong_addr = vfe_wm_set_pong_addr, + .wm_set_subsample = vfe_wm_set_subsample, + .wm_set_ub_cfg = vfe_wm_set_ub_cfg, +}; + +static void vfe_subdev_init(struct device *dev, struct vfe_device *vfe) +{ + vfe->isr_ops = vfe_isr_ops_gen1; + vfe->ops_gen1 = &vfe_ops_gen1_4_8; + vfe->video_ops = vfe_video_ops_gen1; + + vfe->line_num = VFE_LINE_NUM_GEN1; +} + +const struct vfe_hw_ops vfe_ops_4_8 = { + .global_reset = vfe_global_reset, + .hw_version_read = vfe_hw_version_read, + .isr_read = vfe_isr_read, + .isr = vfe_isr, + .pm_domain_off = vfe_pm_domain_off, + .pm_domain_on = vfe_pm_domain_on, + .reg_update_clear = vfe_reg_update_clear, + .reg_update = vfe_reg_update, + .subdev_init = vfe_subdev_init, + .vfe_disable = vfe_gen1_disable, + .vfe_enable = vfe_gen1_enable, + .vfe_halt = vfe_gen1_halt, + .violation_read = vfe_violation_read, +}; diff --git a/drivers/media/platform/qcom/camss/camss-vfe-gen1.c b/drivers/media/platform/qcom/camss/camss-vfe-gen1.c new file mode 100644 index 000000000000..4fd265d01883 --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-vfe-gen1.c @@ -0,0 +1,742 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * camss-vfe-gen1.c + * + * Qualcomm MSM Camera Subsystem - VFE Common functionality for Gen 1 versions of hw (4.1, 4.7..) + * + * Copyright (C) 2020 Linaro Ltd. + */ + +#include "camss.h" +#include "camss-vfe.h" +#include "camss-vfe-gen1.h" + +/* Max number of frame drop updates per frame */ +#define VFE_FRAME_DROP_UPDATES 2 +#define VFE_NEXT_SOF_MS 500 + +int vfe_gen1_halt(struct vfe_device *vfe) +{ + unsigned long time; + + reinit_completion(&vfe->halt_complete); + + vfe->ops_gen1->halt_request(vfe); + + time = wait_for_completion_timeout(&vfe->halt_complete, + msecs_to_jiffies(VFE_HALT_TIMEOUT_MS)); + if (!time) { + dev_err(vfe->camss->dev, "VFE halt timeout\n"); + return -EIO; + } + + return 0; +} + +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; + + spin_lock_irqsave(&vfe->output_lock, flags); + + output->gen1.wait_sof = 1; + spin_unlock_irqrestore(&vfe->output_lock, flags); + + time = wait_for_completion_timeout(&output->sof, msecs_to_jiffies(VFE_NEXT_SOF_MS)); + if (!time) + 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->ops_gen1->wm_enable(vfe, output->wm_idx[i], 0); + + 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(vfe->camss->dev, "VFE reg update timeout\n"); + + spin_lock_irqsave(&vfe->output_lock, flags); + + if (line->id != VFE_LINE_PIX) { + vfe->ops_gen1->wm_frame_based(vfe, output->wm_idx[0], 0); + vfe->ops_gen1->bus_disconnect_wm_from_rdi(vfe, output->wm_idx[0], line->id); + vfe->ops_gen1->enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 0); + vfe->ops_gen1->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->ops_gen1->wm_line_based(vfe, output->wm_idx[i], NULL, i, 0); + vfe->ops_gen1->set_cgc_override(vfe, output->wm_idx[i], 0); + } + + vfe->ops_gen1->enable_irq_pix_line(vfe, 0, line->id, 0); + vfe->ops_gen1->set_module_cfg(vfe, 0); + vfe->ops_gen1->set_realign_cfg(vfe, line, 0); + vfe->ops_gen1->set_xbar_cfg(vfe, output, 0); + vfe->ops_gen1->set_camif_cmd(vfe, 0); + + spin_unlock_irqrestore(&vfe->output_lock, flags); + + vfe->ops_gen1->camif_wait_for_stop(vfe, vfe->camss->dev); + } + + return 0; +} + +/* + * vfe_gen1_disable - Disable streaming on VFE line + * @line: VFE line + * + * Return 0 on success or a negative error code otherwise + */ +int vfe_gen1_disable(struct vfe_line *line) +{ + struct vfe_device *vfe = to_vfe(line); + + vfe_disable_output(line); + + vfe_put_output(line); + + mutex_lock(&vfe->stream_lock); + + if (vfe->stream_count == 1) + vfe->ops_gen1->bus_enable_wr_if(vfe, 0); + + vfe->stream_count--; + + mutex_unlock(&vfe->stream_lock); + + return 0; +} + +static void vfe_output_init_addrs(struct vfe_device *vfe, + struct vfe_output *output, u8 sync, + struct vfe_line *line) +{ + u32 ping_addr; + u32 pong_addr; + unsigned int i; + + output->gen1.active_buf = 0; + + for (i = 0; i < output->wm_num; i++) { + if (output->buf[0]) + ping_addr = output->buf[0]->addr[i]; + else + ping_addr = 0; + + if (output->buf[1]) + pong_addr = output->buf[1]->addr[i]; + else + pong_addr = ping_addr; + + vfe->ops_gen1->wm_set_ping_addr(vfe, output->wm_idx[i], ping_addr); + vfe->ops_gen1->wm_set_pong_addr(vfe, output->wm_idx[i], pong_addr); + if (sync) + vfe->ops_gen1->bus_reload_wm(vfe, output->wm_idx[i]); + } +} + +static void vfe_output_frame_drop(struct vfe_device *vfe, + struct vfe_output *output, + u32 drop_pattern) +{ + u8 drop_period; + unsigned int i; + + /* We need to toggle update period to be valid on next frame */ + output->drop_update_idx++; + output->drop_update_idx %= VFE_FRAME_DROP_UPDATES; + drop_period = VFE_FRAME_DROP_VAL + output->drop_update_idx; + + for (i = 0; i < output->wm_num; i++) { + vfe->ops_gen1->wm_set_framedrop_period(vfe, output->wm_idx[i], drop_period); + vfe->ops_gen1->wm_set_framedrop_pattern(vfe, output->wm_idx[i], drop_pattern); + } + + vfe->ops->reg_update(vfe, container_of(output, struct vfe_line, output)->id); +} + +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; + struct media_entity *sensor; + unsigned long flags; + unsigned int frame_skip = 0; + unsigned int i; + u16 ub_size; + + ub_size = vfe->ops_gen1->get_ub_size(vfe->id); + if (!ub_size) + return -EINVAL; + + sensor = camss_find_sensor(&line->subdev.entity); + if (sensor) { + struct v4l2_subdev *subdev = media_entity_to_v4l2_subdev(sensor); + + v4l2_subdev_call(subdev, sensor, g_skip_frames, &frame_skip); + /* Max frame skip is 29 frames */ + if (frame_skip > VFE_FRAME_DROP_VAL - 1) + frame_skip = VFE_FRAME_DROP_VAL - 1; + } + + spin_lock_irqsave(&vfe->output_lock, flags); + + ops->reg_update_clear(vfe, line->id); + + if (output->state != VFE_OUTPUT_RESERVED) { + dev_err(vfe->camss->dev, "Output is not in reserved state %d\n", output->state); + spin_unlock_irqrestore(&vfe->output_lock, flags); + return -EINVAL; + } + output->state = VFE_OUTPUT_IDLE; + + output->buf[0] = vfe_buf_get_pending(output); + output->buf[1] = vfe_buf_get_pending(output); + + if (!output->buf[0] && output->buf[1]) { + output->buf[0] = output->buf[1]; + output->buf[1] = NULL; + } + + if (output->buf[0]) + output->state = VFE_OUTPUT_SINGLE; + + if (output->buf[1]) + output->state = VFE_OUTPUT_CONTINUOUS; + + switch (output->state) { + case VFE_OUTPUT_SINGLE: + vfe_output_frame_drop(vfe, output, 1 << frame_skip); + break; + case VFE_OUTPUT_CONTINUOUS: + vfe_output_frame_drop(vfe, output, 3 << frame_skip); + break; + default: + vfe_output_frame_drop(vfe, output, 0); + break; + } + + output->sequence = 0; + output->gen1.wait_sof = 0; + output->wait_reg_update = 0; + reinit_completion(&output->sof); + reinit_completion(&output->reg_update); + + vfe_output_init_addrs(vfe, output, 0, line); + + if (line->id != VFE_LINE_PIX) { + vfe->ops_gen1->set_cgc_override(vfe, output->wm_idx[0], 1); + vfe->ops_gen1->enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 1); + vfe->ops_gen1->bus_connect_wm_to_rdi(vfe, output->wm_idx[0], line->id); + vfe->ops_gen1->wm_set_subsample(vfe, output->wm_idx[0]); + vfe->ops_gen1->set_rdi_cid(vfe, line->id, 0); + vfe->ops_gen1->wm_set_ub_cfg(vfe, output->wm_idx[0], + (ub_size + 1) * output->wm_idx[0], ub_size); + vfe->ops_gen1->wm_frame_based(vfe, output->wm_idx[0], 1); + vfe->ops_gen1->wm_enable(vfe, output->wm_idx[0], 1); + vfe->ops_gen1->bus_reload_wm(vfe, output->wm_idx[0]); + } else { + ub_size /= output->wm_num; + for (i = 0; i < output->wm_num; i++) { + vfe->ops_gen1->set_cgc_override(vfe, output->wm_idx[i], 1); + vfe->ops_gen1->wm_set_subsample(vfe, output->wm_idx[i]); + vfe->ops_gen1->wm_set_ub_cfg(vfe, output->wm_idx[i], + (ub_size + 1) * output->wm_idx[i], ub_size); + vfe->ops_gen1->wm_line_based(vfe, output->wm_idx[i], + &line->video_out.active_fmt.fmt.pix_mp, i, 1); + vfe->ops_gen1->wm_enable(vfe, output->wm_idx[i], 1); + vfe->ops_gen1->bus_reload_wm(vfe, output->wm_idx[i]); + } + vfe->ops_gen1->enable_irq_pix_line(vfe, 0, line->id, 1); + vfe->ops_gen1->set_module_cfg(vfe, 1); + vfe->ops_gen1->set_camif_cfg(vfe, line); + vfe->ops_gen1->set_realign_cfg(vfe, line, 1); + vfe->ops_gen1->set_xbar_cfg(vfe, output, 1); + vfe->ops_gen1->set_demux_cfg(vfe, line); + vfe->ops_gen1->set_scale_cfg(vfe, line); + vfe->ops_gen1->set_crop_cfg(vfe, line); + vfe->ops_gen1->set_clamp_cfg(vfe); + vfe->ops_gen1->set_camif_cmd(vfe, 1); + } + + ops->reg_update(vfe, line->id); + + spin_unlock_irqrestore(&vfe->output_lock, flags); + + return 0; +} + +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; + + spin_lock_irqsave(&vfe->output_lock, flags); + + output = &line->output; + if (output->state != VFE_OUTPUT_OFF) { + dev_err(vfe->camss->dev, "Output is running\n"); + goto error; + } + output->state = VFE_OUTPUT_RESERVED; + + output->gen1.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(vfe->camss->dev, "Can not reserve wm\n"); + goto error_get_wm; + } + output->wm_idx[i] = wm_idx; + } + + output->drop_update_idx = 0; + + spin_unlock_irqrestore(&vfe->output_lock, flags); + + return 0; + +error_get_wm: + for (i--; i >= 0; i--) + vfe_release_wm(vfe, output->wm_idx[i]); + output->state = VFE_OUTPUT_OFF; +error: + spin_unlock_irqrestore(&vfe->output_lock, flags); + + return -EINVAL; +} + +int vfe_gen1_enable(struct vfe_line *line) +{ + struct vfe_device *vfe = to_vfe(line); + int ret; + + mutex_lock(&vfe->stream_lock); + + if (!vfe->stream_count) { + vfe->ops_gen1->enable_irq_common(vfe); + vfe->ops_gen1->bus_enable_wr_if(vfe, 1); + vfe->ops_gen1->set_qos(vfe); + vfe->ops_gen1->set_ds(vfe); + } + + vfe->stream_count++; + + mutex_unlock(&vfe->stream_lock); + + ret = vfe_get_output(line); + if (ret < 0) + goto error_get_output; + + ret = vfe_enable_output(line); + if (ret < 0) + goto error_enable_output; + + vfe->was_streaming = 1; + + return 0; + +error_enable_output: + vfe_put_output(line); + +error_get_output: + mutex_lock(&vfe->stream_lock); + + if (vfe->stream_count == 1) + vfe->ops_gen1->bus_enable_wr_if(vfe, 0); + + vfe->stream_count--; + + mutex_unlock(&vfe->stream_lock); + + return ret; +} + +static void vfe_output_update_ping_addr(struct vfe_device *vfe, + struct vfe_output *output, u8 sync, + struct vfe_line *line) +{ + u32 addr; + unsigned int i; + + for (i = 0; i < output->wm_num; i++) { + if (output->buf[0]) + addr = output->buf[0]->addr[i]; + else + addr = 0; + + vfe->ops_gen1->wm_set_ping_addr(vfe, output->wm_idx[i], addr); + if (sync) + vfe->ops_gen1->bus_reload_wm(vfe, output->wm_idx[i]); + } +} + +static void vfe_output_update_pong_addr(struct vfe_device *vfe, + struct vfe_output *output, u8 sync, + struct vfe_line *line) +{ + u32 addr; + unsigned int i; + + for (i = 0; i < output->wm_num; i++) { + if (output->buf[1]) + addr = output->buf[1]->addr[i]; + else + addr = 0; + + vfe->ops_gen1->wm_set_pong_addr(vfe, output->wm_idx[i], addr); + if (sync) + vfe->ops_gen1->bus_reload_wm(vfe, output->wm_idx[i]); + } +} + +static void vfe_buf_update_wm_on_next(struct vfe_device *vfe, + struct vfe_output *output) +{ + switch (output->state) { + case VFE_OUTPUT_CONTINUOUS: + vfe_output_frame_drop(vfe, output, 3); + break; + case VFE_OUTPUT_SINGLE: + default: + dev_err_ratelimited(vfe->camss->dev, + "Next buf in wrong state! %d\n", + output->state); + break; + } +} + +static void vfe_buf_update_wm_on_last(struct vfe_device *vfe, + struct vfe_output *output) +{ + switch (output->state) { + case VFE_OUTPUT_CONTINUOUS: + output->state = VFE_OUTPUT_SINGLE; + vfe_output_frame_drop(vfe, output, 1); + break; + case VFE_OUTPUT_SINGLE: + output->state = VFE_OUTPUT_STOPPING; + vfe_output_frame_drop(vfe, output, 0); + break; + default: + dev_err_ratelimited(vfe->camss->dev, + "Last buff in wrong state! %d\n", + output->state); + break; + } +} + +static void vfe_buf_update_wm_on_new(struct vfe_device *vfe, + struct vfe_output *output, + struct camss_buffer *new_buf, + struct vfe_line *line) +{ + int inactive_idx; + + switch (output->state) { + case VFE_OUTPUT_SINGLE: + inactive_idx = !output->gen1.active_buf; + + if (!output->buf[inactive_idx]) { + output->buf[inactive_idx] = new_buf; + + if (inactive_idx) + vfe_output_update_pong_addr(vfe, output, 0, line); + else + vfe_output_update_ping_addr(vfe, output, 0, line); + + vfe_output_frame_drop(vfe, output, 3); + output->state = VFE_OUTPUT_CONTINUOUS; + } else { + vfe_buf_add_pending(output, new_buf); + dev_err_ratelimited(vfe->camss->dev, + "Inactive buffer is busy\n"); + } + break; + + case VFE_OUTPUT_IDLE: + if (!output->buf[0]) { + output->buf[0] = new_buf; + + vfe_output_init_addrs(vfe, output, 1, line); + vfe_output_frame_drop(vfe, output, 1); + + output->state = VFE_OUTPUT_SINGLE; + } else { + vfe_buf_add_pending(output, new_buf); + dev_err_ratelimited(vfe->camss->dev, + "Output idle with buffer set!\n"); + } + break; + + case VFE_OUTPUT_CONTINUOUS: + default: + vfe_buf_add_pending(output, new_buf); + break; + } +} + +/* + * vfe_isr_halt_ack - Process halt ack + * @vfe: VFE Device + */ +static void vfe_isr_halt_ack(struct vfe_device *vfe) +{ + complete(&vfe->halt_complete); + vfe->ops_gen1->halt_clear(vfe); +} + +/* + * vfe_isr_sof - Process start of frame interrupt + * @vfe: VFE Device + * @line_id: VFE line + */ +static void vfe_isr_sof(struct vfe_device *vfe, enum vfe_line_id line_id) +{ + struct vfe_output *output; + unsigned long flags; + + spin_lock_irqsave(&vfe->output_lock, flags); + output = &vfe->line[line_id].output; + if (output->gen1.wait_sof) { + output->gen1.wait_sof = 0; + complete(&output->sof); + } + spin_unlock_irqrestore(&vfe->output_lock, flags); +} + +/* + * vfe_isr_reg_update - Process reg update interrupt + * @vfe: VFE Device + * @line_id: VFE line + */ +static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id) +{ + struct vfe_output *output; + struct vfe_line *line = &vfe->line[line_id]; + unsigned long flags; + + spin_lock_irqsave(&vfe->output_lock, flags); + vfe->ops->reg_update_clear(vfe, line_id); + + output = &line->output; + + if (output->wait_reg_update) { + output->wait_reg_update = 0; + complete(&output->reg_update); + spin_unlock_irqrestore(&vfe->output_lock, flags); + return; + } + + if (output->state == VFE_OUTPUT_STOPPING) { + /* Release last buffer when hw is idle */ + if (output->last_buffer) { + vb2_buffer_done(&output->last_buffer->vb.vb2_buf, + VB2_BUF_STATE_DONE); + output->last_buffer = NULL; + } + output->state = VFE_OUTPUT_IDLE; + + /* Buffers received in stopping state are queued in */ + /* dma pending queue, start next capture here */ + + output->buf[0] = vfe_buf_get_pending(output); + output->buf[1] = vfe_buf_get_pending(output); + + if (!output->buf[0] && output->buf[1]) { + output->buf[0] = output->buf[1]; + output->buf[1] = NULL; + } + + if (output->buf[0]) + output->state = VFE_OUTPUT_SINGLE; + + if (output->buf[1]) + output->state = VFE_OUTPUT_CONTINUOUS; + + switch (output->state) { + case VFE_OUTPUT_SINGLE: + vfe_output_frame_drop(vfe, output, 2); + break; + case VFE_OUTPUT_CONTINUOUS: + vfe_output_frame_drop(vfe, output, 3); + break; + default: + vfe_output_frame_drop(vfe, output, 0); + break; + } + + vfe_output_init_addrs(vfe, output, 1, &vfe->line[line_id]); + } + + spin_unlock_irqrestore(&vfe->output_lock, flags); +} + +/* + * vfe_isr_wm_done - Process write master done interrupt + * @vfe: VFE Device + * @wm: Write master id + */ +static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm) +{ + struct camss_buffer *ready_buf; + struct vfe_output *output; + dma_addr_t *new_addr; + unsigned long flags; + u32 active_index; + u64 ts = ktime_get_ns(); + unsigned int i; + + active_index = vfe->ops_gen1->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(vfe->camss->dev, + "Received wm done for unmapped index\n"); + goto out_unlock; + } + output = &vfe->line[vfe->wm_output_map[wm]].output; + + if (output->gen1.active_buf == active_index && 0) { + dev_err_ratelimited(vfe->camss->dev, + "Active buffer mismatch!\n"); + goto out_unlock; + } + output->gen1.active_buf = active_index; + + ready_buf = output->buf[!active_index]; + if (!ready_buf) { + dev_err_ratelimited(vfe->camss->dev, + "Missing ready buf %d %d!\n", + !active_index, output->state); + goto out_unlock; + } + + ready_buf->vb.vb2_buf.timestamp = ts; + ready_buf->vb.sequence = output->sequence++; + + /* Get next buffer */ + output->buf[!active_index] = vfe_buf_get_pending(output); + if (!output->buf[!active_index]) { + /* No next buffer - set same address */ + new_addr = ready_buf->addr; + vfe_buf_update_wm_on_last(vfe, output); + } else { + new_addr = output->buf[!active_index]->addr; + vfe_buf_update_wm_on_next(vfe, output); + } + + if (active_index) + for (i = 0; i < output->wm_num; i++) + vfe->ops_gen1->wm_set_ping_addr(vfe, output->wm_idx[i], new_addr[i]); + else + for (i = 0; i < output->wm_num; i++) + vfe->ops_gen1->wm_set_pong_addr(vfe, output->wm_idx[i], new_addr[i]); + + spin_unlock_irqrestore(&vfe->output_lock, flags); + + if (output->state == VFE_OUTPUT_STOPPING) + output->last_buffer = ready_buf; + else + vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); + + return; + +out_unlock: + spin_unlock_irqrestore(&vfe->output_lock, flags); +} + +/* + * vfe_queue_buffer - Add empty buffer + * @vid: Video device structure + * @buf: Buffer to be enqueued + * + * Add an empty buffer - depending on the current number of buffers it will be + * put in pending buffer queue or directly given to the hardware to be filled. + * + * Return 0 on success or a negative error code otherwise + */ +static int vfe_queue_buffer(struct camss_video *vid, struct camss_buffer *buf) +{ + 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; + + output = &line->output; + + spin_lock_irqsave(&vfe->output_lock, flags); + + vfe_buf_update_wm_on_new(vfe, output, buf, line); + + spin_unlock_irqrestore(&vfe->output_lock, flags); + + return 0; +} + +#define CALC_WORD(width, M, N) (((width) * (M) + (N) - 1) / (N)) + +int vfe_word_per_line(u32 format, u32 width) +{ + 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(width, 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(width, 2, 8); + break; + } + + return val; +} + +const struct vfe_isr_ops vfe_isr_ops_gen1 = { + .reset_ack = vfe_isr_reset_ack, + .halt_ack = vfe_isr_halt_ack, + .reg_update = vfe_isr_reg_update, + .sof = vfe_isr_sof, + .comp_done = vfe_isr_comp_done, + .wm_done = vfe_isr_wm_done, +}; + +const struct camss_video_ops vfe_video_ops_gen1 = { + .queue_buffer = vfe_queue_buffer, + .flush_buffers = vfe_flush_buffers, +}; diff --git a/drivers/media/platform/qcom/camss/camss-vfe-gen1.h b/drivers/media/platform/qcom/camss/camss-vfe-gen1.h new file mode 100644 index 000000000000..6d5f9656562c --- /dev/null +++ b/drivers/media/platform/qcom/camss/camss-vfe-gen1.h @@ -0,0 +1,117 @@ +/* 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_GEN1_H +#define QC_MSM_CAMSS_VFE_GEN1_H + +#include "camss-vfe.h" + +enum vfe_line_id; +struct vfe_device; +struct vfe_line; +struct vfe_output; + +struct vfe_hw_ops_gen1 { + void (*bus_connect_wm_to_rdi)(struct vfe_device *vfe, u8 wm, enum vfe_line_id id); + void (*bus_disconnect_wm_from_rdi)(struct vfe_device *vfe, u8 wm, enum vfe_line_id id); + void (*bus_enable_wr_if)(struct vfe_device *vfe, u8 enable); + void (*bus_reload_wm)(struct vfe_device *vfe, u8 wm); + int (*camif_wait_for_stop)(struct vfe_device *vfe, struct device *dev); + void (*enable_irq_common)(struct vfe_device *vfe); + 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); + u16 (*get_ub_size)(u8 vfe_id); + void (*halt_clear)(struct vfe_device *vfe); + void (*halt_request)(struct vfe_device *vfe); + void (*set_camif_cfg)(struct vfe_device *vfe, struct vfe_line *line); + void (*set_camif_cmd)(struct vfe_device *vfe, u8 enable); + void (*set_cgc_override)(struct vfe_device *vfe, u8 wm, u8 enable); + void (*set_clamp_cfg)(struct vfe_device *vfe); + void (*set_crop_cfg)(struct vfe_device *vfe, struct vfe_line *line); + void (*set_demux_cfg)(struct vfe_device *vfe, struct vfe_line *line); + void (*set_ds)(struct vfe_device *vfe); + void (*set_module_cfg)(struct vfe_device *vfe, u8 enable); + void (*set_scale_cfg)(struct vfe_device *vfe, struct vfe_line *line); + 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 (*set_qos)(struct vfe_device *vfe); + void (*set_xbar_cfg)(struct vfe_device *vfe, struct vfe_output *output, 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_ub_cfg)(struct vfe_device *vfe, u8 wm, u16 offset, u16 depth); + void (*wm_set_subsample)(struct vfe_device *vfe, u8 wm); + 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_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 (*wm_enable)(struct vfe_device *vfe, u8 wm, u8 enable); +}; + +/* + * vfe_calc_interp_reso - Calculate interpolation mode + * @input: Input resolution + * @output: Output resolution + * + * Return interpolation mode + */ +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; +} + +/* + * vfe_gen1_disable - Disable streaming on VFE line + * @line: VFE line + * + * Return 0 on success or a negative error code otherwise + */ +int vfe_gen1_disable(struct vfe_line *line); + +/* + * vfe_gen1_enable - Enable VFE module + * @line: VFE line + * + * Return 0 on success + */ +int vfe_gen1_enable(struct vfe_line *line); + +/* + * vfe_gen1_enable - Halt VFE module + * @vfe: VFE device + * + * Return 0 on success + */ +int vfe_gen1_halt(struct vfe_device *vfe); + +/* + * vfe_word_per_line - Calculate number of words per frame width + * @format: V4L2 format + * @width: Frame width + * + * Return number of words per frame width + */ +int vfe_word_per_line(u32 format, u32 width); + +extern const struct vfe_isr_ops vfe_isr_ops_gen1; +extern const struct camss_video_ops vfe_video_ops_gen1; + +#endif /* QC_MSM_CAMSS_VFE_GEN1_H */ diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c index fae2b513b2f9..15695fd466c4 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.c +++ b/drivers/media/platform/qcom/camss/camss-vfe.c @@ -26,22 +26,8 @@ #define MSM_VFE_NAME "msm_vfe" -#define vfe_line_array(ptr_line) \ - ((const struct vfe_line (*)[]) &(ptr_line[-(ptr_line->id)])) - -#define to_vfe(ptr_line) \ - container_of(vfe_line_array(ptr_line), struct vfe_device, line) - /* VFE reset timeout */ #define VFE_RESET_TIMEOUT_MS 50 -/* VFE halt timeout */ -#define VFE_HALT_TIMEOUT_MS 100 -/* Max number of frame drop updates per frame */ -#define VFE_FRAME_DROP_UPDATES 2 -/* Frame drop value. VAL + UPDATES - 1 should not exceed 31 */ -#define VFE_FRAME_DROP_VAL 30 - -#define VFE_NEXT_SOF_MS 500 #define SCALER_RATIO_MAX 16 @@ -110,6 +96,32 @@ static const struct vfe_format formats_pix_8x96[] = { { MEDIA_BUS_FMT_YVYU8_2X8, 8 }, }; +static const struct vfe_format formats_rdi_845[] = { + { 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 }, +}; + /* * vfe_get_bpp - map media bus format to bits per pixel * @formats: supported media bus formats array @@ -206,7 +218,8 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, return sink_code; } else if (vfe->camss->version == CAMSS_8x96 || - vfe->camss->version == CAMSS_660) + vfe->camss->version == CAMSS_660 || + vfe->camss->version == CAMSS_845) switch (sink_code) { case MEDIA_BUS_FMT_YUYV8_2X8: { @@ -270,13 +283,7 @@ static u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, return 0; } -/* - * vfe_reset - Trigger reset on VFE module and wait to complete - * @vfe: VFE device - * - * Return 0 on success or a negative error code otherwise - */ -static int vfe_reset(struct vfe_device *vfe) +int vfe_reset(struct vfe_device *vfe) { unsigned long time; @@ -294,35 +301,11 @@ static int vfe_reset(struct vfe_device *vfe) return 0; } -/* - * vfe_halt - Trigger halt on VFE module and wait to complete - * @vfe: VFE device - * - * Return 0 on success or a negative error code otherwise - */ -static int vfe_halt(struct vfe_device *vfe) -{ - unsigned long time; - - reinit_completion(&vfe->halt_complete); - - vfe->ops->halt_request(vfe); - - time = wait_for_completion_timeout(&vfe->halt_complete, - msecs_to_jiffies(VFE_HALT_TIMEOUT_MS)); - if (!time) { - dev_err(vfe->camss->dev, "VFE halt timeout\n"); - return -EIO; - } - - return 0; -} - static void vfe_init_outputs(struct vfe_device *vfe) { int i; - for (i = 0; i < ARRAY_SIZE(vfe->line); i++) { + for (i = 0; i < vfe->line_num; i++) { struct vfe_output *output = &vfe->line[i].output; output->state = VFE_OUTPUT_OFF; @@ -340,71 +323,7 @@ static void vfe_reset_output_maps(struct vfe_device *vfe) vfe->wm_output_map[i] = VFE_LINE_NONE; } -static void vfe_output_init_addrs(struct vfe_device *vfe, - struct vfe_output *output, u8 sync) -{ - u32 ping_addr; - u32 pong_addr; - unsigned int i; - - output->active_buf = 0; - - for (i = 0; i < output->wm_num; i++) { - if (output->buf[0]) - ping_addr = output->buf[0]->addr[i]; - else - ping_addr = 0; - - if (output->buf[1]) - pong_addr = output->buf[1]->addr[i]; - else - pong_addr = ping_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->ops->bus_reload_wm(vfe, output->wm_idx[i]); - } -} - -static void vfe_output_update_ping_addr(struct vfe_device *vfe, - struct vfe_output *output, u8 sync) -{ - u32 addr; - unsigned int i; - - for (i = 0; i < output->wm_num; i++) { - if (output->buf[0]) - addr = output->buf[0]->addr[i]; - else - addr = 0; - - vfe->ops->wm_set_ping_addr(vfe, output->wm_idx[i], addr); - if (sync) - vfe->ops->bus_reload_wm(vfe, output->wm_idx[i]); - } -} - -static void vfe_output_update_pong_addr(struct vfe_device *vfe, - struct vfe_output *output, u8 sync) -{ - u32 addr; - unsigned int i; - - for (i = 0; i < output->wm_num; i++) { - if (output->buf[1]) - addr = output->buf[1]->addr[i]; - else - addr = 0; - - vfe->ops->wm_set_pong_addr(vfe, output->wm_idx[i], addr); - if (sync) - vfe->ops->bus_reload_wm(vfe, output->wm_idx[i]); - } - -} - -static int vfe_reserve_wm(struct vfe_device *vfe, enum vfe_line_id line_id) +int vfe_reserve_wm(struct vfe_device *vfe, enum vfe_line_id line_id) { int ret = -EBUSY; int i; @@ -420,7 +339,7 @@ static int vfe_reserve_wm(struct vfe_device *vfe, enum vfe_line_id line_id) return ret; } -static int vfe_release_wm(struct vfe_device *vfe, u8 wm) +int vfe_release_wm(struct vfe_device *vfe, u8 wm) { if (wm >= ARRAY_SIZE(vfe->wm_output_map)) return -EINVAL; @@ -430,29 +349,7 @@ static int vfe_release_wm(struct vfe_device *vfe, u8 wm) return 0; } -static void vfe_output_frame_drop(struct vfe_device *vfe, - struct vfe_output *output, - u32 drop_pattern) -{ - u8 drop_period; - unsigned int i; - - /* We need to toggle update period to be valid on next frame */ - output->drop_update_idx++; - output->drop_update_idx %= VFE_FRAME_DROP_UPDATES; - drop_period = VFE_FRAME_DROP_VAL + output->drop_update_idx; - - for (i = 0; i < output->wm_num; i++) { - 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->ops->reg_update(vfe, - container_of(output, struct vfe_line, output)->id); -} - -static struct camss_buffer *vfe_buf_get_pending(struct vfe_output *output) +struct camss_buffer *vfe_buf_get_pending(struct vfe_output *output) { struct camss_buffer *buffer = NULL; @@ -466,13 +363,8 @@ static struct camss_buffer *vfe_buf_get_pending(struct vfe_output *output) return buffer; } -/* - * vfe_buf_add_pending - Add output buffer to list of pending - * @output: VFE output - * @buffer: Video buffer - */ -static void vfe_buf_add_pending(struct vfe_output *output, - struct camss_buffer *buffer) +void vfe_buf_add_pending(struct vfe_output *output, + struct camss_buffer *buffer) { INIT_LIST_HEAD(&buffer->queue); list_add_tail(&buffer->queue, &output->pending_bufs); @@ -495,149 +387,7 @@ static void vfe_buf_flush_pending(struct vfe_output *output, } } -static void vfe_buf_update_wm_on_next(struct vfe_device *vfe, - struct vfe_output *output) -{ - switch (output->state) { - case VFE_OUTPUT_CONTINUOUS: - vfe_output_frame_drop(vfe, output, 3); - break; - case VFE_OUTPUT_SINGLE: - default: - dev_err_ratelimited(vfe->camss->dev, - "Next buf in wrong state! %d\n", - output->state); - break; - } -} - -static void vfe_buf_update_wm_on_last(struct vfe_device *vfe, - struct vfe_output *output) -{ - switch (output->state) { - case VFE_OUTPUT_CONTINUOUS: - output->state = VFE_OUTPUT_SINGLE; - vfe_output_frame_drop(vfe, output, 1); - break; - case VFE_OUTPUT_SINGLE: - output->state = VFE_OUTPUT_STOPPING; - vfe_output_frame_drop(vfe, output, 0); - break; - default: - dev_err_ratelimited(vfe->camss->dev, - "Last buff in wrong state! %d\n", - output->state); - break; - } -} - -static void vfe_buf_update_wm_on_new(struct vfe_device *vfe, - struct vfe_output *output, - struct camss_buffer *new_buf) -{ - int inactive_idx; - - switch (output->state) { - case VFE_OUTPUT_SINGLE: - inactive_idx = !output->active_buf; - - if (!output->buf[inactive_idx]) { - output->buf[inactive_idx] = new_buf; - - if (inactive_idx) - vfe_output_update_pong_addr(vfe, output, 0); - else - vfe_output_update_ping_addr(vfe, output, 0); - - vfe_output_frame_drop(vfe, output, 3); - output->state = VFE_OUTPUT_CONTINUOUS; - } else { - vfe_buf_add_pending(output, new_buf); - dev_err_ratelimited(vfe->camss->dev, - "Inactive buffer is busy\n"); - } - break; - - case VFE_OUTPUT_IDLE: - if (!output->buf[0]) { - output->buf[0] = new_buf; - - vfe_output_init_addrs(vfe, output, 1); - - vfe_output_frame_drop(vfe, output, 1); - output->state = VFE_OUTPUT_SINGLE; - } else { - vfe_buf_add_pending(output, new_buf); - dev_err_ratelimited(vfe->camss->dev, - "Output idle with buffer set!\n"); - } - break; - - case VFE_OUTPUT_CONTINUOUS: - default: - vfe_buf_add_pending(output, new_buf); - break; - } -} - -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; - - spin_lock_irqsave(&vfe->output_lock, flags); - - output = &line->output; - if (output->state != VFE_OUTPUT_OFF) { - 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(vfe->camss->dev, "Can not reserve wm\n"); - goto error_get_wm; - } - output->wm_idx[i] = wm_idx; - } - - output->drop_update_idx = 0; - - spin_unlock_irqrestore(&vfe->output_lock, flags); - - return 0; - -error_get_wm: - for (i--; i >= 0; i--) - vfe_release_wm(vfe, output->wm_idx[i]); - output->state = VFE_OUTPUT_OFF; -error: - spin_unlock_irqrestore(&vfe->output_lock, flags); - - return -EINVAL; -} - -static int vfe_put_output(struct vfe_line *line) +int vfe_put_output(struct vfe_line *line) { struct vfe_device *vfe = to_vfe(line); struct vfe_output *output = &line->output; @@ -655,454 +405,27 @@ static int vfe_put_output(struct vfe_line *line) return 0; } -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; - struct media_entity *sensor; - unsigned long flags; - unsigned int frame_skip = 0; - unsigned int i; - u16 ub_size; - - ub_size = ops->get_ub_size(vfe->id); - if (!ub_size) - return -EINVAL; - - sensor = camss_find_sensor(&line->subdev.entity); - if (sensor) { - struct v4l2_subdev *subdev = - media_entity_to_v4l2_subdev(sensor); - - v4l2_subdev_call(subdev, sensor, g_skip_frames, &frame_skip); - /* Max frame skip is 29 frames */ - if (frame_skip > VFE_FRAME_DROP_VAL - 1) - frame_skip = VFE_FRAME_DROP_VAL - 1; - } - - spin_lock_irqsave(&vfe->output_lock, flags); - - ops->reg_update_clear(vfe, line->id); - - if (output->state != VFE_OUTPUT_RESERVED) { - dev_err(vfe->camss->dev, "Output is not in reserved state %d\n", - output->state); - spin_unlock_irqrestore(&vfe->output_lock, flags); - return -EINVAL; - } - output->state = VFE_OUTPUT_IDLE; - - output->buf[0] = vfe_buf_get_pending(output); - output->buf[1] = vfe_buf_get_pending(output); - - if (!output->buf[0] && output->buf[1]) { - output->buf[0] = output->buf[1]; - output->buf[1] = NULL; - } - - if (output->buf[0]) - output->state = VFE_OUTPUT_SINGLE; - - if (output->buf[1]) - output->state = VFE_OUTPUT_CONTINUOUS; - - switch (output->state) { - case VFE_OUTPUT_SINGLE: - vfe_output_frame_drop(vfe, output, 1 << frame_skip); - break; - case VFE_OUTPUT_CONTINUOUS: - vfe_output_frame_drop(vfe, output, 3 << frame_skip); - break; - default: - vfe_output_frame_drop(vfe, output, 0); - break; - } - - output->sequence = 0; - output->wait_sof = 0; - output->wait_reg_update = 0; - reinit_completion(&output->sof); - reinit_completion(&output->reg_update); - - vfe_output_init_addrs(vfe, output, 0); - - if (line->id != VFE_LINE_PIX) { - 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++) { - 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); - ops->wm_enable(vfe, output->wm_idx[i], 1); - ops->bus_reload_wm(vfe, output->wm_idx[i]); - } - 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); - } - - ops->reg_update(vfe, line->id); - - spin_unlock_irqrestore(&vfe->output_lock, flags); - - return 0; -} - -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; - - spin_lock_irqsave(&vfe->output_lock, flags); - - output->wait_sof = 1; - spin_unlock_irqrestore(&vfe->output_lock, flags); - - time = wait_for_completion_timeout(&output->sof, - msecs_to_jiffies(VFE_NEXT_SOF_MS)); - if (!time) - dev_err(vfe->camss->dev, "VFE sof timeout\n"); - - spin_lock_irqsave(&vfe->output_lock, flags); - for (i = 0; i < output->wm_num; i++) - ops->wm_enable(vfe, output->wm_idx[i], 0); - - 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(vfe->camss->dev, "VFE reg update timeout\n"); - - spin_lock_irqsave(&vfe->output_lock, flags); - - if (line->id != VFE_LINE_PIX) { - 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++) { - ops->wm_line_based(vfe, output->wm_idx[i], NULL, i, 0); - ops->set_cgc_override(vfe, output->wm_idx[i], 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); - - ops->set_camif_cmd(vfe, 0); - spin_unlock_irqrestore(&vfe->output_lock, flags); - - ops->camif_wait_for_stop(vfe, vfe->camss->dev); - } - - return 0; -} - -/* - * vfe_enable - Enable streaming on VFE line - * @line: VFE line - * - * Return 0 on success or a negative error code otherwise - */ -static int vfe_enable(struct vfe_line *line) -{ - struct vfe_device *vfe = to_vfe(line); - int ret; - - mutex_lock(&vfe->stream_lock); - - if (!vfe->stream_count) { - vfe->ops->enable_irq_common(vfe); - - vfe->ops->bus_enable_wr_if(vfe, 1); - - vfe->ops->set_qos(vfe); - - vfe->ops->set_ds(vfe); - } - - vfe->stream_count++; - - mutex_unlock(&vfe->stream_lock); - - ret = vfe_get_output(line); - if (ret < 0) - goto error_get_output; - - ret = vfe_enable_output(line); - if (ret < 0) - goto error_enable_output; - - vfe->was_streaming = 1; - - return 0; - - -error_enable_output: - vfe_put_output(line); - -error_get_output: - mutex_lock(&vfe->stream_lock); - - if (vfe->stream_count == 1) - vfe->ops->bus_enable_wr_if(vfe, 0); - - vfe->stream_count--; - - mutex_unlock(&vfe->stream_lock); - - return ret; -} - -/* - * vfe_disable - Disable streaming on VFE line - * @line: VFE line - * - * Return 0 on success or a negative error code otherwise - */ -static int vfe_disable(struct vfe_line *line) -{ - struct vfe_device *vfe = to_vfe(line); - - vfe_disable_output(line); - - vfe_put_output(line); - - mutex_lock(&vfe->stream_lock); - - if (vfe->stream_count == 1) - vfe->ops->bus_enable_wr_if(vfe, 0); - - vfe->stream_count--; - - mutex_unlock(&vfe->stream_lock); - - return 0; -} - -/* - * vfe_isr_sof - Process start of frame interrupt - * @vfe: VFE Device - * @line_id: VFE line - */ -static void vfe_isr_sof(struct vfe_device *vfe, enum vfe_line_id line_id) -{ - struct vfe_output *output; - unsigned long flags; - - spin_lock_irqsave(&vfe->output_lock, flags); - output = &vfe->line[line_id].output; - if (output->wait_sof) { - output->wait_sof = 0; - complete(&output->sof); - } - spin_unlock_irqrestore(&vfe->output_lock, flags); -} - -/* - * vfe_isr_reg_update - Process reg update interrupt - * @vfe: VFE Device - * @line_id: VFE line - */ -static void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id) -{ - struct vfe_output *output; - unsigned long flags; - - spin_lock_irqsave(&vfe->output_lock, flags); - vfe->ops->reg_update_clear(vfe, line_id); - - output = &vfe->line[line_id].output; - - if (output->wait_reg_update) { - output->wait_reg_update = 0; - complete(&output->reg_update); - spin_unlock_irqrestore(&vfe->output_lock, flags); - return; - } - - if (output->state == VFE_OUTPUT_STOPPING) { - /* Release last buffer when hw is idle */ - if (output->last_buffer) { - vb2_buffer_done(&output->last_buffer->vb.vb2_buf, - VB2_BUF_STATE_DONE); - output->last_buffer = NULL; - } - output->state = VFE_OUTPUT_IDLE; - - /* Buffers received in stopping state are queued in */ - /* dma pending queue, start next capture here */ - - output->buf[0] = vfe_buf_get_pending(output); - output->buf[1] = vfe_buf_get_pending(output); - - if (!output->buf[0] && output->buf[1]) { - output->buf[0] = output->buf[1]; - output->buf[1] = NULL; - } - - if (output->buf[0]) - output->state = VFE_OUTPUT_SINGLE; - - if (output->buf[1]) - output->state = VFE_OUTPUT_CONTINUOUS; - - switch (output->state) { - case VFE_OUTPUT_SINGLE: - vfe_output_frame_drop(vfe, output, 2); - break; - case VFE_OUTPUT_CONTINUOUS: - vfe_output_frame_drop(vfe, output, 3); - break; - default: - vfe_output_frame_drop(vfe, output, 0); - break; - } - - vfe_output_init_addrs(vfe, output, 1); - } - - spin_unlock_irqrestore(&vfe->output_lock, flags); -} - -/* - * vfe_isr_wm_done - Process write master done interrupt - * @vfe: VFE Device - * @wm: Write master id - */ -static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm) -{ - struct camss_buffer *ready_buf; - struct vfe_output *output; - dma_addr_t *new_addr; - unsigned long flags; - u32 active_index; - u64 ts = ktime_get_ns(); - unsigned int i; - - 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(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(vfe->camss->dev, - "Active buffer mismatch!\n"); - goto out_unlock; - } - output->active_buf = active_index; - - ready_buf = output->buf[!active_index]; - if (!ready_buf) { - dev_err_ratelimited(vfe->camss->dev, - "Missing ready buf %d %d!\n", - !active_index, output->state); - goto out_unlock; - } - - ready_buf->vb.vb2_buf.timestamp = ts; - ready_buf->vb.sequence = output->sequence++; - - /* Get next buffer */ - output->buf[!active_index] = vfe_buf_get_pending(output); - if (!output->buf[!active_index]) { - /* No next buffer - set same address */ - new_addr = ready_buf->addr; - vfe_buf_update_wm_on_last(vfe, output); - } else { - new_addr = output->buf[!active_index]->addr; - vfe_buf_update_wm_on_next(vfe, output); - } - - if (active_index) - for (i = 0; i < output->wm_num; 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->ops->wm_set_pong_addr(vfe, output->wm_idx[i], - new_addr[i]); - - spin_unlock_irqrestore(&vfe->output_lock, flags); - - if (output->state == VFE_OUTPUT_STOPPING) - output->last_buffer = ready_buf; - else - vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); - - return; - -out_unlock: - spin_unlock_irqrestore(&vfe->output_lock, flags); -} - -/* - * vfe_isr_wm_done - Process composite image done interrupt +/** + * vfe_isr_comp_done() - Process composite image done interrupt * @vfe: VFE Device * @comp: Composite image id */ -static void vfe_isr_comp_done(struct vfe_device *vfe, u8 comp) +void vfe_isr_comp_done(struct vfe_device *vfe, u8 comp) { unsigned int i; for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++) if (vfe->wm_output_map[i] == VFE_LINE_PIX) { - vfe_isr_wm_done(vfe, i); + vfe->isr_ops.wm_done(vfe, i); break; } } -static inline void vfe_isr_reset_ack(struct vfe_device *vfe) +void vfe_isr_reset_ack(struct vfe_device *vfe) { complete(&vfe->reset_complete); } -static inline void vfe_isr_halt_ack(struct vfe_device *vfe) -{ - complete(&vfe->halt_complete); - vfe->ops->halt_clear(vfe); -} - /* * vfe_set_clock_rates - Calculate and set clock rates on VFE module * @vfe: VFE device @@ -1112,11 +435,11 @@ static inline void vfe_isr_halt_ack(struct vfe_device *vfe) static int vfe_set_clock_rates(struct vfe_device *vfe) { struct device *dev = vfe->camss->dev; - u32 pixel_clock[MSM_VFE_LINE_NUM]; + u64 pixel_clock[VFE_LINE_NUM_MAX]; int i, j; int ret; - for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) { + for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) { ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity, &pixel_clock[i]); if (ret) @@ -1127,11 +450,12 @@ static int vfe_set_clock_rates(struct vfe_device *vfe) struct camss_clock *clock = &vfe->clock[i]; if (!strcmp(clock->name, "vfe0") || - !strcmp(clock->name, "vfe1")) { + !strcmp(clock->name, "vfe1") || + !strcmp(clock->name, "vfe_lite")) { u64 min_rate = 0; long rate; - for (j = VFE_LINE_RDI0; j <= VFE_LINE_PIX; j++) { + for (j = VFE_LINE_RDI0; j < vfe->line_num; j++) { u32 tmp; u8 bpp; @@ -1194,11 +518,11 @@ static int vfe_set_clock_rates(struct vfe_device *vfe) */ static int vfe_check_clock_rates(struct vfe_device *vfe) { - u32 pixel_clock[MSM_VFE_LINE_NUM]; + u64 pixel_clock[VFE_LINE_NUM_MAX]; int i, j; int ret; - for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) { + for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) { ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity, &pixel_clock[i]); if (ret) @@ -1213,7 +537,7 @@ static int vfe_check_clock_rates(struct vfe_device *vfe) u64 min_rate = 0; unsigned long rate; - for (j = VFE_LINE_RDI0; j <= VFE_LINE_PIX; j++) { + for (j = VFE_LINE_RDI0; j < vfe->line_num; j++) { u32 tmp; u8 bpp; @@ -1256,7 +580,7 @@ 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); + ret = vfe->ops->pm_domain_on(vfe); if (ret < 0) goto error_pm_domain; @@ -1296,7 +620,7 @@ error_reset: error_pm_runtime_get: pm_runtime_put_sync(vfe->camss->dev); - camss_pm_domain_off(vfe->camss, vfe->id); + vfe->ops->pm_domain_off(vfe); error_pm_domain: mutex_unlock(&vfe->power_lock); @@ -1318,11 +642,11 @@ static void vfe_put(struct vfe_device *vfe) } else if (vfe->power_count == 1) { if (vfe->was_streaming) { vfe->was_streaming = 0; - vfe_halt(vfe); + vfe->ops->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->ops->pm_domain_off(vfe); } vfe->power_count--; @@ -1332,35 +656,6 @@ exit: } /* - * vfe_queue_buffer - Add empty buffer - * @vid: Video device structure - * @buf: Buffer to be enqueued - * - * Add an empty buffer - depending on the current number of buffers it will be - * put in pending buffer queue or directly given to the hardware to be filled. - * - * Return 0 on success or a negative error code otherwise - */ -static int vfe_queue_buffer(struct camss_video *vid, - struct camss_buffer *buf) -{ - 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; - - output = &line->output; - - spin_lock_irqsave(&vfe->output_lock, flags); - - vfe_buf_update_wm_on_new(vfe, output, buf); - - spin_unlock_irqrestore(&vfe->output_lock, flags); - - return 0; -} - -/* * vfe_flush_buffers - Return all vb2 buffers * @vid: Video device structure * @state: vb2 buffer state of the returned buffers @@ -1370,8 +665,8 @@ static int vfe_queue_buffer(struct camss_video *vid, * * Return 0 on success or a negative error code otherwise */ -static int vfe_flush_buffers(struct camss_video *vid, - enum vb2_buffer_state state) +int vfe_flush_buffers(struct camss_video *vid, + enum vb2_buffer_state state) { struct vfe_line *line = container_of(vid, struct vfe_line, video_out); struct vfe_device *vfe = to_vfe(line); @@ -1442,12 +737,12 @@ static int vfe_set_stream(struct v4l2_subdev *sd, int enable) int ret; if (enable) { - ret = vfe_enable(line); + ret = vfe->ops->vfe_enable(line); if (ret < 0) dev_err(vfe->camss->dev, "Failed to enable vfe outputs\n"); } else { - ret = vfe_disable(line); + ret = vfe->ops->vfe_disable(line); if (ret < 0) dev_err(vfe->camss->dev, "Failed to disable vfe outputs\n"); @@ -1985,13 +1280,6 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *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; - switch (camss->version) { case CAMSS_8x16: vfe->ops = &vfe_ops_4_1; @@ -2002,9 +1290,14 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, case CAMSS_660: vfe->ops = &vfe_ops_4_8; break; + + case CAMSS_845: + vfe->ops = &vfe_ops_170; + break; default: return -EINVAL; } + vfe->ops->subdev_init(dev, vfe); /* Memory */ @@ -2086,7 +1379,7 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, vfe->id = id; vfe->reg_update = 0; - for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) { + for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) { struct vfe_line *l = &vfe->line[i]; l->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; @@ -2112,6 +1405,9 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, l->formats = formats_rdi_8x96; l->nformats = ARRAY_SIZE(formats_rdi_8x96); } + } else if (camss->version == CAMSS_845) { + l->formats = formats_rdi_845; + l->nformats = ARRAY_SIZE(formats_rdi_845); } else { return -EINVAL; } @@ -2209,11 +1505,6 @@ static const struct media_entity_operations vfe_media_ops = { .link_validate = v4l2_subdev_link_validate, }; -static const struct camss_video_ops camss_vfe_video_ops = { - .queue_buffer = vfe_queue_buffer, - .flush_buffers = vfe_flush_buffers, -}; - /* * msm_vfe_register_entities - Register subdev node for VFE module * @vfe: VFE device @@ -2236,7 +1527,7 @@ int msm_vfe_register_entities(struct vfe_device *vfe, int ret; int i; - for (i = 0; i < ARRAY_SIZE(vfe->line); i++) { + for (i = 0; i < vfe->line_num; i++) { char name[32]; sd = &vfe->line[i].subdev; @@ -2279,7 +1570,7 @@ int msm_vfe_register_entities(struct vfe_device *vfe, goto error_reg_subdev; } - video_out->ops = &camss_vfe_video_ops; + video_out->ops = &vfe->video_ops; video_out->bpl_alignment = 8; video_out->line_based = 0; if (i == VFE_LINE_PIX) { @@ -2343,7 +1634,7 @@ void msm_vfe_unregister_entities(struct vfe_device *vfe) mutex_destroy(&vfe->power_lock); mutex_destroy(&vfe->stream_lock); - for (i = 0; i < ARRAY_SIZE(vfe->line); i++) { + for (i = 0; i < vfe->line_num; i++) { struct v4l2_subdev *sd = &vfe->line[i].subdev; struct camss_video *video_out = &vfe->line[i].video_out; diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h index 5bce6736e4bb..844b9275031d 100644 --- a/drivers/media/platform/qcom/camss/camss-vfe.h +++ b/drivers/media/platform/qcom/camss/camss-vfe.h @@ -17,22 +17,34 @@ #include <media/v4l2-subdev.h> #include "camss-video.h" +#include "camss-vfe-gen1.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 +/* VFE halt timeout */ +#define VFE_HALT_TIMEOUT_MS 100 +/* Frame drop value. VAL + UPDATES - 1 should not exceed 31 */ +#define VFE_FRAME_DROP_VAL 30 + +#define vfe_line_array(ptr_line) \ + ((const struct vfe_line (*)[]) &(ptr_line)[-(ptr_line)->id]) + +#define to_vfe(ptr_line) \ + container_of(vfe_line_array(ptr_line), struct vfe_device, line) + enum vfe_output_state { VFE_OUTPUT_OFF, VFE_OUTPUT_RESERVED, VFE_OUTPUT_SINGLE, VFE_OUTPUT_CONTINUOUS, VFE_OUTPUT_IDLE, - VFE_OUTPUT_STOPPING + VFE_OUTPUT_STOPPING, + VFE_OUTPUT_ON, }; enum vfe_line_id { @@ -40,23 +52,34 @@ enum vfe_line_id { VFE_LINE_RDI0 = 0, VFE_LINE_RDI1 = 1, VFE_LINE_RDI2 = 2, - VFE_LINE_PIX = 3 + VFE_LINE_NUM_GEN2 = 3, + VFE_LINE_PIX = 3, + VFE_LINE_NUM_GEN1 = 4, + VFE_LINE_NUM_MAX = 4 }; 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; + union { + struct { + int active_buf; + int wait_sof; + } gen1; + struct { + int active_num; + } gen2; + }; enum vfe_output_state state; unsigned int sequence; - int wait_sof; + int wait_reg_update; struct completion sof; struct completion reg_update; @@ -78,59 +101,21 @@ struct vfe_line { 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 (*enable_irq_common)(struct vfe_device *vfe); 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 (*hw_version_read)(struct vfe_device *vfe, struct device *dev); + irqreturn_t (*isr)(int irq, void *dev); + void (*isr_read)(struct vfe_device *vfe, u32 *value0, u32 *value1); + void (*pm_domain_off)(struct vfe_device *vfe); + int (*pm_domain_on)(struct vfe_device *vfe); 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 (*subdev_init)(struct device *dev, struct vfe_device *vfe); + int (*vfe_disable)(struct vfe_line *line); + int (*vfe_enable)(struct vfe_line *line); + int (*vfe_halt)(struct vfe_device *vfe); void (*violation_read)(struct vfe_device *vfe); - irqreturn_t (*isr)(int irq, void *dev); }; struct vfe_isr_ops { @@ -158,11 +143,14 @@ struct vfe_device { 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]; + struct vfe_line line[VFE_LINE_NUM_MAX]; + u8 line_num; u32 reg_update; u8 was_streaming; const struct vfe_hw_ops *ops; + const struct vfe_hw_ops_gen1 *ops_gen1; struct vfe_isr_ops isr_ops; + struct camss_video_ops video_ops; }; struct resources; @@ -178,8 +166,40 @@ 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); +/* + * vfe_buf_add_pending - Add output buffer to list of pending + * @output: VFE output + * @buffer: Video buffer + */ +void vfe_buf_add_pending(struct vfe_output *output, struct camss_buffer *buffer); + +struct camss_buffer *vfe_buf_get_pending(struct vfe_output *output); + +int vfe_flush_buffers(struct camss_video *vid, enum vb2_buffer_state state); + +/* + * vfe_isr_comp_done - Process composite image done interrupt + * @vfe: VFE Device + * @comp: Composite image id + */ +void vfe_isr_comp_done(struct vfe_device *vfe, u8 comp); + +void vfe_isr_reset_ack(struct vfe_device *vfe); +int vfe_put_output(struct vfe_line *line); +int vfe_release_wm(struct vfe_device *vfe, u8 wm); +int vfe_reserve_wm(struct vfe_device *vfe, enum vfe_line_id line_id); + +/* + * vfe_reset - Trigger reset on VFE module and wait to complete + * @vfe: VFE device + * + * Return 0 on success or a negative error code otherwise + */ +int vfe_reset(struct vfe_device *vfe); + extern const struct vfe_hw_ops vfe_ops_4_1; extern const struct vfe_hw_ops vfe_ops_4_7; extern const struct vfe_hw_ops vfe_ops_4_8; +extern const struct vfe_hw_ops vfe_ops_170; #endif /* QC_MSM_CAMSS_VFE_H */ diff --git a/drivers/media/platform/qcom/camss/camss-video.c b/drivers/media/platform/qcom/camss/camss-video.c index 97cea7c4d769..f282275af626 100644 --- a/drivers/media/platform/qcom/camss/camss-video.c +++ b/drivers/media/platform/qcom/camss/camss-video.c @@ -133,6 +133,55 @@ static const struct camss_format_info formats_rdi_8x96[] = { { { 1, 1 } }, { { 1, 1 } }, { 16 } }, }; +static const struct camss_format_info formats_rdi_845[] = { + { 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 } }, @@ -960,6 +1009,9 @@ int msm_video_register(struct camss_video *video, struct v4l2_device *v4l2_dev, video->formats = formats_rdi_8x96; video->nformats = ARRAY_SIZE(formats_rdi_8x96); } + } else if (video->camss->version == CAMSS_845) { + video->formats = formats_rdi_845; + video->nformats = ARRAY_SIZE(formats_rdi_845); } else { ret = -EINVAL; goto error_video_register; diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c index 7c0f669f8aa6..ef100d5f7763 100644 --- a/drivers/media/platform/qcom/camss/camss.c +++ b/drivers/media/platform/qcom/camss/camss.c @@ -465,6 +465,203 @@ static const struct resources vfe_res_660[] = { } }; +static const struct resources csiphy_res_845[] = { + /* CSIPHY0 */ + { + .regulator = { NULL }, + .clock = { "camnoc_axi", "soc_ahb", "slow_ahb_src", + "cpas_ahb", "cphy_rx_src", "csiphy0", + "csiphy0_timer_src", "csiphy0_timer" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 19200000, 240000000, 269333333 } }, + .reg = { "csiphy0" }, + .interrupt = { "csiphy0" } + }, + + /* CSIPHY1 */ + { + .regulator = { NULL }, + .clock = { "camnoc_axi", "soc_ahb", "slow_ahb_src", + "cpas_ahb", "cphy_rx_src", "csiphy1", + "csiphy1_timer_src", "csiphy1_timer" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 19200000, 240000000, 269333333 } }, + .reg = { "csiphy1" }, + .interrupt = { "csiphy1" } + }, + + /* CSIPHY2 */ + { + .regulator = { NULL }, + .clock = { "camnoc_axi", "soc_ahb", "slow_ahb_src", + "cpas_ahb", "cphy_rx_src", "csiphy2", + "csiphy2_timer_src", "csiphy2_timer" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 19200000, 240000000, 269333333 } }, + .reg = { "csiphy2" }, + .interrupt = { "csiphy2" } + }, + + /* CSIPHY3 */ + { + .regulator = { NULL }, + .clock = { "camnoc_axi", "soc_ahb", "slow_ahb_src", + "cpas_ahb", "cphy_rx_src", "csiphy3", + "csiphy3_timer_src", "csiphy3_timer" }, + .clock_rate = { { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 0 }, + { 19200000, 240000000, 269333333 } }, + .reg = { "csiphy3" }, + .interrupt = { "csiphy3" } + } +}; + +static const struct resources csid_res_845[] = { + /* CSID0 */ + { + .regulator = { "vdda-csi0" }, + .clock = { "cpas_ahb", "cphy_rx_src", "slow_ahb_src", + "soc_ahb", "vfe0", "vfe0_src", + "vfe0_cphy_rx", "csi0", + "csi0_src" }, + .clock_rate = { { 0 }, + { 384000000 }, + { 80000000 }, + { 0 }, + { 19200000, 100000000, 320000000, 404000000, 480000000, 600000000 }, + { 320000000 }, + { 0 }, + { 19200000, 75000000, 384000000, 538666667 }, + { 384000000 } }, + .reg = { "csid0" }, + .interrupt = { "csid0" } + }, + + /* CSID1 */ + { + .regulator = { "vdda-csi1" }, + .clock = { "cpas_ahb", "cphy_rx_src", "slow_ahb_src", + "soc_ahb", "vfe1", "vfe1_src", + "vfe1_cphy_rx", "csi1", + "csi1_src" }, + .clock_rate = { { 0 }, + { 384000000 }, + { 80000000 }, + { 0 }, + { 19200000, 100000000, 320000000, 404000000, 480000000, 600000000 }, + { 320000000 }, + { 0 }, + { 19200000, 75000000, 384000000, 538666667 }, + { 384000000 } }, + .reg = { "csid1" }, + .interrupt = { "csid1" } + }, + + /* CSID2 */ + { + .regulator = { "vdda-csi2" }, + .clock = { "cpas_ahb", "cphy_rx_src", "slow_ahb_src", + "soc_ahb", "vfe_lite", "vfe_lite_src", + "vfe_lite_cphy_rx", "csi2", + "csi2_src" }, + .clock_rate = { { 0 }, + { 384000000 }, + { 80000000 }, + { 0 }, + { 19200000, 100000000, 320000000, 404000000, 480000000, 600000000 }, + { 320000000 }, + { 0 }, + { 19200000, 75000000, 384000000, 538666667 }, + { 384000000 } }, + .reg = { "csid2" }, + .interrupt = { "csid2" } + } +}; + +static const struct resources vfe_res_845[] = { + /* VFE0 */ + { + .regulator = { NULL }, + .clock = { "camnoc_axi", "cpas_ahb", "slow_ahb_src", + "soc_ahb", "vfe0", "vfe0_axi", + "vfe0_src", "csi0", + "csi0_src"}, + .clock_rate = { { 0 }, + { 0 }, + { 80000000 }, + { 0 }, + { 19200000, 100000000, 320000000, 404000000, 480000000, 600000000 }, + { 0 }, + { 320000000 }, + { 19200000, 75000000, 384000000, 538666667 }, + { 384000000 } }, + .reg = { "vfe0" }, + .interrupt = { "vfe0" } + }, + + /* VFE1 */ + { + .regulator = { NULL }, + .clock = { "camnoc_axi", "cpas_ahb", "slow_ahb_src", + "soc_ahb", "vfe1", "vfe1_axi", + "vfe1_src", "csi1", + "csi1_src"}, + .clock_rate = { { 0 }, + { 0 }, + { 80000000 }, + { 0 }, + { 19200000, 100000000, 320000000, 404000000, 480000000, 600000000 }, + { 0 }, + { 320000000 }, + { 19200000, 75000000, 384000000, 538666667 }, + { 384000000 } }, + .reg = { "vfe1" }, + .interrupt = { "vfe1" } + }, + + /* VFE-lite */ + { + .regulator = { NULL }, + .clock = { "camnoc_axi", "cpas_ahb", "slow_ahb_src", + "soc_ahb", "vfe_lite", + "vfe_lite_src", "csi2", + "csi2_src"}, + .clock_rate = { { 0 }, + { 0 }, + { 80000000 }, + { 0 }, + { 19200000, 100000000, 320000000, 404000000, 480000000, 600000000 }, + { 320000000 }, + { 19200000, 75000000, 384000000, 538666667 }, + { 384000000 } }, + .reg = { "vfe_lite" }, + .interrupt = { "vfe_lite" } + } +}; + /* * camss_add_clock_margin - Add margin to clock frequency rate * @rate: Clock frequency rate @@ -548,6 +745,29 @@ struct media_entity *camss_find_sensor(struct media_entity *entity) } } +/** + * camss_get_link_freq - Get link frequency from sensor + * @entity: Media entity in the current pipeline + * @bpp: Number of bits per pixel for the current format + * @lanes: Number of lanes in the link to the sensor + * + * Return link frequency on success or a negative error code otherwise + */ +s64 camss_get_link_freq(struct media_entity *entity, unsigned int bpp, + unsigned int lanes) +{ + struct media_entity *sensor; + struct v4l2_subdev *subdev; + + sensor = camss_find_sensor(entity); + if (!sensor) + return -ENODEV; + + subdev = media_entity_to_v4l2_subdev(sensor); + + return v4l2_get_link_freq(subdev->ctrl_handler, bpp, 2 * lanes); +} + /* * camss_get_pixel_clock - Get pixel clock rate from sensor * @entity: Media entity in the current pipeline @@ -555,7 +775,7 @@ struct media_entity *camss_find_sensor(struct media_entity *entity) * * Return 0 on success or a negative error code otherwise */ -int camss_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock) +int camss_get_pixel_clock(struct media_entity *entity, u64 *pixel_clock) { struct media_entity *sensor; struct v4l2_subdev *subdev; @@ -579,24 +799,24 @@ int camss_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock) int camss_pm_domain_on(struct camss *camss, int id) { - if (camss->version == CAMSS_8x96 || - camss->version == CAMSS_660) { - camss->genpd_link[id] = device_link_add(camss->dev, - camss->genpd[id], DL_FLAG_STATELESS | - DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE); + int ret = 0; + + if (id < camss->vfe_num) { + struct vfe_device *vfe = &camss->vfe[id]; - if (!camss->genpd_link[id]) - return -EINVAL; + ret = vfe->ops->pm_domain_on(vfe); } - return 0; + return ret; } void camss_pm_domain_off(struct camss *camss, int id) { - if (camss->version == CAMSS_8x96 || - camss->version == CAMSS_660) - device_link_del(camss->genpd_link[id]); + if (id < camss->vfe_num) { + struct vfe_device *vfe = &camss->vfe[id]; + + vfe->ops->pm_domain_off(vfe); + } } /* @@ -719,6 +939,12 @@ static int camss_init_subdevices(struct camss *camss) csid_res = csid_res_660; ispif_res = &ispif_res_660; vfe_res = vfe_res_660; + } else if (camss->version == CAMSS_845) { + csiphy_res = csiphy_res_845; + csid_res = csid_res_845; + /* Titan VFEs don't have an ISPIF */ + ispif_res = NULL; + vfe_res = vfe_res_845; } else { return -EINVAL; } @@ -745,10 +971,10 @@ static int camss_init_subdevices(struct camss *camss) } } - ret = msm_ispif_subdev_init(&camss->ispif, ispif_res); + ret = msm_ispif_subdev_init(camss, ispif_res); if (ret < 0) { dev_err(camss->dev, "Failed to init ispif sub-device: %d\n", - ret); + ret); return ret; } @@ -798,10 +1024,11 @@ static int camss_register_entities(struct camss *camss) } } - ret = msm_ispif_register_entities(&camss->ispif, &camss->v4l2_dev); + ret = msm_ispif_register_entities(camss->ispif, + &camss->v4l2_dev); if (ret < 0) { dev_err(camss->dev, "Failed to register ispif entities: %d\n", - ret); + ret); goto err_reg_ispif; } @@ -835,43 +1062,68 @@ static int camss_register_entities(struct camss *camss) } } - 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, - &camss->ispif.line[j].subdev.entity, - MSM_ISPIF_PAD_SINK, - 0); - if (ret < 0) { - dev_err(camss->dev, - "Failed to link %s->%s entities: %d\n", - camss->csid[i].subdev.entity.name, - camss->ispif.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++) { + if (camss->ispif) { + for (i = 0; i < camss->csid_num; i++) { + for (j = 0; j < camss->ispif->line_num; 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, + &camss->csid[i].subdev.entity, + MSM_CSID_PAD_SRC, + &camss->ispif->line[j].subdev.entity, + MSM_ISPIF_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, + camss->csid[i].subdev.entity.name, + camss->ispif->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 < camss->vfe[k].line_num; j++) { + struct v4l2_subdev *ispif = &camss->ispif->line[i].subdev; + struct v4l2_subdev *vfe = &camss->vfe[k].line[j].subdev; + + ret = media_create_pad_link(&ispif->entity, + MSM_ISPIF_PAD_SRC, + &vfe->entity, + MSM_VFE_PAD_SINK, + 0); + if (ret < 0) { + dev_err(camss->dev, + "Failed to link %s->%s entities: %d\n", + ispif->entity.name, + vfe->entity.name, + ret); + goto err_link; + } + } + } else { + for (i = 0; i < camss->csid_num; i++) + for (k = 0; k < camss->vfe_num; k++) + for (j = 0; j < camss->vfe[k].line_num; j++) { + struct v4l2_subdev *csid = &camss->csid[i].subdev; + struct v4l2_subdev *vfe = &camss->vfe[k].line[j].subdev; + + ret = media_create_pad_link(&csid->entity, + MSM_CSID_PAD_SRC, + &vfe->entity, + MSM_VFE_PAD_SINK, + 0); + if (ret < 0) { + dev_err(camss->dev, + "Failed to link %s->%s entities: %d\n", + csid->entity.name, + vfe->entity.name, + ret); + goto err_link; + } + } + } return 0; @@ -881,8 +1133,8 @@ err_reg_vfe: for (i--; i >= 0; i--) msm_vfe_unregister_entities(&camss->vfe[i]); - msm_ispif_unregister_entities(&camss->ispif); err_reg_ispif: + msm_ispif_unregister_entities(camss->ispif); i = camss->csid_num; err_reg_csid: @@ -913,7 +1165,7 @@ static void camss_unregister_entities(struct camss *camss) for (i = 0; i < camss->csid_num; i++) msm_csid_unregister_entity(&camss->csid[i]); - msm_ispif_unregister_entities(&camss->ispif); + msm_ispif_unregister_entities(camss->ispif); for (i = 0; i < camss->vfe_num; i++) msm_vfe_unregister_entities(&camss->vfe[i]); @@ -988,6 +1240,49 @@ static const struct media_device_ops camss_media_ops = { .link_notify = v4l2_pipeline_link_notify, }; +static int camss_configure_pd(struct camss *camss) +{ + int nbr_pm_domains = 0; + int last_pm_domain = 0; + int i; + int ret; + + if (camss->version == CAMSS_8x96 || + camss->version == CAMSS_660) + nbr_pm_domains = PM_DOMAIN_GEN1_COUNT; + else if (camss->version == CAMSS_845) + nbr_pm_domains = PM_DOMAIN_GEN2_COUNT; + + for (i = 0; i < nbr_pm_domains; i++) { + camss->genpd[i] = dev_pm_domain_attach_by_id(camss->dev, i); + if (IS_ERR(camss->genpd[i])) { + ret = PTR_ERR(camss->genpd[i]); + goto fail_pm; + } + + camss->genpd_link[i] = device_link_add(camss->dev, camss->genpd[i], + DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME | + DL_FLAG_RPM_ACTIVE); + if (!camss->genpd_link[i]) { + dev_pm_domain_detach(camss->genpd[i], true); + ret = -EINVAL; + goto fail_pm; + } + + last_pm_domain = i; + } + + return 0; + +fail_pm: + for (i = 0; i < last_pm_domain; i++) { + device_link_del(camss->genpd_link[i]); + dev_pm_domain_detach(camss->genpd[i], true); + } + + return ret; +} + /* * camss_probe - Probe CAMSS platform device * @pdev: Pointer to CAMSS platform device @@ -1025,6 +1320,12 @@ static int camss_probe(struct platform_device *pdev) camss->csiphy_num = 3; camss->csid_num = 4; camss->vfe_num = 2; + } else if (of_device_is_compatible(dev->of_node, + "qcom,sdm845-camss")) { + camss->version = CAMSS_845; + camss->csiphy_num = 4; + camss->csid_num = 3; + camss->vfe_num = 3; } else { ret = -EINVAL; goto err_free; @@ -1044,6 +1345,15 @@ static int camss_probe(struct platform_device *pdev) goto err_free; } + if (camss->version == CAMSS_8x16 || + camss->version == CAMSS_8x96) { + camss->ispif = devm_kcalloc(dev, 1, sizeof(*camss->ispif), GFP_KERNEL); + if (!camss->ispif) { + ret = -ENOMEM; + goto err_free; + } + } + camss->vfe = devm_kcalloc(dev, camss->vfe_num, sizeof(*camss->vfe), GFP_KERNEL); if (!camss->vfe) { @@ -1111,20 +1421,10 @@ static int camss_probe(struct platform_device *pdev) } } - if (camss->version == CAMSS_8x96 || - camss->version == CAMSS_660) { - 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]); - } + ret = camss_configure_pd(camss); + if (ret < 0) { + dev_err(dev, "Failed to configure power domains: %d\n", ret); + return ret; } pm_runtime_enable(dev); @@ -1145,6 +1445,9 @@ err_free: void camss_delete(struct camss *camss) { + int nbr_pm_domains = 0; + int i; + v4l2_device_unregister(&camss->v4l2_dev); media_device_unregister(&camss->media_dev); media_device_cleanup(&camss->media_dev); @@ -1152,9 +1455,14 @@ void camss_delete(struct camss *camss) pm_runtime_disable(camss->dev); if (camss->version == CAMSS_8x96 || - camss->version == CAMSS_660) { - dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE0], true); - dev_pm_domain_detach(camss->genpd[PM_DOMAIN_VFE1], true); + camss->version == CAMSS_660) + nbr_pm_domains = PM_DOMAIN_GEN1_COUNT; + else if (camss->version == CAMSS_845) + nbr_pm_domains = PM_DOMAIN_GEN2_COUNT; + + for (i = 0; i < nbr_pm_domains; i++) { + device_link_del(camss->genpd_link[i]); + dev_pm_domain_detach(camss->genpd[i], true); } kfree(camss); @@ -1184,6 +1492,7 @@ static const struct of_device_id camss_dt_match[] = { { .compatible = "qcom,msm8916-camss" }, { .compatible = "qcom,msm8996-camss" }, { .compatible = "qcom,sdm660-camss" }, + { .compatible = "qcom,sdm845-camss" }, { } }; diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h index 3a0484683cd6..dc8b4154f92b 100644 --- a/drivers/media/platform/qcom/camss/camss.h +++ b/drivers/media/platform/qcom/camss/camss.h @@ -57,15 +57,18 @@ struct resources_ispif { }; enum pm_domain { - PM_DOMAIN_VFE0, - PM_DOMAIN_VFE1, - PM_DOMAIN_COUNT + PM_DOMAIN_VFE0 = 0, + PM_DOMAIN_VFE1 = 1, + PM_DOMAIN_GEN1_COUNT = 2, /* CAMSS series of ISPs */ + PM_DOMAIN_VFELITE = 2, /* VFELITE / TOP GDSC */ + PM_DOMAIN_GEN2_COUNT = 3, /* Titan series of ISPs */ }; enum camss_version { CAMSS_8x16, CAMSS_8x96, CAMSS_660, + CAMSS_845, }; struct camss { @@ -78,12 +81,12 @@ struct camss { struct csiphy_device *csiphy; int csid_num; struct csid_device *csid; - struct ispif_device ispif; + struct ispif_device *ispif; 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 device *genpd[PM_DOMAIN_GEN2_COUNT]; + struct device_link *genpd_link[PM_DOMAIN_GEN2_COUNT]; }; struct camss_camera_interface { @@ -108,7 +111,9 @@ int camss_enable_clocks(int nclocks, struct camss_clock *clock, struct device *dev); void camss_disable_clocks(int nclocks, struct camss_clock *clock); struct media_entity *camss_find_sensor(struct media_entity *entity); -int camss_get_pixel_clock(struct media_entity *entity, u32 *pixel_clock); +s64 camss_get_link_freq(struct media_entity *entity, unsigned int bpp, + unsigned int lanes); +int camss_get_pixel_clock(struct media_entity *entity, u64 *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); diff --git a/drivers/media/platform/qcom/venus/core.c b/drivers/media/platform/qcom/venus/core.c index f9896c121fd8..54bac7ec14c5 100644 --- a/drivers/media/platform/qcom/venus/core.c +++ b/drivers/media/platform/qcom/venus/core.c @@ -5,6 +5,7 @@ */ #include <linux/init.h> #include <linux/interconnect.h> +#include <linux/io.h> #include <linux/ioctl.h> #include <linux/delay.h> #include <linux/devcoredump.h> @@ -22,6 +23,7 @@ #include "core.h" #include "firmware.h" #include "pm_helpers.h" +#include "hfi_venus_io.h" static void venus_coredump(struct venus_core *core) { @@ -206,6 +208,27 @@ err: return ret; } +static void venus_assign_register_offsets(struct venus_core *core) +{ + if (IS_V6(core)) { + core->vbif_base = core->base + VBIF_BASE; + core->cpu_base = core->base + CPU_BASE_V6; + core->cpu_cs_base = core->base + CPU_CS_BASE_V6; + core->cpu_ic_base = core->base + CPU_IC_BASE_V6; + core->wrapper_base = core->base + WRAPPER_BASE_V6; + core->wrapper_tz_base = core->base + WRAPPER_TZ_BASE_V6; + core->aon_base = core->base + AON_BASE_V6; + } else { + core->vbif_base = core->base + VBIF_BASE; + core->cpu_base = core->base + CPU_BASE; + core->cpu_cs_base = core->base + CPU_CS_BASE; + core->cpu_ic_base = core->base + CPU_IC_BASE; + core->wrapper_base = core->base + WRAPPER_BASE; + core->wrapper_tz_base = NULL; + core->aon_base = NULL; + } +} + static int venus_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -218,18 +241,17 @@ static int venus_probe(struct platform_device *pdev) return -ENOMEM; core->dev = dev; - platform_set_drvdata(pdev, core); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); core->base = devm_ioremap_resource(dev, r); if (IS_ERR(core->base)) return PTR_ERR(core->base); - core->video_path = of_icc_get(dev, "video-mem"); + core->video_path = devm_of_icc_get(dev, "video-mem"); if (IS_ERR(core->video_path)) return PTR_ERR(core->video_path); - core->cpucfg_path = of_icc_get(dev, "cpu-cfg"); + core->cpucfg_path = devm_of_icc_get(dev, "cpu-cfg"); if (IS_ERR(core->cpucfg_path)) return PTR_ERR(core->cpucfg_path); @@ -248,7 +270,7 @@ static int venus_probe(struct platform_device *pdev) return -ENODEV; if (core->pm_ops->core_get) { - ret = core->pm_ops->core_get(dev); + ret = core->pm_ops->core_get(core); if (ret) return ret; } @@ -273,6 +295,14 @@ static int venus_probe(struct platform_device *pdev) if (ret) goto err_core_put; + venus_assign_register_offsets(core); + + ret = v4l2_device_register(dev, &core->v4l2_dev); + if (ret) + goto err_core_deinit; + + platform_set_drvdata(pdev, core); + pm_runtime_enable(dev); ret = pm_runtime_get_sync(dev); @@ -307,10 +337,6 @@ static int venus_probe(struct platform_device *pdev) if (ret) goto err_venus_shutdown; - ret = v4l2_device_register(dev, &core->v4l2_dev); - if (ret) - goto err_core_deinit; - ret = pm_runtime_put_sync(dev); if (ret) { pm_runtime_get_noresume(dev); @@ -323,8 +349,6 @@ static int venus_probe(struct platform_device *pdev) err_dev_unregister: v4l2_device_unregister(&core->v4l2_dev); -err_core_deinit: - hfi_core_deinit(core, false); err_venus_shutdown: venus_shutdown(core); err_runtime_disable: @@ -332,9 +356,11 @@ err_runtime_disable: pm_runtime_set_suspended(dev); pm_runtime_disable(dev); hfi_destroy(core); +err_core_deinit: + hfi_core_deinit(core, false); err_core_put: if (core->pm_ops->core_put) - core->pm_ops->core_put(dev); + core->pm_ops->core_put(core); return ret; } @@ -360,14 +386,12 @@ static int venus_remove(struct platform_device *pdev) pm_runtime_disable(dev); if (pm_ops->core_put) - pm_ops->core_put(dev); + pm_ops->core_put(core); - hfi_destroy(core); + v4l2_device_unregister(&core->v4l2_dev); - icc_put(core->video_path); - icc_put(core->cpucfg_path); + hfi_destroy(core); - v4l2_device_unregister(&core->v4l2_dev); mutex_destroy(&core->pm_lock); mutex_destroy(&core->lock); venus_dbgfs_deinit(core); @@ -396,7 +420,7 @@ static __maybe_unused int venus_runtime_suspend(struct device *dev) return ret; if (pm_ops->core_power) { - ret = pm_ops->core_power(dev, POWER_OFF); + ret = pm_ops->core_power(core, POWER_OFF); if (ret) return ret; } @@ -414,7 +438,7 @@ static __maybe_unused int venus_runtime_suspend(struct device *dev) err_video_path: icc_set_bw(core->cpucfg_path, kbps_to_icc(1000), 0); err_cpucfg_path: - pm_ops->core_power(dev, POWER_ON); + pm_ops->core_power(core, POWER_ON); return ret; } @@ -434,7 +458,7 @@ static __maybe_unused int venus_runtime_resume(struct device *dev) return ret; if (pm_ops->core_power) { - ret = pm_ops->core_power(dev, POWER_ON); + ret = pm_ops->core_power(core, POWER_ON); if (ret) return ret; } @@ -625,12 +649,66 @@ static const struct venus_resources sc7180_res = { .fwname = "qcom/venus-5.4/venus.mdt", }; +static const struct freq_tbl sm8250_freq_table[] = { + { 0, 444000000 }, + { 0, 366000000 }, + { 0, 338000000 }, + { 0, 240000000 }, +}; + +static const struct bw_tbl sm8250_bw_table_enc[] = { + { 1944000, 1954000, 0, 3711000, 0 }, /* 3840x2160@60 */ + { 972000, 996000, 0, 1905000, 0 }, /* 3840x2160@30 */ + { 489600, 645000, 0, 977000, 0 }, /* 1920x1080@60 */ + { 244800, 332000, 0, 498000, 0 }, /* 1920x1080@30 */ +}; + +static const struct bw_tbl sm8250_bw_table_dec[] = { + { 2073600, 2403000, 0, 4113000, 0 }, /* 4096x2160@60 */ + { 1036800, 1224000, 0, 2079000, 0 }, /* 4096x2160@30 */ + { 489600, 812000, 0, 998000, 0 }, /* 1920x1080@60 */ + { 244800, 416000, 0, 509000, 0 }, /* 1920x1080@30 */ +}; + +static const struct reg_val sm8250_reg_preset[] = { + { 0xb0088, 0 }, +}; + +static const struct venus_resources sm8250_res = { + .freq_tbl = sm8250_freq_table, + .freq_tbl_size = ARRAY_SIZE(sm8250_freq_table), + .reg_tbl = sm8250_reg_preset, + .reg_tbl_size = ARRAY_SIZE(sm8250_reg_preset), + .bw_tbl_enc = sm8250_bw_table_enc, + .bw_tbl_enc_size = ARRAY_SIZE(sm8250_bw_table_enc), + .bw_tbl_dec = sm8250_bw_table_dec, + .bw_tbl_dec_size = ARRAY_SIZE(sm8250_bw_table_dec), + .clks = {"core", "iface"}, + .clks_num = 2, + .resets = { "bus", "core" }, + .resets_num = 2, + .vcodec0_clks = { "vcodec0_core" }, + .vcodec_clks_num = 1, + .vcodec_pmdomains = { "venus", "vcodec0" }, + .vcodec_pmdomains_num = 2, + .opp_pmdomain = (const char *[]) { "mx", NULL }, + .vcodec_num = 1, + .max_load = 7833600, + .hfi_version = HFI_VERSION_6XX, + .vmem_id = VIDC_RESOURCE_NONE, + .vmem_size = 0, + .vmem_addr = 0, + .dma_mask = 0xe0000000 - 1, + .fwname = "qcom/vpu-1.0/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, }, { .compatible = "qcom,sdm845-venus-v2", .data = &sdm845_res_v2, }, { .compatible = "qcom,sc7180-venus", .data = &sc7180_res, }, + { .compatible = "qcom,sm8250-venus", .data = &sm8250_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 a252ed32cc14..745f226a523f 100644 --- a/drivers/media/platform/qcom/venus/core.h +++ b/drivers/media/platform/qcom/venus/core.h @@ -24,6 +24,7 @@ #define VIDC_CLKS_NUM_MAX 4 #define VIDC_VCODEC_CLKS_NUM_MAX 2 #define VIDC_PMDOMAINS_NUM_MAX 3 +#define VIDC_RESETS_NUM_MAX 2 extern int venus_fw_debug; @@ -64,6 +65,8 @@ struct venus_resources { unsigned int vcodec_pmdomains_num; const char **opp_pmdomain; unsigned int vcodec_num; + const char * const resets[VIDC_RESETS_NUM_MAX]; + unsigned int resets_num; enum hfi_version hfi_version; u32 max_load; unsigned int vmem_id; @@ -87,11 +90,25 @@ struct venus_format { * struct venus_core - holds core parameters valid for all instances * * @base: IO memory base address + * @vbif_base: IO memory vbif base address + * @cpu_base: IO memory cpu base address + * @cpu_cs_base: IO memory cpu_cs base address + * @cpu_ic_base: IO memory cpu_ic base address + * @wrapper_base: IO memory wrapper base address + * @wrapper_tz_base: IO memory wrapper TZ base address + * @aon_base: AON base address * @irq: Venus irq * @clks: an array of struct clk pointers * @vcodec0_clks: an array of vcodec0 struct clk pointers * @vcodec1_clks: an array of vcodec1 struct clk pointers + * @video_path: an interconnect handle to video to/from memory path + * @cpucfg_path: an interconnect handle to cpu configuration path + * @opp_table: an device OPP table handle + * @has_opp_table: does OPP table exist * @pmdomains: an array of pmdomains struct device pointers + * @opp_dl_venus: an device-link for device OPP + * @opp_pmdomain: an OPP power-domain + * @resets: an array of reset signals * @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 @@ -100,6 +117,7 @@ struct venus_format { * @dev_dec: convenience struct device pointer for decoder device * @dev_enc: convenience struct device pointer for encoder device * @use_tz: a flag that suggests presence of trustzone + * @fw: structure of firmware parameters * @lock: a lock for this strucure * @instances: a list_head of all instances * @insts_count: num of instances @@ -108,6 +126,7 @@ struct venus_format { * @error: an error returned during last HFI sync operations * @sys_error: an error flag that signal system error event * @core_ops: the core operations + * @pm_ops: a pointer to pm operations * @pm_lock: a lock for PM operations * @enc_codecs: encoders supported by this core * @dec_codecs: decoders supported by this core @@ -115,10 +134,21 @@ struct venus_format { * @priv: a private filed for HFI operations * @ops: the core HFI operations * @work: a delayed work for handling system fatal error + * @caps: an array of supported HFI capabilities + * @codecs_count: platform codecs count + * @core0_usage_count: usage counter for core0 + * @core1_usage_count: usage counter for core1 * @root: debugfs root directory */ struct venus_core { void __iomem *base; + void __iomem *vbif_base; + void __iomem *cpu_base; + void __iomem *cpu_cs_base; + void __iomem *cpu_ic_base; + void __iomem *wrapper_base; + void __iomem *wrapper_tz_base; + void __iomem *aon_base; int irq; struct clk *clks[VIDC_CLKS_NUM_MAX]; struct clk *vcodec0_clks[VIDC_VCODEC_CLKS_NUM_MAX]; @@ -130,6 +160,7 @@ struct venus_core { struct device *pmdomains[VIDC_PMDOMAINS_NUM_MAX]; struct device_link *opp_dl_venus; struct device *opp_pmdomain; + struct reset_control *resets[VIDC_RESETS_NUM_MAX]; struct video_device *vdev_dec; struct video_device *vdev_enc; struct v4l2_device v4l2_dev; @@ -172,6 +203,9 @@ struct vdec_controls { u32 post_loop_deb_mode; u32 profile; u32 level; + u32 display_delay; + u32 display_delay_enable; + u64 conceal_color; }; struct venc_controls { @@ -222,6 +256,7 @@ struct venc_controls { u32 multi_slice_max_mb; u32 header_mode; + bool aud_enable; struct { u32 h264; @@ -238,6 +273,9 @@ struct venc_controls { } level; u32 base_priority_id; + u32 ltr_count; + struct v4l2_ctrl_hdr10_cll_info cll; + struct v4l2_ctrl_hdr10_mastering_display mastering; }; struct venus_buffer { @@ -284,10 +322,11 @@ struct venus_ts_metadata { * @list: used for attach an instance to the core * @lock: instance lock * @core: a reference to the core struct + * @clk_data: clock data per core ID * @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 + * @delayed_process: a list of delayed buffers * @delayed_process_work: a work_struct for process delayed buffers * @ctrl_handler: v4l control handler * @controls: a union of decoder and encoder control parameters @@ -296,22 +335,26 @@ struct venus_ts_metadata { * @streamon_out: stream on flag for output queue * @width: current capture width * @height: current capture height + * @crop: current crop rectangle * @out_width: current output width * @out_height: current output height * @colorspace: current color space + * @ycbcr_enc: current YCbCr encoding * @quantization: current quantization * @xfer_func: current xfer function * @codec_state: current codec API state (see DEC/ENC_STATE_) * @reconf_wait: wait queue for resolution change event * @subscriptions: used to hold current events subscriptions * @buf_count: used to count number of buffers (reqbuf(0)) + * @tss: timestamp metadata + * @payloads: cache plane payload to use it for clock/BW scaling * @fps: holds current FPS * @timeperframe: holds current time per frame structure * @fmt_out: a reference to output format structure * @fmt_cap: a reference to capture format structure * @num_input_bufs: holds number of input buffers * @num_output_bufs: holds number of output buffers - * @input_buf_size holds input buffer size + * @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 @@ -332,7 +375,11 @@ struct venus_ts_metadata { * @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 + * @core_acquired: the Core has been acquired + * @bit_depth: current bitstream bit-depth + * @pic_struct: bitstream progressive vs interlaced * @next_buf_last: a flag to mark next queued capture buffer as last + * @drain_active: Drain sequence is in progress */ struct venus_inst { struct list_head list; @@ -403,6 +450,7 @@ struct venus_inst { #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 IS_V6(core) ((core)->res->hfi_version == HFI_VERSION_6XX) #define ctrl_to_inst(ctrl) \ container_of((ctrl)->handler, struct venus_inst, ctrl_handler) diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c index 89defc21ea81..227bd3b3f84c 100644 --- a/drivers/media/platform/qcom/venus/firmware.c +++ b/drivers/media/platform/qcom/venus/firmware.c @@ -27,19 +27,19 @@ static void venus_reset_cpu(struct venus_core *core) { u32 fw_size = core->fw.mapped_mem_size; - void __iomem *base = core->base; + void __iomem *wrapper_base = core->wrapper_base; - writel(0, base + WRAPPER_FW_START_ADDR); - writel(fw_size, base + WRAPPER_FW_END_ADDR); - writel(0, base + WRAPPER_CPA_START_ADDR); - writel(fw_size, base + WRAPPER_CPA_END_ADDR); - writel(fw_size, base + WRAPPER_NONPIX_START_ADDR); - writel(fw_size, base + WRAPPER_NONPIX_END_ADDR); - writel(0x0, base + WRAPPER_CPU_CGC_DIS); - writel(0x0, base + WRAPPER_CPU_CLOCK_CONFIG); + writel(0, wrapper_base + WRAPPER_FW_START_ADDR); + writel(fw_size, wrapper_base + WRAPPER_FW_END_ADDR); + writel(0, wrapper_base + WRAPPER_CPA_START_ADDR); + writel(fw_size, wrapper_base + WRAPPER_CPA_END_ADDR); + writel(fw_size, wrapper_base + WRAPPER_NONPIX_START_ADDR); + writel(fw_size, wrapper_base + WRAPPER_NONPIX_END_ADDR); + writel(0x0, wrapper_base + WRAPPER_CPU_CGC_DIS); + writel(0x0, wrapper_base + WRAPPER_CPU_CLOCK_CONFIG); /* Bring ARM9 out of reset */ - writel(0, base + WRAPPER_A9SS_SW_RESET); + writel(0, wrapper_base + WRAPPER_A9SS_SW_RESET); } int venus_set_hw_state(struct venus_core *core, bool resume) @@ -53,10 +53,12 @@ int venus_set_hw_state(struct venus_core *core, bool resume) return ret; } - if (resume) + if (resume) { venus_reset_cpu(core); - else - writel(1, core->base + WRAPPER_A9SS_SW_RESET); + } else { + if (!IS_V6(core)) + writel(1, core->wrapper_base + WRAPPER_A9SS_SW_RESET); + } return 0; } @@ -159,12 +161,12 @@ static int venus_shutdown_no_tz(struct venus_core *core) size_t unmapped; u32 reg; struct device *dev = core->fw.dev; - void __iomem *base = core->base; + void __iomem *wrapper_base = core->wrapper_base; /* Assert the reset to ARM9 */ - reg = readl_relaxed(base + WRAPPER_A9SS_SW_RESET); + reg = readl_relaxed(wrapper_base + WRAPPER_A9SS_SW_RESET); reg |= WRAPPER_A9SS_SW_RESET_BIT; - writel_relaxed(reg, base + WRAPPER_A9SS_SW_RESET); + writel_relaxed(reg, wrapper_base + WRAPPER_A9SS_SW_RESET); /* Make sure reset is asserted before the mapping is removed */ mb(); @@ -187,6 +189,7 @@ int venus_boot(struct venus_core *core) { struct device *dev = core->dev; const struct venus_resources *res = core->res; + const char *fwpath = NULL; phys_addr_t mem_phys; size_t mem_size; int ret; @@ -195,7 +198,12 @@ int venus_boot(struct venus_core *core) (core->use_tz && !qcom_scm_is_available())) return -EPROBE_DEFER; - ret = venus_load_fw(core, core->res->fwname, &mem_phys, &mem_size); + ret = of_property_read_string_index(dev->of_node, "firmware-name", 0, + &fwpath); + if (ret) + fwpath = core->res->fwname; + + ret = venus_load_fw(core, fwpath, &mem_phys, &mem_size); if (ret) { dev_err(dev, "fail to load video firmware\n"); return -EINVAL; diff --git a/drivers/media/platform/qcom/venus/helpers.c b/drivers/media/platform/qcom/venus/helpers.c index 76ece2ff8d39..b813d6dba481 100644 --- a/drivers/media/platform/qcom/venus/helpers.c +++ b/drivers/media/platform/qcom/venus/helpers.c @@ -18,6 +18,9 @@ #include "hfi_platform.h" #include "hfi_parser.h" +#define NUM_MBS_720P (((1280 + 15) >> 4) * ((720 + 15) >> 4)) +#define NUM_MBS_4K (((4096 + 15) >> 4) * ((2304 + 15) >> 4)) + struct intbuf { struct list_head list; u32 type; @@ -279,13 +282,24 @@ static const unsigned int intbuf_types_4xx[] = { HFI_BUFFER_INTERNAL_PERSIST_1, }; +static const unsigned int intbuf_types_6xx[] = { + HFI_BUFFER_INTERNAL_SCRATCH(HFI_VERSION_6XX), + HFI_BUFFER_INTERNAL_SCRATCH_1(HFI_VERSION_6XX), + HFI_BUFFER_INTERNAL_SCRATCH_2(HFI_VERSION_6XX), + HFI_BUFFER_INTERNAL_PERSIST, + HFI_BUFFER_INTERNAL_PERSIST_1, +}; + int venus_helper_intbufs_alloc(struct venus_inst *inst) { const unsigned int *intbuf; size_t arr_sz, i; int ret; - if (IS_V4(inst->core)) { + if (IS_V6(inst->core)) { + arr_sz = ARRAY_SIZE(intbuf_types_6xx); + intbuf = intbuf_types_6xx; + } else if (IS_V4(inst->core)) { arr_sz = ARRAY_SIZE(intbuf_types_4xx); intbuf = intbuf_types_4xx; } else { @@ -488,7 +502,7 @@ static bool is_dynamic_bufmode(struct venus_inst *inst) * v4 doesn't send BUFFER_ALLOC_MODE_SUPPORTED property and supports * dynamic buffer mode by default for HFI_BUFFER_OUTPUT/OUTPUT2. */ - if (IS_V4(core)) + if (IS_V4(core) || IS_V6(core)) return true; caps = venus_caps_by_codec(core, inst->hfi_codec, inst->session_type); @@ -1079,20 +1093,67 @@ 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) +static u32 venus_helper_get_work_mode(struct venus_inst *inst) +{ + u32 mode; + u32 num_mbs; + + mode = VIDC_WORK_MODE_2; + if (inst->session_type == VIDC_SESSION_TYPE_DEC) { + num_mbs = (ALIGN(inst->height, 16) * ALIGN(inst->width, 16)) / 256; + if (inst->hfi_codec == HFI_VIDEO_CODEC_MPEG2 || + inst->pic_struct != HFI_INTERLACE_FRAME_PROGRESSIVE || + num_mbs <= NUM_MBS_720P) + mode = VIDC_WORK_MODE_1; + } else { + num_mbs = (ALIGN(inst->out_height, 16) * ALIGN(inst->out_width, 16)) / 256; + if (inst->hfi_codec == HFI_VIDEO_CODEC_VP8 && + num_mbs <= NUM_MBS_4K) + mode = VIDC_WORK_MODE_1; + } + + return mode; +} + +int venus_helper_set_work_mode(struct venus_inst *inst) { const u32 ptype = HFI_PROPERTY_PARAM_WORK_MODE; struct hfi_video_work_mode wm; + u32 mode; - if (!IS_V4(inst->core)) + if (!IS_V4(inst->core) && !IS_V6(inst->core)) return 0; + mode = venus_helper_get_work_mode(inst); 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_format_constraints(struct venus_inst *inst) +{ + const u32 ptype = HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO; + struct hfi_uncompressed_plane_actual_constraints_info pconstraint; + + if (!IS_V6(inst->core)) + return 0; + + pconstraint.buffer_type = HFI_BUFFER_OUTPUT2; + pconstraint.num_planes = 2; + pconstraint.plane_format[0].stride_multiples = 128; + pconstraint.plane_format[0].max_stride = 8192; + pconstraint.plane_format[0].min_plane_buffer_height_multiple = 32; + pconstraint.plane_format[0].buffer_alignment = 256; + + pconstraint.plane_format[1].stride_multiples = 128; + pconstraint.plane_format[1].max_stride = 8192; + pconstraint.plane_format[1].min_plane_buffer_height_multiple = 16; + pconstraint.plane_format[1].buffer_alignment = 256; + + return hfi_session_set_property(inst, ptype, &pconstraint); +} +EXPORT_SYMBOL_GPL(venus_helper_set_format_constraints); + int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs, unsigned int output_bufs, unsigned int output2_bufs) diff --git a/drivers/media/platform/qcom/venus/helpers.h b/drivers/media/platform/qcom/venus/helpers.h index 351093845499..e6269b4be3af 100644 --- a/drivers/media/platform/qcom/venus/helpers.h +++ b/drivers/media/platform/qcom/venus/helpers.h @@ -32,7 +32,8 @@ int venus_helper_set_input_resolution(struct venus_inst *inst, int venus_helper_set_output_resolution(struct venus_inst *inst, unsigned int width, unsigned int height, u32 buftype); -int venus_helper_set_work_mode(struct venus_inst *inst, u32 mode); +int venus_helper_set_work_mode(struct venus_inst *inst); +int venus_helper_set_format_constraints(struct venus_inst *inst); int venus_helper_set_num_bufs(struct venus_inst *inst, unsigned int input_bufs, unsigned int output_bufs, unsigned int output2_bufs); diff --git a/drivers/media/platform/qcom/venus/hfi_cmds.c b/drivers/media/platform/qcom/venus/hfi_cmds.c index 4f7565834469..11a8347e5f5c 100644 --- a/drivers/media/platform/qcom/venus/hfi_cmds.c +++ b/drivers/media/platform/qcom/venus/hfi_cmds.c @@ -254,7 +254,7 @@ int pkt_session_unset_buffers(struct hfi_session_release_buffer_pkt *pkt, int pkt_session_etb_decoder(struct hfi_session_empty_buffer_compressed_pkt *pkt, void *cookie, struct hfi_frame_data *in_frame) { - if (!cookie || !in_frame->device_addr) + if (!cookie) return -EINVAL; pkt->shdr.hdr.size = sizeof(*pkt); @@ -760,7 +760,9 @@ static int pkt_session_set_property_1x(struct hfi_session_set_property_pkt *pkt, struct hfi_conceal_color *color = prop_data; u32 *in = pdata; - color->conceal_color = *in; + color->conceal_color = *in & 0xff; + color->conceal_color |= ((*in >> 10) & 0xff) << 8; + color->conceal_color |= ((*in >> 20) & 0xff) << 16; pkt->shdr.hdr.size += sizeof(u32) + sizeof(*color); break; } @@ -1039,6 +1041,18 @@ static int pkt_session_set_property_1x(struct hfi_session_set_property_pkt *pkt, pkt->shdr.hdr.size += sizeof(u32) + sizeof(*hierp); break; } + case HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO: { + struct hfi_uncompressed_plane_actual_info *in = pdata; + struct hfi_uncompressed_plane_actual_info *info = prop_data; + + info->buffer_type = in->buffer_type; + info->num_planes = in->num_planes; + info->plane_format[0] = in->plane_format[0]; + if (in->num_planes > 1) + info->plane_format[1] = in->plane_format[1]; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*info); + break; + } /* FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET */ case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS: @@ -1205,18 +1219,14 @@ pkt_session_set_property_4xx(struct hfi_session_set_property_pkt *pkt, pkt->shdr.hdr.size += sizeof(u32) + sizeof(*cu); break; } - case HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO: { - struct hfi_uncompressed_plane_actual_info *in = pdata; - struct hfi_uncompressed_plane_actual_info *info = prop_data; + case HFI_PROPERTY_PARAM_VENC_HDR10_PQ_SEI: { + struct hfi_hdr10_pq_sei *in = pdata, *hdr10 = prop_data; - info->buffer_type = in->buffer_type; - info->num_planes = in->num_planes; - info->plane_format[0] = in->plane_format[0]; - if (in->num_planes > 1) - info->plane_format[1] = in->plane_format[1]; - pkt->shdr.hdr.size += sizeof(u32) + sizeof(*info); + memcpy(hdr10, in, sizeof(*hdr10)); + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*hdr10); break; } + case HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE: case HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER: case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE: @@ -1249,13 +1259,38 @@ pkt_session_set_property_6xx(struct hfi_session_set_property_pkt *pkt, pkt->data[0] = ptype; switch (ptype) { + case HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO: { + struct hfi_uncompressed_plane_actual_constraints_info *in = pdata; + struct hfi_uncompressed_plane_actual_constraints_info *info = prop_data; + + info->buffer_type = in->buffer_type; + info->num_planes = in->num_planes; + info->plane_format[0] = in->plane_format[0]; + if (in->num_planes > 1) + info->plane_format[1] = in->plane_format[1]; + + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*info); + break; + } case HFI_PROPERTY_CONFIG_HEIC_FRAME_QUALITY: { struct hfi_heic_frame_quality *in = pdata, *cq = prop_data; cq->frame_quality = in->frame_quality; pkt->shdr.hdr.size += sizeof(u32) + sizeof(*cq); break; - } default: + } + case HFI_PROPERTY_PARAM_VDEC_CONCEAL_COLOR: { + struct hfi_conceal_color_v4 *color = prop_data; + u32 *in = pdata; + + color->conceal_color_8bit = *in & 0xff; + color->conceal_color_8bit |= ((*in >> 10) & 0xff) << 8; + color->conceal_color_8bit |= ((*in >> 20) & 0xff) << 16; + color->conceal_color_10bit = *in; + pkt->shdr.hdr.size += sizeof(u32) + sizeof(*color); + break; + } + default: return pkt_session_set_property_4xx(pkt, cookie, ptype, pdata); } diff --git a/drivers/media/platform/qcom/venus/hfi_helper.h b/drivers/media/platform/qcom/venus/hfi_helper.h index 6b524c7cde5f..63cd347a62da 100644 --- a/drivers/media/platform/qcom/venus/hfi_helper.h +++ b/drivers/media/platform/qcom/venus/hfi_helper.h @@ -395,11 +395,14 @@ #define HFI_BUFFER_INTERNAL_PERSIST 0x4 #define HFI_BUFFER_INTERNAL_PERSIST_1 0x5 #define HFI_BUFFER_INTERNAL_SCRATCH(ver) \ - (((ver) == HFI_VERSION_4XX) ? 0x6 : 0x1000001) + (((ver) == HFI_VERSION_4XX || \ + (ver) == HFI_VERSION_6XX) ? 0x6 : 0x1000001) #define HFI_BUFFER_INTERNAL_SCRATCH_1(ver) \ - (((ver) == HFI_VERSION_4XX) ? 0x7 : 0x1000005) + (((ver) == HFI_VERSION_4XX || \ + (ver) == HFI_VERSION_6XX) ? 0x7 : 0x1000005) #define HFI_BUFFER_INTERNAL_SCRATCH_2(ver) \ - (((ver) == HFI_VERSION_4XX) ? 0x8 : 0x1000006) + (((ver) == HFI_VERSION_4XX || \ + (ver) == HFI_VERSION_6XX) ? 0x8 : 0x1000006) #define HFI_BUFFER_EXTRADATA_INPUT(ver) \ (((ver) == HFI_VERSION_4XX) ? 0xc : 0x1000002) #define HFI_BUFFER_EXTRADATA_OUTPUT(ver) \ @@ -513,6 +516,7 @@ #define HFI_PROPERTY_PARAM_VENC_VPX_ERROR_RESILIENCE_MODE 0x2005029 #define HFI_PROPERTY_PARAM_VENC_HIER_B_MAX_NUM_ENH_LAYER 0x200502c #define HFI_PROPERTY_PARAM_VENC_HIER_P_HYBRID_MODE 0x200502f +#define HFI_PROPERTY_PARAM_VENC_HDR10_PQ_SEI 0x2005036 /* * HFI_PROPERTY_CONFIG_VENC_COMMON_START @@ -685,10 +689,20 @@ struct hfi_vc1e_perf_cfg_type { u32 search_range_y_subsampled[3]; }; +/* + * 0 - 7bit -> Luma (def: 16) + * 8 - 15bit -> Chroma (def: 128) + * format is valid up to v4 + */ struct hfi_conceal_color { u32 conceal_color; }; +struct hfi_conceal_color_v4 { + u32 conceal_color_8bit; + u32 conceal_color_10bit; +}; + struct hfi_intra_period { u32 pframes; u32 bframes; @@ -809,6 +823,25 @@ struct hfi_ltr_mark { u32 mark_frame; }; +struct hfi_mastering_display_colour_sei_payload { + u32 display_primaries_x[3]; + u32 display_primaries_y[3]; + u32 white_point_x; + u32 white_point_y; + u32 max_display_mastering_luminance; + u32 min_display_mastering_luminance; +}; + +struct hfi_content_light_level_sei_payload { + u32 max_content_light; + u32 max_pic_average_light; +}; + +struct hfi_hdr10_pq_sei { + struct hfi_mastering_display_colour_sei_payload mastering; + struct hfi_content_light_level_sei_payload cll; +}; + struct hfi_framesize { u32 buffer_type; u32 width; diff --git a/drivers/media/platform/qcom/venus/hfi_msgs.c b/drivers/media/platform/qcom/venus/hfi_msgs.c index 06a1908ca225..a2d436d407b2 100644 --- a/drivers/media/platform/qcom/venus/hfi_msgs.c +++ b/drivers/media/platform/qcom/venus/hfi_msgs.c @@ -6,6 +6,7 @@ #include <linux/hash.h> #include <linux/list.h> #include <linux/slab.h> +#include <linux/soc/qcom/smem.h> #include <media/videobuf2-v4l2.h> #include "core.h" @@ -14,6 +15,10 @@ #include "hfi_msgs.h" #include "hfi_parser.h" +#define SMEM_IMG_VER_TBL 469 +#define VER_STR_SZ 128 +#define SMEM_IMG_OFFSET_VENUS (14 * 128) + static void event_seq_changed(struct venus_core *core, struct venus_inst *inst, struct hfi_msg_event_notify_pkt *pkt) { @@ -239,15 +244,26 @@ static void sys_get_prop_image_version(struct device *dev, struct hfi_msg_sys_property_info_pkt *pkt) { + u8 *smem_tbl_ptr; + u8 *img_ver; int req_bytes; + size_t smem_blk_sz; req_bytes = pkt->hdr.size - sizeof(*pkt); - if (req_bytes < 128 || !pkt->data[1] || pkt->num_properties > 1) + if (req_bytes < VER_STR_SZ || !pkt->data[1] || pkt->num_properties > 1) /* bad packet */ return; - dev_dbg(dev, VDBGL "F/W version: %s\n", (u8 *)&pkt->data[1]); + img_ver = (u8 *)&pkt->data[1]; + + dev_dbg(dev, VDBGL "F/W version: %s\n", img_ver); + + smem_tbl_ptr = qcom_smem_get(QCOM_SMEM_HOST_ANY, + SMEM_IMG_VER_TBL, &smem_blk_sz); + if (smem_tbl_ptr && smem_blk_sz >= SMEM_IMG_OFFSET_VENUS + VER_STR_SZ) + memcpy(smem_tbl_ptr + SMEM_IMG_OFFSET_VENUS, + img_ver, VER_STR_SZ); } static void hfi_sys_property_info(struct venus_core *core, diff --git a/drivers/media/platform/qcom/venus/hfi_parser.c b/drivers/media/platform/qcom/venus/hfi_parser.c index 7263c0c32695..5b8389b98299 100644 --- a/drivers/media/platform/qcom/venus/hfi_parser.c +++ b/drivers/media/platform/qcom/venus/hfi_parser.c @@ -235,13 +235,13 @@ static int hfi_platform_parser(struct venus_core *core, struct venus_inst *inst) u32 enc_codecs, dec_codecs, count = 0; unsigned int entries; - if (inst) - return 0; - plat = hfi_platform_get(core->res->hfi_version); if (!plat) return -EINVAL; + if (inst) + return 0; + if (plat->codecs) plat->codecs(&enc_codecs, &dec_codecs, &count); @@ -277,8 +277,10 @@ u32 hfi_parser(struct venus_core *core, struct venus_inst *inst, void *buf, parser_init(inst, &codecs, &domain); - core->codecs_count = 0; - memset(core->caps, 0, sizeof(core->caps)); + if (core->res->hfi_version > HFI_VERSION_1XX) { + core->codecs_count = 0; + memset(core->caps, 0, sizeof(core->caps)); + } while (words_count) { data = word + 1; diff --git a/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c b/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c index d43d1a53e72d..479178b0600d 100644 --- a/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c +++ b/drivers/media/platform/qcom/venus/hfi_plat_bufs_v6.c @@ -40,7 +40,8 @@ #define MAX_TILE_COLUMNS 32 /* 8K/256 */ -#define NUM_HW_PIC_BUF 10 +#define VPP_CMD_MAX_SIZE BIT(20) +#define NUM_HW_PIC_BUF 32 #define BIN_BUFFER_THRESHOLD (1280 * 736) #define H264D_MAX_SLICE 1800 /* sizeof(h264d_buftab_t) aligned to 256 */ @@ -90,6 +91,7 @@ #define SIZE_SLIST_BUF_H264 512 #define LCU_MAX_SIZE_PELS 64 #define LCU_MIN_SIZE_PELS 16 +#define SIZE_SEI_USERDATA 4096 #define H265D_MAX_SLICE 600 #define SIZE_H265D_HW_PIC_T SIZE_H264D_HW_PIC_T @@ -199,7 +201,7 @@ static inline u32 size_vpxd_lb_se_left_ctrl(u32 width, u32 height) #define VPX_DECODER_FRAME_BIN_RES_BUDGET_RATIO_DEN 2 #define VP8_NUM_FRAME_INFO_BUF (5 + 1) -#define VP9_NUM_FRAME_INFO_BUF (8 + 2 + 1 + 8) +#define VP9_NUM_FRAME_INFO_BUF 32 #define VP8_NUM_PROBABILITY_TABLE_BUF VP8_NUM_FRAME_INFO_BUF #define VP9_NUM_PROBABILITY_TABLE_BUF (VP9_NUM_FRAME_INFO_BUF + 4) #define VP8_PROB_TABLE_SIZE 3840 @@ -211,7 +213,7 @@ static inline u32 size_vpxd_lb_se_left_ctrl(u32 width, u32 height) #define QMATRIX_SIZE (sizeof(u32) * 128 + 256) #define MP2D_QPDUMP_SIZE 115200 -#define HFI_IRIS2_ENC_PERSIST_SIZE 102400 +#define HFI_IRIS2_ENC_PERSIST_SIZE 204800 #define HFI_MAX_COL_FRAME 6 #define HFI_VENUS_VENC_TRE_WB_BUFF_SIZE (65 << 4) /* in Bytes */ #define HFI_VENUS_VENC_DB_LINE_BUFF_PER_MB 512 @@ -467,7 +469,7 @@ static u32 hfi_iris2_h264d_comv_size(u32 width, u32 height, { u32 frame_width_in_mbs = ((width + 15) >> 4); u32 frame_height_in_mbs = ((height + 15) >> 4); - u32 col_mv_aligned_width = (frame_width_in_mbs << 6); + u32 col_mv_aligned_width = (frame_width_in_mbs << 7); u32 col_zero_aligned_width = (frame_width_in_mbs << 2); u32 col_zero_size = 0, size_colloc = 0, comv_size = 0; @@ -500,9 +502,14 @@ static u32 size_h264d_bse_cmd_buf(u32 height) static u32 size_h264d_vpp_cmd_buf(u32 height) { u32 aligned_height = ALIGN(height, 32); + u32 size; - return min_t(u32, (((aligned_height + 15) >> 4) * 3 * 4), + size = min_t(u32, (((aligned_height + 15) >> 4) * 3 * 4), H264D_MAX_SLICE) * SIZE_H264D_VPP_CMD_PER_BUF; + if (size > VPP_CMD_MAX_SIZE) + size = VPP_CMD_MAX_SIZE; + + return size; } static u32 hfi_iris2_h264d_non_comv_size(u32 width, u32 height, @@ -559,8 +566,11 @@ static u32 size_h265d_vpp_cmd_buf(u32 width, u32 height) size = min_t(u32, size, H265D_MAX_SLICE + 1); size = ALIGN(size, 4); size = 2 * size * SIZE_H265D_VPP_CMD_PER_BUF; + size = ALIGN(size, HFI_DMA_ALIGNMENT); + if (size > VPP_CMD_MAX_SIZE) + size = VPP_CMD_MAX_SIZE; - return ALIGN(size, HFI_DMA_ALIGNMENT); + return size; } static u32 hfi_iris2_h265d_comv_size(u32 width, u32 height, @@ -1004,8 +1014,8 @@ static u32 enc_persist_size(void) static u32 h264d_persist1_size(void) { - return ALIGN((SIZE_SLIST_BUF_H264 * NUM_SLIST_BUF_H264), - HFI_DMA_ALIGNMENT); + return ALIGN((SIZE_SLIST_BUF_H264 * NUM_SLIST_BUF_H264 + + NUM_HW_PIC_BUF * SIZE_SEI_USERDATA), HFI_DMA_ALIGNMENT); } static u32 h265d_persist1_size(void) @@ -1159,7 +1169,7 @@ static int output_buffer_count(u32 session_type, u32 codec) case V4L2_PIX_FMT_H264: case V4L2_PIX_FMT_HEVC: default: - output_min_count = 8; + output_min_count = 18; break; } } else { @@ -1233,7 +1243,7 @@ static int bufreq_dec(struct hfi_plat_buffers_params *params, u32 buftype, } else if (buftype == HFI_BUFFER_INTERNAL_PERSIST_1) { bufreq->size = dec_ops->persist1(); } else { - return -EINVAL; + bufreq->size = 0; } return 0; @@ -1301,7 +1311,7 @@ static int bufreq_enc(struct hfi_plat_buffers_params *params, u32 buftype, } else if (buftype == HFI_BUFFER_INTERNAL_PERSIST) { bufreq->size = enc_ops->persist(); } else { - return -EINVAL; + bufreq->size = 0; } return 0; diff --git a/drivers/media/platform/qcom/venus/hfi_platform_v6.c b/drivers/media/platform/qcom/venus/hfi_platform_v6.c index 2278be13cb90..dd1a03911b6c 100644 --- a/drivers/media/platform/qcom/venus/hfi_platform_v6.c +++ b/drivers/media/platform/qcom/venus/hfi_platform_v6.c @@ -9,15 +9,15 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_H264, .domain = VIDC_SESSION_TYPE_DEC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 5760, 1}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 5760, 1}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 8192, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 8192, 1}, /* ((5760 * 2880) / 256) */ - .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 36, 64800, 1}, - .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 200000000, 1}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 138240, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 220000000, 1}, .caps[4] = {HFI_CAPABILITY_SCALE_X, 65536, 65536, 1}, .caps[5] = {HFI_CAPABILITY_SCALE_Y, 65536, 65536, 1}, - .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 36, 1958400, 1}, - .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 64, 7833600, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 960, 1}, .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 0, 1, 1}, .num_caps = 9, .pl[0] = {HFI_H264_PROFILE_BASELINE, HFI_H264_LEVEL_52}, @@ -35,15 +35,15 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_HEVC, .domain = VIDC_SESSION_TYPE_DEC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, - .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, - .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, - .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1}, - .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1}, - .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 2073600, 1}, - .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, - .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 8192, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 8192, 1}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 138240, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 220000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 65536, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 65536, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 64, 7833600, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 960, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 0, 1, 1}, .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 3, 1}, .num_caps = 10, .pl[0] = {HFI_HEVC_PROFILE_MAIN, HFI_HEVC_LEVEL_6 | HFI_HEVC_TIER_HIGH0}, @@ -61,15 +61,15 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_VP8, .domain = VIDC_SESSION_TYPE_DEC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, - .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, - .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, - .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1}, - .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1}, - .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 2073600, 1}, - .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, - .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 4096, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 4096, 1}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 36864, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 100000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 65536, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 65536, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 64, 4423680, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 120, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 0, 1, 1}, .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 3, 1}, .num_caps = 10, .pl[0] = {HFI_VPX_PROFILE_MAIN, HFI_VPX_LEVEL_VERSION_0}, @@ -86,15 +86,15 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_VP9, .domain = VIDC_SESSION_TYPE_DEC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 1}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 1}, - .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, - .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, - .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1}, - .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1}, - .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 2073600, 1}, - .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, - .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 8192, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 8192, 1}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 138240, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 220000000, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 65536, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 65536, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 64, 7833600, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 960, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 0, 1, 1}, .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 3, 1}, .num_caps = 10, .pl[0] = {HFI_VP9_PROFILE_P0, 200}, @@ -112,15 +112,15 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_MPEG2, .domain = VIDC_SESSION_TYPE_DEC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 1920, 1}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 1920, 1}, - .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 8160, 1}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 1920, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 1920, 1}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 8160, 1}, .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 40000000, 1}, - .caps[4] = {HFI_CAPABILITY_SCALE_X, 4096, 65536, 1}, - .caps[5] = {HFI_CAPABILITY_SCALE_Y, 4096, 65536, 1}, - .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 244800, 1}, + .caps[4] = {HFI_CAPABILITY_SCALE_X, 65536, 65536, 1}, + .caps[5] = {HFI_CAPABILITY_SCALE_Y, 65536, 65536, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 64, 7833600, 1}, .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 30, 1}, - .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 2, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 0, 1, 1}, .caps[9] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 1, 1}, .num_caps = 10, .pl[0] = {HFI_MPEG2_PROFILE_SIMPLE, HFI_MPEG2_LEVEL_H14}, @@ -135,21 +135,21 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_H264, .domain = VIDC_SESSION_TYPE_ENC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, - .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, - .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 8192, 1}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 8192, 1}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 138240, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 220000000, 1}, .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, .caps[5] = {HFI_CAPABILITY_SCALE_Y, 8192, 65536, 1}, - .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 1036800, 1}, - .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, - .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 3, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 64, 7833600, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 960, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 0, 1, 1}, .caps[9] = {HFI_CAPABILITY_PEAKBITRATE, 32000, 160000000, 1}, - .caps[10] = {HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS, 0, 5, 1}, - .caps[11] = {HFI_CAPABILITY_ENC_LTR_COUNT, 0, 4, 1}, + .caps[10] = {HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS, 0, 6, 1}, + .caps[11] = {HFI_CAPABILITY_ENC_LTR_COUNT, 0, 2, 1}, .caps[12] = {HFI_CAPABILITY_LCU_SIZE, 16, 16, 1}, .caps[13] = {HFI_CAPABILITY_BFRAME, 0, 1, 1}, - .caps[14] = {HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS, 0, 5, 1}, + .caps[14] = {HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS, 0, 6, 1}, .caps[15] = {HFI_CAPABILITY_I_FRAME_QP, 0, 51, 1}, .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 51, 1}, .caps[17] = {HFI_CAPABILITY_B_FRAME_QP, 0, 51, 1}, @@ -172,24 +172,24 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_HEVC, .domain = VIDC_SESSION_TYPE_ENC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, - .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, - .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 8192, 16}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 8192, 16}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 138240, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 160000000, 1}, .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, .caps[5] = {HFI_CAPABILITY_SCALE_Y, 8192, 65536, 1}, - .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 1036800, 1}, - .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 480, 1}, - .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 3, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 64, 7833600, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 960, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 0, 1, 1}, .caps[9] = {HFI_CAPABILITY_PEAKBITRATE, 32000, 160000000, 1}, .caps[10] = {HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS, 0, 5, 1}, - .caps[11] = {HFI_CAPABILITY_ENC_LTR_COUNT, 0, 4, 1}, + .caps[11] = {HFI_CAPABILITY_ENC_LTR_COUNT, 0, 2, 1}, .caps[12] = {HFI_CAPABILITY_LCU_SIZE, 32, 32, 1}, .caps[13] = {HFI_CAPABILITY_BFRAME, 0, 1, 1}, .caps[14] = {HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS, 0, 5, 1}, - .caps[15] = {HFI_CAPABILITY_I_FRAME_QP, 0, 63, 1}, - .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 63, 1}, - .caps[17] = {HFI_CAPABILITY_B_FRAME_QP, 0, 63, 1}, + .caps[15] = {HFI_CAPABILITY_I_FRAME_QP, 0, 51, 1}, + .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 51, 1}, + .caps[17] = {HFI_CAPABILITY_B_FRAME_QP, 0, 51, 1}, .caps[18] = {HFI_CAPABILITY_MAX_WORKMODES, 1, 2, 1}, .caps[19] = {HFI_CAPABILITY_RATE_CONTROL_MODES, 0x1000001, 0x1000005, 1}, .caps[20] = {HFI_CAPABILITY_COLOR_SPACE_CONVERSION, 0, 2, 1}, @@ -209,20 +209,20 @@ static const struct hfi_plat_caps caps[] = { .codec = HFI_VIDEO_CODEC_VP8, .domain = VIDC_SESSION_TYPE_ENC, .cap_bufs_mode_dynamic = true, - .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 96, 4096, 16}, - .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 96, 4096, 16}, - .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 1, 36864, 1}, - .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 120000000, 1}, + .caps[0] = {HFI_CAPABILITY_FRAME_WIDTH, 128, 4096, 16}, + .caps[1] = {HFI_CAPABILITY_FRAME_HEIGHT, 128, 4096, 16}, + .caps[2] = {HFI_CAPABILITY_MBS_PER_FRAME, 64, 36864, 1}, + .caps[3] = {HFI_CAPABILITY_BITRATE, 1, 74000000, 1}, .caps[4] = {HFI_CAPABILITY_SCALE_X, 8192, 65536, 1}, .caps[5] = {HFI_CAPABILITY_SCALE_Y, 8192, 65536, 1}, - .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 1, 1036800, 1}, - .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 240, 1}, - .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 1, 3, 1}, + .caps[6] = {HFI_CAPABILITY_MBS_PER_SECOND, 64, 4423680, 1}, + .caps[7] = {HFI_CAPABILITY_FRAMERATE, 1, 120, 1}, + .caps[8] = {HFI_CAPABILITY_MAX_VIDEOCORES, 0, 1, 1}, .caps[9] = {HFI_CAPABILITY_PEAKBITRATE, 32000, 160000000, 1}, .caps[10] = {HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS, 0, 3, 1}, .caps[11] = {HFI_CAPABILITY_ENC_LTR_COUNT, 0, 2, 1}, .caps[12] = {HFI_CAPABILITY_LCU_SIZE, 16, 16, 1}, - .caps[13] = {HFI_CAPABILITY_BFRAME, 0, 1, 1}, + .caps[13] = {HFI_CAPABILITY_BFRAME, 0, 0, 1}, .caps[14] = {HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS, 0, 5, 1}, .caps[15] = {HFI_CAPABILITY_I_FRAME_QP, 0, 127, 1}, .caps[16] = {HFI_CAPABILITY_P_FRAME_QP, 0, 127, 1}, diff --git a/drivers/media/platform/qcom/venus/hfi_venus.c b/drivers/media/platform/qcom/venus/hfi_venus.c index 50e03f8fc278..ce98c523b3c6 100644 --- a/drivers/media/platform/qcom/venus/hfi_venus.c +++ b/drivers/media/platform/qcom/venus/hfi_venus.c @@ -345,16 +345,6 @@ static void venus_free(struct venus_hfi_device *hdev, struct mem_desc *mem) dma_free_attrs(dev, mem->size, mem->kva, mem->da, mem->attrs); } -static void venus_writel(struct venus_hfi_device *hdev, u32 reg, u32 value) -{ - writel(value, hdev->core->base + reg); -} - -static u32 venus_readl(struct venus_hfi_device *hdev, u32 reg) -{ - return readl(hdev->core->base + reg); -} - static void venus_set_registers(struct venus_hfi_device *hdev) { const struct venus_resources *res = hdev->core->res; @@ -363,12 +353,20 @@ static void venus_set_registers(struct venus_hfi_device *hdev) unsigned int i; for (i = 0; i < count; i++) - venus_writel(hdev, tbl[i].reg, tbl[i].value); + writel(tbl[i].value, hdev->core->base + tbl[i].reg); } static void venus_soft_int(struct venus_hfi_device *hdev) { - venus_writel(hdev, CPU_IC_SOFTINT, BIT(CPU_IC_SOFTINT_H2A_SHIFT)); + void __iomem *cpu_ic_base = hdev->core->cpu_ic_base; + u32 clear_bit; + + if (IS_V6(hdev->core)) + clear_bit = BIT(CPU_IC_SOFTINT_H2A_SHIFT_V6); + else + clear_bit = BIT(CPU_IC_SOFTINT_H2A_SHIFT); + + writel(clear_bit, cpu_ic_base + CPU_IC_SOFTINT); } static int venus_iface_cmdq_write_nolock(struct venus_hfi_device *hdev, @@ -450,16 +448,25 @@ static int venus_boot_core(struct venus_hfi_device *hdev) { struct device *dev = hdev->core->dev; static const unsigned int max_tries = 100; - u32 ctrl_status = 0; + u32 ctrl_status = 0, mask_val; unsigned int count = 0; + void __iomem *cpu_cs_base = hdev->core->cpu_cs_base; + void __iomem *wrapper_base = hdev->core->wrapper_base; int ret = 0; - venus_writel(hdev, VIDC_CTRL_INIT, BIT(VIDC_CTRL_INIT_CTRL_SHIFT)); - venus_writel(hdev, WRAPPER_INTR_MASK, WRAPPER_INTR_MASK_A2HVCODEC_MASK); - venus_writel(hdev, CPU_CS_SCIACMDARG3, 1); + writel(BIT(VIDC_CTRL_INIT_CTRL_SHIFT), cpu_cs_base + VIDC_CTRL_INIT); + if (IS_V6(hdev->core)) { + mask_val = readl(wrapper_base + WRAPPER_INTR_MASK); + mask_val &= ~(WRAPPER_INTR_MASK_A2HWD_BASK_V6 | + WRAPPER_INTR_MASK_A2HCPU_MASK); + } else { + mask_val = WRAPPER_INTR_MASK_A2HVCODEC_MASK; + } + writel(mask_val, wrapper_base + WRAPPER_INTR_MASK); + writel(1, cpu_cs_base + CPU_CS_SCIACMDARG3); while (!ctrl_status && count < max_tries) { - ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); + ctrl_status = readl(cpu_cs_base + CPU_CS_SCIACMDARG0); if ((ctrl_status & CPU_CS_SCIACMDARG0_ERROR_STATUS_MASK) == 4) { dev_err(dev, "invalid setting for UC_REGION\n"); ret = -EINVAL; @@ -473,15 +480,22 @@ static int venus_boot_core(struct venus_hfi_device *hdev) if (count >= max_tries) ret = -ETIMEDOUT; + if (IS_V6(hdev->core)) { + writel(0x1, cpu_cs_base + CPU_CS_H2XSOFTINTEN_V6); + writel(0x0, cpu_cs_base + CPU_CS_X2RPMH_V6); + } + return ret; } static u32 venus_hwversion(struct venus_hfi_device *hdev) { struct device *dev = hdev->core->dev; - u32 ver = venus_readl(hdev, WRAPPER_HW_VERSION); + void __iomem *wrapper_base = hdev->core->wrapper_base; + u32 ver; u32 major, minor, step; + ver = readl(wrapper_base + WRAPPER_HW_VERSION); major = ver & WRAPPER_HW_VERSION_MAJOR_VERSION_MASK; major = major >> WRAPPER_HW_VERSION_MAJOR_VERSION_SHIFT; minor = ver & WRAPPER_HW_VERSION_MINOR_VERSION_MASK; @@ -496,6 +510,7 @@ static u32 venus_hwversion(struct venus_hfi_device *hdev) static int venus_run(struct venus_hfi_device *hdev) { struct device *dev = hdev->core->dev; + void __iomem *cpu_cs_base = hdev->core->cpu_cs_base; int ret; /* @@ -504,12 +519,12 @@ static int venus_run(struct venus_hfi_device *hdev) */ venus_set_registers(hdev); - venus_writel(hdev, UC_REGION_ADDR, hdev->ifaceq_table.da); - venus_writel(hdev, UC_REGION_SIZE, SHARED_QSIZE); - venus_writel(hdev, CPU_CS_SCIACMDARG2, hdev->ifaceq_table.da); - venus_writel(hdev, CPU_CS_SCIACMDARG1, 0x01); + writel(hdev->ifaceq_table.da, cpu_cs_base + UC_REGION_ADDR); + writel(SHARED_QSIZE, cpu_cs_base + UC_REGION_SIZE); + writel(hdev->ifaceq_table.da, cpu_cs_base + CPU_CS_SCIACMDARG2); + writel(0x01, cpu_cs_base + CPU_CS_SCIACMDARG1); if (hdev->sfr.da) - venus_writel(hdev, SFR_ADDR, hdev->sfr.da); + writel(hdev->sfr.da, cpu_cs_base + SFR_ADDR); ret = venus_boot_core(hdev); if (ret) { @@ -524,17 +539,50 @@ static int venus_run(struct venus_hfi_device *hdev) static int venus_halt_axi(struct venus_hfi_device *hdev) { - void __iomem *base = hdev->core->base; + void __iomem *wrapper_base = hdev->core->wrapper_base; + void __iomem *vbif_base = hdev->core->vbif_base; + void __iomem *cpu_cs_base = hdev->core->cpu_cs_base; + void __iomem *aon_base = hdev->core->aon_base; struct device *dev = hdev->core->dev; u32 val; + u32 mask_val; int ret; + if (IS_V6(hdev->core)) { + writel(0x3, cpu_cs_base + CPU_CS_X2RPMH_V6); + + writel(0x1, aon_base + AON_WRAPPER_MVP_NOC_LPI_CONTROL); + ret = readl_poll_timeout(aon_base + AON_WRAPPER_MVP_NOC_LPI_STATUS, + val, + val & BIT(0), + POLL_INTERVAL_US, + VBIF_AXI_HALT_ACK_TIMEOUT_US); + if (ret) + return -ETIMEDOUT; + + mask_val = (BIT(2) | BIT(1) | BIT(0)); + writel(mask_val, wrapper_base + WRAPPER_DEBUG_BRIDGE_LPI_CONTROL_V6); + + writel(0x00, wrapper_base + WRAPPER_DEBUG_BRIDGE_LPI_CONTROL_V6); + ret = readl_poll_timeout(wrapper_base + WRAPPER_DEBUG_BRIDGE_LPI_STATUS_V6, + val, + val == 0, + POLL_INTERVAL_US, + VBIF_AXI_HALT_ACK_TIMEOUT_US); + + if (ret) { + dev_err(dev, "DBLP Release: lpi_status %x\n", val); + return -ETIMEDOUT; + } + return 0; + } + if (IS_V4(hdev->core)) { - val = venus_readl(hdev, WRAPPER_CPU_AXI_HALT); + val = readl(wrapper_base + WRAPPER_CPU_AXI_HALT); val |= WRAPPER_CPU_AXI_HALT_HALT; - venus_writel(hdev, WRAPPER_CPU_AXI_HALT, val); + writel(val, wrapper_base + WRAPPER_CPU_AXI_HALT); - ret = readl_poll_timeout(base + WRAPPER_CPU_AXI_HALT_STATUS, + ret = readl_poll_timeout(wrapper_base + WRAPPER_CPU_AXI_HALT_STATUS, val, val & WRAPPER_CPU_AXI_HALT_STATUS_IDLE, POLL_INTERVAL_US, @@ -548,12 +596,12 @@ static int venus_halt_axi(struct venus_hfi_device *hdev) } /* Halt AXI and AXI IMEM VBIF Access */ - val = venus_readl(hdev, VBIF_AXI_HALT_CTRL0); + val = readl(vbif_base + VBIF_AXI_HALT_CTRL0); val |= VBIF_AXI_HALT_CTRL0_HALT_REQ; - venus_writel(hdev, VBIF_AXI_HALT_CTRL0, val); + writel(val, vbif_base + VBIF_AXI_HALT_CTRL0); /* Request for AXI bus port halt */ - ret = readl_poll_timeout(base + VBIF_AXI_HALT_CTRL1, val, + ret = readl_poll_timeout(vbif_base + VBIF_AXI_HALT_CTRL1, val, val & VBIF_AXI_HALT_CTRL1_HALT_ACK, POLL_INTERVAL_US, VBIF_AXI_HALT_ACK_TIMEOUT_US); @@ -881,7 +929,7 @@ static int venus_sys_set_default_properties(struct venus_hfi_device *hdev) * enable it explicitly in order to make suspend functional by checking * WFI (wait-for-interrupt) bit. */ - if (IS_V4(hdev->core)) + if (IS_V4(hdev->core) || IS_V6(hdev->core)) venus_sys_idle_indicator = true; ret = venus_sys_set_idle_message(hdev, venus_sys_idle_indicator); @@ -1046,19 +1094,30 @@ static irqreturn_t venus_isr(struct venus_core *core) { struct venus_hfi_device *hdev = to_hfi_priv(core); u32 status; + void __iomem *cpu_cs_base; + void __iomem *wrapper_base; if (!hdev) return IRQ_NONE; - status = venus_readl(hdev, WRAPPER_INTR_STATUS); - - if (status & WRAPPER_INTR_STATUS_A2H_MASK || - status & WRAPPER_INTR_STATUS_A2HWD_MASK || - status & CPU_CS_SCIACMDARG0_INIT_IDLE_MSG_MASK) - hdev->irq_status = status; + cpu_cs_base = hdev->core->cpu_cs_base; + wrapper_base = hdev->core->wrapper_base; - venus_writel(hdev, CPU_CS_A2HSOFTINTCLR, 1); - venus_writel(hdev, WRAPPER_INTR_CLEAR, status); + status = readl(wrapper_base + WRAPPER_INTR_STATUS); + if (IS_V6(core)) { + if (status & WRAPPER_INTR_STATUS_A2H_MASK || + status & WRAPPER_INTR_STATUS_A2HWD_MASK_V6 || + status & CPU_CS_SCIACMDARG0_INIT_IDLE_MSG_MASK) + hdev->irq_status = status; + } else { + if (status & WRAPPER_INTR_STATUS_A2H_MASK || + status & WRAPPER_INTR_STATUS_A2HWD_MASK || + status & CPU_CS_SCIACMDARG0_INIT_IDLE_MSG_MASK) + hdev->irq_status = status; + } + writel(1, cpu_cs_base + CPU_CS_A2HSOFTINTCLR); + if (!IS_V6(core)) + writel(status, wrapper_base + WRAPPER_INTR_CLEAR); return IRQ_WAKE_THREAD; } @@ -1391,6 +1450,7 @@ static int venus_suspend_1xx(struct venus_core *core) { struct venus_hfi_device *hdev = to_hfi_priv(core); struct device *dev = core->dev; + void __iomem *cpu_cs_base = hdev->core->cpu_cs_base; u32 ctrl_status; int ret; @@ -1425,7 +1485,7 @@ static int venus_suspend_1xx(struct venus_core *core) return -EINVAL; } - ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); + ctrl_status = readl(cpu_cs_base + CPU_CS_SCIACMDARG0); if (!(ctrl_status & CPU_CS_SCIACMDARG0_PC_READY)) { mutex_unlock(&hdev->lock); return -EINVAL; @@ -1446,10 +1506,16 @@ static int venus_suspend_1xx(struct venus_core *core) static bool venus_cpu_and_video_core_idle(struct venus_hfi_device *hdev) { + void __iomem *wrapper_base = hdev->core->wrapper_base; + void __iomem *wrapper_tz_base = hdev->core->wrapper_tz_base; + void __iomem *cpu_cs_base = hdev->core->cpu_cs_base; u32 ctrl_status, cpu_status; - cpu_status = venus_readl(hdev, WRAPPER_CPU_STATUS); - ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); + if (IS_V6(hdev->core)) + cpu_status = readl(wrapper_tz_base + WRAPPER_TZ_CPU_STATUS_V6); + else + cpu_status = readl(wrapper_base + WRAPPER_CPU_STATUS); + ctrl_status = readl(cpu_cs_base + CPU_CS_SCIACMDARG0); if (cpu_status & WRAPPER_CPU_STATUS_WFI && ctrl_status & CPU_CS_SCIACMDARG0_INIT_IDLE_MSG_MASK) @@ -1460,10 +1526,16 @@ static bool venus_cpu_and_video_core_idle(struct venus_hfi_device *hdev) static bool venus_cpu_idle_and_pc_ready(struct venus_hfi_device *hdev) { + void __iomem *wrapper_base = hdev->core->wrapper_base; + void __iomem *wrapper_tz_base = hdev->core->wrapper_tz_base; + void __iomem *cpu_cs_base = hdev->core->cpu_cs_base; u32 ctrl_status, cpu_status; - cpu_status = venus_readl(hdev, WRAPPER_CPU_STATUS); - ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); + if (IS_V6(hdev->core)) + cpu_status = readl(wrapper_tz_base + WRAPPER_TZ_CPU_STATUS_V6); + else + cpu_status = readl(wrapper_base + WRAPPER_CPU_STATUS); + ctrl_status = readl(cpu_cs_base + CPU_CS_SCIACMDARG0); if (cpu_status & WRAPPER_CPU_STATUS_WFI && ctrl_status & CPU_CS_SCIACMDARG0_PC_READY) @@ -1476,6 +1548,7 @@ static int venus_suspend_3xx(struct venus_core *core) { struct venus_hfi_device *hdev = to_hfi_priv(core); struct device *dev = core->dev; + void __iomem *cpu_cs_base = hdev->core->cpu_cs_base; u32 ctrl_status; bool val; int ret; @@ -1492,7 +1565,7 @@ static int venus_suspend_3xx(struct venus_core *core) return -EINVAL; } - ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0); + ctrl_status = readl(cpu_cs_base + CPU_CS_SCIACMDARG0); if (ctrl_status & CPU_CS_SCIACMDARG0_PC_READY) goto power_off; @@ -1539,7 +1612,7 @@ power_off: static int venus_suspend(struct venus_core *core) { - if (IS_V3(core) || IS_V4(core)) + if (IS_V3(core) || IS_V4(core) || IS_V6(core)) return venus_suspend_3xx(core); return venus_suspend_1xx(core); @@ -1580,10 +1653,10 @@ void venus_hfi_destroy(struct venus_core *core) { struct venus_hfi_device *hdev = to_hfi_priv(core); + core->priv = NULL; venus_interface_queues_release(hdev); mutex_destroy(&hdev->lock); kfree(hdev); - core->priv = NULL; core->ops = NULL; } diff --git a/drivers/media/platform/qcom/venus/hfi_venus_io.h b/drivers/media/platform/qcom/venus/hfi_venus_io.h index 3b52f98478db..300c6e47e72f 100644 --- a/drivers/media/platform/qcom/venus/hfi_venus_io.h +++ b/drivers/media/platform/qcom/venus/hfi_venus_io.h @@ -8,27 +8,31 @@ #define VBIF_BASE 0x80000 -#define VBIF_AXI_HALT_CTRL0 (VBIF_BASE + 0x208) -#define VBIF_AXI_HALT_CTRL1 (VBIF_BASE + 0x20c) +#define VBIF_AXI_HALT_CTRL0 0x208 +#define VBIF_AXI_HALT_CTRL1 0x20c #define VBIF_AXI_HALT_CTRL0_HALT_REQ BIT(0) #define VBIF_AXI_HALT_CTRL1_HALT_ACK BIT(0) #define VBIF_AXI_HALT_ACK_TIMEOUT_US 500000 #define CPU_BASE 0xc0000 + #define CPU_CS_BASE (CPU_BASE + 0x12000) #define CPU_IC_BASE (CPU_BASE + 0x1f000) +#define CPU_BASE_V6 0xa0000 +#define CPU_CS_BASE_V6 CPU_BASE_V6 +#define CPU_IC_BASE_V6 (CPU_BASE_V6 + 0x138) -#define CPU_CS_A2HSOFTINTCLR (CPU_CS_BASE + 0x1c) +#define CPU_CS_A2HSOFTINTCLR 0x1c -#define VIDC_CTRL_INIT (CPU_CS_BASE + 0x48) +#define VIDC_CTRL_INIT 0x48 #define VIDC_CTRL_INIT_RESERVED_BITS31_1_MASK 0xfffffffe #define VIDC_CTRL_INIT_RESERVED_BITS31_1_SHIFT 1 #define VIDC_CTRL_INIT_CTRL_MASK 0x1 #define VIDC_CTRL_INIT_CTRL_SHIFT 0 /* HFI control status */ -#define CPU_CS_SCIACMDARG0 (CPU_CS_BASE + 0x4c) +#define CPU_CS_SCIACMDARG0 0x4c #define CPU_CS_SCIACMDARG0_MASK 0xff #define CPU_CS_SCIACMDARG0_SHIFT 0x0 #define CPU_CS_SCIACMDARG0_ERROR_STATUS_MASK 0xfe @@ -39,42 +43,56 @@ #define CPU_CS_SCIACMDARG0_INIT_IDLE_MSG_MASK BIT(30) /* HFI queue table info */ -#define CPU_CS_SCIACMDARG1 (CPU_CS_BASE + 0x50) +#define CPU_CS_SCIACMDARG1 0x50 /* HFI queue table address */ -#define CPU_CS_SCIACMDARG2 (CPU_CS_BASE + 0x54) +#define CPU_CS_SCIACMDARG2 0x54 /* Venus cpu */ -#define CPU_CS_SCIACMDARG3 (CPU_CS_BASE + 0x58) - -#define SFR_ADDR (CPU_CS_BASE + 0x5c) -#define MMAP_ADDR (CPU_CS_BASE + 0x60) -#define UC_REGION_ADDR (CPU_CS_BASE + 0x64) -#define UC_REGION_SIZE (CPU_CS_BASE + 0x68) - -#define CPU_IC_SOFTINT (CPU_IC_BASE + 0x18) +#define CPU_CS_SCIACMDARG3 0x58 + +#define SFR_ADDR 0x5c +#define MMAP_ADDR 0x60 +#define UC_REGION_ADDR 0x64 +#define UC_REGION_SIZE 0x68 + +#define CPU_CS_H2XSOFTINTEN_V6 0x148 + +#define CPU_CS_X2RPMH_V6 0x168 +#define CPU_CS_X2RPMH_MASK0_BMSK_V6 0x1 +#define CPU_CS_X2RPMH_MASK0_SHFT_V6 0x0 +#define CPU_CS_X2RPMH_MASK1_BMSK_V6 0x2 +#define CPU_CS_X2RPMH_MASK1_SHFT_V6 0x1 +#define CPU_CS_X2RPMH_SWOVERRIDE_BMSK_V6 0x4 +#define CPU_CS_X2RPMH_SWOVERRIDE_SHFT_V6 0x3 + +/* Relative to CPU_IC_BASE */ +#define CPU_IC_SOFTINT 0x18 +#define CPU_IC_SOFTINT_V6 0x150 #define CPU_IC_SOFTINT_H2A_MASK 0x8000 #define CPU_IC_SOFTINT_H2A_SHIFT 0xf +#define CPU_IC_SOFTINT_H2A_SHIFT_V6 0x0 /* Venus wrapper */ +#define WRAPPER_BASE_V6 0x000b0000 #define WRAPPER_BASE 0x000e0000 -#define WRAPPER_HW_VERSION (WRAPPER_BASE + 0x00) +#define WRAPPER_HW_VERSION 0x00 #define WRAPPER_HW_VERSION_MAJOR_VERSION_MASK 0x78000000 #define WRAPPER_HW_VERSION_MAJOR_VERSION_SHIFT 28 #define WRAPPER_HW_VERSION_MINOR_VERSION_MASK 0xfff0000 #define WRAPPER_HW_VERSION_MINOR_VERSION_SHIFT 16 #define WRAPPER_HW_VERSION_STEP_VERSION_MASK 0xffff -#define WRAPPER_CLOCK_CONFIG (WRAPPER_BASE + 0x04) +#define WRAPPER_CLOCK_CONFIG 0x04 -#define WRAPPER_INTR_STATUS (WRAPPER_BASE + 0x0c) +#define WRAPPER_INTR_STATUS 0x0c #define WRAPPER_INTR_STATUS_A2HWD_MASK 0x10 #define WRAPPER_INTR_STATUS_A2HWD_SHIFT 0x4 #define WRAPPER_INTR_STATUS_A2H_MASK 0x4 #define WRAPPER_INTR_STATUS_A2H_SHIFT 0x2 -#define WRAPPER_INTR_MASK (WRAPPER_BASE + 0x10) +#define WRAPPER_INTR_MASK 0x10 #define WRAPPER_INTR_MASK_A2HWD_BASK 0x10 #define WRAPPER_INTR_MASK_A2HWD_SHIFT 0x4 #define WRAPPER_INTR_MASK_A2HVCODEC_MASK 0x8 @@ -82,41 +100,59 @@ #define WRAPPER_INTR_MASK_A2HCPU_MASK 0x4 #define WRAPPER_INTR_MASK_A2HCPU_SHIFT 0x2 -#define WRAPPER_INTR_CLEAR (WRAPPER_BASE + 0x14) +#define WRAPPER_INTR_STATUS_A2HWD_MASK_V6 0x8 +#define WRAPPER_INTR_MASK_A2HWD_BASK_V6 0x8 + +#define WRAPPER_INTR_CLEAR 0x14 #define WRAPPER_INTR_CLEAR_A2HWD_MASK 0x10 #define WRAPPER_INTR_CLEAR_A2HWD_SHIFT 0x4 #define WRAPPER_INTR_CLEAR_A2H_MASK 0x4 #define WRAPPER_INTR_CLEAR_A2H_SHIFT 0x2 -#define WRAPPER_POWER_STATUS (WRAPPER_BASE + 0x44) -#define WRAPPER_VDEC_VCODEC_POWER_CONTROL (WRAPPER_BASE + 0x48) -#define WRAPPER_VENC_VCODEC_POWER_CONTROL (WRAPPER_BASE + 0x4c) -#define WRAPPER_VDEC_VENC_AHB_BRIDGE_SYNC_RESET (WRAPPER_BASE + 0x64) +#define WRAPPER_POWER_STATUS 0x44 +#define WRAPPER_VDEC_VCODEC_POWER_CONTROL 0x48 +#define WRAPPER_VENC_VCODEC_POWER_CONTROL 0x4c +#define WRAPPER_DEBUG_BRIDGE_LPI_CONTROL_V6 0x54 +#define WRAPPER_DEBUG_BRIDGE_LPI_STATUS_V6 0x58 +#define WRAPPER_VDEC_VENC_AHB_BRIDGE_SYNC_RESET 0x64 -#define WRAPPER_CPU_CLOCK_CONFIG (WRAPPER_BASE + 0x2000) -#define WRAPPER_CPU_AXI_HALT (WRAPPER_BASE + 0x2008) +#define WRAPPER_CPU_CLOCK_CONFIG 0x2000 +#define WRAPPER_CPU_AXI_HALT 0x2008 #define WRAPPER_CPU_AXI_HALT_HALT BIT(16) -#define WRAPPER_CPU_AXI_HALT_STATUS (WRAPPER_BASE + 0x200c) +#define WRAPPER_CPU_AXI_HALT_STATUS 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_CGC_DIS 0x2010 +#define WRAPPER_CPU_STATUS 0x2014 #define WRAPPER_CPU_STATUS_WFI BIT(0) -#define WRAPPER_SW_RESET (WRAPPER_BASE + 0x3000) -#define WRAPPER_CPA_START_ADDR (WRAPPER_BASE + 0x1020) -#define WRAPPER_CPA_END_ADDR (WRAPPER_BASE + 0x1024) -#define WRAPPER_FW_START_ADDR (WRAPPER_BASE + 0x1028) -#define WRAPPER_FW_END_ADDR (WRAPPER_BASE + 0x102C) -#define WRAPPER_NONPIX_START_ADDR (WRAPPER_BASE + 0x1030) -#define WRAPPER_NONPIX_END_ADDR (WRAPPER_BASE + 0x1034) -#define WRAPPER_A9SS_SW_RESET (WRAPPER_BASE + 0x3000) +#define WRAPPER_SW_RESET 0x3000 +#define WRAPPER_CPA_START_ADDR 0x1020 +#define WRAPPER_CPA_END_ADDR 0x1024 +#define WRAPPER_FW_START_ADDR 0x1028 +#define WRAPPER_FW_END_ADDR 0x102C +#define WRAPPER_NONPIX_START_ADDR 0x1030 +#define WRAPPER_NONPIX_END_ADDR 0x1034 +#define WRAPPER_A9SS_SW_RESET 0x3000 #define WRAPPER_A9SS_SW_RESET_BIT BIT(4) /* Venus 4xx */ -#define WRAPPER_VCODEC0_MMCC_POWER_STATUS (WRAPPER_BASE + 0x90) -#define WRAPPER_VCODEC0_MMCC_POWER_CONTROL (WRAPPER_BASE + 0x94) +#define WRAPPER_VCODEC0_MMCC_POWER_STATUS 0x90 +#define WRAPPER_VCODEC0_MMCC_POWER_CONTROL 0x94 + +#define WRAPPER_VCODEC1_MMCC_POWER_STATUS 0x110 +#define WRAPPER_VCODEC1_MMCC_POWER_CONTROL 0x114 + +/* Venus 6xx */ +#define WRAPPER_CORE_POWER_STATUS_V6 0x80 +#define WRAPPER_CORE_POWER_CONTROL_V6 0x84 + +/* Wrapper TZ 6xx */ +#define WRAPPER_TZ_BASE_V6 0x000c0000 +#define WRAPPER_TZ_CPU_STATUS_V6 0x10 -#define WRAPPER_VCODEC1_MMCC_POWER_STATUS (WRAPPER_BASE + 0x110) -#define WRAPPER_VCODEC1_MMCC_POWER_CONTROL (WRAPPER_BASE + 0x114) +/* Venus AON */ +#define AON_BASE_V6 0x000e0000 +#define AON_WRAPPER_MVP_NOC_LPI_CONTROL 0x00 +#define AON_WRAPPER_MVP_NOC_LPI_STATUS 0x04 #endif diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c b/drivers/media/platform/qcom/venus/pm_helpers.c index 43c4e3d9e281..c7e1ebec47ee 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.c +++ b/drivers/media/platform/qcom/venus/pm_helpers.c @@ -11,6 +11,7 @@ #include <linux/pm_domain.h> #include <linux/pm_opp.h> #include <linux/pm_runtime.h> +#include <linux/reset.h> #include <linux/types.h> #include <media/v4l2-mem2mem.h> @@ -40,10 +41,24 @@ static int core_clks_get(struct venus_core *core) static int core_clks_enable(struct venus_core *core) { const struct venus_resources *res = core->res; + const struct freq_tbl *freq_tbl = core->res->freq_tbl; + unsigned int freq_tbl_size = core->res->freq_tbl_size; + unsigned long freq; unsigned int i; int ret; + if (!freq_tbl) + return -EINVAL; + + freq = freq_tbl[freq_tbl_size - 1].freq; + for (i = 0; i < res->clks_num; i++) { + if (IS_V6(core)) { + ret = clk_set_rate(core->clks[i], freq); + if (ret) + goto err; + } + ret = clk_prepare_enable(core->clks[i]); if (ret) goto err; @@ -186,7 +201,7 @@ static void mbs_to_bw(struct venus_inst *inst, u32 mbs, u32 *avg, u32 *peak) return; for (i = 0; i < num_rows; i++) { - if (mbs > bw_tbl[i].mbs_per_sec) + if (i != 0 && mbs > bw_tbl[i].mbs_per_sec) break; if (inst->dpb_fmt & HFI_COLOR_FORMAT_10_BIT_BASE) { @@ -277,16 +292,28 @@ set_freq: return 0; } -static int core_get_v1(struct device *dev) +static int core_get_v1(struct venus_core *core) { - struct venus_core *core = dev_get_drvdata(dev); + int ret; + + ret = core_clks_get(core); + if (ret) + return ret; + + core->opp_table = dev_pm_opp_set_clkname(core->dev, "core"); + if (IS_ERR(core->opp_table)) + return PTR_ERR(core->opp_table); - return core_clks_get(core); + return 0; } -static int core_power_v1(struct device *dev, int on) +static void core_put_v1(struct venus_core *core) +{ + dev_pm_opp_put_clkname(core->opp_table); +} + +static int core_power_v1(struct venus_core *core, int on) { - struct venus_core *core = dev_get_drvdata(dev); int ret = 0; if (on == POWER_ON) @@ -299,6 +326,7 @@ static int core_power_v1(struct device *dev, int on) static const struct venus_pm_ops pm_ops_v1 = { .core_get = core_get_v1, + .core_put = core_put_v1, .core_power = core_power_v1, .load_scale = load_scale_v1, }; @@ -309,9 +337,9 @@ vcodec_control_v3(struct venus_core *core, u32 session_type, bool enable) void __iomem *ctrl; if (session_type == VIDC_SESSION_TYPE_DEC) - ctrl = core->base + WRAPPER_VDEC_VCODEC_POWER_CONTROL; + ctrl = core->wrapper_base + WRAPPER_VDEC_VCODEC_POWER_CONTROL; else - ctrl = core->base + WRAPPER_VENC_VCODEC_POWER_CONTROL; + ctrl = core->wrapper_base + WRAPPER_VENC_VCODEC_POWER_CONTROL; if (enable) writel(0, ctrl); @@ -371,6 +399,7 @@ static int venc_power_v3(struct device *dev, int on) static const struct venus_pm_ops pm_ops_v3 = { .core_get = core_get_v1, + .core_put = core_put_v1, .core_power = core_power_v1, .vdec_get = vdec_get_v3, .vdec_power = vdec_power_v3, @@ -385,12 +414,15 @@ static int vcodec_control_v4(struct venus_core *core, u32 coreid, bool enable) u32 val; int ret; - if (coreid == VIDC_CORE_ID_1) { - ctrl = core->base + WRAPPER_VCODEC0_MMCC_POWER_CONTROL; - stat = core->base + WRAPPER_VCODEC0_MMCC_POWER_STATUS; + if (IS_V6(core)) { + ctrl = core->wrapper_base + WRAPPER_CORE_POWER_CONTROL_V6; + stat = core->wrapper_base + WRAPPER_CORE_POWER_STATUS_V6; + } else if (coreid == VIDC_CORE_ID_1) { + ctrl = core->wrapper_base + WRAPPER_VCODEC0_MMCC_POWER_CONTROL; + stat = core->wrapper_base + WRAPPER_VCODEC0_MMCC_POWER_STATUS; } else { - ctrl = core->base + WRAPPER_VCODEC1_MMCC_POWER_CONTROL; - stat = core->base + WRAPPER_VCODEC1_MMCC_POWER_STATUS; + ctrl = core->wrapper_base + WRAPPER_VCODEC1_MMCC_POWER_CONTROL; + stat = core->wrapper_base + WRAPPER_VCODEC1_MMCC_POWER_STATUS; } if (enable) { @@ -753,12 +785,12 @@ static int venc_power_v4(struct device *dev, int on) return ret; } -static int vcodec_domains_get(struct device *dev) +static int vcodec_domains_get(struct venus_core *core) { int ret; struct opp_table *opp_table; struct device **opp_virt_dev; - struct venus_core *core = dev_get_drvdata(dev); + struct device *dev = core->dev; const struct venus_resources *res = core->res; struct device *pd; unsigned int i; @@ -809,9 +841,8 @@ opp_attach_err: return ret; } -static void vcodec_domains_put(struct device *dev) +static void vcodec_domains_put(struct venus_core *core) { - struct venus_core *core = dev_get_drvdata(dev); const struct venus_resources *res = core->res; unsigned int i; @@ -834,9 +865,55 @@ skip_pmdomains: dev_pm_opp_detach_genpd(core->opp_table); } -static int core_get_v4(struct device *dev) +static int core_resets_reset(struct venus_core *core) { - struct venus_core *core = dev_get_drvdata(dev); + const struct venus_resources *res = core->res; + unsigned int i; + int ret; + + if (!res->resets_num) + return 0; + + for (i = 0; i < res->resets_num; i++) { + ret = reset_control_assert(core->resets[i]); + if (ret) + goto err; + + usleep_range(150, 250); + ret = reset_control_deassert(core->resets[i]); + if (ret) + goto err; + } + +err: + return ret; +} + +static int core_resets_get(struct venus_core *core) +{ + struct device *dev = core->dev; + const struct venus_resources *res = core->res; + unsigned int i; + int ret; + + if (!res->resets_num) + return 0; + + for (i = 0; i < res->resets_num; i++) { + core->resets[i] = + devm_reset_control_get_exclusive(dev, res->resets[i]); + if (IS_ERR(core->resets[i])) { + ret = PTR_ERR(core->resets[i]); + return ret; + } + } + + return 0; +} + +static int core_get_v4(struct venus_core *core) +{ + struct device *dev = core->dev; const struct venus_resources *res = core->res; int ret; @@ -857,6 +934,10 @@ static int core_get_v4(struct device *dev) if (ret) return ret; + ret = core_resets_get(core); + if (ret) + return ret; + if (legacy_binding) return 0; @@ -875,7 +956,7 @@ static int core_get_v4(struct device *dev) } } - ret = vcodec_domains_get(dev); + ret = vcodec_domains_get(core); if (ret) { if (core->has_opp_table) dev_pm_opp_of_remove_table(dev); @@ -886,14 +967,14 @@ static int core_get_v4(struct device *dev) return 0; } -static void core_put_v4(struct device *dev) +static void core_put_v4(struct venus_core *core) { - struct venus_core *core = dev_get_drvdata(dev); + struct device *dev = core->dev; if (legacy_binding) return; - vcodec_domains_put(dev); + vcodec_domains_put(core); if (core->has_opp_table) dev_pm_opp_of_remove_table(dev); @@ -901,9 +982,9 @@ static void core_put_v4(struct device *dev) } -static int core_power_v4(struct device *dev, int on) +static int core_power_v4(struct venus_core *core, int on) { - struct venus_core *core = dev_get_drvdata(dev); + struct device *dev = core->dev; struct device *pmctrl = core->pmdomains[0]; int ret = 0; @@ -916,6 +997,13 @@ static int core_power_v4(struct device *dev, int on) } } + ret = core_resets_reset(core); + if (ret) { + if (pmctrl) + pm_runtime_put_sync(pmctrl); + return ret; + } + ret = core_clks_enable(core); if (ret < 0 && pmctrl) pm_runtime_put_sync(pmctrl); @@ -926,6 +1014,8 @@ static int core_power_v4(struct device *dev, int on) core_clks_disable(core); + ret = core_resets_reset(core); + if (pmctrl) pm_runtime_put_sync(pmctrl); } @@ -993,7 +1083,7 @@ static int load_scale_v4(struct venus_inst *inst) freq = max(freq_core1, freq_core2); - if (freq >= table[0].freq) { + if (freq > table[0].freq) { freq = table[0].freq; dev_warn(dev, "HW is overloaded, needed: %lu max: %lu\n", freq, table[0].freq); @@ -1049,6 +1139,7 @@ const struct venus_pm_ops *venus_pm_get(enum hfi_version version) case HFI_VERSION_3XX: return &pm_ops_v3; case HFI_VERSION_4XX: + case HFI_VERSION_6XX: return &pm_ops_v4; } diff --git a/drivers/media/platform/qcom/venus/pm_helpers.h b/drivers/media/platform/qcom/venus/pm_helpers.h index aa2f6afa2354..a492c50c5543 100644 --- a/drivers/media/platform/qcom/venus/pm_helpers.h +++ b/drivers/media/platform/qcom/venus/pm_helpers.h @@ -4,14 +4,15 @@ #define __VENUS_PM_HELPERS_H__ struct device; +struct venus_core; #define POWER_ON 1 #define POWER_OFF 0 struct venus_pm_ops { - int (*core_get)(struct device *dev); - void (*core_put)(struct device *dev); - int (*core_power)(struct device *dev, int on); + int (*core_get)(struct venus_core *core); + void (*core_put)(struct venus_core *core); + int (*core_power)(struct venus_core *core, int on); int (*vdec_get)(struct device *dev); void (*vdec_put)(struct device *dev); diff --git a/drivers/media/platform/qcom/venus/vdec.c b/drivers/media/platform/qcom/venus/vdec.c index e4dc97f00fc3..ddb7cd39424e 100644 --- a/drivers/media/platform/qcom/venus/vdec.c +++ b/drivers/media/platform/qcom/venus/vdec.c @@ -515,7 +515,10 @@ vdec_decoder_cmd(struct file *file, void *fh, struct v4l2_decoder_cmd *cmd) fdata.buffer_type = HFI_BUFFER_INPUT; fdata.flags |= HFI_BUFFERFLAG_EOS; - fdata.device_addr = 0xdeadb000; + if (IS_V6(inst->core)) + fdata.device_addr = 0; + else + fdata.device_addr = 0xdeadb000; ret = hfi_session_process_buf(inst, &fdata); @@ -620,7 +623,7 @@ static int vdec_set_properties(struct venus_inst *inst) { struct vdec_controls *ctr = &inst->controls.dec; struct hfi_enable en = { .enable = 1 }; - u32 ptype; + u32 ptype, decode_order, conceal; int ret; if (ctr->post_loop_deb_mode) { @@ -630,6 +633,23 @@ static int vdec_set_properties(struct venus_inst *inst) return ret; } + if (ctr->display_delay_enable && ctr->display_delay == 0) { + ptype = HFI_PROPERTY_PARAM_VDEC_OUTPUT_ORDER; + decode_order = HFI_OUTPUT_ORDER_DECODE; + ret = hfi_session_set_property(inst, ptype, &decode_order); + if (ret) + return ret; + } + + ptype = HFI_PROPERTY_PARAM_VDEC_CONCEAL_COLOR; + conceal = ctr->conceal_color & 0xffff; + conceal |= ((ctr->conceal_color >> 16) & 0xffff) << 10; + conceal |= ((ctr->conceal_color >> 32) & 0xffff) << 20; + + ret = hfi_session_set_property(inst, ptype, &conceal); + if (ret) + return ret; + return 0; } @@ -647,7 +667,7 @@ static int vdec_output_conf(struct venus_inst *inst) u32 ptype; int ret; - ret = venus_helper_set_work_mode(inst, VIDC_WORK_MODE_2); + ret = venus_helper_set_work_mode(inst); if (ret) return ret; @@ -662,8 +682,8 @@ static int vdec_output_conf(struct venus_inst *inst) if (width > 1920 && height > ALIGN(1080, 32)) ubwc = true; - /* For Venus v4 UBWC format is mandatory */ - if (IS_V4(core)) + /* For Venus v4/v6 UBWC format is mandatory */ + if (IS_V4(core) || IS_V6(core)) ubwc = true; ret = venus_helper_get_out_fmts(inst, inst->fmt_cap->pixfmt, &out_fmt, @@ -698,6 +718,10 @@ static int vdec_output_conf(struct venus_inst *inst) if (ret) return ret; + ret = venus_helper_set_format_constraints(inst); + if (ret) + return ret; + if (inst->dpb_fmt) { ret = venus_helper_set_multistream(inst, false, true); if (ret) @@ -714,7 +738,7 @@ static int vdec_output_conf(struct venus_inst *inst) return ret; } - if (IS_V3(core) || IS_V4(core)) { + if (IS_V3(core) || IS_V4(core) || IS_V6(core)) { ret = venus_helper_get_bufreq(inst, HFI_BUFFER_OUTPUT, &bufreq); if (ret) return ret; diff --git a/drivers/media/platform/qcom/venus/vdec_ctrls.c b/drivers/media/platform/qcom/venus/vdec_ctrls.c index 974110b75b93..fbe12a608b21 100644 --- a/drivers/media/platform/qcom/venus/vdec_ctrls.c +++ b/drivers/media/platform/qcom/venus/vdec_ctrls.c @@ -30,6 +30,15 @@ static int vdec_op_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_VP9_LEVEL: ctr->level = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY: + ctr->display_delay = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE: + ctr->display_delay_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_DEC_CONCEAL_COLOR: + ctr->conceal_color = *ctrl->p_new.p_s64; + break; default: return -EINVAL; } @@ -89,7 +98,7 @@ int vdec_ctrl_init(struct venus_inst *inst) struct v4l2_ctrl *ctrl; int ret; - ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 9); + ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 12); if (ret) return ret; @@ -158,6 +167,18 @@ int vdec_ctrl_init(struct venus_inst *inst) if (ctrl) ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; + v4l2_ctrl_new_std(&inst->ctrl_handler, &vdec_ctrl_ops, + V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY, + 0, 16383, 1, 0); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &vdec_ctrl_ops, + V4L2_CID_MPEG_VIDEO_DEC_DISPLAY_DELAY_ENABLE, + 0, 1, 1, 0); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &vdec_ctrl_ops, + V4L2_CID_MPEG_VIDEO_DEC_CONCEAL_COLOR, 0, + 0xffffffffffffLL, 1, 0x8000800010LL); + ret = inst->ctrl_handler.error; if (ret) { v4l2_ctrl_handler_free(&inst->ctrl_handler); diff --git a/drivers/media/platform/qcom/venus/venc.c b/drivers/media/platform/qcom/venus/venc.c index 6976ed553647..4a7291f934b6 100644 --- a/drivers/media/platform/qcom/venus/venc.c +++ b/drivers/media/platform/qcom/venus/venc.c @@ -546,11 +546,12 @@ static int venc_set_properties(struct venus_inst *inst) struct hfi_quantization quant; struct hfi_quantization_range quant_range; struct hfi_enable en; + struct hfi_ltr_mode ltr_mode; u32 ptype, rate_control, bitrate; u32 profile, level; int ret; - ret = venus_helper_set_work_mode(inst, VIDC_WORK_MODE_2); + ret = venus_helper_set_work_mode(inst); if (ret) return ret; @@ -612,6 +613,35 @@ static int venc_set_properties(struct venus_inst *inst) return ret; } + if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) { + struct hfi_hdr10_pq_sei hdr10; + unsigned int c; + + ptype = HFI_PROPERTY_PARAM_VENC_HDR10_PQ_SEI; + + for (c = 0; c < 3; c++) { + hdr10.mastering.display_primaries_x[c] = + ctr->mastering.display_primaries_x[c]; + hdr10.mastering.display_primaries_y[c] = + ctr->mastering.display_primaries_y[c]; + } + + hdr10.mastering.white_point_x = ctr->mastering.white_point_x; + hdr10.mastering.white_point_y = ctr->mastering.white_point_y; + hdr10.mastering.max_display_mastering_luminance = + ctr->mastering.max_display_mastering_luminance; + hdr10.mastering.min_display_mastering_luminance = + ctr->mastering.min_display_mastering_luminance; + + hdr10.cll.max_content_light = ctr->cll.max_content_light_level; + hdr10.cll.max_pic_average_light = + ctr->cll.max_pic_average_light_level; + + ret = hfi_session_set_property(inst, ptype, &hdr10); + if (ret) + return ret; + } + if (ctr->num_b_frames) { u32 max_num_b_frames = NUM_B_FRAMES_MAX; @@ -722,6 +752,14 @@ static int venc_set_properties(struct venus_inst *inst) if (ret) return ret; + ptype = HFI_PROPERTY_PARAM_VENC_LTRMODE; + ltr_mode.ltr_count = ctr->ltr_count; + ltr_mode.ltr_mode = HFI_LTR_MODE_MANUAL; + ltr_mode.trust_mode = 1; + ret = hfi_session_set_property(inst, ptype, <r_mode); + if (ret) + return ret; + switch (inst->hfi_codec) { case HFI_VIDEO_CODEC_H264: profile = ctr->profile.h264; @@ -754,6 +792,20 @@ static int venc_set_properties(struct venus_inst *inst) if (ret) return ret; + if (inst->fmt_cap->pixfmt == V4L2_PIX_FMT_H264 || + inst->fmt_cap->pixfmt == V4L2_PIX_FMT_HEVC) { + struct hfi_enable en = {}; + + ptype = HFI_PROPERTY_PARAM_VENC_H264_GENERATE_AUDNAL; + + if (ctr->aud_enable) + en.enable = 1; + + ret = hfi_session_set_property(inst, ptype, &en); + if (ret) + return ret; + } + return 0; } diff --git a/drivers/media/platform/qcom/venus/venc_ctrls.c b/drivers/media/platform/qcom/venus/venc_ctrls.c index a52b80055173..637c92f6c5be 100644 --- a/drivers/media/platform/qcom/venus/venc_ctrls.c +++ b/drivers/media/platform/qcom/venus/venc_ctrls.c @@ -20,6 +20,7 @@ #define INTRA_REFRESH_MBS_MAX 300 #define AT_SLICE_BOUNDARY \ V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY +#define MAX_LTR_FRAME_COUNT 4 static int venc_calc_bpframes(u32 gop_size, u32 conseq_b, u32 *bf, u32 *pf) { @@ -72,6 +73,8 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) struct venc_controls *ctr = &inst->controls.enc; struct hfi_enable en = { .enable = 1 }; struct hfi_bitrate brate; + struct hfi_ltr_use ltr_use; + struct hfi_ltr_mark ltr_mark; u32 bframes; u32 ptype; int ret; @@ -276,6 +279,46 @@ static int venc_op_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_MPEG_VIDEO_BASELAYER_PRIORITY_ID: ctr->base_priority_id = ctrl->val; break; + case V4L2_CID_MPEG_VIDEO_AU_DELIMITER: + ctr->aud_enable = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_LTR_COUNT: + ctr->ltr_count = ctrl->val; + break; + case V4L2_CID_MPEG_VIDEO_FRAME_LTR_INDEX: + mutex_lock(&inst->lock); + if (inst->streamon_out && inst->streamon_cap) { + ptype = HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME; + ltr_mark.mark_frame = ctrl->val; + ret = hfi_session_set_property(inst, ptype, <r_mark); + if (ret) { + mutex_unlock(&inst->lock); + return ret; + } + } + mutex_unlock(&inst->lock); + break; + case V4L2_CID_MPEG_VIDEO_USE_LTR_FRAMES: + mutex_lock(&inst->lock); + if (inst->streamon_out && inst->streamon_cap) { + ptype = HFI_PROPERTY_CONFIG_VENC_USELTRFRAME; + ltr_use.ref_ltr = ctrl->val; + ltr_use.use_constrnt = true; + ltr_use.frames = 0; + ret = hfi_session_set_property(inst, ptype, <r_use); + if (ret) { + mutex_unlock(&inst->lock); + return ret; + } + } + mutex_unlock(&inst->lock); + break; + case V4L2_CID_COLORIMETRY_HDR10_CLL_INFO: + ctr->cll = *ctrl->p_new.p_hdr10_cll; + break; + case V4L2_CID_COLORIMETRY_HDR10_MASTERING_DISPLAY: + ctr->mastering = *ctrl->p_new.p_hdr10_mastering; + break; default: return -EINVAL; } @@ -291,7 +334,7 @@ int venc_ctrl_init(struct venus_inst *inst) { int ret; - ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 51); + ret = v4l2_ctrl_handler_init(&inst->ctrl_handler, 57); if (ret) return ret; @@ -359,7 +402,7 @@ int venc_ctrl_init(struct venus_inst *inst) V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME, ~((1 << V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) | (1 << V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME)), - V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE); + V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME); v4l2_ctrl_new_std_menu(&inst->ctrl_handler, &venc_ctrl_ops, V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE, @@ -498,6 +541,29 @@ int venc_ctrl_init(struct venus_inst *inst) V4L2_CID_MPEG_VIDEO_BASELAYER_PRIORITY_ID, 0, 6, 1, 0); + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_AU_DELIMITER, 0, 1, 1, 0); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_USE_LTR_FRAMES, 0, + ((1 << MAX_LTR_FRAME_COUNT) - 1), 0, 0); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_LTR_COUNT, 0, + MAX_LTR_FRAME_COUNT, 1, 0); + + v4l2_ctrl_new_std(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_MPEG_VIDEO_FRAME_LTR_INDEX, 0, + (MAX_LTR_FRAME_COUNT - 1), 1, 0); + + v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_COLORIMETRY_HDR10_CLL_INFO, + v4l2_ctrl_ptr_create(NULL)); + + v4l2_ctrl_new_std_compound(&inst->ctrl_handler, &venc_ctrl_ops, + V4L2_CID_COLORIMETRY_HDR10_MASTERING_DISPLAY, + v4l2_ctrl_ptr_create(NULL)); + ret = inst->ctrl_handler.error; if (ret) goto err; |