// SPDX-License-Identifier: GPL-2.0 /* * camss-csid-4-1.c * * Qualcomm MSM Camera Subsystem - CSID (CSI Decoder) Module * * Copyright (C) 2020 Linaro Ltd. */ #include #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 0x00c #define CAMSS_CSID_CID_LUT_VC_n(n) (0x010 + 0x4 * (n)) #define CAMSS_CSID_CID_n_CFG(n) (0x020 + 0x4 * (n)) #define CAMSS_CSID_CID_n_CFG_ISPIF_EN BIT(0) #define CAMSS_CSID_CID_n_CFG_RDI_EN BIT(1) #define CAMSS_CSID_CID_n_CFG_DECODE_FORMAT_SHIFT 4 #define CAMSS_CSID_CID_n_CFG_PLAIN_FORMAT_8 (PLAIN_FORMAT_PLAIN8 << 8) #define CAMSS_CSID_CID_n_CFG_PLAIN_FORMAT_16 (PLAIN_FORMAT_PLAIN16 << 8) #define CAMSS_CSID_CID_n_CFG_PLAIN_ALIGNMENT_LSB (0 << 9) #define CAMSS_CSID_CID_n_CFG_PLAIN_ALIGNMENT_MSB (1 << 9) #define CAMSS_CSID_CID_n_CFG_RDI_MODE_RAW_DUMP (0 << 10) #define CAMSS_CSID_CID_n_CFG_RDI_MODE_PLAIN_PACKING (1 << 10) #define CAMSS_CSID_IRQ_CLEAR_CMD 0x060 #define CAMSS_CSID_IRQ_MASK 0x064 #define CAMSS_CSID_IRQ_STATUS 0x068 #define CAMSS_CSID_TG_CTRL 0x0a0 #define CAMSS_CSID_TG_CTRL_DISABLE 0xa06436 #define CAMSS_CSID_TG_CTRL_ENABLE 0xa06437 #define CAMSS_CSID_TG_VC_CFG 0x0a4 #define CAMSS_CSID_TG_VC_CFG_H_BLANKING 0x3ff #define CAMSS_CSID_TG_VC_CFG_V_BLANKING 0x7f #define CAMSS_CSID_TG_DT_n_CGG_0(n) (0x0ac + 0xc * (n)) #define CAMSS_CSID_TG_DT_n_CGG_1(n) (0x0b0 + 0xc * (n)) #define CAMSS_CSID_TG_DT_n_CGG_2(n) (0x0b4 + 0xc * (n)) static const struct csid_format csid_formats[] = { { MEDIA_BUS_FMT_UYVY8_2X8, DATA_TYPE_YUV422_8BIT, DECODE_FORMAT_UNCOMPRESSED_8_BIT, 8, 2, }, { MEDIA_BUS_FMT_VYUY8_2X8, DATA_TYPE_YUV422_8BIT, DECODE_FORMAT_UNCOMPRESSED_8_BIT, 8, 2, }, { MEDIA_BUS_FMT_YUYV8_2X8, DATA_TYPE_YUV422_8BIT, DECODE_FORMAT_UNCOMPRESSED_8_BIT, 8, 2, }, { MEDIA_BUS_FMT_YVYU8_2X8, DATA_TYPE_YUV422_8BIT, DECODE_FORMAT_UNCOMPRESSED_8_BIT, 8, 2, }, { MEDIA_BUS_FMT_SBGGR8_1X8, DATA_TYPE_RAW_8BIT, DECODE_FORMAT_UNCOMPRESSED_8_BIT, 8, 1, }, { MEDIA_BUS_FMT_SGBRG8_1X8, DATA_TYPE_RAW_8BIT, DECODE_FORMAT_UNCOMPRESSED_8_BIT, 8, 1, }, { MEDIA_BUS_FMT_SGRBG8_1X8, DATA_TYPE_RAW_8BIT, DECODE_FORMAT_UNCOMPRESSED_8_BIT, 8, 1, }, { MEDIA_BUS_FMT_SRGGB8_1X8, DATA_TYPE_RAW_8BIT, DECODE_FORMAT_UNCOMPRESSED_8_BIT, 8, 1, }, { MEDIA_BUS_FMT_SBGGR10_1X10, DATA_TYPE_RAW_10BIT, DECODE_FORMAT_UNCOMPRESSED_10_BIT, 10, 1, }, { MEDIA_BUS_FMT_SGBRG10_1X10, DATA_TYPE_RAW_10BIT, DECODE_FORMAT_UNCOMPRESSED_10_BIT, 10, 1, }, { MEDIA_BUS_FMT_SGRBG10_1X10, DATA_TYPE_RAW_10BIT, DECODE_FORMAT_UNCOMPRESSED_10_BIT, 10, 1, }, { MEDIA_BUS_FMT_SRGGB10_1X10, DATA_TYPE_RAW_10BIT, DECODE_FORMAT_UNCOMPRESSED_10_BIT, 10, 1, }, { MEDIA_BUS_FMT_SBGGR12_1X12, DATA_TYPE_RAW_12BIT, DECODE_FORMAT_UNCOMPRESSED_12_BIT, 12, 1, }, { MEDIA_BUS_FMT_SGBRG12_1X12, DATA_TYPE_RAW_12BIT, DECODE_FORMAT_UNCOMPRESSED_12_BIT, 12, 1, }, { MEDIA_BUS_FMT_SGRBG12_1X12, DATA_TYPE_RAW_12BIT, DECODE_FORMAT_UNCOMPRESSED_12_BIT, 12, 1, }, { MEDIA_BUS_FMT_SRGGB12_1X12, DATA_TYPE_RAW_12BIT, DECODE_FORMAT_UNCOMPRESSED_12_BIT, 12, 1, }, { MEDIA_BUS_FMT_Y10_1X10, DATA_TYPE_RAW_10BIT, DECODE_FORMAT_UNCOMPRESSED_10_BIT, 10, 1, }, }; static void csid_configure_stream(struct csid_device *csid, u8 enable) { struct csid_testgen_config *tg = &csid->testgen; u32 val; if (enable) { struct v4l2_mbus_framefmt *input_format; const struct csid_format *format; u8 vc = 0; /* Virtual Channel 0 */ u8 cid = vc * 4; /* id of Virtual Channel and Data Type set */ u8 dt_shift; if (tg->enabled) { /* Config Test Generator */ u32 num_lines, num_bytes_per_line; input_format = &csid->fmt[MSM_CSID_PAD_SRC]; format = csid_get_fmt_entry(csid->formats, csid->nformats, input_format->code); num_bytes_per_line = input_format->width * format->bpp * format->spp / 8; num_lines = input_format->height; /* 31:24 V blank, 23:13 H blank, 3:2 num of active DT */ /* 1:0 VC */ val = ((CAMSS_CSID_TG_VC_CFG_V_BLANKING & 0xff) << 24) | ((CAMSS_CSID_TG_VC_CFG_H_BLANKING & 0x7ff) << 13); writel_relaxed(val, csid->base + CAMSS_CSID_TG_VC_CFG); /* 28:16 bytes per lines, 12:0 num of lines */ val = ((num_bytes_per_line & 0x1fff) << 16) | (num_lines & 0x1fff); writel_relaxed(val, csid->base + CAMSS_CSID_TG_DT_n_CGG_0(0)); /* 5:0 data type */ val = format->data_type; writel_relaxed(val, csid->base + CAMSS_CSID_TG_DT_n_CGG_1(0)); /* 2:0 output test pattern */ val = tg->mode - 1; writel_relaxed(val, csid->base + CAMSS_CSID_TG_DT_n_CGG_2(0)); } else { struct csid_phy_config *phy = &csid->phy; input_format = &csid->fmt[MSM_CSID_PAD_SINK]; format = csid_get_fmt_entry(csid->formats, csid->nformats, input_format->code); val = phy->lane_cnt - 1; val |= phy->lane_assign << 4; writel_relaxed(val, csid->base + CAMSS_CSID_CORE_CTRL_0); val = phy->csiphy_id << 17; val |= 0x9; writel_relaxed(val, csid->base + CAMSS_CSID_CORE_CTRL_1); } /* Config LUT */ dt_shift = (cid % 4) * 8; val = readl_relaxed(csid->base + CAMSS_CSID_CID_LUT_VC_n(vc)); val &= ~(0xff << dt_shift); val |= format->data_type << dt_shift; writel_relaxed(val, csid->base + CAMSS_CSID_CID_LUT_VC_n(vc)); val = CAMSS_CSID_CID_n_CFG_ISPIF_EN; val |= CAMSS_CSID_CID_n_CFG_RDI_EN; val |= format->decode_format << CAMSS_CSID_CID_n_CFG_DECODE_FORMAT_SHIFT; val |= CAMSS_CSID_CID_n_CFG_RDI_MODE_RAW_DUMP; writel_relaxed(val, csid->base + CAMSS_CSID_CID_n_CFG(cid)); if (tg->enabled) { val = CAMSS_CSID_TG_CTRL_ENABLE; writel_relaxed(val, csid->base + CAMSS_CSID_TG_CTRL); } } else { if (tg->enabled) { val = CAMSS_CSID_TG_CTRL_DISABLE; writel_relaxed(val, csid->base + CAMSS_CSID_TG_CTRL); } } } static int csid_configure_testgen_pattern(struct csid_device *csid, s32 val) { if (val > 0 && val <= csid->testgen.nmodes) csid->testgen.mode = val; return 0; } static u32 csid_hw_version(struct csid_device *csid) { u32 hw_version = readl_relaxed(csid->base + CAMSS_CSID_HW_VERSION); dev_dbg(csid->camss->dev, "CSID HW Version = 0x%08x\n", hw_version); return hw_version; } static irqreturn_t csid_isr(int irq, void *dev) { struct csid_device *csid = dev; u32 value; value = readl_relaxed(csid->base + CAMSS_CSID_IRQ_STATUS); writel_relaxed(value, csid->base + CAMSS_CSID_IRQ_CLEAR_CMD); if ((value >> 11) & 0x1) complete(&csid->reset_complete); return IRQ_HANDLED; } static int csid_reset(struct csid_device *csid) { unsigned long time; reinit_completion(&csid->reset_complete); writel_relaxed(0x7fff, csid->base + CAMSS_CSID_RST_CMD); time = wait_for_completion_timeout(&csid->reset_complete, msecs_to_jiffies(CSID_RESET_TIMEOUT_MS)); if (!time) { dev_err(csid->camss->dev, "CSID reset timeout\n"); return -EIO; } return 0; } static u32 csid_src_pad_code(struct csid_device *csid, u32 sink_code, unsigned int match_format_idx, u32 match_code) { if (match_format_idx > 0) return 0; return sink_code; } static void csid_subdev_init(struct csid_device *csid) { csid->formats = csid_formats; csid->nformats = ARRAY_SIZE(csid_formats); csid->testgen.modes = csid_testgen_modes; csid->testgen.nmodes = CSID_PAYLOAD_MODE_NUM_SUPPORTED_GEN1; } const struct csid_hw_ops csid_ops_4_1 = { .configure_stream = csid_configure_stream, .configure_testgen_pattern = csid_configure_testgen_pattern, .hw_version = csid_hw_version, .isr = csid_isr, .reset = csid_reset, .src_pad_code = csid_src_pad_code, .subdev_init = csid_subdev_init, };