diff options
Diffstat (limited to 'drivers/media/platform/ti-vpe/cal.c')
-rw-r--r-- | drivers/media/platform/ti-vpe/cal.c | 2521 |
1 files changed, 488 insertions, 2033 deletions
diff --git a/drivers/media/platform/ti-vpe/cal.c b/drivers/media/platform/ti-vpe/cal.c index 9b18db7af6c3..59a0266b1f39 100644 --- a/drivers/media/platform/ti-vpe/cal.c +++ b/drivers/media/platform/ti-vpe/cal.c @@ -1,976 +1,167 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * TI CAL camera interface driver + * TI Camera Access Layer (CAL) - Driver * - * Copyright (c) 2015 Texas Instruments Inc. - * Benoit Parrot, <bparrot@ti.com> + * Copyright (c) 2015-2020 Texas Instruments Inc. + * + * Authors: + * Benoit Parrot <bparrot@ti.com> + * Laurent Pinchart <laurent.pinchart@ideasonboard.com> */ #include <linux/clk.h> #include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/ioctl.h> +#include <linux/mfd/syscon.h> #include <linux/module.h> +#include <linux/of_device.h> #include <linux/platform_device.h> -#include <linux/delay.h> #include <linux/pm_runtime.h> -#include <linux/slab.h> -#include <linux/mfd/syscon.h> #include <linux/regmap.h> +#include <linux/slab.h> #include <linux/videodev2.h> -#include <linux/of_device.h> -#include <linux/of_graph.h> -#include <media/v4l2-fwnode.h> +#include <media/media-device.h> #include <media/v4l2-async.h> #include <media/v4l2-common.h> -#include <media/v4l2-ctrls.h> #include <media/v4l2-device.h> -#include <media/v4l2-event.h> -#include <media/v4l2-ioctl.h> -#include <media/v4l2-fh.h> #include <media/videobuf2-core.h> #include <media/videobuf2-dma-contig.h> -#include "cal_regs.h" - -#define CAL_MODULE_NAME "cal" - -#define MAX_WIDTH_BYTES (8192 * 8) -#define MAX_HEIGHT_LINES 16383 -#define CAL_VERSION "0.1.0" +#include "cal.h" +#include "cal_regs.h" MODULE_DESCRIPTION("TI CAL driver"); MODULE_AUTHOR("Benoit Parrot, <bparrot@ti.com>"); MODULE_LICENSE("GPL v2"); -MODULE_VERSION(CAL_VERSION); +MODULE_VERSION("0.1.0"); -static unsigned video_nr = -1; -module_param(video_nr, uint, 0644); +int cal_video_nr = -1; +module_param_named(video_nr, cal_video_nr, uint, 0644); MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect"); -static unsigned debug; -module_param(debug, uint, 0644); +unsigned int cal_debug; +module_param_named(debug, cal_debug, uint, 0644); MODULE_PARM_DESC(debug, "activates debug info"); -/* timeperframe: min/max and default */ -static const struct v4l2_fract - tpf_default = {.numerator = 1001, .denominator = 30000}; - -#define cal_dbg(level, caldev, fmt, arg...) \ - v4l2_dbg(level, debug, &caldev->v4l2_dev, fmt, ##arg) -#define cal_info(caldev, fmt, arg...) \ - v4l2_info(&caldev->v4l2_dev, fmt, ##arg) -#define cal_err(caldev, fmt, arg...) \ - v4l2_err(&caldev->v4l2_dev, fmt, ##arg) - -#define ctx_dbg(level, ctx, fmt, arg...) \ - v4l2_dbg(level, debug, &ctx->v4l2_dev, fmt, ##arg) -#define ctx_info(ctx, fmt, arg...) \ - v4l2_info(&ctx->v4l2_dev, fmt, ##arg) -#define ctx_err(ctx, fmt, arg...) \ - v4l2_err(&ctx->v4l2_dev, fmt, ##arg) - -#define CAL_NUM_INPUT 1 -#define CAL_NUM_CONTEXT 2 - -#define reg_read(dev, offset) ioread32(dev->base + offset) -#define reg_write(dev, offset, val) iowrite32(val, dev->base + offset) - -#define reg_read_field(dev, offset, mask) get_field(reg_read(dev, offset), \ - mask) -#define reg_write_field(dev, offset, field, mask) { \ - u32 val = reg_read(dev, offset); \ - set_field(&val, field, mask); \ - reg_write(dev, offset, val); } - /* ------------------------------------------------------------------ - * Basic structures + * Platform Data * ------------------------------------------------------------------ */ -struct cal_fmt { - u32 fourcc; - u32 code; - /* Bits per pixel */ - u8 bpp; -}; - -static struct cal_fmt cal_formats[] = { - { - .fourcc = V4L2_PIX_FMT_YUYV, - .code = MEDIA_BUS_FMT_YUYV8_2X8, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_UYVY, - .code = MEDIA_BUS_FMT_UYVY8_2X8, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_YVYU, - .code = MEDIA_BUS_FMT_YVYU8_2X8, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_VYUY, - .code = MEDIA_BUS_FMT_VYUY8_2X8, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ - .code = MEDIA_BUS_FMT_RGB565_2X8_LE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ - .code = MEDIA_BUS_FMT_RGB565_2X8_BE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */ - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ - .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, - .bpp = 16, - }, { - .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ - .code = MEDIA_BUS_FMT_RGB888_2X12_LE, - .bpp = 24, - }, { - .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ - .code = MEDIA_BUS_FMT_RGB888_2X12_BE, - .bpp = 24, - }, { - .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ - .code = MEDIA_BUS_FMT_ARGB8888_1X32, - .bpp = 32, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR8, - .code = MEDIA_BUS_FMT_SBGGR8_1X8, - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG8, - .code = MEDIA_BUS_FMT_SGBRG8_1X8, - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG8, - .code = MEDIA_BUS_FMT_SGRBG8_1X8, - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB8, - .code = MEDIA_BUS_FMT_SRGGB8_1X8, - .bpp = 8, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR10, - .code = MEDIA_BUS_FMT_SBGGR10_1X10, - .bpp = 10, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG10, - .code = MEDIA_BUS_FMT_SGBRG10_1X10, - .bpp = 10, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG10, - .code = MEDIA_BUS_FMT_SGRBG10_1X10, - .bpp = 10, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB10, - .code = MEDIA_BUS_FMT_SRGGB10_1X10, - .bpp = 10, - }, { - .fourcc = V4L2_PIX_FMT_SBGGR12, - .code = MEDIA_BUS_FMT_SBGGR12_1X12, - .bpp = 12, - }, { - .fourcc = V4L2_PIX_FMT_SGBRG12, - .code = MEDIA_BUS_FMT_SGBRG12_1X12, - .bpp = 12, - }, { - .fourcc = V4L2_PIX_FMT_SGRBG12, - .code = MEDIA_BUS_FMT_SGRBG12_1X12, - .bpp = 12, - }, { - .fourcc = V4L2_PIX_FMT_SRGGB12, - .code = MEDIA_BUS_FMT_SRGGB12_1X12, - .bpp = 12, - }, -}; - -/* Print Four-character-code (FOURCC) */ -static char *fourcc_to_str(u32 fmt) -{ - static char code[5]; - - code[0] = (unsigned char)(fmt & 0xff); - code[1] = (unsigned char)((fmt >> 8) & 0xff); - code[2] = (unsigned char)((fmt >> 16) & 0xff); - code[3] = (unsigned char)((fmt >> 24) & 0xff); - code[4] = '\0'; - - return code; -} - -/* buffer for one video frame */ -struct cal_buffer { - /* common v4l buffer stuff -- must be first */ - struct vb2_v4l2_buffer vb; - struct list_head list; - const struct cal_fmt *fmt; -}; - -struct cal_dmaqueue { - struct list_head active; - - /* Counters to control fps rate */ - int frame; - int ini_jiffies; -}; - -struct cc_data { - void __iomem *base; - struct resource *res; - - struct platform_device *pdev; -}; - -/* CTRL_CORE_CAMERRX_CONTROL register field id */ -enum cal_camerarx_field { - F_CTRLCLKEN, - F_CAMMODE, - F_LANEENABLE, - F_CSI_MODE, - - F_MAX_FIELDS, -}; - -struct cal_csi2_phy { - struct regmap_field *fields[F_MAX_FIELDS]; - struct reg_field *base_fields; - const int num_lanes; -}; - -struct cal_data { - const int num_csi2_phy; - struct cal_csi2_phy *csi2_phy_core; - - const unsigned int flags; -}; - -static struct reg_field dra72x_ctrl_core_csi0_reg_fields[F_MAX_FIELDS] = { - [F_CTRLCLKEN] = REG_FIELD(0, 10, 10), - [F_CAMMODE] = REG_FIELD(0, 11, 12), - [F_LANEENABLE] = REG_FIELD(0, 13, 16), - [F_CSI_MODE] = REG_FIELD(0, 17, 17), -}; - -static struct reg_field dra72x_ctrl_core_csi1_reg_fields[F_MAX_FIELDS] = { - [F_CTRLCLKEN] = REG_FIELD(0, 0, 0), - [F_CAMMODE] = REG_FIELD(0, 1, 2), - [F_LANEENABLE] = REG_FIELD(0, 3, 4), - [F_CSI_MODE] = REG_FIELD(0, 5, 5), -}; - -static struct cal_csi2_phy dra72x_cal_csi_phy[] = { +static const struct cal_camerarx_data dra72x_cal_camerarx[] = { { - .base_fields = dra72x_ctrl_core_csi0_reg_fields, + .fields = { + [F_CTRLCLKEN] = { 10, 10 }, + [F_CAMMODE] = { 11, 12 }, + [F_LANEENABLE] = { 13, 16 }, + [F_CSI_MODE] = { 17, 17 }, + }, .num_lanes = 4, }, { - .base_fields = dra72x_ctrl_core_csi1_reg_fields, + .fields = { + [F_CTRLCLKEN] = { 0, 0 }, + [F_CAMMODE] = { 1, 2 }, + [F_LANEENABLE] = { 3, 4 }, + [F_CSI_MODE] = { 5, 5 }, + }, .num_lanes = 2, }, }; static const struct cal_data dra72x_cal_data = { - .csi2_phy_core = dra72x_cal_csi_phy, - .num_csi2_phy = ARRAY_SIZE(dra72x_cal_csi_phy), + .camerarx = dra72x_cal_camerarx, + .num_csi2_phy = ARRAY_SIZE(dra72x_cal_camerarx), }; static const struct cal_data dra72x_es1_cal_data = { - .csi2_phy_core = dra72x_cal_csi_phy, - .num_csi2_phy = ARRAY_SIZE(dra72x_cal_csi_phy), + .camerarx = dra72x_cal_camerarx, + .num_csi2_phy = ARRAY_SIZE(dra72x_cal_camerarx), .flags = DRA72_CAL_PRE_ES2_LDO_DISABLE, }; -static struct reg_field dra76x_ctrl_core_csi0_reg_fields[F_MAX_FIELDS] = { - [F_CTRLCLKEN] = REG_FIELD(0, 8, 8), - [F_CAMMODE] = REG_FIELD(0, 9, 10), - [F_CSI_MODE] = REG_FIELD(0, 11, 11), - [F_LANEENABLE] = REG_FIELD(0, 27, 31), -}; - -static struct reg_field dra76x_ctrl_core_csi1_reg_fields[F_MAX_FIELDS] = { - [F_CTRLCLKEN] = REG_FIELD(0, 0, 0), - [F_CAMMODE] = REG_FIELD(0, 1, 2), - [F_CSI_MODE] = REG_FIELD(0, 3, 3), - [F_LANEENABLE] = REG_FIELD(0, 24, 26), -}; - -static struct cal_csi2_phy dra76x_cal_csi_phy[] = { +static const struct cal_camerarx_data dra76x_cal_csi_phy[] = { { - .base_fields = dra76x_ctrl_core_csi0_reg_fields, + .fields = { + [F_CTRLCLKEN] = { 8, 8 }, + [F_CAMMODE] = { 9, 10 }, + [F_CSI_MODE] = { 11, 11 }, + [F_LANEENABLE] = { 27, 31 }, + }, .num_lanes = 5, }, { - .base_fields = dra76x_ctrl_core_csi1_reg_fields, + .fields = { + [F_CTRLCLKEN] = { 0, 0 }, + [F_CAMMODE] = { 1, 2 }, + [F_CSI_MODE] = { 3, 3 }, + [F_LANEENABLE] = { 24, 26 }, + }, .num_lanes = 3, }, }; static const struct cal_data dra76x_cal_data = { - .csi2_phy_core = dra76x_cal_csi_phy, + .camerarx = dra76x_cal_csi_phy, .num_csi2_phy = ARRAY_SIZE(dra76x_cal_csi_phy), }; -static struct reg_field am654_ctrl_core_csi0_reg_fields[F_MAX_FIELDS] = { - [F_CTRLCLKEN] = REG_FIELD(0, 15, 15), - [F_CAMMODE] = REG_FIELD(0, 24, 25), - [F_LANEENABLE] = REG_FIELD(0, 0, 4), -}; - -static struct cal_csi2_phy am654_cal_csi_phy[] = { +static const struct cal_camerarx_data am654_cal_csi_phy[] = { { - .base_fields = am654_ctrl_core_csi0_reg_fields, + .fields = { + [F_CTRLCLKEN] = { 15, 15 }, + [F_CAMMODE] = { 24, 25 }, + [F_LANEENABLE] = { 0, 4 }, + }, .num_lanes = 5, }, }; static const struct cal_data am654_cal_data = { - .csi2_phy_core = am654_cal_csi_phy, + .camerarx = am654_cal_csi_phy, .num_csi2_phy = ARRAY_SIZE(am654_cal_csi_phy), }; -/* - * there is one cal_dev structure in the driver, it is shared by - * all instances. - */ -struct cal_dev { - struct clk *fclk; - int irq; - void __iomem *base; - struct resource *res; - struct platform_device *pdev; - struct v4l2_device v4l2_dev; - - /* Controller flags for special cases */ - unsigned int flags; - - const struct cal_data *data; - - /* Control Module handle */ - struct regmap *syscon_camerrx; - u32 syscon_camerrx_offset; - - /* Camera Core Module handle */ - struct cc_data *cc[CAL_NUM_CSI2_PORTS]; - - struct cal_ctx *ctx[CAL_NUM_CONTEXT]; -}; - -/* - * There is one cal_ctx structure for each camera core context. - */ -struct cal_ctx { - struct v4l2_device v4l2_dev; - struct v4l2_ctrl_handler ctrl_handler; - struct video_device vdev; - struct v4l2_async_notifier notifier; - struct v4l2_subdev *sensor; - struct v4l2_fwnode_endpoint endpoint; - - struct v4l2_fh fh; - struct cal_dev *dev; - struct cc_data *cc; - - /* v4l2_ioctl mutex */ - struct mutex mutex; - /* v4l2 buffers lock */ - spinlock_t slock; - - /* Several counters */ - unsigned long jiffies; - - struct cal_dmaqueue vidq; - - /* Input Number */ - int input; - - /* video capture */ - const struct cal_fmt *fmt; - /* Used to store current pixel format */ - struct v4l2_format v_fmt; - /* Used to store current mbus frame format */ - struct v4l2_mbus_framefmt m_fmt; - - /* Current subdev enumerated format */ - struct cal_fmt *active_fmt[ARRAY_SIZE(cal_formats)]; - int num_active_fmt; - - struct v4l2_fract timeperframe; - unsigned int sequence; - unsigned int external_rate; - struct vb2_queue vb_vidq; - unsigned int seq_count; - unsigned int csi2_port; - unsigned int virtual_channel; - - /* Pointer pointing to current v4l2_buffer */ - struct cal_buffer *cur_frm; - /* Pointer pointing to next v4l2_buffer */ - struct cal_buffer *next_frm; - - bool dma_act; -}; - -static const struct cal_fmt *find_format_by_pix(struct cal_ctx *ctx, - u32 pixelformat) -{ - const struct cal_fmt *fmt; - unsigned int k; - - for (k = 0; k < ctx->num_active_fmt; k++) { - fmt = ctx->active_fmt[k]; - if (fmt->fourcc == pixelformat) - return fmt; - } - - return NULL; -} - -static const struct cal_fmt *find_format_by_code(struct cal_ctx *ctx, - u32 code) -{ - const struct cal_fmt *fmt; - unsigned int k; - - for (k = 0; k < ctx->num_active_fmt; k++) { - fmt = ctx->active_fmt[k]; - if (fmt->code == code) - return fmt; - } - - return NULL; -} - -static inline struct cal_ctx *notifier_to_ctx(struct v4l2_async_notifier *n) -{ - return container_of(n, struct cal_ctx, notifier); -} - -static inline int get_field(u32 value, u32 mask) -{ - return (value & mask) >> __ffs(mask); -} - -static inline void set_field(u32 *valp, u32 field, u32 mask) -{ - u32 val = *valp; - - val &= ~mask; - val |= (field << __ffs(mask)) & mask; - *valp = val; -} - -static u32 cal_data_get_phy_max_lanes(struct cal_ctx *ctx) -{ - struct cal_dev *dev = ctx->dev; - u32 phy_id = ctx->csi2_port - 1; - - return dev->data->csi2_phy_core[phy_id].num_lanes; -} - -static u32 cal_data_get_num_csi2_phy(struct cal_dev *dev) -{ - return dev->data->num_csi2_phy; -} - -static int cal_camerarx_regmap_init(struct cal_dev *dev) -{ - struct reg_field *field; - struct cal_csi2_phy *phy; - int i, j; - - if (!dev->data) - return -EINVAL; - - for (i = 0; i < cal_data_get_num_csi2_phy(dev); i++) { - phy = &dev->data->csi2_phy_core[i]; - for (j = 0; j < F_MAX_FIELDS; j++) { - field = &phy->base_fields[j]; - /* - * Here we update the reg offset with the - * value found in DT - */ - field->reg = dev->syscon_camerrx_offset; - phy->fields[j] = - devm_regmap_field_alloc(&dev->pdev->dev, - dev->syscon_camerrx, - *field); - if (IS_ERR(phy->fields[j])) { - cal_err(dev, "Unable to allocate regmap fields\n"); - return PTR_ERR(phy->fields[j]); - } - } - } - return 0; -} - -static const struct regmap_config cal_regmap_config = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, -}; - -static struct regmap *cal_get_camerarx_regmap(struct cal_dev *dev) -{ - struct platform_device *pdev = dev->pdev; - struct regmap *regmap; - void __iomem *base; - u32 reg_io_width; - struct regmap_config r_config = cal_regmap_config; - struct resource *res; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "camerrx_control"); - base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) { - cal_err(dev, "failed to ioremap\n"); - return ERR_CAST(base); - } - - cal_dbg(1, dev, "ioresource %s at %pa - %pa\n", - res->name, &res->start, &res->end); - - reg_io_width = 4; - r_config.reg_stride = reg_io_width; - r_config.val_bits = reg_io_width * 8; - r_config.max_register = resource_size(res) - reg_io_width; - - regmap = regmap_init_mmio(NULL, base, &r_config); - if (IS_ERR(regmap)) - pr_err("regmap init failed\n"); - - return regmap; -} - -/* - * Control Module CAMERARX block access - */ -static void camerarx_phy_enable(struct cal_ctx *ctx) -{ - struct cal_csi2_phy *phy; - u32 phy_id = ctx->csi2_port - 1; - u32 max_lanes; - - phy = &ctx->dev->data->csi2_phy_core[phy_id]; - regmap_field_write(phy->fields[F_CAMMODE], 0); - /* Always enable all lanes at the phy control level */ - max_lanes = (1 << cal_data_get_phy_max_lanes(ctx)) - 1; - regmap_field_write(phy->fields[F_LANEENABLE], max_lanes); - /* F_CSI_MODE is not present on every architecture */ - if (phy->fields[F_CSI_MODE]) - regmap_field_write(phy->fields[F_CSI_MODE], 1); - regmap_field_write(phy->fields[F_CTRLCLKEN], 1); -} - -static void camerarx_phy_disable(struct cal_ctx *ctx) -{ - struct cal_csi2_phy *phy; - u32 phy_id = ctx->csi2_port - 1; - - phy = &ctx->dev->data->csi2_phy_core[phy_id]; - regmap_field_write(phy->fields[F_CTRLCLKEN], 0); -} - -/* - * Camera Instance access block - */ -static struct cc_data *cc_create(struct cal_dev *dev, unsigned int core) -{ - struct platform_device *pdev = dev->pdev; - struct cc_data *cc; - - cc = devm_kzalloc(&pdev->dev, sizeof(*cc), GFP_KERNEL); - if (!cc) - return ERR_PTR(-ENOMEM); - - cc->res = platform_get_resource_byname(pdev, - IORESOURCE_MEM, - (core == 0) ? - "cal_rx_core0" : - "cal_rx_core1"); - cc->base = devm_ioremap_resource(&pdev->dev, cc->res); - if (IS_ERR(cc->base)) { - cal_err(dev, "failed to ioremap\n"); - return ERR_CAST(cc->base); - } - - cal_dbg(1, dev, "ioresource %s at %pa - %pa\n", - cc->res->name, &cc->res->start, &cc->res->end); - - return cc; -} - -/* - * Get Revision and HW info +/* ------------------------------------------------------------------ + * I/O Register Accessors + * ------------------------------------------------------------------ */ -static void cal_get_hwinfo(struct cal_dev *dev) -{ - u32 revision = 0; - u32 hwinfo = 0; - - revision = reg_read(dev, CAL_HL_REVISION); - cal_dbg(3, dev, "CAL_HL_REVISION = 0x%08x (expecting 0x40000200)\n", - revision); - hwinfo = reg_read(dev, CAL_HL_HWINFO); - cal_dbg(3, dev, "CAL_HL_HWINFO = 0x%08x (expecting 0xA3C90469)\n", - hwinfo); -} - -/* - * Errata i913: CSI2 LDO Needs to be disabled when module is powered on - * - * Enabling CSI2 LDO shorts it to core supply. It is crucial the 2 CSI2 - * LDOs on the device are disabled if CSI-2 module is powered on - * (0x4845 B304 | 0x4845 B384 [28:27] = 0x1) or in ULPS (0x4845 B304 - * | 0x4845 B384 [28:27] = 0x2) mode. Common concerns include: high - * current draw on the module supply in active mode. - * - * Errata does not apply when CSI-2 module is powered off - * (0x4845 B304 | 0x4845 B384 [28:27] = 0x0). - * - * SW Workaround: - * Set the following register bits to disable the LDO, - * which is essentially CSI2 REG10 bit 6: - * - * Core 0: 0x4845 B828 = 0x0000 0040 - * Core 1: 0x4845 B928 = 0x0000 0040 - */ -static void i913_errata(struct cal_dev *dev, unsigned int port) +void cal_quickdump_regs(struct cal_dev *cal) { - u32 reg10 = reg_read(dev->cc[port], CAL_CSI2_PHY_REG10); - - set_field(®10, 1, CAL_CSI2_PHY_REG10_I933_LDO_DISABLE_MASK); - - cal_dbg(1, dev, "CSI2_%d_REG10 = 0x%08x\n", port, reg10); - reg_write(dev->cc[port], CAL_CSI2_PHY_REG10, reg10); -} + unsigned int i; -static void cal_quickdump_regs(struct cal_dev *dev) -{ - cal_info(dev, "CAL Registers @ 0x%pa:\n", &dev->res->start); + cal_info(cal, "CAL Registers @ 0x%pa:\n", &cal->res->start); print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, - (__force const void *)dev->base, - resource_size(dev->res), false); + (__force const void *)cal->base, + resource_size(cal->res), false); - if (dev->ctx[0]) { - cal_info(dev, "CSI2 Core 0 Registers @ %pa:\n", - &dev->ctx[0]->cc->res->start); - print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, - (__force const void *)dev->ctx[0]->cc->base, - resource_size(dev->ctx[0]->cc->res), - false); - } + for (i = 0; i < ARRAY_SIZE(cal->phy); ++i) { + struct cal_camerarx *phy = cal->phy[i]; - if (dev->ctx[1]) { - cal_info(dev, "CSI2 Core 1 Registers @ %pa:\n", - &dev->ctx[1]->cc->res->start); + if (!phy) + continue; + + cal_info(cal, "CSI2 Core %u Registers @ %pa:\n", i, + &phy->res->start); print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, - (__force const void *)dev->ctx[1]->cc->base, - resource_size(dev->ctx[1]->cc->res), + (__force const void *)phy->base, + resource_size(phy->res), false); } } -/* - * Enable the expected IRQ sources +/* ------------------------------------------------------------------ + * Context Management + * ------------------------------------------------------------------ */ -static void enable_irqs(struct cal_ctx *ctx) -{ - u32 val; - - const u32 cio_err_mask = - CAL_CSI2_COMPLEXIO_IRQ_LANE_ERRORS_MASK | - CAL_CSI2_COMPLEXIO_IRQ_FIFO_OVR_MASK | - CAL_CSI2_COMPLEXIO_IRQ_SHORT_PACKET_MASK | - CAL_CSI2_COMPLEXIO_IRQ_ECC_NO_CORRECTION_MASK; - - /* Enable CIO error irqs */ - reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(1), - CAL_HL_IRQ_CIO_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_IRQENABLE(ctx->csi2_port), - cio_err_mask); - - /* Always enable OCPO error */ - reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(1), CAL_HL_IRQ_OCPO_ERR_MASK); - - /* Enable IRQ_WDMA_END 0/1 */ - val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(2), val); - /* Enable IRQ_WDMA_START 0/1 */ - val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_HL_IRQENABLE_SET(3), val); - /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(1), 0xFF000000); -} - -static void disable_irqs(struct cal_ctx *ctx) -{ - u32 val; - - /* Disable CIO error irqs */ - reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(1), - CAL_HL_IRQ_CIO_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_IRQENABLE(ctx->csi2_port), - 0); - - /* Disable IRQ_WDMA_END 0/1 */ - val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(2), val); - /* Disable IRQ_WDMA_START 0/1 */ - val = 0; - set_field(&val, 1, CAL_HL_IRQ_MASK(ctx->csi2_port)); - reg_write(ctx->dev, CAL_HL_IRQENABLE_CLR(3), val); - /* Todo: Add VC_IRQ and CSI2_COMPLEXIO_IRQ handling */ - reg_write(ctx->dev, CAL_CSI2_VC_IRQENABLE(1), 0); -} - -static void csi2_cio_power(struct cal_ctx *ctx, bool enable) -{ - u32 target_state; - unsigned int i; - - target_state = enable ? CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_ON : - CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_STATE_OFF; - - reg_write_field(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), - target_state, CAL_CSI2_COMPLEXIO_CFG_PWR_CMD_MASK); - - for (i = 0; i < 10; i++) { - u32 current_state; - - current_state = reg_read_field(ctx->dev, - CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), - CAL_CSI2_COMPLEXIO_CFG_PWR_STATUS_MASK); - - if (current_state == target_state) - break; - - usleep_range(1000, 1100); - } - - if (i == 10) - ctx_err(ctx, "Failed to power %s complexio\n", - enable ? "up" : "down"); -} - -static void csi2_phy_config(struct cal_ctx *ctx); - -static void csi2_phy_init(struct cal_ctx *ctx) -{ - u32 val; - u32 sscounter; - - /* Steps - * 1. Configure D-PHY mode and enable required lanes - * 2. Reset complex IO - Wait for completion of reset - * Note if the external sensor is not sending byte clock, - * the reset will timeout - * 3 Program Stop States - * A. Program THS_TERM, THS_SETTLE, etc... Timings parameters - * in terms of DDR clock periods - * B. Enable stop state transition timeouts - * 4.Force FORCERXMODE - * D. Enable pull down using pad control - * E. Power up PHY - * F. Wait for power up completion - * G. Wait for all enabled lane to reach stop state - * H. Disable pull down using pad control - */ - - /* 1. Configure D-PHY mode and enable required lanes */ - camerarx_phy_enable(ctx); - - /* 2. Reset complex IO - Do not wait for reset completion */ - reg_write_field(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), - CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_OPERATIONAL, - CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); - ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x De-assert Complex IO Reset\n", - ctx->csi2_port, - reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port))); - - /* Dummy read to allow SCP reset to complete */ - reg_read(ctx->cc, CAL_CSI2_PHY_REG0); - - /* 3.A. Program Phy Timing Parameters */ - csi2_phy_config(ctx); - - /* 3.B. Program Stop States */ - /* - * The stop-state-counter is based on fclk cycles, and we always use - * the x16 and x4 settings, so stop-state-timeout = - * fclk-cycle * 16 * 4 * counter. - * - * Stop-state-timeout must be more than 100us as per CSI2 spec, so we - * calculate a timeout that's 100us (rounding up). - */ - sscounter = DIV_ROUND_UP(clk_get_rate(ctx->dev->fclk), 10000 * 16 * 4); - - val = reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port)); - set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X16_IO1_MASK); - set_field(&val, 1, CAL_CSI2_TIMING_STOP_STATE_X4_IO1_MASK); - set_field(&val, sscounter, CAL_CSI2_TIMING_STOP_STATE_COUNTER_IO1_MASK); - reg_write(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port), val); - ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x Stop States\n", - ctx->csi2_port, - reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port))); - - /* 4. Force FORCERXMODE */ - reg_write_field(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port), - 1, CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK); - ctx_dbg(3, ctx, "CAL_CSI2_TIMING(%d) = 0x%08x Force RXMODE\n", - ctx->csi2_port, - reg_read(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port))); - - /* E. Power up the PHY using the complex IO */ - csi2_cio_power(ctx, true); -} - -static void csi2_wait_complexio_reset(struct cal_ctx *ctx) -{ - unsigned long timeout; - - timeout = jiffies + msecs_to_jiffies(750); - while (time_before(jiffies, timeout)) { - if (reg_read_field(ctx->dev, - CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) - break; - usleep_range(500, 5000); - } - - if (reg_read_field(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) != - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETCOMPLETED) - ctx_err(ctx, "Timeout waiting for Complex IO reset done\n"); -} - -static void csi2_wait_stop_state(struct cal_ctx *ctx) -{ - unsigned long timeout; - - timeout = jiffies + msecs_to_jiffies(750); - while (time_before(jiffies, timeout)) { - if (reg_read_field(ctx->dev, - CAL_CSI2_TIMING(ctx->csi2_port), - CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) == 0) - break; - usleep_range(500, 5000); - } - - if (reg_read_field(ctx->dev, CAL_CSI2_TIMING(ctx->csi2_port), - CAL_CSI2_TIMING_FORCE_RX_MODE_IO1_MASK) != 0) - ctx_err(ctx, "Timeout waiting for stop state\n"); -} - -static void csi2_wait_for_phy(struct cal_ctx *ctx) -{ - /* Steps - * 2. Wait for completion of reset - * Note if the external sensor is not sending byte clock, - * the reset will timeout - * 4.Force FORCERXMODE - * G. Wait for all enabled lane to reach stop state - * H. Disable pull down using pad control - */ - - /* 2. Wait for reset completion */ - csi2_wait_complexio_reset(ctx); - - /* 4. G. Wait for all enabled lane to reach stop state */ - csi2_wait_stop_state(ctx); - - ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x (Bit(31,28) should be set!)\n", - (ctx->csi2_port - 1), reg_read(ctx->cc, CAL_CSI2_PHY_REG1)); -} - -static void csi2_phy_deinit(struct cal_ctx *ctx) -{ - int i; - - csi2_cio_power(ctx, false); - - /* Assert Comple IO Reset */ - reg_write_field(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), - CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL, - CAL_CSI2_COMPLEXIO_CFG_RESET_CTRL_MASK); - - /* Wait for power down completion */ - for (i = 0; i < 10; i++) { - if (reg_read_field(ctx->dev, - CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_MASK) == - CAL_CSI2_COMPLEXIO_CFG_RESET_DONE_RESETONGOING) - break; - usleep_range(1000, 1100); - } - ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x Complex IO in Reset (%d) %s\n", - ctx->csi2_port, - reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)), i, - (i >= 10) ? "(timeout)" : ""); - - /* Disable the phy */ - camerarx_phy_disable(ctx); -} - -static void csi2_lane_config(struct cal_ctx *ctx) -{ - u32 val = reg_read(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port)); - u32 lane_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POSITION_MASK; - u32 polarity_mask = CAL_CSI2_COMPLEXIO_CFG_CLOCK_POL_MASK; - struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = - &ctx->endpoint.bus.mipi_csi2; - int lane; - - set_field(&val, mipi_csi2->clock_lane + 1, lane_mask); - set_field(&val, mipi_csi2->lane_polarities[0], polarity_mask); - for (lane = 0; lane < mipi_csi2->num_data_lanes; lane++) { - /* - * Every lane are one nibble apart starting with the - * clock followed by the data lanes so shift masks by 4. - */ - lane_mask <<= 4; - polarity_mask <<= 4; - set_field(&val, mipi_csi2->data_lanes[lane] + 1, lane_mask); - set_field(&val, mipi_csi2->lane_polarities[lane + 1], - polarity_mask); - } - - reg_write(ctx->dev, CAL_CSI2_COMPLEXIO_CFG(ctx->csi2_port), val); - ctx_dbg(3, ctx, "CAL_CSI2_COMPLEXIO_CFG(%d) = 0x%08x\n", - ctx->csi2_port, val); -} - -static void csi2_ppi_enable(struct cal_ctx *ctx) -{ - reg_write(ctx->dev, CAL_CSI2_PPI_CTRL(ctx->csi2_port), BIT(3)); - reg_write_field(ctx->dev, CAL_CSI2_PPI_CTRL(ctx->csi2_port), - 1, CAL_CSI2_PPI_CTRL_IF_EN_MASK); -} - -static void csi2_ppi_disable(struct cal_ctx *ctx) -{ - reg_write_field(ctx->dev, CAL_CSI2_PPI_CTRL(ctx->csi2_port), - 0, CAL_CSI2_PPI_CTRL_IF_EN_MASK); -} -static void csi2_ctx_config(struct cal_ctx *ctx) +void cal_ctx_csi2_config(struct cal_ctx *ctx) { u32 val; - val = reg_read(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port)); - set_field(&val, ctx->csi2_port, CAL_CSI2_CTX_CPORT_MASK); + val = cal_read(ctx->cal, CAL_CSI2_CTX0(ctx->index)); + cal_set_field(&val, ctx->cport, CAL_CSI2_CTX_CPORT_MASK); /* * DT type: MIPI CSI-2 Specs * 0x1: All - DT filter is disabled @@ -979,19 +170,18 @@ static void csi2_ctx_config(struct cal_ctx *ctx) * 0x2A: RAW8 1 pixel = 1 byte * 0x1E: YUV422 2 pixels = 4 bytes */ - set_field(&val, 0x1, CAL_CSI2_CTX_DT_MASK); - /* Virtual Channel from the CSI2 sensor usually 0! */ - set_field(&val, ctx->virtual_channel, CAL_CSI2_CTX_VC_MASK); - set_field(&val, ctx->v_fmt.fmt.pix.height, CAL_CSI2_CTX_LINES_MASK); - set_field(&val, CAL_CSI2_CTX_ATT_PIX, CAL_CSI2_CTX_ATT_MASK); - set_field(&val, CAL_CSI2_CTX_PACK_MODE_LINE, - CAL_CSI2_CTX_PACK_MODE_MASK); - reg_write(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port), val); - ctx_dbg(3, ctx, "CAL_CSI2_CTX0(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->dev, CAL_CSI2_CTX0(ctx->csi2_port))); + cal_set_field(&val, 0x1, CAL_CSI2_CTX_DT_MASK); + cal_set_field(&val, 0, CAL_CSI2_CTX_VC_MASK); + cal_set_field(&val, ctx->v_fmt.fmt.pix.height, CAL_CSI2_CTX_LINES_MASK); + cal_set_field(&val, CAL_CSI2_CTX_ATT_PIX, CAL_CSI2_CTX_ATT_MASK); + cal_set_field(&val, CAL_CSI2_CTX_PACK_MODE_LINE, + CAL_CSI2_CTX_PACK_MODE_MASK); + cal_write(ctx->cal, CAL_CSI2_CTX0(ctx->index), val); + ctx_dbg(3, ctx, "CAL_CSI2_CTX0(%d) = 0x%08x\n", ctx->index, + cal_read(ctx->cal, CAL_CSI2_CTX0(ctx->index))); } -static void pix_proc_config(struct cal_ctx *ctx) +void cal_ctx_pix_proc_config(struct cal_ctx *ctx) { u32 val, extract, pack; @@ -1022,7 +212,7 @@ static void pix_proc_config(struct cal_ctx *ctx) * * Instead of failing here just use 8 bpp as a default. */ - dev_warn_once(&ctx->dev->pdev->dev, + dev_warn_once(ctx->cal->dev, "%s:%d:%s: bpp:%d unsupported! Overwritten with 8.\n", __FILE__, __LINE__, __func__, ctx->fmt->bpp); extract = CAL_PIX_PROC_EXTRACT_B8; @@ -1030,145 +220,82 @@ static void pix_proc_config(struct cal_ctx *ctx) break; } - val = reg_read(ctx->dev, CAL_PIX_PROC(ctx->csi2_port)); - set_field(&val, extract, CAL_PIX_PROC_EXTRACT_MASK); - set_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK); - set_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK); - set_field(&val, pack, CAL_PIX_PROC_PACK_MASK); - set_field(&val, ctx->csi2_port, CAL_PIX_PROC_CPORT_MASK); - set_field(&val, 1, CAL_PIX_PROC_EN_MASK); - reg_write(ctx->dev, CAL_PIX_PROC(ctx->csi2_port), val); - ctx_dbg(3, ctx, "CAL_PIX_PROC(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->dev, CAL_PIX_PROC(ctx->csi2_port))); + val = cal_read(ctx->cal, CAL_PIX_PROC(ctx->index)); + cal_set_field(&val, extract, CAL_PIX_PROC_EXTRACT_MASK); + cal_set_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK); + cal_set_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK); + cal_set_field(&val, pack, CAL_PIX_PROC_PACK_MASK); + cal_set_field(&val, ctx->cport, CAL_PIX_PROC_CPORT_MASK); + cal_set_field(&val, 1, CAL_PIX_PROC_EN_MASK); + cal_write(ctx->cal, CAL_PIX_PROC(ctx->index), val); + ctx_dbg(3, ctx, "CAL_PIX_PROC(%d) = 0x%08x\n", ctx->index, + cal_read(ctx->cal, CAL_PIX_PROC(ctx->index))); } -static void cal_wr_dma_config(struct cal_ctx *ctx, - unsigned int width, unsigned int height) +void cal_ctx_wr_dma_config(struct cal_ctx *ctx, unsigned int width, + unsigned int height) { u32 val; - val = reg_read(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port)); - set_field(&val, ctx->csi2_port, CAL_WR_DMA_CTRL_CPORT_MASK); - set_field(&val, height, CAL_WR_DMA_CTRL_YSIZE_MASK); - set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT, - CAL_WR_DMA_CTRL_DTAG_MASK); - set_field(&val, CAL_WR_DMA_CTRL_MODE_CONST, - CAL_WR_DMA_CTRL_MODE_MASK); - set_field(&val, CAL_WR_DMA_CTRL_PATTERN_LINEAR, - CAL_WR_DMA_CTRL_PATTERN_MASK); - set_field(&val, 1, CAL_WR_DMA_CTRL_STALL_RD_MASK); - reg_write(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port), val); - ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->dev, CAL_WR_DMA_CTRL(ctx->csi2_port))); + val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index)); + cal_set_field(&val, ctx->cport, CAL_WR_DMA_CTRL_CPORT_MASK); + cal_set_field(&val, height, CAL_WR_DMA_CTRL_YSIZE_MASK); + cal_set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT, + CAL_WR_DMA_CTRL_DTAG_MASK); + cal_set_field(&val, CAL_WR_DMA_CTRL_MODE_CONST, + CAL_WR_DMA_CTRL_MODE_MASK); + cal_set_field(&val, CAL_WR_DMA_CTRL_PATTERN_LINEAR, + CAL_WR_DMA_CTRL_PATTERN_MASK); + cal_set_field(&val, 1, CAL_WR_DMA_CTRL_STALL_RD_MASK); + cal_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->index), val); + ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->index, + cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->index))); /* * width/16 not sure but giving it a whirl. * zero does not work right */ - reg_write_field(ctx->dev, - CAL_WR_DMA_OFST(ctx->csi2_port), + cal_write_field(ctx->cal, + CAL_WR_DMA_OFST(ctx->index), (width / 16), CAL_WR_DMA_OFST_MASK); - ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->dev, CAL_WR_DMA_OFST(ctx->csi2_port))); + ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->index, + cal_read(ctx->cal, CAL_WR_DMA_OFST(ctx->index))); - val = reg_read(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port)); + val = cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index)); /* 64 bit word means no skipping */ - set_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK); + cal_set_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK); /* * (width*8)/64 this should be size of an entire line * in 64bit word but 0 means all data until the end * is detected automagically */ - set_field(&val, (width / 8), CAL_WR_DMA_XSIZE_MASK); - reg_write(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port), val); - ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->csi2_port, - reg_read(ctx->dev, CAL_WR_DMA_XSIZE(ctx->csi2_port))); - - val = reg_read(ctx->dev, CAL_CTRL); - set_field(&val, CAL_CTRL_BURSTSIZE_BURST128, CAL_CTRL_BURSTSIZE_MASK); - set_field(&val, 0xF, CAL_CTRL_TAGCNT_MASK); - set_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED, - CAL_CTRL_POSTED_WRITES_MASK); - set_field(&val, 0xFF, CAL_CTRL_MFLAGL_MASK); - set_field(&val, 0xFF, CAL_CTRL_MFLAGH_MASK); - reg_write(ctx->dev, CAL_CTRL, val); - ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", reg_read(ctx->dev, CAL_CTRL)); -} + cal_set_field(&val, (width / 8), CAL_WR_DMA_XSIZE_MASK); + cal_write(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index), val); + ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->index, + cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->index))); -static void cal_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) -{ - reg_write(ctx->dev, CAL_WR_DMA_ADDR(ctx->csi2_port), dmaaddr); + val = cal_read(ctx->cal, CAL_CTRL); + cal_set_field(&val, CAL_CTRL_BURSTSIZE_BURST128, + CAL_CTRL_BURSTSIZE_MASK); + cal_set_field(&val, 0xF, CAL_CTRL_TAGCNT_MASK); + cal_set_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED, + CAL_CTRL_POSTED_WRITES_MASK); + cal_set_field(&val, 0xFF, CAL_CTRL_MFLAGL_MASK); + cal_set_field(&val, 0xFF, CAL_CTRL_MFLAGH_MASK); + cal_write(ctx->cal, CAL_CTRL, val); + ctx_dbg(3, ctx, "CAL_CTRL = 0x%08x\n", cal_read(ctx->cal, CAL_CTRL)); } -/* - * TCLK values are OK at their reset values - */ -#define TCLK_TERM 0 -#define TCLK_MISS 1 -#define TCLK_SETTLE 14 - -static void csi2_phy_config(struct cal_ctx *ctx) +void cal_ctx_wr_dma_addr(struct cal_ctx *ctx, unsigned int dmaaddr) { - unsigned int reg0, reg1; - unsigned int ths_term, ths_settle; - unsigned int csi2_ddrclk_khz; - struct v4l2_fwnode_bus_mipi_csi2 *mipi_csi2 = - &ctx->endpoint.bus.mipi_csi2; - u32 num_lanes = mipi_csi2->num_data_lanes; - - /* DPHY timing configuration */ - /* CSI-2 is DDR and we only count used lanes. */ - csi2_ddrclk_khz = ctx->external_rate / 1000 - / (2 * num_lanes) * ctx->fmt->bpp; - ctx_dbg(1, ctx, "csi2_ddrclk_khz: %d\n", csi2_ddrclk_khz); - - /* THS_TERM: Programmed value = floor(20 ns/DDRClk period) */ - ths_term = 20 * csi2_ddrclk_khz / 1000000; - ctx_dbg(1, ctx, "ths_term: %d (0x%02x)\n", ths_term, ths_term); - - /* THS_SETTLE: Programmed value = floor(105 ns/DDRClk period) + 4 */ - ths_settle = (105 * csi2_ddrclk_khz / 1000000) + 4; - ctx_dbg(1, ctx, "ths_settle: %d (0x%02x)\n", ths_settle, ths_settle); - - reg0 = reg_read(ctx->cc, CAL_CSI2_PHY_REG0); - set_field(®0, CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_DISABLE, - CAL_CSI2_PHY_REG0_HSCLOCKCONFIG_MASK); - set_field(®0, ths_term, CAL_CSI2_PHY_REG0_THS_TERM_MASK); - set_field(®0, ths_settle, CAL_CSI2_PHY_REG0_THS_SETTLE_MASK); - - ctx_dbg(1, ctx, "CSI2_%d_REG0 = 0x%08x\n", (ctx->csi2_port - 1), reg0); - reg_write(ctx->cc, CAL_CSI2_PHY_REG0, reg0); - - reg1 = reg_read(ctx->cc, CAL_CSI2_PHY_REG1); - set_field(®1, TCLK_TERM, CAL_CSI2_PHY_REG1_TCLK_TERM_MASK); - set_field(®1, 0xb8, CAL_CSI2_PHY_REG1_DPHY_HS_SYNC_PATTERN_MASK); - set_field(®1, TCLK_MISS, CAL_CSI2_PHY_REG1_CTRLCLK_DIV_FACTOR_MASK); - set_field(®1, TCLK_SETTLE, CAL_CSI2_PHY_REG1_TCLK_SETTLE_MASK); - - ctx_dbg(1, ctx, "CSI2_%d_REG1 = 0x%08x\n", (ctx->csi2_port - 1), reg1); - reg_write(ctx->cc, CAL_CSI2_PHY_REG1, reg1); + cal_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->index), dmaaddr); } -static int cal_get_external_info(struct cal_ctx *ctx) -{ - struct v4l2_ctrl *ctrl; - - if (!ctx->sensor) - return -ENODEV; - - ctrl = v4l2_ctrl_find(ctx->sensor->ctrl_handler, V4L2_CID_PIXEL_RATE); - if (!ctrl) { - ctx_err(ctx, "no pixel rate control in subdev: %s\n", - ctx->sensor->name); - return -EPIPE; - } - - ctx->external_rate = v4l2_ctrl_g_ctrl_int64(ctrl); - ctx_dbg(3, ctx, "sensor Pixel Rate: %d\n", ctx->external_rate); - - return 0; -} +/* ------------------------------------------------------------------ + * IRQ Handling + * ------------------------------------------------------------------ + */ static inline void cal_schedule_next_buffer(struct cal_ctx *ctx) { @@ -1181,7 +308,7 @@ static inline void cal_schedule_next_buffer(struct cal_ctx *ctx) list_del(&buf->list); addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); - cal_wr_dma_addr(ctx, addr); + cal_ctx_wr_dma_addr(ctx, addr); } static inline void cal_process_buffer_complete(struct cal_ctx *ctx) @@ -1194,52 +321,47 @@ static inline void cal_process_buffer_complete(struct cal_ctx *ctx) ctx->cur_frm = ctx->next_frm; } -#define isvcirqset(irq, vc, ff) (irq & \ - (CAL_CSI2_VC_IRQENABLE_ ##ff ##_IRQ_##vc ##_MASK)) - -#define isportirqset(irq, port) (irq & CAL_HL_IRQ_MASK(port)) - static irqreturn_t cal_irq(int irq_cal, void *data) { - struct cal_dev *dev = (struct cal_dev *)data; + struct cal_dev *cal = data; struct cal_ctx *ctx; struct cal_dmaqueue *dma_q; - u32 irqst1, irqst2, irqst3; + u32 status; - irqst1 = reg_read(dev, CAL_HL_IRQSTATUS(1)); - if (irqst1) { - int i; + status = cal_read(cal, CAL_HL_IRQSTATUS(0)); + if (status) { + unsigned int i; - reg_write(dev, CAL_HL_IRQSTATUS(1), irqst1); + cal_write(cal, CAL_HL_IRQSTATUS(0), status); - if (irqst1 & CAL_HL_IRQ_OCPO_ERR_MASK) - dev_err_ratelimited(&dev->pdev->dev, "OCPO ERROR\n"); + if (status & CAL_HL_IRQ_OCPO_ERR_MASK) + dev_err_ratelimited(cal->dev, "OCPO ERROR\n"); - for (i = 1; i <= 2; ++i) { - if (irqst1 & CAL_HL_IRQ_CIO_MASK(i)) { - u32 cio_stat = reg_read(dev, + for (i = 0; i < CAL_NUM_CSI2_PORTS; ++i) { + if (status & CAL_HL_IRQ_CIO_MASK(i)) { + u32 cio_stat = cal_read(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i)); - dev_err_ratelimited(&dev->pdev->dev, - "CIO%d error: %#08x\n", i, cio_stat); + dev_err_ratelimited(cal->dev, + "CIO%u error: %#08x\n", i, cio_stat); - reg_write(dev, CAL_CSI2_COMPLEXIO_IRQSTATUS(i), + cal_write(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i), cio_stat); } } } /* Check which DMA just finished */ - irqst2 = reg_read(dev, CAL_HL_IRQSTATUS(2)); - if (irqst2) { - int i; + status = cal_read(cal, CAL_HL_IRQSTATUS(1)); + if (status) { + unsigned int i; /* Clear Interrupt status */ - reg_write(dev, CAL_HL_IRQSTATUS(2), irqst2); + cal_write(cal, CAL_HL_IRQSTATUS(1), status); - for (i = 1; i <= 2; ++i) { - if (isportirqset(irqst2, i)) { - ctx = dev->ctx[i - 1]; + for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { + if (status & CAL_HL_IRQ_MASK(i)) { + ctx = cal->ctx[i]; spin_lock(&ctx->slock); ctx->dma_act = false; @@ -1253,16 +375,16 @@ static irqreturn_t cal_irq(int irq_cal, void *data) } /* Check which DMA just started */ - irqst3 = reg_read(dev, CAL_HL_IRQSTATUS(3)); - if (irqst3) { - int i; + status = cal_read(cal, CAL_HL_IRQSTATUS(2)); + if (status) { + unsigned int i; /* Clear Interrupt status */ - reg_write(dev, CAL_HL_IRQSTATUS(3), irqst3); + cal_write(cal, CAL_HL_IRQSTATUS(2), status); - for (i = 1; i <= 2; ++i) { - if (isportirqset(irqst3, i)) { - ctx = dev->ctx[i - 1]; + for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { + if (status & CAL_HL_IRQ_MASK(i)) { + ctx = cal->ctx[i]; dma_q = &ctx->vidq; spin_lock(&ctx->slock); @@ -1278,963 +400,234 @@ static irqreturn_t cal_irq(int irq_cal, void *data) return IRQ_HANDLED; } -/* - * video ioctls +/* ------------------------------------------------------------------ + * Asynchronous V4L2 subdev binding + * ------------------------------------------------------------------ */ -static int cal_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct cal_ctx *ctx = video_drvdata(file); - - strscpy(cap->driver, CAL_MODULE_NAME, sizeof(cap->driver)); - strscpy(cap->card, CAL_MODULE_NAME, sizeof(cap->card)); - - snprintf(cap->bus_info, sizeof(cap->bus_info), - "platform:%s", ctx->v4l2_dev.name); - return 0; -} - -static int cal_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt = NULL; - - if (f->index >= ctx->num_active_fmt) - return -EINVAL; - - fmt = ctx->active_fmt[f->index]; - - f->pixelformat = fmt->fourcc; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - return 0; -} - -static int __subdev_get_format(struct cal_ctx *ctx, - struct v4l2_mbus_framefmt *fmt) -{ - struct v4l2_subdev_format sd_fmt; - struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; - int ret; - - sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; - sd_fmt.pad = 0; - ret = v4l2_subdev_call(ctx->sensor, pad, get_fmt, NULL, &sd_fmt); - if (ret) - return ret; - - *fmt = *mbus_fmt; - - ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__, - fmt->width, fmt->height, fmt->code); - - return 0; -} - -static int __subdev_set_format(struct cal_ctx *ctx, - struct v4l2_mbus_framefmt *fmt) -{ - struct v4l2_subdev_format sd_fmt; - struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; - int ret; - - sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; - sd_fmt.pad = 0; - *mbus_fmt = *fmt; - - ret = v4l2_subdev_call(ctx->sensor, pad, set_fmt, NULL, &sd_fmt); - if (ret) - return ret; - - ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__, - fmt->width, fmt->height, fmt->code); - - return 0; -} - -static int cal_calc_format_size(struct cal_ctx *ctx, - const struct cal_fmt *fmt, - struct v4l2_format *f) -{ - u32 bpl, max_width; - - if (!fmt) { - ctx_dbg(3, ctx, "No cal_fmt provided!\n"); - return -EINVAL; - } - - /* - * Maximum width is bound by the DMA max width in bytes. - * We need to recalculate the actual maxi width depending on the - * number of bytes per pixels required. - */ - max_width = MAX_WIDTH_BYTES / (ALIGN(fmt->bpp, 8) >> 3); - v4l_bound_align_image(&f->fmt.pix.width, 48, max_width, 2, - &f->fmt.pix.height, 32, MAX_HEIGHT_LINES, 0, 0); - - bpl = (f->fmt.pix.width * ALIGN(fmt->bpp, 8)) >> 3; - f->fmt.pix.bytesperline = ALIGN(bpl, 16); - - f->fmt.pix.sizeimage = f->fmt.pix.height * - f->fmt.pix.bytesperline; - - ctx_dbg(3, ctx, "%s: fourcc: %s size: %dx%d bpl:%d img_size:%d\n", - __func__, fourcc_to_str(f->fmt.pix.pixelformat), - f->fmt.pix.width, f->fmt.pix.height, - f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); - - return 0; -} - -static int cal_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cal_ctx *ctx = video_drvdata(file); - - *f = ctx->v_fmt; - - return 0; -} - -static int cal_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt; - struct v4l2_subdev_frame_size_enum fse; - int ret, found; - - fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat); - if (!fmt) { - ctx_dbg(3, ctx, "Fourcc format (0x%08x) not found.\n", - f->fmt.pix.pixelformat); - - /* Just get the first one enumerated */ - fmt = ctx->active_fmt[0]; - f->fmt.pix.pixelformat = fmt->fourcc; - } - - f->fmt.pix.field = ctx->v_fmt.fmt.pix.field; - - /* check for/find a valid width/height */ - ret = 0; - found = false; - fse.pad = 0; - fse.code = fmt->code; - fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; - for (fse.index = 0; ; fse.index++) { - ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_size, - NULL, &fse); - if (ret) - break; - - if ((f->fmt.pix.width == fse.max_width) && - (f->fmt.pix.height == fse.max_height)) { - found = true; - break; - } else if ((f->fmt.pix.width >= fse.min_width) && - (f->fmt.pix.width <= fse.max_width) && - (f->fmt.pix.height >= fse.min_height) && - (f->fmt.pix.height <= fse.max_height)) { - found = true; - break; - } - } - - if (!found) { - /* use existing values as default */ - f->fmt.pix.width = ctx->v_fmt.fmt.pix.width; - f->fmt.pix.height = ctx->v_fmt.fmt.pix.height; - } - - /* - * Use current colorspace for now, it will get - * updated properly during s_fmt - */ - f->fmt.pix.colorspace = ctx->v_fmt.fmt.pix.colorspace; - return cal_calc_format_size(ctx, fmt, f); -} +struct cal_v4l2_async_subdev { + struct v4l2_async_subdev asd; + struct cal_camerarx *phy; +}; -static int cal_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) +static inline struct cal_v4l2_async_subdev * +to_cal_asd(struct v4l2_async_subdev *asd) { - struct cal_ctx *ctx = video_drvdata(file); - struct vb2_queue *q = &ctx->vb_vidq; - const struct cal_fmt *fmt; - struct v4l2_mbus_framefmt mbus_fmt; - int ret; - - if (vb2_is_busy(q)) { - ctx_dbg(3, ctx, "%s device busy\n", __func__); - return -EBUSY; - } - - ret = cal_try_fmt_vid_cap(file, priv, f); - if (ret < 0) - return ret; - - fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat); - - v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code); - - ret = __subdev_set_format(ctx, &mbus_fmt); - if (ret) - return ret; - - /* Just double check nothing has gone wrong */ - if (mbus_fmt.code != fmt->code) { - ctx_dbg(3, ctx, - "%s subdev changed format on us, this should not happen\n", - __func__); - return -EINVAL; - } - - v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); - ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; - cal_calc_format_size(ctx, fmt, &ctx->v_fmt); - ctx->fmt = fmt; - ctx->m_fmt = mbus_fmt; - *f = ctx->v_fmt; - - return 0; + return container_of(asd, struct cal_v4l2_async_subdev, asd); } -static int cal_enum_framesizes(struct file *file, void *fh, - struct v4l2_frmsizeenum *fsize) +static int cal_async_notifier_bound(struct v4l2_async_notifier *notifier, + struct v4l2_subdev *subdev, + struct v4l2_async_subdev *asd) { - struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt; - struct v4l2_subdev_frame_size_enum fse; - int ret; + struct cal_camerarx *phy = to_cal_asd(asd)->phy; - /* check for valid format */ - fmt = find_format_by_pix(ctx, fsize->pixel_format); - if (!fmt) { - ctx_dbg(3, ctx, "Invalid pixel code: %x\n", - fsize->pixel_format); - return -EINVAL; + if (phy->sensor) { + phy_info(phy, "Rejecting subdev %s (Already set!!)", + subdev->name); + return 0; } - fse.index = fsize->index; - fse.pad = 0; - fse.code = fmt->code; - fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; - - ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_size, NULL, &fse); - if (ret) - return ret; - - ctx_dbg(1, ctx, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n", - __func__, fse.index, fse.code, fse.min_width, fse.max_width, - fse.min_height, fse.max_height); - - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = fse.max_width; - fsize->discrete.height = fse.max_height; - - return 0; -} - -static int cal_enum_input(struct file *file, void *priv, - struct v4l2_input *inp) -{ - if (inp->index >= CAL_NUM_INPUT) - return -EINVAL; - - inp->type = V4L2_INPUT_TYPE_CAMERA; - sprintf(inp->name, "Camera %u", inp->index); - return 0; -} - -static int cal_g_input(struct file *file, void *priv, unsigned int *i) -{ - struct cal_ctx *ctx = video_drvdata(file); - - *i = ctx->input; - return 0; -} - -static int cal_s_input(struct file *file, void *priv, unsigned int i) -{ - struct cal_ctx *ctx = video_drvdata(file); - - if (i >= CAL_NUM_INPUT) - return -EINVAL; - - ctx->input = i; - return 0; -} - -/* timeperframe is arbitrary and continuous */ -static int cal_enum_frameintervals(struct file *file, void *priv, - struct v4l2_frmivalenum *fival) -{ - struct cal_ctx *ctx = video_drvdata(file); - const struct cal_fmt *fmt; - struct v4l2_subdev_frame_interval_enum fie = { - .index = fival->index, - .width = fival->width, - .height = fival->height, - .which = V4L2_SUBDEV_FORMAT_ACTIVE, - }; - int ret; - - fmt = find_format_by_pix(ctx, fival->pixel_format); - if (!fmt) - return -EINVAL; - - fie.code = fmt->code; - ret = v4l2_subdev_call(ctx->sensor, pad, enum_frame_interval, - NULL, &fie); - if (ret) - return ret; - fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; - fival->discrete = fie.interval; + phy->sensor = subdev; + phy_dbg(1, phy, "Using sensor %s for capture\n", subdev->name); return 0; } -/* - * Videobuf operations - */ -static int cal_queue_setup(struct vb2_queue *vq, - unsigned int *nbuffers, unsigned int *nplanes, - unsigned int sizes[], struct device *alloc_devs[]) +static int cal_async_notifier_complete(struct v4l2_async_notifier *notifier) { - struct cal_ctx *ctx = vb2_get_drv_priv(vq); - unsigned size = ctx->v_fmt.fmt.pix.sizeimage; - - if (vq->num_buffers + *nbuffers < 3) - *nbuffers = 3 - vq->num_buffers; - - if (*nplanes) { - if (sizes[0] < size) - return -EINVAL; - size = sizes[0]; - } - - *nplanes = 1; - sizes[0] = size; - - ctx_dbg(3, ctx, "nbuffers=%d, size=%d\n", *nbuffers, sizes[0]); - - return 0; -} + struct cal_dev *cal = container_of(notifier, struct cal_dev, notifier); + unsigned int i; -static int cal_buffer_prepare(struct vb2_buffer *vb) -{ - struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct cal_buffer *buf = container_of(vb, struct cal_buffer, - vb.vb2_buf); - unsigned long size; - - if (WARN_ON(!ctx->fmt)) - return -EINVAL; - - size = ctx->v_fmt.fmt.pix.sizeimage; - if (vb2_plane_size(vb, 0) < size) { - ctx_err(ctx, - "data will not fit into plane (%lu < %lu)\n", - vb2_plane_size(vb, 0), size); - return -EINVAL; + for (i = 0; i < ARRAY_SIZE(cal->ctx); ++i) { + if (cal->ctx[i]) + cal_ctx_v4l2_register(cal->ctx[i]); } - vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); return 0; } -static void cal_buffer_queue(struct vb2_buffer *vb) -{ - struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct cal_buffer *buf = container_of(vb, struct cal_buffer, - vb.vb2_buf); - struct cal_dmaqueue *vidq = &ctx->vidq; - unsigned long flags = 0; - - /* recheck locking */ - spin_lock_irqsave(&ctx->slock, flags); - list_add_tail(&buf->list, &vidq->active); - spin_unlock_irqrestore(&ctx->slock, flags); -} +static const struct v4l2_async_notifier_operations cal_async_notifier_ops = { + .bound = cal_async_notifier_bound, + .complete = cal_async_notifier_complete, +}; -static int cal_start_streaming(struct vb2_queue *vq, unsigned int count) +static int cal_async_notifier_register(struct cal_dev *cal) { - struct cal_ctx *ctx = vb2_get_drv_priv(vq); - struct cal_dmaqueue *dma_q = &ctx->vidq; - struct cal_buffer *buf, *tmp; - unsigned long addr = 0; - unsigned long flags; + unsigned int i; int ret; - spin_lock_irqsave(&ctx->slock, flags); - if (list_empty(&dma_q->active)) { - spin_unlock_irqrestore(&ctx->slock, flags); - ctx_dbg(3, ctx, "buffer queue is empty\n"); - return -EIO; - } + v4l2_async_notifier_init(&cal->notifier); + cal->notifier.ops = &cal_async_notifier_ops; - buf = list_entry(dma_q->active.next, struct cal_buffer, list); - ctx->cur_frm = buf; - ctx->next_frm = buf; - list_del(&buf->list); - spin_unlock_irqrestore(&ctx->slock, flags); + for (i = 0; i < ARRAY_SIZE(cal->phy); ++i) { + struct cal_camerarx *phy = cal->phy[i]; + struct cal_v4l2_async_subdev *casd; + struct v4l2_async_subdev *asd; + struct fwnode_handle *fwnode; - addr = vb2_dma_contig_plane_dma_addr(&ctx->cur_frm->vb.vb2_buf, 0); - ctx->sequence = 0; + if (!phy || !phy->sensor_node) + continue; - ret = cal_get_external_info(ctx); - if (ret < 0) - goto err; + fwnode = of_fwnode_handle(phy->sensor_node); + asd = v4l2_async_notifier_add_fwnode_subdev(&cal->notifier, + fwnode, + sizeof(*asd)); + if (IS_ERR(asd)) { + phy_err(phy, "Failed to add subdev to notifier\n"); + ret = PTR_ERR(asd); + goto error; + } - ret = v4l2_subdev_call(ctx->sensor, core, s_power, 1); - if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) { - ctx_err(ctx, "power on failed in subdev\n"); - goto err; + casd = to_cal_asd(asd); + casd->phy = phy; } - pm_runtime_get_sync(&ctx->dev->pdev->dev); - - csi2_ctx_config(ctx); - pix_proc_config(ctx); - cal_wr_dma_config(ctx, ctx->v_fmt.fmt.pix.bytesperline, - ctx->v_fmt.fmt.pix.height); - csi2_lane_config(ctx); - - enable_irqs(ctx); - csi2_phy_init(ctx); - - ret = v4l2_subdev_call(ctx->sensor, video, s_stream, 1); + ret = v4l2_async_notifier_register(&cal->v4l2_dev, &cal->notifier); if (ret) { - v4l2_subdev_call(ctx->sensor, core, s_power, 0); - ctx_err(ctx, "stream on failed in subdev\n"); - pm_runtime_put_sync(&ctx->dev->pdev->dev); - goto err; + cal_err(cal, "Error registering async notifier\n"); + goto error; } - csi2_wait_for_phy(ctx); - cal_wr_dma_addr(ctx, addr); - csi2_ppi_enable(ctx); - - if (debug >= 4) - cal_quickdump_regs(ctx->dev); - return 0; -err: - spin_lock_irqsave(&ctx->slock, flags); - vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_QUEUED); - ctx->cur_frm = NULL; - ctx->next_frm = NULL; - list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { - list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); - } - spin_unlock_irqrestore(&ctx->slock, flags); +error: + v4l2_async_notifier_cleanup(&cal->notifier); return ret; } -static void cal_stop_streaming(struct vb2_queue *vq) +static void cal_async_notifier_unregister(struct cal_dev *cal) { - struct cal_ctx *ctx = vb2_get_drv_priv(vq); - struct cal_dmaqueue *dma_q = &ctx->vidq; - struct cal_buffer *buf, *tmp; - unsigned long timeout; - unsigned long flags; - int ret; - bool dma_act; - - csi2_ppi_disable(ctx); - - /* wait for stream and dma to finish */ - dma_act = true; - timeout = jiffies + msecs_to_jiffies(500); - while (dma_act && time_before(jiffies, timeout)) { - msleep(50); - - spin_lock_irqsave(&ctx->slock, flags); - dma_act = ctx->dma_act; - spin_unlock_irqrestore(&ctx->slock, flags); - } - - if (dma_act) - ctx_err(ctx, "failed to disable dma cleanly\n"); - - disable_irqs(ctx); - csi2_phy_deinit(ctx); - - if (v4l2_subdev_call(ctx->sensor, video, s_stream, 0)) - ctx_err(ctx, "stream off failed in subdev\n"); - - ret = v4l2_subdev_call(ctx->sensor, core, s_power, 0); - if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) - ctx_err(ctx, "power off failed in subdev\n"); - - /* Release all active buffers */ - spin_lock_irqsave(&ctx->slock, flags); - list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { - list_del(&buf->list); - vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); - } - - if (ctx->cur_frm == ctx->next_frm) { - vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); - } else { - vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); - vb2_buffer_done(&ctx->next_frm->vb.vb2_buf, - VB2_BUF_STATE_ERROR); - } - ctx->cur_frm = NULL; - ctx->next_frm = NULL; - spin_unlock_irqrestore(&ctx->slock, flags); - - pm_runtime_put_sync(&ctx->dev->pdev->dev); + v4l2_async_notifier_unregister(&cal->notifier); + v4l2_async_notifier_cleanup(&cal->notifier); } -static const struct vb2_ops cal_video_qops = { - .queue_setup = cal_queue_setup, - .buf_prepare = cal_buffer_prepare, - .buf_queue = cal_buffer_queue, - .start_streaming = cal_start_streaming, - .stop_streaming = cal_stop_streaming, - .wait_prepare = vb2_ops_wait_prepare, - .wait_finish = vb2_ops_wait_finish, -}; - -static const struct v4l2_file_operations cal_fops = { - .owner = THIS_MODULE, - .open = v4l2_fh_open, - .release = vb2_fop_release, - .read = vb2_fop_read, - .poll = vb2_fop_poll, - .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ - .mmap = vb2_fop_mmap, -}; - -static const struct v4l2_ioctl_ops cal_ioctl_ops = { - .vidioc_querycap = cal_querycap, - .vidioc_enum_fmt_vid_cap = cal_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = cal_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = cal_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = cal_s_fmt_vid_cap, - .vidioc_enum_framesizes = cal_enum_framesizes, - .vidioc_reqbufs = vb2_ioctl_reqbufs, - .vidioc_create_bufs = vb2_ioctl_create_bufs, - .vidioc_prepare_buf = vb2_ioctl_prepare_buf, - .vidioc_querybuf = vb2_ioctl_querybuf, - .vidioc_qbuf = vb2_ioctl_qbuf, - .vidioc_dqbuf = vb2_ioctl_dqbuf, - .vidioc_expbuf = vb2_ioctl_expbuf, - .vidioc_enum_input = cal_enum_input, - .vidioc_g_input = cal_g_input, - .vidioc_s_input = cal_s_input, - .vidioc_enum_frameintervals = cal_enum_frameintervals, - .vidioc_streamon = vb2_ioctl_streamon, - .vidioc_streamoff = vb2_ioctl_streamoff, - .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static const struct video_device cal_videodev = { - .name = CAL_MODULE_NAME, - .fops = &cal_fops, - .ioctl_ops = &cal_ioctl_ops, - .minor = -1, - .release = video_device_release_empty, - .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | - V4L2_CAP_READWRITE, -}; - -/* ----------------------------------------------------------------- - * Initialization and module stuff +/* ------------------------------------------------------------------ + * Media and V4L2 device handling * ------------------------------------------------------------------ */ -static int cal_complete_ctx(struct cal_ctx *ctx); -static int cal_async_bound(struct v4l2_async_notifier *notifier, - struct v4l2_subdev *subdev, - struct v4l2_async_subdev *asd) +/* + * Register user-facing devices. To be called at the end of the probe function + * when all resources are initialized and ready. + */ +static int cal_media_register(struct cal_dev *cal) { - struct cal_ctx *ctx = notifier_to_ctx(notifier); - struct v4l2_subdev_mbus_code_enum mbus_code; - int ret = 0; - int i, j, k; - - if (ctx->sensor) { - ctx_info(ctx, "Rejecting subdev %s (Already set!!)", - subdev->name); - return 0; - } - - ctx->sensor = subdev; - ctx_dbg(1, ctx, "Using sensor %s for capture\n", subdev->name); - - /* Enumerate sub device formats and enable all matching local formats */ - ctx->num_active_fmt = 0; - for (j = 0, i = 0; ret != -EINVAL; ++j) { - struct cal_fmt *fmt; - - memset(&mbus_code, 0, sizeof(mbus_code)); - mbus_code.index = j; - mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; - ret = v4l2_subdev_call(subdev, pad, enum_mbus_code, - NULL, &mbus_code); - if (ret) - continue; - - ctx_dbg(2, ctx, - "subdev %s: code: %04x idx: %d\n", - subdev->name, mbus_code.code, j); - - for (k = 0; k < ARRAY_SIZE(cal_formats); k++) { - fmt = &cal_formats[k]; + int ret; - if (mbus_code.code == fmt->code) { - ctx->active_fmt[i] = fmt; - ctx_dbg(2, ctx, - "matched fourcc: %s: code: %04x idx: %d\n", - fourcc_to_str(fmt->fourcc), - fmt->code, i); - ctx->num_active_fmt = ++i; - } - } + ret = media_device_register(&cal->mdev); + if (ret) { + cal_err(cal, "Failed to register media device\n"); + return ret; } - if (i == 0) { - ctx_err(ctx, "No suitable format reported by subdev %s\n", - subdev->name); - return -EINVAL; + /* + * Register the async notifier. This may trigger registration of the + * V4L2 video devices if all subdevs are ready. + */ + ret = cal_async_notifier_register(cal); + if (ret) { + media_device_unregister(&cal->mdev); + return ret; } - cal_complete_ctx(ctx); - return 0; } -static int cal_async_complete(struct v4l2_async_notifier *notifier) +/* + * Unregister the user-facing devices, but don't free memory yet. To be called + * at the beginning of the remove function, to disallow access from userspace. + */ +static void cal_media_unregister(struct cal_dev *cal) { - struct cal_ctx *ctx = notifier_to_ctx(notifier); - const struct cal_fmt *fmt; - struct v4l2_mbus_framefmt mbus_fmt; - int ret; - - ret = __subdev_get_format(ctx, &mbus_fmt); - if (ret) - return ret; + unsigned int i; - fmt = find_format_by_code(ctx, mbus_fmt.code); - if (!fmt) { - ctx_dbg(3, ctx, "mbus code format (0x%08x) not found.\n", - mbus_fmt.code); - return -EINVAL; + /* Unregister all the V4L2 video devices. */ + for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { + if (cal->ctx[i]) + cal_ctx_v4l2_unregister(cal->ctx[i]); } - /* Save current subdev format */ - v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); - ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; - cal_calc_format_size(ctx, fmt, &ctx->v_fmt); - ctx->fmt = fmt; - ctx->m_fmt = mbus_fmt; - - return 0; + cal_async_notifier_unregister(cal); + media_device_unregister(&cal->mdev); } -static const struct v4l2_async_notifier_operations cal_async_ops = { - .bound = cal_async_bound, - .complete = cal_async_complete, -}; - -static int cal_complete_ctx(struct cal_ctx *ctx) +/* + * Initialize the in-kernel objects. To be called at the beginning of the probe + * function, before the V4L2 device is used by the driver. + */ +static int cal_media_init(struct cal_dev *cal) { - struct video_device *vfd; - struct vb2_queue *q; + struct media_device *mdev = &cal->mdev; int ret; - ctx->timeperframe = tpf_default; - ctx->external_rate = 192000000; - - /* initialize locks */ - spin_lock_init(&ctx->slock); - mutex_init(&ctx->mutex); - - /* initialize queue */ - q = &ctx->vb_vidq; - q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; - q->drv_priv = ctx; - q->buf_struct_size = sizeof(struct cal_buffer); - q->ops = &cal_video_qops; - q->mem_ops = &vb2_dma_contig_memops; - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; - q->lock = &ctx->mutex; - q->min_buffers_needed = 3; - q->dev = ctx->v4l2_dev.dev; - - ret = vb2_queue_init(q); - if (ret) - return ret; - - /* init video dma queues */ - INIT_LIST_HEAD(&ctx->vidq.active); - - vfd = &ctx->vdev; - *vfd = cal_videodev; - vfd->v4l2_dev = &ctx->v4l2_dev; - vfd->queue = q; + mdev->dev = cal->dev; + mdev->hw_revision = cal->revision; + strscpy(mdev->model, "CAL", sizeof(mdev->model)); + snprintf(mdev->bus_info, sizeof(mdev->bus_info), "platform:%s", + dev_name(mdev->dev)); + media_device_init(mdev); /* - * Provide a mutex to v4l2 core. It will be used to protect - * all fops and v4l2 ioctls. + * Initialize the V4L2 device (despite the function name, this performs + * initialization, not registration). */ - vfd->lock = &ctx->mutex; - video_set_drvdata(vfd, ctx); - - ret = video_register_device(vfd, VFL_TYPE_VIDEO, video_nr); - if (ret < 0) + cal->v4l2_dev.mdev = mdev; + ret = v4l2_device_register(cal->dev, &cal->v4l2_dev); + if (ret) { + cal_err(cal, "Failed to register V4L2 device\n"); return ret; + } - v4l2_info(&ctx->v4l2_dev, "V4L2 device registered as %s\n", - video_device_node_name(vfd)); + vb2_dma_contig_set_max_seg_size(cal->dev, DMA_BIT_MASK(32)); return 0; } -static struct device_node * -of_get_next_port(const struct device_node *parent, - struct device_node *prev) +/* + * Cleanup the in-kernel objects, freeing memory. To be called at the very end + * of the remove sequence, when nothing (including userspace) can access the + * objects anymore. + */ +static void cal_media_cleanup(struct cal_dev *cal) { - struct device_node *port = NULL; - - if (!parent) - return NULL; + unsigned int i; - if (!prev) { - struct device_node *ports; - /* - * It's the first call, we have to find a port subnode - * within this node or within an optional 'ports' node. - */ - ports = of_get_child_by_name(parent, "ports"); - if (ports) - parent = ports; - - port = of_get_child_by_name(parent, "port"); - - /* release the 'ports' node */ - of_node_put(ports); - } else { - struct device_node *ports; - - ports = of_get_parent(prev); - if (!ports) - return NULL; - - do { - port = of_get_next_child(ports, prev); - if (!port) { - of_node_put(ports); - return NULL; - } - prev = port; - } while (!of_node_name_eq(port, "port")); - of_node_put(ports); + for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { + if (cal->ctx[i]) + cal_ctx_v4l2_cleanup(cal->ctx[i]); } - return port; -} - -static struct device_node * -of_get_next_endpoint(const struct device_node *parent, - struct device_node *prev) -{ - struct device_node *ep = NULL; - - if (!parent) - return NULL; - - do { - ep = of_get_next_child(parent, prev); - if (!ep) - return NULL; - prev = ep; - } while (!of_node_name_eq(ep, "endpoint")); + v4l2_device_unregister(&cal->v4l2_dev); + media_device_cleanup(&cal->mdev); - return ep; + vb2_dma_contig_clear_max_seg_size(cal->dev); } -static int of_cal_create_instance(struct cal_ctx *ctx, int inst) -{ - struct platform_device *pdev = ctx->dev->pdev; - struct device_node *ep_node, *port, *sensor_node, *parent; - struct v4l2_fwnode_endpoint *endpoint; - struct v4l2_async_subdev *asd; - u32 regval = 0; - int ret, index, found_port = 0, lane; - - parent = pdev->dev.of_node; - - endpoint = &ctx->endpoint; - - ep_node = NULL; - port = NULL; - sensor_node = NULL; - ret = -EINVAL; - - ctx_dbg(3, ctx, "Scanning Port node for csi2 port: %d\n", inst); - for (index = 0; index < CAL_NUM_CSI2_PORTS; index++) { - port = of_get_next_port(parent, port); - if (!port) { - ctx_dbg(1, ctx, "No port node found for csi2 port:%d\n", - index); - goto cleanup_exit; - } - - /* Match the slice number with <REG> */ - of_property_read_u32(port, "reg", ®val); - ctx_dbg(3, ctx, "port:%d inst:%d <reg>:%d\n", - index, inst, regval); - if ((regval == inst) && (index == inst)) { - found_port = 1; - break; - } - } - - if (!found_port) { - ctx_dbg(1, ctx, "No port node matches csi2 port:%d\n", - inst); - goto cleanup_exit; - } - - ctx_dbg(3, ctx, "Scanning sub-device for csi2 port: %d\n", - inst); - - ep_node = of_get_next_endpoint(port, ep_node); - if (!ep_node) { - ctx_dbg(3, ctx, "can't get next endpoint\n"); - goto cleanup_exit; - } - - sensor_node = of_graph_get_remote_port_parent(ep_node); - if (!sensor_node) { - ctx_dbg(3, ctx, "can't get remote parent\n"); - goto cleanup_exit; - } - - v4l2_fwnode_endpoint_parse(of_fwnode_handle(ep_node), endpoint); - - if (endpoint->bus_type != V4L2_MBUS_CSI2_DPHY) { - ctx_err(ctx, "Port:%d sub-device %pOFn is not a CSI2 device\n", - inst, sensor_node); - goto cleanup_exit; - } - - /* Store Virtual Channel number */ - ctx->virtual_channel = endpoint->base.id; - - ctx_dbg(3, ctx, "Port:%d v4l2-endpoint: CSI2\n", inst); - ctx_dbg(3, ctx, "Virtual Channel=%d\n", ctx->virtual_channel); - ctx_dbg(3, ctx, "flags=0x%08x\n", endpoint->bus.mipi_csi2.flags); - ctx_dbg(3, ctx, "clock_lane=%d\n", endpoint->bus.mipi_csi2.clock_lane); - ctx_dbg(3, ctx, "num_data_lanes=%d\n", - endpoint->bus.mipi_csi2.num_data_lanes); - ctx_dbg(3, ctx, "data_lanes= <\n"); - for (lane = 0; lane < endpoint->bus.mipi_csi2.num_data_lanes; lane++) - ctx_dbg(3, ctx, "\t%d\n", - endpoint->bus.mipi_csi2.data_lanes[lane]); - ctx_dbg(3, ctx, "\t>\n"); - - ctx_dbg(1, ctx, "Port: %d found sub-device %pOFn\n", - inst, sensor_node); - - v4l2_async_notifier_init(&ctx->notifier); - - asd = kzalloc(sizeof(*asd), GFP_KERNEL); - if (!asd) - goto cleanup_exit; - - asd->match_type = V4L2_ASYNC_MATCH_FWNODE; - asd->match.fwnode = of_fwnode_handle(sensor_node); - - ret = v4l2_async_notifier_add_subdev(&ctx->notifier, asd); - if (ret) { - ctx_err(ctx, "Error adding asd\n"); - kfree(asd); - goto cleanup_exit; - } - - ctx->notifier.ops = &cal_async_ops; - ret = v4l2_async_notifier_register(&ctx->v4l2_dev, - &ctx->notifier); - if (ret) { - ctx_err(ctx, "Error registering async notifier\n"); - v4l2_async_notifier_cleanup(&ctx->notifier); - ret = -EINVAL; - } - - /* - * On success we need to keep reference on sensor_node, or - * if notifier_cleanup was called above, sensor_node was - * already put. - */ - sensor_node = NULL; - -cleanup_exit: - of_node_put(sensor_node); - of_node_put(ep_node); - of_node_put(port); - - return ret; -} +/* ------------------------------------------------------------------ + * Initialization and module stuff + * ------------------------------------------------------------------ + */ -static struct cal_ctx *cal_create_instance(struct cal_dev *dev, int inst) +static struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst) { struct cal_ctx *ctx; - struct v4l2_ctrl_handler *hdl; int ret; - ctx = devm_kzalloc(&dev->pdev->dev, sizeof(*ctx), GFP_KERNEL); + ctx = devm_kzalloc(cal->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return NULL; - /* save the cal_dev * for future ref */ - ctx->dev = dev; + ctx->cal = cal; + ctx->phy = cal->phy[inst]; + ctx->index = inst; + ctx->cport = inst; - snprintf(ctx->v4l2_dev.name, sizeof(ctx->v4l2_dev.name), - "%s-%03d", CAL_MODULE_NAME, inst); - ret = v4l2_device_register(&dev->pdev->dev, &ctx->v4l2_dev); + ret = cal_ctx_v4l2_init(ctx); if (ret) - goto err_exit; - - hdl = &ctx->ctrl_handler; - ret = v4l2_ctrl_handler_init(hdl, 11); - if (ret) { - ctx_err(ctx, "Failed to init ctrl handler\n"); - goto unreg_dev; - } - ctx->v4l2_dev.ctrl_handler = hdl; - - /* Make sure Camera Core H/W register area is available */ - ctx->cc = dev->cc[inst]; - - /* Store the instance id */ - ctx->csi2_port = inst + 1; + return NULL; - ret = of_cal_create_instance(ctx, inst); - if (ret) { - ret = -EINVAL; - goto free_hdl; - } return ctx; - -free_hdl: - v4l2_ctrl_handler_free(hdl); -unreg_dev: - v4l2_device_unregister(&ctx->v4l2_dev); -err_exit: - return NULL; } static const struct of_device_id cal_of_match[] = { @@ -2258,191 +651,253 @@ static const struct of_device_id cal_of_match[] = { }; MODULE_DEVICE_TABLE(of, cal_of_match); +/* Get hardware revision and info. */ + +#define CAL_HL_HWINFO_VALUE 0xa3c90469 + +static void cal_get_hwinfo(struct cal_dev *cal) +{ + u32 hwinfo; + + cal->revision = cal_read(cal, CAL_HL_REVISION); + switch (FIELD_GET(CAL_HL_REVISION_SCHEME_MASK, cal->revision)) { + case CAL_HL_REVISION_SCHEME_H08: + cal_dbg(3, cal, "CAL HW revision %lu.%lu.%lu (0x%08x)\n", + FIELD_GET(CAL_HL_REVISION_MAJOR_MASK, cal->revision), + FIELD_GET(CAL_HL_REVISION_MINOR_MASK, cal->revision), + FIELD_GET(CAL_HL_REVISION_RTL_MASK, cal->revision), + cal->revision); + break; + + case CAL_HL_REVISION_SCHEME_LEGACY: + default: + cal_info(cal, "Unexpected CAL HW revision 0x%08x\n", + cal->revision); + break; + } + + hwinfo = cal_read(cal, CAL_HL_HWINFO); + if (hwinfo != CAL_HL_HWINFO_VALUE) + cal_info(cal, "CAL_HL_HWINFO = 0x%08x, expected 0x%08x\n", + hwinfo, CAL_HL_HWINFO_VALUE); +} + +static int cal_init_camerarx_regmap(struct cal_dev *cal) +{ + struct platform_device *pdev = to_platform_device(cal->dev); + struct device_node *np = cal->dev->of_node; + struct regmap_config config = { }; + struct regmap *syscon; + struct resource *res; + unsigned int offset; + void __iomem *base; + + syscon = syscon_regmap_lookup_by_phandle_args(np, "ti,camerrx-control", + 1, &offset); + if (!IS_ERR(syscon)) { + cal->syscon_camerrx = syscon; + cal->syscon_camerrx_offset = offset; + return 0; + } + + dev_warn(cal->dev, "failed to get ti,camerrx-control: %ld\n", + PTR_ERR(syscon)); + + /* + * Backward DTS compatibility. If syscon entry is not present then + * check if the camerrx_control resource is present. + */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "camerrx_control"); + base = devm_ioremap_resource(cal->dev, res); + if (IS_ERR(base)) { + cal_err(cal, "failed to ioremap camerrx_control\n"); + return PTR_ERR(base); + } + + cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", + res->name, &res->start, &res->end); + + config.reg_bits = 32; + config.reg_stride = 4; + config.val_bits = 32; + config.max_register = resource_size(res) - 4; + + syscon = regmap_init_mmio(NULL, base, &config); + if (IS_ERR(syscon)) { + pr_err("regmap init failed\n"); + return PTR_ERR(syscon); + } + + /* + * In this case the base already point to the direct CM register so no + * need for an offset. + */ + cal->syscon_camerrx = syscon; + cal->syscon_camerrx_offset = 0; + + return 0; +} + static int cal_probe(struct platform_device *pdev) { - struct cal_dev *dev; + struct cal_dev *cal; struct cal_ctx *ctx; - struct device_node *parent = pdev->dev.of_node; - struct regmap *syscon_camerrx = NULL; - u32 syscon_camerrx_offset = 0; + bool connected = false; + unsigned int i; int ret; int irq; - int i; - dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); - if (!dev) + cal = devm_kzalloc(&pdev->dev, sizeof(*cal), GFP_KERNEL); + if (!cal) return -ENOMEM; - dev->data = of_device_get_match_data(&pdev->dev); - if (!dev->data) { + cal->data = of_device_get_match_data(&pdev->dev); + if (!cal->data) { dev_err(&pdev->dev, "Could not get feature data based on compatible version\n"); return -ENODEV; } - dev->flags = dev->data->flags; - - /* set pseudo v4l2 device name so we can use v4l2_printk */ - strscpy(dev->v4l2_dev.name, CAL_MODULE_NAME, - sizeof(dev->v4l2_dev.name)); + cal->dev = &pdev->dev; + platform_set_drvdata(pdev, cal); - /* save pdev pointer */ - dev->pdev = pdev; - - dev->fclk = devm_clk_get(&pdev->dev, "fck"); - if (IS_ERR(dev->fclk)) { + /* Acquire resources: clocks, CAMERARX regmap, I/O memory and IRQ. */ + cal->fclk = devm_clk_get(&pdev->dev, "fck"); + if (IS_ERR(cal->fclk)) { dev_err(&pdev->dev, "cannot get CAL fclk\n"); - return PTR_ERR(dev->fclk); + return PTR_ERR(cal->fclk); } - syscon_camerrx = syscon_regmap_lookup_by_phandle(parent, - "ti,camerrx-control"); - ret = of_property_read_u32_index(parent, "ti,camerrx-control", 1, - &syscon_camerrx_offset); - if (IS_ERR(syscon_camerrx)) - ret = PTR_ERR(syscon_camerrx); - if (ret) { - dev_warn(&pdev->dev, "failed to get ti,camerrx-control: %d\n", - ret); - - /* - * Backward DTS compatibility. - * If syscon entry is not present then check if the - * camerrx_control resource is present. - */ - syscon_camerrx = cal_get_camerarx_regmap(dev); - if (IS_ERR(syscon_camerrx)) { - dev_err(&pdev->dev, "failed to get camerrx_control regmap\n"); - return PTR_ERR(syscon_camerrx); - } - /* In this case the base already point to the direct - * CM register so no need for an offset - */ - syscon_camerrx_offset = 0; - } - - dev->syscon_camerrx = syscon_camerrx; - dev->syscon_camerrx_offset = syscon_camerrx_offset; - ret = cal_camerarx_regmap_init(dev); - if (ret) + ret = cal_init_camerarx_regmap(cal); + if (ret < 0) return ret; - dev->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + cal->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cal_top"); - dev->base = devm_ioremap_resource(&pdev->dev, dev->res); - if (IS_ERR(dev->base)) - return PTR_ERR(dev->base); + cal->base = devm_ioremap_resource(&pdev->dev, cal->res); + if (IS_ERR(cal->base)) + return PTR_ERR(cal->base); - cal_dbg(1, dev, "ioresource %s at %pa - %pa\n", - dev->res->name, &dev->res->start, &dev->res->end); + cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", + cal->res->name, &cal->res->start, &cal->res->end); irq = platform_get_irq(pdev, 0); - cal_dbg(1, dev, "got irq# %d\n", irq); + cal_dbg(1, cal, "got irq# %d\n", irq); ret = devm_request_irq(&pdev->dev, irq, cal_irq, 0, CAL_MODULE_NAME, - dev); + cal); if (ret) return ret; - platform_set_drvdata(pdev, dev); + /* Read the revision and hardware info to verify hardware access. */ + pm_runtime_enable(&pdev->dev); + ret = pm_runtime_get_sync(&pdev->dev); + if (ret) + goto error_pm_runtime; - dev->cc[0] = cc_create(dev, 0); - if (IS_ERR(dev->cc[0])) - return PTR_ERR(dev->cc[0]); + cal_get_hwinfo(cal); + pm_runtime_put_sync(&pdev->dev); - if (cal_data_get_num_csi2_phy(dev) > 1) { - dev->cc[1] = cc_create(dev, 1); - if (IS_ERR(dev->cc[1])) - return PTR_ERR(dev->cc[1]); - } else { - dev->cc[1] = NULL; - } + /* Create CAMERARX PHYs. */ + for (i = 0; i < cal->data->num_csi2_phy; ++i) { + cal->phy[i] = cal_camerarx_create(cal, i); + if (IS_ERR(cal->phy[i])) { + ret = PTR_ERR(cal->phy[i]); + cal->phy[i] = NULL; + goto error_camerarx; + } - dev->ctx[0] = NULL; - dev->ctx[1] = NULL; + if (cal->phy[i]->sensor_node) + connected = true; + } - dev->ctx[0] = cal_create_instance(dev, 0); - if (cal_data_get_num_csi2_phy(dev) > 1) - dev->ctx[1] = cal_create_instance(dev, 1); - if (!dev->ctx[0] && !dev->ctx[1]) { - cal_err(dev, "Neither port is configured, no point in staying up\n"); - return -ENODEV; + if (!connected) { + cal_err(cal, "Neither port is configured, no point in staying up\n"); + ret = -ENODEV; + goto error_camerarx; } - vb2_dma_contig_set_max_seg_size(&pdev->dev, DMA_BIT_MASK(32)); + /* Initialize the media device. */ + ret = cal_media_init(cal); + if (ret < 0) + goto error_camerarx; - pm_runtime_enable(&pdev->dev); + /* Create contexts. */ + for (i = 0; i < cal->data->num_csi2_phy; ++i) { + if (!cal->phy[i]->sensor_node) + continue; - ret = pm_runtime_get_sync(&pdev->dev); + cal->ctx[i] = cal_ctx_create(cal, i); + if (!cal->ctx[i]) { + cal_err(cal, "Failed to create context %u\n", i); + ret = -ENODEV; + goto error_context; + } + } + + /* Register the media device. */ + ret = cal_media_register(cal); if (ret) - goto runtime_disable; + goto error_context; - /* Just check we can actually access the module */ - cal_get_hwinfo(dev); + return 0; - pm_runtime_put_sync(&pdev->dev); +error_context: + for (i = 0; i < ARRAY_SIZE(cal->ctx); i++) { + ctx = cal->ctx[i]; + if (ctx) + cal_ctx_v4l2_cleanup(ctx); + } - return 0; + cal_media_cleanup(cal); -runtime_disable: - vb2_dma_contig_clear_max_seg_size(&pdev->dev); +error_camerarx: + for (i = 0; i < ARRAY_SIZE(cal->phy); i++) + cal_camerarx_destroy(cal->phy[i]); +error_pm_runtime: pm_runtime_disable(&pdev->dev); - for (i = 0; i < CAL_NUM_CONTEXT; i++) { - ctx = dev->ctx[i]; - if (ctx) { - v4l2_async_notifier_unregister(&ctx->notifier); - v4l2_async_notifier_cleanup(&ctx->notifier); - v4l2_ctrl_handler_free(&ctx->ctrl_handler); - v4l2_device_unregister(&ctx->v4l2_dev); - } - } return ret; } static int cal_remove(struct platform_device *pdev) { - struct cal_dev *dev = - (struct cal_dev *)platform_get_drvdata(pdev); - struct cal_ctx *ctx; - int i; + struct cal_dev *cal = platform_get_drvdata(pdev); + unsigned int i; - cal_dbg(1, dev, "Removing %s\n", CAL_MODULE_NAME); + cal_dbg(1, cal, "Removing %s\n", CAL_MODULE_NAME); pm_runtime_get_sync(&pdev->dev); - for (i = 0; i < CAL_NUM_CONTEXT; i++) { - ctx = dev->ctx[i]; - if (ctx) { - ctx_dbg(1, ctx, "unregistering %s\n", - video_device_node_name(&ctx->vdev)); - camerarx_phy_disable(ctx); - v4l2_async_notifier_unregister(&ctx->notifier); - v4l2_async_notifier_cleanup(&ctx->notifier); - v4l2_ctrl_handler_free(&ctx->ctrl_handler); - v4l2_device_unregister(&ctx->v4l2_dev); - video_unregister_device(&ctx->vdev); - } + cal_media_unregister(cal); + + for (i = 0; i < ARRAY_SIZE(cal->phy); i++) { + if (cal->phy[i]) + cal_camerarx_disable(cal->phy[i]); } + cal_media_cleanup(cal); + + for (i = 0; i < ARRAY_SIZE(cal->phy); i++) + cal_camerarx_destroy(cal->phy[i]); + pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); - vb2_dma_contig_clear_max_seg_size(&pdev->dev); - return 0; } static int cal_runtime_resume(struct device *dev) { - struct cal_dev *caldev = dev_get_drvdata(dev); + struct cal_dev *cal = dev_get_drvdata(dev); - if (caldev->flags & DRA72_CAL_PRE_ES2_LDO_DISABLE) { + if (cal->data->flags & DRA72_CAL_PRE_ES2_LDO_DISABLE) { /* * Apply errata on both port everytime we (re-)enable * the clock */ - i913_errata(caldev, 0); - i913_errata(caldev, 1); + cal_camerarx_i913_errata(cal->phy[0]); + cal_camerarx_i913_errata(cal->phy[1]); } return 0; |