Message ID | 20210304120326.153966-1-robert.foss@linaro.org |
---|---|
Headers | show |
Series | Add support for the SDM845 Camera Subsystem | expand |
Hi Robert, The testgen_pattern in this version are in the right direction, but the test patterns aren't working properly in v6: On db410c: ========== -----8<----- $ v4l2-ctl -L -d /dev/v4l-subdev2 Image Processing Controls test_pattern 0x009f0903 (menu) : min=0 max=7 default=0 value=1 0: Disabled 1: Incrementing 2: Alternating 0x55/0xAA 3: All Zeros 0x00 4: All Ones 0xFF 5: Pseudo-random Data 6: User Specified 7: Complex pattern $ -----8<----- - "max=7" is not right; it should be "max=6", and "7: Complex pattern" should not be listed for this SOC. On db845c: ========== -----8<----- $ v4l2-ctl -L -d /dev/v4l-subdev4 Image Processing Controls test_pattern 0x009f0903 (menu) : min=0 max=10 default=0 value=0 0: Disabled 1: Incrementing 2: Alternating 0x55/0xAA 3: All Zeros 0x00 4: All Ones 0xFF 5: Pseudo-random Data 6: User Specified 7: Complex pattern 8: Color box 9: Color bars Message from syslogd@linaro-gnome at Mar 8 21:09:26 ... kernel:[ 3936.604286] Internal error: Oops: 96000004 [#1] PREEMPT SMP Message from syslogd@linaro-gnome at Mar 8 21:09:26 ... kernel:[ 3936.858085] Code: a8c27bfd d50323bf d65f03c0 b4ffff61 (39400020) Segmentation fault $ -----8<----- - "max=10" is too much; it goes beyond the array of test pattern names and segfaults. The rest looks good for me. Thanks, Andrey On 04.03.2021 15:03, Robert Foss wrote: > Add register definitions for version 170 of the Titan architecture > and implement support for the CSID subdevice. > > Signed-off-by: Robert Foss <robert.foss@linaro.org> > Reviewed-by: Andrey Konovalov <andrey.konovalov@linaro.org> > --- > > Changes since v5: > - Andrey: Fix test pattern selection logic > - Andrey: Add r-b > - Move Titan 170 specific test modes to this commit > > > drivers/media/platform/qcom/camss/Makefile | 1 + > .../platform/qcom/camss/camss-csid-170.c | 601 ++++++++++++++++++ > .../media/platform/qcom/camss/camss-csid.c | 4 + > .../media/platform/qcom/camss/camss-csid.h | 9 + > .../media/platform/qcom/camss/camss-vfe-170.c | 1 - > drivers/media/platform/qcom/camss/camss.c | 62 ++ > 6 files changed, 677 insertions(+), 1 deletion(-) > create mode 100644 drivers/media/platform/qcom/camss/camss-csid-170.c > > diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile > index cff388b653ba..0752c46ea37b 100644 > --- a/drivers/media/platform/qcom/camss/Makefile > +++ b/drivers/media/platform/qcom/camss/Makefile > @@ -6,6 +6,7 @@ qcom-camss-objs += \ > 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 \ > 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..ee16efecd466 > --- /dev/null > +++ b/drivers/media/platform/qcom/camss/camss-csid-170.c > @@ -0,0 +1,601 @@ > +// 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_170; > +} > + > +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.c b/drivers/media/platform/qcom/camss/camss-csid.c > index a76ad213604e..a5a94425923a 100644 > --- a/drivers/media/platform/qcom/camss/camss-csid.c > +++ b/drivers/media/platform/qcom/camss/camss-csid.c > @@ -124,6 +124,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]); > } > } > > @@ -545,6 +547,8 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, > } else if (camss->version == CAMSS_8x96 || > camss->version == CAMSS_660) { > csid->ops = &csid_ops_4_7; > + } else if (camss->version == CAMSS_845) { > + csid->ops = &csid_ops_170; > } else { > return -EINVAL; > } > diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h > index 60933f436daa..892150e1f7a0 100644 > --- a/drivers/media/platform/qcom/camss/camss-csid.h > +++ b/drivers/media/platform/qcom/camss/camss-csid.h > @@ -58,6 +58,10 @@ enum csid_testgen_mode { > CSID_PAYLOAD_MODE_USER_SPECIFIED = 6, > CSID_PAYLOAD_MODE_NUM_SUPPORTED_4_1 = 7, > CSID_PAYLOAD_MODE_NUM_SUPPORTED_4_7 = 7, > + CSID_PAYLOAD_MODE_COMPLEX_PATTERN = 7, > + CSID_PAYLOAD_MODE_COLOR_BOX = 8, > + CSID_PAYLOAD_MODE_COLOR_BARS = 9, > + CSID_PAYLOAD_MODE_NUM_SUPPORTED_170 = 10, > }; > > static const char * const csid_testgen_modes[] = { > @@ -68,6 +72,9 @@ static const char * const csid_testgen_modes[] = { > "All Ones 0xFF", > "Pseudo-random Data", > "User Specified", > + "Complex pattern", > + "Color box", > + "Color bars", > }; > > struct csid_format { > @@ -213,5 +220,7 @@ void msm_csid_get_csid_id(struct media_entity *entity, u8 *id); > > 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-vfe-170.c b/drivers/media/platform/qcom/camss/camss-vfe-170.c > index 9ab5964b1e99..ce1130108e01 100644 > --- a/drivers/media/platform/qcom/camss/camss-vfe-170.c > +++ b/drivers/media/platform/qcom/camss/camss-vfe-170.c > @@ -274,7 +274,6 @@ static void vfe_wm_start(struct vfe_device *vfe, u8 wm, struct vfe_line *line) > writel_relaxed(val, vfe->base + VFE_BUS_WM_PACKER_CFG(wm)); // XXX 1 for PLAIN8? > > /* Configure stride for RDIs */ > - //val = pix->plane_fmt[0].bytesperline; > val = WM_STRIDE_DEFAULT_STRIDE; > writel_relaxed(val, vfe->base + VFE_BUS_WM_STRIDE(wm)); > > diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c > index 0e006def1996..0b1693c34fbc 100644 > --- a/drivers/media/platform/qcom/camss/camss.c > +++ b/drivers/media/platform/qcom/camss/camss.c > @@ -465,6 +465,68 @@ static const struct resources vfe_res_660[] = { > } > }; > > +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 */ > { >
Hi Robert, Thank you for your patch! On 04.03.2021 15:03, Robert Foss wrote: > Add register definitions for version 170 of the Titan architecture > and implement support for the RDI output mode. > > The RDI mode as opposed to the PIX output mode for the VFE unit does > not support any ISP functionality. This means essentially only > supporting dumping the output of the whatever the CSI decoder receives > from the sensor. > > For example will a sensor outputting YUV pixel format frames, only > allow the VFE to dump those frames as they are received by the ISP > to memory through the RDI interface. > > Signed-off-by: Robert Foss <robert.foss@linaro.org> > --- Reviewed-by: Andrey Konovalov <andrey.konovalov@linaro.org> Thanks, Andrey > Changes since v1: > - Andrey: Remove commented out chunk > - Remove left over WIP comments > > Changes since v4: > - Andrey: Remove traces of PIX support > - Andrey: Fix vfe_global_reset() overwriting reset command > - Remove unused variable > > Changes since v5: > - Andrey: Fix copy/paste issues in header > - Andrey: Remove unecessary looping for 1 value > > drivers/media/platform/qcom/camss/Makefile | 1 + > .../media/platform/qcom/camss/camss-vfe-170.c | 786 ++++++++++++++++++ > drivers/media/platform/qcom/camss/camss-vfe.c | 47 +- > drivers/media/platform/qcom/camss/camss-vfe.h | 26 +- > .../media/platform/qcom/camss/camss-video.c | 52 ++ > drivers/media/platform/qcom/camss/camss.c | 61 ++ > 6 files changed, 953 insertions(+), 20 deletions(-) > create mode 100644 drivers/media/platform/qcom/camss/camss-vfe-170.c > > diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile > index 940c0ae3e003..052c4f405fa3 100644 > --- a/drivers/media/platform/qcom/camss/Makefile > +++ b/drivers/media/platform/qcom/camss/Makefile > @@ -11,6 +11,7 @@ qcom-camss-objs += \ > 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-vfe-170.c b/drivers/media/platform/qcom/camss/camss-vfe-170.c > new file mode 100644 > index 000000000000..9ab5964b1e99 > --- /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) > + > +// XXX different, don't exist in TITAN register docs > +#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 CAMIF_TIMEOUT_SLEEP_US 1000 > +#define CAMIF_TIMEOUT_ALL_US 1000000 > + > +#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 = pix->plane_fmt[0].bytesperline; > + 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; > + > + return 0; > + > + 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) > +{ > + > +} > + > +/* > + * 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_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; > +} > + > +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, > + .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.c b/drivers/media/platform/qcom/camss/camss-vfe.c > index 375843bd16af..6fafeb8a5484 100644 > --- a/drivers/media/platform/qcom/camss/camss-vfe.c > +++ b/drivers/media/platform/qcom/camss/camss-vfe.c > @@ -96,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 > @@ -192,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: > { > @@ -256,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; > > @@ -429,7 +450,8 @@ 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; > > @@ -1268,6 +1290,10 @@ 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; > } > @@ -1379,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; > } > diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h > index aad5dc74c2c0..29b3d930ffc6 100644 > --- a/drivers/media/platform/qcom/camss/camss-vfe.h > +++ b/drivers/media/platform/qcom/camss/camss-vfe.h > @@ -19,7 +19,6 @@ > #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 > @@ -38,14 +37,14 @@ > #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 { > @@ -53,6 +52,7 @@ enum vfe_line_id { > VFE_LINE_RDI0 = 0, > VFE_LINE_RDI1 = 1, > VFE_LINE_RDI2 = 2, > + VFE_LINE_NUM_GEN2 = 3, > VFE_LINE_PIX = 3, > VFE_LINE_NUM_GEN1 = 4, > VFE_LINE_NUM_MAX = 4 > @@ -73,6 +73,9 @@ struct vfe_output { > int active_buf; > int wait_sof; > } gen1; > + struct { > + int active_num; > + } gen2; > }; > enum vfe_output_state state; > unsigned int sequence; > @@ -171,14 +174,6 @@ void vfe_buf_add_pending(struct vfe_output *output, struct camss_buffer *buffer) > > struct camss_buffer *vfe_buf_get_pending(struct vfe_output *output); > > -/* > - * vfe_disable - Disable streaming on VFE line > - * @line: VFE line > - * > - * Return 0 on success or a negative error code otherwise > - */ > -int vfe_disable(struct vfe_line *line); > - > int vfe_flush_buffers(struct camss_video *vid, enum vb2_buffer_state state); > > /* > @@ -193,8 +188,17 @@ 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 6c6f1e59ccd8..0e006def1996 100644 > --- a/drivers/media/platform/qcom/camss/camss.c > +++ b/drivers/media/platform/qcom/camss/camss.c > @@ -465,6 +465,67 @@ static const struct resources vfe_res_660[] = { > } > }; > > +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 >
Hi Robert, Thank you for your patch! On 04.03.2021 15:03, Robert Foss wrote: > The CSI-2 spec defines format identifier for Data Type (DT), > and how the Decode Format (DF) & Encode Format (EF) are implemented. > The spec does however not define the DF, EF or Plain Format (PF) > identifiers, as those are vendor specific. > > Plain formats describe the size of the pixels written by the RDI > units to memory. PLAIN8 for example has the size 8 bits, and > PLAIN32 32 bits. The appropriate Plain Format is determined by > the Decode Format used. The smallest Plain Format that is able > to contain a pixel of the used Decode Format is the appropriate > one to use. > > As the vendor specific identifiers differ between hardware > generations, split them out into separate headers. > > Signed-off-by: Robert Foss <robert.foss@linaro.org> Reviewed-by: Andrey Konovalov <andrey.konovalov@linaro.org> Thanks, Andrey > --- > > Changes since v5 > - Andrey: Gen 1 & Gen2 devices have different decode/encode/plain > format definitions, list in separate headers > - Andrey: Make commit msg more clear about what is in MIPI spec or not > > > .../platform/qcom/camss/camss-csid-gen1.h | 27 +++++++++++++ > .../platform/qcom/camss/camss-csid-gen2.h | 39 +++++++++++++++++++ > .../media/platform/qcom/camss/camss-csid.c | 20 ++-------- > .../media/platform/qcom/camss/camss-csid.h | 24 ++++++++++++ > 4 files changed, 94 insertions(+), 16 deletions(-) > create mode 100644 drivers/media/platform/qcom/camss/camss-csid-gen1.h > create mode 100644 drivers/media/platform/qcom/camss/camss-csid-gen2.h > > 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..697b51d6ad38 100644 > --- a/drivers/media/platform/qcom/camss/camss-csid.c > +++ b/drivers/media/platform/qcom/camss/camss-csid.c > @@ -22,6 +22,7 @@ > #include <media/v4l2-subdev.h> > > #include "camss-csid.h" > +#include "camss-csid-gen1.h" > #include "camss.h" > > #define MSM_CSID_NAME "msm_csid" > @@ -37,8 +38,8 @@ > #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_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) > @@ -59,22 +60,9 @@ > #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; > diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h > index 1824b3745e10..318c19bb26c9 100644 > --- a/drivers/media/platform/qcom/camss/camss-csid.h > +++ b/drivers/media/platform/qcom/camss/camss-csid.h > @@ -21,6 +21,30 @@ > #define MSM_CSID_PAD_SRC 1 > #define MSM_CSID_PADS_NUM 2 > > +#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 > + > + > enum csid_payload_mode { > CSID_PAYLOAD_MODE_INCREMENTING = 0, > CSID_PAYLOAD_MODE_ALTERNATING_55_AA = 1, >
Hi Robert, Thank you for your patch! On 04.03.2021 15:03, Robert Foss wrote: > Add register definitions for version 170 of the Titan architecture > and implement support for the CSIPHY subdevice. > > Signed-off-by: Robert Foss <robert.foss@linaro.org> Reviewed-by: Andrey Konovalov <andrey.konovalov@linaro.org> Thanks, Andrey > --- > > Changes since v3 > - Sakari: Make variable const > > Changes since v4 > - kernel test robot: Fix warning related to const > > Changes since v5: > - Andrey: Fix computed register value being overwritten > > > .../qcom/camss/camss-csiphy-3ph-1-0.c | 181 ++++++++++++++++-- > .../media/platform/qcom/camss/camss-csiphy.c | 66 +++++-- > drivers/media/platform/qcom/camss/camss.c | 74 +++++++ > 3 files changed, 289 insertions(+), 32 deletions(-) > > 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..791d1eca2ea2 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 { > + int32_t reg_addr; > + int32_t reg_data; > + int32_t delay; > + uint32_t 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) > { > @@ -135,26 +234,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 +294,65 @@ 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, > + u32 pixel_clock, u8 bpp, 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(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 = 1; > + 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..e3fdc268050c 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 > @@ -257,16 +281,20 @@ static int csiphy_stream_on(struct csiphy_device *csiphy) > 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); > > @@ -557,6 +585,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; > } > @@ -570,11 +602,17 @@ int msm_csiphy_subdev_init(struct camss *camss, > 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)) { > + dev_err(dev, "could not map memory\n"); > + return PTR_ERR(csiphy->base_clk_mux); > + } > + } else { > + csiphy->base_clk_mux = NULL; > } > > /* Interrupt */ > diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c > index 0b1693c34fbc..5d0479b5589c 100644 > --- a/drivers/media/platform/qcom/camss/camss.c > +++ b/drivers/media/platform/qcom/camss/camss.c > @@ -465,6 +465,80 @@ 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 */ > { >
Hi Robert, Thank you for your patch! On 04.03.2021 15:03, Robert Foss wrote: > In order to support Qualcomm ISP hardware architectures that diverge > from older architectures, the VFE subdevice driver needs to be refactored > to better abstract the different ISP architectures. > > Gen1 represents the CAMSS ISP architecture. The ISP architecture developed > after CAMSS, Titan, will be referred to as Gen2. > > Signed-off-by: Robert Foss <robert.foss@linaro.org> Reviewed-by: Andrey Konovalov <andrey.konovalov@linaro.org> Thanks, Andrey > --- > > Changes since v1 > - kernel test robot: Re-add chunk missing from > vfe_output_update_pong_addr > - Andrey: Fix file name error > - Andrey: Change hardware version number in comment > - Changed copyright year to 2021 for camss-vfe-4-8.c > > Changes since v3: > - Nicolas: Replace trace_printk() with dev_dbg() > - Removed spurious whitespace > > Changes since v4: > - Andrey: Refactor to make PIX support optional > > Changes since v5: > - Andrey: Fix function description > - Andrey: Change RDI/PIX iteration to use vfe->line_num > - Andrey: Added r-b > > drivers/media/platform/qcom/camss/Makefile | 2 + > .../media/platform/qcom/camss/camss-vfe-4-1.c | 118 +- > .../media/platform/qcom/camss/camss-vfe-4-7.c | 241 ++-- > .../media/platform/qcom/camss/camss-vfe-4-8.c | 1166 +++++++++++++++++ > .../platform/qcom/camss/camss-vfe-gen1.c | 763 +++++++++++ > .../platform/qcom/camss/camss-vfe-gen1.h | 110 ++ > drivers/media/platform/qcom/camss/camss-vfe.c | 790 +---------- > drivers/media/platform/qcom/camss/camss-vfe.h | 121 +- > drivers/media/platform/qcom/camss/camss.c | 4 +- > 9 files changed, 2264 insertions(+), 1051 deletions(-) > create mode 100644 drivers/media/platform/qcom/camss/camss-vfe-4-8.c > create mode 100644 drivers/media/platform/qcom/camss/camss-vfe-gen1.c > create mode 100644 drivers/media/platform/qcom/camss/camss-vfe-gen1.h > > diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile > index 63c1b1b2943c..940c0ae3e003 100644 > --- a/drivers/media/platform/qcom/camss/Makefile > +++ b/drivers/media/platform/qcom/camss/Makefile > @@ -10,6 +10,8 @@ qcom-camss-objs += \ > camss-ispif.o \ > camss-vfe-4-1.o \ > camss-vfe-4-7.o \ > + camss-vfe-4-8.o \ > + camss-vfe-gen1.o \ > camss-vfe.o \ > camss-video.o \ > > 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 85b9bcbc7321..81756d7fd5c2 100644 > --- a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c > +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c > @@ -14,6 +14,7 @@ > > #include "camss.h" > #include "camss-vfe.h" > +#include "camss-vfe-gen1.h" > > #define VFE_0_HW_VERSION 0x000 > > @@ -284,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) > { > @@ -666,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; > @@ -975,46 +938,63 @@ 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, > + > +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, > + .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 f7e00a2de393..5ecb87f1b047 100644 > --- a/drivers/media/platform/qcom/camss/camss-vfe-4-7.c > +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c > @@ -14,6 +14,8 @@ > > #include "camss.h" > #include "camss-vfe.h" > +#include "camss-vfe-gen1.h" > + > > #define VFE_0_HW_VERSION 0x000 > > @@ -258,7 +260,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; > @@ -296,6 +298,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); > } > @@ -311,7 +315,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), > @@ -460,8 +464,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(); > } > > @@ -675,8 +683,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(); > } > > @@ -780,20 +792,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; > @@ -895,7 +893,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; > @@ -910,7 +908,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; > @@ -994,6 +992,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) > @@ -1036,24 +1036,7 @@ 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 - VFE module interrupt handler > @@ -1082,7 +1065,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); > > @@ -1108,150 +1091,82 @@ 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); > > -static void vfe48_wm_enable(struct vfe_device *vfe, u8 wm, u8 enable) > -{ > - 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); > + writel_relaxed(*value0, vfe->base + VFE_0_IRQ_CLEAR_0); > + writel_relaxed(*value1, vfe->base + VFE_0_IRQ_CLEAR_1); > > - /* The WM must be enabled before sending other commands */ > + /* 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_set_qos(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; > - > - 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 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, > +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, > + .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..732b93b365d3 > --- /dev/null > +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-8.c > @@ -0,0 +1,1166 @@ > +// 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/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); > +} > + > +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); > +} > + > +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, > + .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..6737d9cf5150 > --- /dev/null > +++ b/drivers/media/platform/qcom/camss/camss-vfe-gen1.c > @@ -0,0 +1,763 @@ > +// 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; > + > + return 0; > + > + 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; > +} > + > +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; > +} > + > +#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..42e8a3333034 > --- /dev/null > +++ b/drivers/media/platform/qcom/camss/camss-vfe-gen1.h > @@ -0,0 +1,110 @@ > +/* 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 > + */ > +inline u8 vfe_calc_interp_reso(u16 input, u16 output); > + > +/* > + * 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 94c9ca7d5cbb..375843bd16af 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 > > @@ -294,35 +280,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 +302,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 +318,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 +328,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 +342,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 +366,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 +384,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_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 +414,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]; > + u32 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) > @@ -1131,7 +433,7 @@ static int vfe_set_clock_rates(struct vfe_device *vfe) > 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 +496,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]; > + u32 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 +515,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; > > @@ -1318,7 +620,7 @@ 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); > @@ -1331,35 +633,6 @@ static void vfe_put(struct vfe_device *vfe) > mutex_unlock(&vfe->power_lock); > } > > -/* > - * 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 > @@ -1370,8 +643,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 +715,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 +1258,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; > @@ -2005,6 +1271,7 @@ int msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, > default: > return -EINVAL; > } > + vfe->ops->subdev_init(dev, vfe); > > /* Memory */ > > @@ -2086,7 +1353,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; > @@ -2209,11 +1476,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 +1498,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 +1541,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 +1605,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..aad5dc74c2c0 100644 > --- a/drivers/media/platform/qcom/camss/camss-vfe.h > +++ b/drivers/media/platform/qcom/camss/camss-vfe.h > @@ -17,15 +17,28 @@ > #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, > @@ -40,23 +53,30 @@ enum vfe_line_id { > VFE_LINE_RDI0 = 0, > VFE_LINE_RDI1 = 1, > VFE_LINE_RDI2 = 2, > - VFE_LINE_PIX = 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; > + }; > enum vfe_output_state state; > unsigned int sequence; > - int wait_sof; > + > int wait_reg_update; > struct completion sof; > struct completion reg_update; > @@ -78,59 +98,19 @@ 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 (*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 +138,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,6 +161,38 @@ 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); > + > +/* > + * vfe_disable - Disable streaming on VFE line > + * @line: VFE line > + * > + * Return 0 on success or a negative error code otherwise > + */ > +int vfe_disable(struct vfe_line *line); > + > +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); > + > 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; > diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c > index 5ac2dfc67c8b..6c6f1e59ccd8 100644 > --- a/drivers/media/platform/qcom/camss/camss.c > +++ b/drivers/media/platform/qcom/camss/camss.c > @@ -858,7 +858,7 @@ static int camss_register_entities(struct camss *camss) > > 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++) { > + for (j = 0; j < camss->vfe[k].line_num; j++) { > ret = media_create_pad_link( > &camss->ispif->line[i].subdev.entity, > MSM_ISPIF_PAD_SRC, > @@ -877,7 +877,7 @@ static int camss_register_entities(struct camss *camss) > } else { > for (i = 0; i < camss->csid_num; i++) > for (k = 0; k < camss->vfe_num; k++) > - for (j = 0; j < ARRAY_SIZE(camss->vfe[k].line); j++) { > + for (j = 0; j < camss->vfe[k].line_num; j++) { > ret = media_create_pad_link( > &camss->csid[i].subdev.entity, > MSM_CSID_PAD_SRC, >
Hey Andrey, On Tue, 9 Mar 2021 at 14:54, Andrey Konovalov <andrey.konovalov@linaro.org> wrote: > > Hi Robert, > > The testgen_pattern in this version are in the right direction, but > the test patterns aren't working properly in v6: > > On db410c: > ========== > -----8<----- > $ v4l2-ctl -L -d /dev/v4l-subdev2 > > Image Processing Controls > > test_pattern 0x009f0903 (menu) : min=0 max=7 default=0 value=1 > 0: Disabled > 1: Incrementing > 2: Alternating 0x55/0xAA > 3: All Zeros 0x00 > 4: All Ones 0xFF > 5: Pseudo-random Data > 6: User Specified > 7: Complex pattern > $ > -----8<----- > - "max=7" is not right; it should be "max=6", and "7: Complex pattern" should not > be listed for this SOC. > > On db845c: > ========== > -----8<----- > $ v4l2-ctl -L -d /dev/v4l-subdev4 > > Image Processing Controls > > test_pattern 0x009f0903 (menu) : min=0 max=10 default=0 value=0 > 0: Disabled > 1: Incrementing > 2: Alternating 0x55/0xAA > 3: All Zeros 0x00 > 4: All Ones 0xFF > 5: Pseudo-random Data > 6: User Specified > 7: Complex pattern > 8: Color box > 9: Color bars > > Message from syslogd@linaro-gnome at Mar 8 21:09:26 ... > kernel:[ 3936.604286] Internal error: Oops: 96000004 [#1] PREEMPT SMP > > Message from syslogd@linaro-gnome at Mar 8 21:09:26 ... > kernel:[ 3936.858085] Code: a8c27bfd d50323bf d65f03c0 b4ffff61 (39400020) > Segmentation fault > $ > -----8<----- > - "max=10" is too much; it goes beyond the array of test pattern names and segfaults. > Thanks for finding this, and sending the logs! I'll fix this in v7. > > The rest looks good for me. > > > Thanks, > Andrey > > On 04.03.2021 15:03, Robert Foss wrote: > > Add register definitions for version 170 of the Titan architecture > > and implement support for the CSID subdevice. > > > > Signed-off-by: Robert Foss <robert.foss@linaro.org> > > Reviewed-by: Andrey Konovalov <andrey.konovalov@linaro.org> > > --- > > > > Changes since v5: > > - Andrey: Fix test pattern selection logic > > - Andrey: Add r-b > > - Move Titan 170 specific test modes to this commit > > > > > > drivers/media/platform/qcom/camss/Makefile | 1 + > > .../platform/qcom/camss/camss-csid-170.c | 601 ++++++++++++++++++ > > .../media/platform/qcom/camss/camss-csid.c | 4 + > > .../media/platform/qcom/camss/camss-csid.h | 9 + > > .../media/platform/qcom/camss/camss-vfe-170.c | 1 - > > drivers/media/platform/qcom/camss/camss.c | 62 ++ > > 6 files changed, 677 insertions(+), 1 deletion(-) > > create mode 100644 drivers/media/platform/qcom/camss/camss-csid-170.c > > > > diff --git a/drivers/media/platform/qcom/camss/Makefile b/drivers/media/platform/qcom/camss/Makefile > > index cff388b653ba..0752c46ea37b 100644 > > --- a/drivers/media/platform/qcom/camss/Makefile > > +++ b/drivers/media/platform/qcom/camss/Makefile > > @@ -6,6 +6,7 @@ qcom-camss-objs += \ > > 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 \ > > 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..ee16efecd466 > > --- /dev/null > > +++ b/drivers/media/platform/qcom/camss/camss-csid-170.c > > @@ -0,0 +1,601 @@ > > +// 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_170; > > +} > > + > > +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.c b/drivers/media/platform/qcom/camss/camss-csid.c > > index a76ad213604e..a5a94425923a 100644 > > --- a/drivers/media/platform/qcom/camss/camss-csid.c > > +++ b/drivers/media/platform/qcom/camss/camss-csid.c > > @@ -124,6 +124,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]); > > } > > } > > > > @@ -545,6 +547,8 @@ int msm_csid_subdev_init(struct camss *camss, struct csid_device *csid, > > } else if (camss->version == CAMSS_8x96 || > > camss->version == CAMSS_660) { > > csid->ops = &csid_ops_4_7; > > + } else if (camss->version == CAMSS_845) { > > + csid->ops = &csid_ops_170; > > } else { > > return -EINVAL; > > } > > diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h > > index 60933f436daa..892150e1f7a0 100644 > > --- a/drivers/media/platform/qcom/camss/camss-csid.h > > +++ b/drivers/media/platform/qcom/camss/camss-csid.h > > @@ -58,6 +58,10 @@ enum csid_testgen_mode { > > CSID_PAYLOAD_MODE_USER_SPECIFIED = 6, > > CSID_PAYLOAD_MODE_NUM_SUPPORTED_4_1 = 7, > > CSID_PAYLOAD_MODE_NUM_SUPPORTED_4_7 = 7, > > + CSID_PAYLOAD_MODE_COMPLEX_PATTERN = 7, > > + CSID_PAYLOAD_MODE_COLOR_BOX = 8, > > + CSID_PAYLOAD_MODE_COLOR_BARS = 9, > > + CSID_PAYLOAD_MODE_NUM_SUPPORTED_170 = 10, > > }; > > > > static const char * const csid_testgen_modes[] = { > > @@ -68,6 +72,9 @@ static const char * const csid_testgen_modes[] = { > > "All Ones 0xFF", > > "Pseudo-random Data", > > "User Specified", > > + "Complex pattern", > > + "Color box", > > + "Color bars", > > }; > > > > struct csid_format { > > @@ -213,5 +220,7 @@ void msm_csid_get_csid_id(struct media_entity *entity, u8 *id); > > > > 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-vfe-170.c b/drivers/media/platform/qcom/camss/camss-vfe-170.c > > index 9ab5964b1e99..ce1130108e01 100644 > > --- a/drivers/media/platform/qcom/camss/camss-vfe-170.c > > +++ b/drivers/media/platform/qcom/camss/camss-vfe-170.c > > @@ -274,7 +274,6 @@ static void vfe_wm_start(struct vfe_device *vfe, u8 wm, struct vfe_line *line) > > writel_relaxed(val, vfe->base + VFE_BUS_WM_PACKER_CFG(wm)); // XXX 1 for PLAIN8? > > > > /* Configure stride for RDIs */ > > - //val = pix->plane_fmt[0].bytesperline; > > val = WM_STRIDE_DEFAULT_STRIDE; > > writel_relaxed(val, vfe->base + VFE_BUS_WM_STRIDE(wm)); > > > > diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c > > index 0e006def1996..0b1693c34fbc 100644 > > --- a/drivers/media/platform/qcom/camss/camss.c > > +++ b/drivers/media/platform/qcom/camss/camss.c > > @@ -465,6 +465,68 @@ static const struct resources vfe_res_660[] = { > > } > > }; > > > > +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 */ > > { > >
Hi Robert, Thank you for the patch! On 04.03.2021 15:03, Robert Foss wrote: > For Titan ISPs clocks fail to re-enable during vfe_get() > after any vfe has been halted and its corresponding power > domain power has been detached. > > Since all of the clocks depend on all of the PDs, per > VFE PD detaching is no option for this generation of HW. > > Signed-off-by: Robert Foss <robert.foss@linaro.org> > --- > > > Changes since v5: > - Andrey: Bifurcated PD support into Gen1 & Gen2 paths Reviewed-by: Andrey Konovalov <andrey.konovalov@linaro.org> Thanks, Andrey > .../media/platform/qcom/camss/camss-vfe-170.c | 21 +++++ > .../media/platform/qcom/camss/camss-vfe-4-1.c | 19 ++++ > .../media/platform/qcom/camss/camss-vfe-4-7.c | 39 ++++++++ > .../media/platform/qcom/camss/camss-vfe-4-8.c | 34 +++++++ > drivers/media/platform/qcom/camss/camss-vfe.c | 6 +- > drivers/media/platform/qcom/camss/camss-vfe.h | 2 + > drivers/media/platform/qcom/camss/camss.c | 94 +++++++++++++------ > drivers/media/platform/qcom/camss/camss.h | 10 +- > 8 files changed, 189 insertions(+), 36 deletions(-) > > diff --git a/drivers/media/platform/qcom/camss/camss-vfe-170.c b/drivers/media/platform/qcom/camss/camss-vfe-170.c > index ce1130108e01..99f27124ad3b 100644 > --- a/drivers/media/platform/qcom/camss/camss-vfe-170.c > +++ b/drivers/media/platform/qcom/camss/camss-vfe-170.c > @@ -714,6 +714,25 @@ static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm) > 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) > +{ > + > +} > + > + > +/* > + * 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 > @@ -775,6 +794,8 @@ const struct vfe_hw_ops vfe_ops_170 = { > .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, > 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 81756d7fd5c2..9fc44be3ccb8 100644 > --- a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c > +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c > @@ -938,6 +938,23 @@ static irqreturn_t vfe_isr(int irq, void *dev) > return IRQ_HANDLED; > } > > +/* > + * vfe_pm_domain_off - Disable power domains specific to this VFE. > + * @vfe: VFE Device > + */ > +static void vfe_pm_domain_off(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) > +{ > + return 0; > +} > > const struct vfe_hw_ops_gen1 vfe_ops_gen1_4_1 = { > .bus_connect_wm_to_rdi = vfe_bus_connect_wm_to_rdi, > @@ -990,6 +1007,8 @@ const struct vfe_hw_ops vfe_ops_4_1 = { > .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, > 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 5ecb87f1b047..41cf150d9efe 100644 > --- a/drivers/media/platform/qcom/camss/camss-vfe-4-7.c > +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c > @@ -8,6 +8,7 @@ > * Copyright (C) 2015-2018 Linaro Ltd. > */ > > +#include <linux/device.h> > #include <linux/interrupt.h> > #include <linux/io.h> > #include <linux/iopoll.h> > @@ -1104,6 +1105,42 @@ static void vfe_isr_read(struct vfe_device *vfe, u32 *value0, u32 *value1) > 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; > + > + if (!vfe) > + return; > + > + 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); > @@ -1162,6 +1199,8 @@ const struct vfe_hw_ops vfe_ops_4_7 = { > .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, > diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-8.c b/drivers/media/platform/qcom/camss/camss-vfe-4-8.c > index 732b93b365d3..f368870ae36d 100644 > --- a/drivers/media/platform/qcom/camss/camss-vfe-4-8.c > +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-8.c > @@ -8,6 +8,7 @@ > * Copyright (C) 2015-2021 Linaro Ltd. > */ > > +#include <linux/device.h> > #include <linux/interrupt.h> > #include <linux/io.h> > #include <linux/iopoll.h> > @@ -1098,6 +1099,37 @@ static void vfe_isr_read(struct vfe_device *vfe, u32 *value0, u32 *value1) > 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); > @@ -1156,6 +1188,8 @@ const struct vfe_hw_ops vfe_ops_4_8 = { > .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, > diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c > index 6fafeb8a5484..402f18729f9f 100644 > --- a/drivers/media/platform/qcom/camss/camss-vfe.c > +++ b/drivers/media/platform/qcom/camss/camss-vfe.c > @@ -580,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; > > @@ -620,7 +620,7 @@ static int vfe_get(struct vfe_device *vfe) > > 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); > @@ -646,7 +646,7 @@ static void vfe_put(struct vfe_device *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--; > diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h > index 29b3d930ffc6..05839a9f60f6 100644 > --- a/drivers/media/platform/qcom/camss/camss-vfe.h > +++ b/drivers/media/platform/qcom/camss/camss-vfe.h > @@ -106,6 +106,8 @@ struct vfe_hw_ops { > 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); > diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c > index 5d0479b5589c..0a9388227c59 100644 > --- a/drivers/media/platform/qcom/camss/camss.c > +++ b/drivers/media/platform/qcom/camss/camss.c > @@ -776,24 +776,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); > + } > } > > /* > @@ -1207,6 +1207,48 @@ 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; > + > + 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 > @@ -1339,20 +1381,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); > @@ -1373,6 +1405,9 @@ static int camss_probe(struct platform_device *pdev) > > 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); > @@ -1380,9 +1415,12 @@ 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; > + > + 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); > diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h > index b7ad8e9f68a8..244991710879 100644 > --- a/drivers/media/platform/qcom/camss/camss.h > +++ b/drivers/media/platform/qcom/camss/camss.h > @@ -57,9 +57,9 @@ 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 */ > }; > > enum camss_version { > @@ -83,8 +83,8 @@ struct camss { > 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_GEN1_COUNT]; > + struct device_link *genpd_link[PM_DOMAIN_GEN1_COUNT]; > }; > > struct camss_camera_interface { >
On Thu, 11 Mar 2021 at 15:43, Andrey Konovalov <andrey.konovalov@linaro.org> wrote: > > Hi Robert, > > Thank you for the patch! > > On 04.03.2021 15:03, Robert Foss wrote: > > For Titan ISPs clocks fail to re-enable during vfe_get() > > after any vfe has been halted and its corresponding power > > domain power has been detached. > > > > Since all of the clocks depend on all of the PDs, per > > VFE PD detaching is no option for this generation of HW. > > > > Signed-off-by: Robert Foss <robert.foss@linaro.org> > > --- > > > > > > Changes since v5: > > - Andrey: Bifurcated PD support into Gen1 & Gen2 paths > > Reviewed-by: Andrey Konovalov <andrey.konovalov@linaro.org> Thanks! > > Thanks, > Andrey > > > .../media/platform/qcom/camss/camss-vfe-170.c | 21 +++++ > > .../media/platform/qcom/camss/camss-vfe-4-1.c | 19 ++++ > > .../media/platform/qcom/camss/camss-vfe-4-7.c | 39 ++++++++ > > .../media/platform/qcom/camss/camss-vfe-4-8.c | 34 +++++++ > > drivers/media/platform/qcom/camss/camss-vfe.c | 6 +- > > drivers/media/platform/qcom/camss/camss-vfe.h | 2 + > > drivers/media/platform/qcom/camss/camss.c | 94 +++++++++++++------ > > drivers/media/platform/qcom/camss/camss.h | 10 +- > > 8 files changed, 189 insertions(+), 36 deletions(-) > > > > diff --git a/drivers/media/platform/qcom/camss/camss-vfe-170.c b/drivers/media/platform/qcom/camss/camss-vfe-170.c > > index ce1130108e01..99f27124ad3b 100644 > > --- a/drivers/media/platform/qcom/camss/camss-vfe-170.c > > +++ b/drivers/media/platform/qcom/camss/camss-vfe-170.c > > @@ -714,6 +714,25 @@ static void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm) > > 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) > > +{ > > + > > +} > > + > > + > > +/* > > + * 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 > > @@ -775,6 +794,8 @@ const struct vfe_hw_ops vfe_ops_170 = { > > .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, > > 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 81756d7fd5c2..9fc44be3ccb8 100644 > > --- a/drivers/media/platform/qcom/camss/camss-vfe-4-1.c > > +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-1.c > > @@ -938,6 +938,23 @@ static irqreturn_t vfe_isr(int irq, void *dev) > > return IRQ_HANDLED; > > } > > > > +/* > > + * vfe_pm_domain_off - Disable power domains specific to this VFE. > > + * @vfe: VFE Device > > + */ > > +static void vfe_pm_domain_off(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) > > +{ > > + return 0; > > +} > > > > const struct vfe_hw_ops_gen1 vfe_ops_gen1_4_1 = { > > .bus_connect_wm_to_rdi = vfe_bus_connect_wm_to_rdi, > > @@ -990,6 +1007,8 @@ const struct vfe_hw_ops vfe_ops_4_1 = { > > .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, > > 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 5ecb87f1b047..41cf150d9efe 100644 > > --- a/drivers/media/platform/qcom/camss/camss-vfe-4-7.c > > +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-7.c > > @@ -8,6 +8,7 @@ > > * Copyright (C) 2015-2018 Linaro Ltd. > > */ > > > > +#include <linux/device.h> > > #include <linux/interrupt.h> > > #include <linux/io.h> > > #include <linux/iopoll.h> > > @@ -1104,6 +1105,42 @@ static void vfe_isr_read(struct vfe_device *vfe, u32 *value0, u32 *value1) > > 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; > > + > > + if (!vfe) > > + return; > > + > > + 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); > > @@ -1162,6 +1199,8 @@ const struct vfe_hw_ops vfe_ops_4_7 = { > > .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, > > diff --git a/drivers/media/platform/qcom/camss/camss-vfe-4-8.c b/drivers/media/platform/qcom/camss/camss-vfe-4-8.c > > index 732b93b365d3..f368870ae36d 100644 > > --- a/drivers/media/platform/qcom/camss/camss-vfe-4-8.c > > +++ b/drivers/media/platform/qcom/camss/camss-vfe-4-8.c > > @@ -8,6 +8,7 @@ > > * Copyright (C) 2015-2021 Linaro Ltd. > > */ > > > > +#include <linux/device.h> > > #include <linux/interrupt.h> > > #include <linux/io.h> > > #include <linux/iopoll.h> > > @@ -1098,6 +1099,37 @@ static void vfe_isr_read(struct vfe_device *vfe, u32 *value0, u32 *value1) > > 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); > > @@ -1156,6 +1188,8 @@ const struct vfe_hw_ops vfe_ops_4_8 = { > > .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, > > diff --git a/drivers/media/platform/qcom/camss/camss-vfe.c b/drivers/media/platform/qcom/camss/camss-vfe.c > > index 6fafeb8a5484..402f18729f9f 100644 > > --- a/drivers/media/platform/qcom/camss/camss-vfe.c > > +++ b/drivers/media/platform/qcom/camss/camss-vfe.c > > @@ -580,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; > > > > @@ -620,7 +620,7 @@ static int vfe_get(struct vfe_device *vfe) > > > > 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); > > @@ -646,7 +646,7 @@ static void vfe_put(struct vfe_device *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--; > > diff --git a/drivers/media/platform/qcom/camss/camss-vfe.h b/drivers/media/platform/qcom/camss/camss-vfe.h > > index 29b3d930ffc6..05839a9f60f6 100644 > > --- a/drivers/media/platform/qcom/camss/camss-vfe.h > > +++ b/drivers/media/platform/qcom/camss/camss-vfe.h > > @@ -106,6 +106,8 @@ struct vfe_hw_ops { > > 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); > > diff --git a/drivers/media/platform/qcom/camss/camss.c b/drivers/media/platform/qcom/camss/camss.c > > index 5d0479b5589c..0a9388227c59 100644 > > --- a/drivers/media/platform/qcom/camss/camss.c > > +++ b/drivers/media/platform/qcom/camss/camss.c > > @@ -776,24 +776,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); > > + } > > } > > > > /* > > @@ -1207,6 +1207,48 @@ 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; > > + > > + 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 > > @@ -1339,20 +1381,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); > > @@ -1373,6 +1405,9 @@ static int camss_probe(struct platform_device *pdev) > > > > 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); > > @@ -1380,9 +1415,12 @@ 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; > > + > > + 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); > > diff --git a/drivers/media/platform/qcom/camss/camss.h b/drivers/media/platform/qcom/camss/camss.h > > index b7ad8e9f68a8..244991710879 100644 > > --- a/drivers/media/platform/qcom/camss/camss.h > > +++ b/drivers/media/platform/qcom/camss/camss.h > > @@ -57,9 +57,9 @@ 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 */ > > }; > > > > enum camss_version { > > @@ -83,8 +83,8 @@ struct camss { > > 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_GEN1_COUNT]; > > + struct device_link *genpd_link[PM_DOMAIN_GEN1_COUNT]; > > }; > > > > struct camss_camera_interface { > >