// 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 #include #include #include #include #include "camss-csid.h" #include "camss-csid-gen1.h" #include "camss.h" #define CAMSS_CSID_HW_VERSION 0x0 #define CAMSS_CSID_CORE_CTRL_0 0x004 #define CAMSS_CSID_CORE_CTRL_1 0x008 #define CAMSS_CSID_RST_CMD 0x010 #define CAMSS_CSID_CID_LUT_VC_n(n) (0x014 + 0x4 * (n)) #define CAMSS_CSID_CID_n_CFG(n) (0x024 + 0x4 * (n)) #define CAMSS_CSID_CID_n_CFG_ISPIF_EN BIT(0) #define CAMSS_CSID_CID_n_CFG_RDI_EN BIT(1) #define CAMSS_CSID_CID_n_CFG_DECODE_FORMAT_SHIFT 4 #define CAMSS_CSID_CID_n_CFG_PLAIN_FORMAT_8 (PLAIN_FORMAT_PLAIN8 << 8) #define CAMSS_CSID_CID_n_CFG_PLAIN_FORMAT_16 (PLAIN_FORMAT_PLAIN16 << 8) #define CAMSS_CSID_CID_n_CFG_PLAIN_ALIGNMENT_LSB (0 << 9) #define CAMSS_CSID_CID_n_CFG_PLAIN_ALIGNMENT_MSB (1 << 9) #define CAMSS_CSID_CID_n_CFG_RDI_MODE_RAW_DUMP (0 << 10) #define CAMSS_CSID_CID_n_CFG_RDI_MODE_PLAIN_PACKING (1 << 10) #define CAMSS_CSID_IRQ_CLEAR_CMD 0x064 #define CAMSS_CSID_IRQ_MASK 0x068 #define CAMSS_CSID_IRQ_STATUS 0x06c #define CAMSS_CSID_TG_CTRL 0x0a8 #define CAMSS_CSID_TG_CTRL_DISABLE 0xa06436 #define CAMSS_CSID_TG_CTRL_ENABLE 0xa06437 #define CAMSS_CSID_TG_VC_CFG 0x0ac #define CAMSS_CSID_TG_VC_CFG_H_BLANKING 0x3ff #define CAMSS_CSID_TG_VC_CFG_V_BLANKING 0x7f #define CAMSS_CSID_TG_DT_n_CGG_0(n) (0x0b4 + 0xc * (n)) #define CAMSS_CSID_TG_DT_n_CGG_1(n) (0x0b8 + 0xc * (n)) #define CAMSS_CSID_TG_DT_n_CGG_2(n) (0x0bc + 0xc * (n)) static const struct csid_format csid_formats[] = { { MEDIA_BUS_FMT_UYVY8_1X16, DATA_TYPE_YUV422_8BIT, DECODE_FORMAT_UNCOMPRESSED_8_BIT, 8, 2, }, { MEDIA_BUS_FMT_VYUY8_1X16, DATA_TYPE_YUV422_8BIT, DECODE_FORMAT_UNCOMPRESSED_8_BIT, 8, 2, }, { MEDIA_BUS_FMT_YUYV8_1X16, DATA_TYPE_YUV422_8BIT, DECODE_FORMAT_UNCOMPRESSED_8_BIT, 8, 2, }, { MEDIA_BUS_FMT_YVYU8_1X16, DATA_TYPE_YUV422_8BIT, DECODE_FORMAT_UNCOMPRESSED_8_BIT, 8, 2, }, { MEDIA_BUS_FMT_SBGGR8_1X8, DATA_TYPE_RAW_8BIT, DECODE_FORMAT_UNCOMPRESSED_8_BIT, 8, 1, }, { MEDIA_BUS_FMT_SGBRG8_1X8, DATA_TYPE_RAW_8BIT, DECODE_FORMAT_UNCOMPRESSED_8_BIT, 8, 1, }, { MEDIA_BUS_FMT_SGRBG8_1X8, DATA_TYPE_RAW_8BIT, DECODE_FORMAT_UNCOMPRESSED_8_BIT, 8, 1, }, { MEDIA_BUS_FMT_SRGGB8_1X8, DATA_TYPE_RAW_8BIT, DECODE_FORMAT_UNCOMPRESSED_8_BIT, 8, 1, }, { MEDIA_BUS_FMT_SBGGR10_1X10, DATA_TYPE_RAW_10BIT, DECODE_FORMAT_UNCOMPRESSED_10_BIT, 10, 1, }, { MEDIA_BUS_FMT_SGBRG10_1X10, DATA_TYPE_RAW_10BIT, DECODE_FORMAT_UNCOMPRESSED_10_BIT, 10, 1, }, { MEDIA_BUS_FMT_SGRBG10_1X10, DATA_TYPE_RAW_10BIT, DECODE_FORMAT_UNCOMPRESSED_10_BIT, 10, 1, }, { MEDIA_BUS_FMT_SRGGB10_1X10, DATA_TYPE_RAW_10BIT, DECODE_FORMAT_UNCOMPRESSED_10_BIT, 10, 1, }, { MEDIA_BUS_FMT_SBGGR12_1X12, DATA_TYPE_RAW_12BIT, DECODE_FORMAT_UNCOMPRESSED_12_BIT, 12, 1, }, { MEDIA_BUS_FMT_SGBRG12_1X12, DATA_TYPE_RAW_12BIT, DECODE_FORMAT_UNCOMPRESSED_12_BIT, 12, 1, }, { MEDIA_BUS_FMT_SGRBG12_1X12, DATA_TYPE_RAW_12BIT, DECODE_FORMAT_UNCOMPRESSED_12_BIT, 12, 1, }, { MEDIA_BUS_FMT_SRGGB12_1X12, DATA_TYPE_RAW_12BIT, DECODE_FORMAT_UNCOMPRESSED_12_BIT, 12, 1, }, { MEDIA_BUS_FMT_SBGGR14_1X14, DATA_TYPE_RAW_14BIT, DECODE_FORMAT_UNCOMPRESSED_14_BIT, 14, 1, }, { MEDIA_BUS_FMT_SGBRG14_1X14, DATA_TYPE_RAW_14BIT, DECODE_FORMAT_UNCOMPRESSED_14_BIT, 14, 1, }, { MEDIA_BUS_FMT_SGRBG14_1X14, DATA_TYPE_RAW_14BIT, DECODE_FORMAT_UNCOMPRESSED_14_BIT, 14, 1, }, { MEDIA_BUS_FMT_SRGGB14_1X14, DATA_TYPE_RAW_14BIT, DECODE_FORMAT_UNCOMPRESSED_14_BIT, 14, 1, }, { MEDIA_BUS_FMT_Y10_1X10, DATA_TYPE_RAW_10BIT, DECODE_FORMAT_UNCOMPRESSED_10_BIT, 10, 1, }, }; static void csid_configure_stream(struct csid_device *csid, u8 enable) { struct csid_testgen_config *tg = &csid->testgen; u32 sink_code = csid->fmt[MSM_CSID_PAD_SINK].code; u32 src_code = csid->fmt[MSM_CSID_PAD_SRC].code; u32 val; if (enable) { struct v4l2_mbus_framefmt *input_format; const struct csid_format *format; u8 vc = 0; /* Virtual Channel 0 */ u8 cid = vc * 4; /* id of Virtual Channel and Data Type set */ u8 dt_shift; if (tg->enabled) { /* Config Test Generator */ u32 num_bytes_per_line, num_lines; input_format = &csid->fmt[MSM_CSID_PAD_SRC]; format = csid_get_fmt_entry(csid->formats, csid->nformats, input_format->code); num_bytes_per_line = input_format->width * format->bpp * format->spp / 8; num_lines = input_format->height; /* 31:24 V blank, 23:13 H blank, 3:2 num of active DT */ /* 1:0 VC */ val = ((CAMSS_CSID_TG_VC_CFG_V_BLANKING & 0xff) << 24) | ((CAMSS_CSID_TG_VC_CFG_H_BLANKING & 0x7ff) << 13); writel_relaxed(val, csid->base + CAMSS_CSID_TG_VC_CFG); /* 28:16 bytes per lines, 12:0 num of lines */ val = ((num_bytes_per_line & 0x1fff) << 16) | (num_lines & 0x1fff); writel_relaxed(val, csid->base + CAMSS_CSID_TG_DT_n_CGG_0(0)); /* 5:0 data type */ val = format->data_type; writel_relaxed(val, csid->base + CAMSS_CSID_TG_DT_n_CGG_1(0)); /* 2:0 output test pattern */ val = tg->mode - 1; writel_relaxed(val, csid->base + CAMSS_CSID_TG_DT_n_CGG_2(0)); } else { struct csid_phy_config *phy = &csid->phy; input_format = &csid->fmt[MSM_CSID_PAD_SINK]; format = csid_get_fmt_entry(csid->formats, csid->nformats, input_format->code); val = phy->lane_cnt - 1; val |= phy->lane_assign << 4; writel_relaxed(val, csid->base + CAMSS_CSID_CORE_CTRL_0); val = phy->csiphy_id << 17; val |= 0x9; writel_relaxed(val, csid->base + CAMSS_CSID_CORE_CTRL_1); } /* Config LUT */ dt_shift = (cid % 4) * 8; val = readl_relaxed(csid->base + CAMSS_CSID_CID_LUT_VC_n(vc)); val &= ~(0xff << dt_shift); val |= format->data_type << dt_shift; writel_relaxed(val, csid->base + CAMSS_CSID_CID_LUT_VC_n(vc)); val = CAMSS_CSID_CID_n_CFG_ISPIF_EN; val |= CAMSS_CSID_CID_n_CFG_RDI_EN; val |= format->decode_format << CAMSS_CSID_CID_n_CFG_DECODE_FORMAT_SHIFT; val |= CAMSS_CSID_CID_n_CFG_RDI_MODE_RAW_DUMP; if ((sink_code == MEDIA_BUS_FMT_SBGGR10_1X10 && src_code == MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE) || (sink_code == MEDIA_BUS_FMT_Y10_1X10 && src_code == MEDIA_BUS_FMT_Y10_2X8_PADHI_LE)) { val |= CAMSS_CSID_CID_n_CFG_RDI_MODE_PLAIN_PACKING; val |= CAMSS_CSID_CID_n_CFG_PLAIN_FORMAT_16; val |= CAMSS_CSID_CID_n_CFG_PLAIN_ALIGNMENT_LSB; } writel_relaxed(val, csid->base + CAMSS_CSID_CID_n_CFG(cid)); if (tg->enabled) { val = CAMSS_CSID_TG_CTRL_ENABLE; writel_relaxed(val, csid->base + CAMSS_CSID_TG_CTRL); } } else { if (tg->enabled) { val = CAMSS_CSID_TG_CTRL_DISABLE; writel_relaxed(val, csid->base + CAMSS_CSID_TG_CTRL); } } } static int csid_configure_testgen_pattern(struct csid_device *csid, s32 val) { if (val > 0 && val <= csid->testgen.nmodes) csid->testgen.mode = val; return 0; } static u32 csid_hw_version(struct csid_device *csid) { u32 hw_version = readl_relaxed(csid->base + CAMSS_CSID_HW_VERSION); dev_dbg(csid->camss->dev, "CSID HW Version = 0x%08x\n", hw_version); return hw_version; } /* * isr - CSID module interrupt service routine * @irq: Interrupt line * @dev: CSID device * * Return IRQ_HANDLED on success */ static irqreturn_t csid_isr(int irq, void *dev) { struct csid_device *csid = dev; u32 value; value = readl_relaxed(csid->base + CAMSS_CSID_IRQ_STATUS); writel_relaxed(value, csid->base + CAMSS_CSID_IRQ_CLEAR_CMD); if ((value >> 11) & 0x1) complete(&csid->reset_complete); return IRQ_HANDLED; } /* * csid_reset - Trigger reset on CSID module and wait to complete * @csid: CSID device * * Return 0 on success or a negative error code otherwise */ static int csid_reset(struct csid_device *csid) { unsigned long time; reinit_completion(&csid->reset_complete); writel_relaxed(0x7fff, csid->base + CAMSS_CSID_RST_CMD); time = wait_for_completion_timeout(&csid->reset_complete, msecs_to_jiffies(CSID_RESET_TIMEOUT_MS)); if (!time) { dev_err(csid->camss->dev, "CSID reset timeout\n"); return -EIO; } return 0; } static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code, unsigned int match_format_idx, u32 match_code) { switch (sink_code) { case MEDIA_BUS_FMT_SBGGR10_1X10: { u32 src_code[] = { MEDIA_BUS_FMT_SBGGR10_1X10, MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, }; return csid_find_code(src_code, ARRAY_SIZE(src_code), match_format_idx, match_code); } case MEDIA_BUS_FMT_Y10_1X10: { u32 src_code[] = { MEDIA_BUS_FMT_Y10_1X10, MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, }; return csid_find_code(src_code, ARRAY_SIZE(src_code), match_format_idx, match_code); } default: if (match_format_idx > 0) return 0; return sink_code; } } static void csid_subdev_init(struct csid_device *csid) { csid->formats = csid_formats; csid->nformats = ARRAY_SIZE(csid_formats); csid->testgen.modes = csid_testgen_modes; csid->testgen.nmodes = CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN1; } const struct csid_hw_ops csid_ops_4_7 = { .configure_stream = csid_configure_stream, .configure_testgen_pattern = csid_configure_testgen_pattern, .hw_version = csid_hw_version, .isr = csid_isr, .reset = csid_reset, .src_pad_code = csid_src_pad_code, .subdev_init = csid_subdev_init, };