162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * TI Camera Access Layer (CAL) - Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2015-2020 Texas Instruments Inc. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Authors: 862306a36Sopenharmony_ci * Benoit Parrot <bparrot@ti.com> 962306a36Sopenharmony_ci * Laurent Pinchart <laurent.pinchart@ideasonboard.com> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/clk.h> 1362306a36Sopenharmony_ci#include <linux/interrupt.h> 1462306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/of.h> 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1962306a36Sopenharmony_ci#include <linux/regmap.h> 2062306a36Sopenharmony_ci#include <linux/slab.h> 2162306a36Sopenharmony_ci#include <linux/videodev2.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <media/media-device.h> 2462306a36Sopenharmony_ci#include <media/v4l2-async.h> 2562306a36Sopenharmony_ci#include <media/v4l2-common.h> 2662306a36Sopenharmony_ci#include <media/v4l2-device.h> 2762306a36Sopenharmony_ci#include <media/videobuf2-core.h> 2862306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include "cal.h" 3162306a36Sopenharmony_ci#include "cal_regs.h" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ciMODULE_DESCRIPTION("TI CAL driver"); 3462306a36Sopenharmony_ciMODULE_AUTHOR("Benoit Parrot, <bparrot@ti.com>"); 3562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 3662306a36Sopenharmony_ciMODULE_VERSION("0.1.0"); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ciint cal_video_nr = -1; 3962306a36Sopenharmony_cimodule_param_named(video_nr, cal_video_nr, uint, 0644); 4062306a36Sopenharmony_ciMODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect"); 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ciunsigned int cal_debug; 4362306a36Sopenharmony_cimodule_param_named(debug, cal_debug, uint, 0644); 4462306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "activates debug info"); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#ifdef CONFIG_VIDEO_TI_CAL_MC 4762306a36Sopenharmony_ci#define CAL_MC_API_DEFAULT 1 4862306a36Sopenharmony_ci#else 4962306a36Sopenharmony_ci#define CAL_MC_API_DEFAULT 0 5062306a36Sopenharmony_ci#endif 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cibool cal_mc_api = CAL_MC_API_DEFAULT; 5362306a36Sopenharmony_cimodule_param_named(mc_api, cal_mc_api, bool, 0444); 5462306a36Sopenharmony_ciMODULE_PARM_DESC(mc_api, "activates the MC API"); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* ------------------------------------------------------------------ 5762306a36Sopenharmony_ci * Format Handling 5862306a36Sopenharmony_ci * ------------------------------------------------------------------ 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ciconst struct cal_format_info cal_formats[] = { 6262306a36Sopenharmony_ci { 6362306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_YUYV, 6462306a36Sopenharmony_ci .code = MEDIA_BUS_FMT_YUYV8_1X16, 6562306a36Sopenharmony_ci .bpp = 16, 6662306a36Sopenharmony_ci }, { 6762306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_UYVY, 6862306a36Sopenharmony_ci .code = MEDIA_BUS_FMT_UYVY8_1X16, 6962306a36Sopenharmony_ci .bpp = 16, 7062306a36Sopenharmony_ci }, { 7162306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_YVYU, 7262306a36Sopenharmony_ci .code = MEDIA_BUS_FMT_YVYU8_1X16, 7362306a36Sopenharmony_ci .bpp = 16, 7462306a36Sopenharmony_ci }, { 7562306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_VYUY, 7662306a36Sopenharmony_ci .code = MEDIA_BUS_FMT_VYUY8_1X16, 7762306a36Sopenharmony_ci .bpp = 16, 7862306a36Sopenharmony_ci }, { 7962306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB565, 8062306a36Sopenharmony_ci .code = MEDIA_BUS_FMT_RGB565_1X16, 8162306a36Sopenharmony_ci .bpp = 16, 8262306a36Sopenharmony_ci }, { 8362306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SBGGR8, 8462306a36Sopenharmony_ci .code = MEDIA_BUS_FMT_SBGGR8_1X8, 8562306a36Sopenharmony_ci .bpp = 8, 8662306a36Sopenharmony_ci }, { 8762306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SGBRG8, 8862306a36Sopenharmony_ci .code = MEDIA_BUS_FMT_SGBRG8_1X8, 8962306a36Sopenharmony_ci .bpp = 8, 9062306a36Sopenharmony_ci }, { 9162306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SGRBG8, 9262306a36Sopenharmony_ci .code = MEDIA_BUS_FMT_SGRBG8_1X8, 9362306a36Sopenharmony_ci .bpp = 8, 9462306a36Sopenharmony_ci }, { 9562306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SRGGB8, 9662306a36Sopenharmony_ci .code = MEDIA_BUS_FMT_SRGGB8_1X8, 9762306a36Sopenharmony_ci .bpp = 8, 9862306a36Sopenharmony_ci }, { 9962306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SBGGR10, 10062306a36Sopenharmony_ci .code = MEDIA_BUS_FMT_SBGGR10_1X10, 10162306a36Sopenharmony_ci .bpp = 10, 10262306a36Sopenharmony_ci }, { 10362306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SGBRG10, 10462306a36Sopenharmony_ci .code = MEDIA_BUS_FMT_SGBRG10_1X10, 10562306a36Sopenharmony_ci .bpp = 10, 10662306a36Sopenharmony_ci }, { 10762306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SGRBG10, 10862306a36Sopenharmony_ci .code = MEDIA_BUS_FMT_SGRBG10_1X10, 10962306a36Sopenharmony_ci .bpp = 10, 11062306a36Sopenharmony_ci }, { 11162306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SRGGB10, 11262306a36Sopenharmony_ci .code = MEDIA_BUS_FMT_SRGGB10_1X10, 11362306a36Sopenharmony_ci .bpp = 10, 11462306a36Sopenharmony_ci }, { 11562306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SBGGR12, 11662306a36Sopenharmony_ci .code = MEDIA_BUS_FMT_SBGGR12_1X12, 11762306a36Sopenharmony_ci .bpp = 12, 11862306a36Sopenharmony_ci }, { 11962306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SGBRG12, 12062306a36Sopenharmony_ci .code = MEDIA_BUS_FMT_SGBRG12_1X12, 12162306a36Sopenharmony_ci .bpp = 12, 12262306a36Sopenharmony_ci }, { 12362306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SGRBG12, 12462306a36Sopenharmony_ci .code = MEDIA_BUS_FMT_SGRBG12_1X12, 12562306a36Sopenharmony_ci .bpp = 12, 12662306a36Sopenharmony_ci }, { 12762306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SRGGB12, 12862306a36Sopenharmony_ci .code = MEDIA_BUS_FMT_SRGGB12_1X12, 12962306a36Sopenharmony_ci .bpp = 12, 13062306a36Sopenharmony_ci }, 13162306a36Sopenharmony_ci}; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ciconst unsigned int cal_num_formats = ARRAY_SIZE(cal_formats); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ciconst struct cal_format_info *cal_format_by_fourcc(u32 fourcc) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci unsigned int i; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cal_formats); ++i) { 14062306a36Sopenharmony_ci if (cal_formats[i].fourcc == fourcc) 14162306a36Sopenharmony_ci return &cal_formats[i]; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci return NULL; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ciconst struct cal_format_info *cal_format_by_code(u32 code) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci unsigned int i; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cal_formats); ++i) { 15262306a36Sopenharmony_ci if (cal_formats[i].code == code) 15362306a36Sopenharmony_ci return &cal_formats[i]; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return NULL; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* ------------------------------------------------------------------ 16062306a36Sopenharmony_ci * Platform Data 16162306a36Sopenharmony_ci * ------------------------------------------------------------------ 16262306a36Sopenharmony_ci */ 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic const struct cal_camerarx_data dra72x_cal_camerarx[] = { 16562306a36Sopenharmony_ci { 16662306a36Sopenharmony_ci .fields = { 16762306a36Sopenharmony_ci [F_CTRLCLKEN] = { 10, 10 }, 16862306a36Sopenharmony_ci [F_CAMMODE] = { 11, 12 }, 16962306a36Sopenharmony_ci [F_LANEENABLE] = { 13, 16 }, 17062306a36Sopenharmony_ci [F_CSI_MODE] = { 17, 17 }, 17162306a36Sopenharmony_ci }, 17262306a36Sopenharmony_ci .num_lanes = 4, 17362306a36Sopenharmony_ci }, 17462306a36Sopenharmony_ci { 17562306a36Sopenharmony_ci .fields = { 17662306a36Sopenharmony_ci [F_CTRLCLKEN] = { 0, 0 }, 17762306a36Sopenharmony_ci [F_CAMMODE] = { 1, 2 }, 17862306a36Sopenharmony_ci [F_LANEENABLE] = { 3, 4 }, 17962306a36Sopenharmony_ci [F_CSI_MODE] = { 5, 5 }, 18062306a36Sopenharmony_ci }, 18162306a36Sopenharmony_ci .num_lanes = 2, 18262306a36Sopenharmony_ci }, 18362306a36Sopenharmony_ci}; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic const struct cal_data dra72x_cal_data = { 18662306a36Sopenharmony_ci .camerarx = dra72x_cal_camerarx, 18762306a36Sopenharmony_ci .num_csi2_phy = ARRAY_SIZE(dra72x_cal_camerarx), 18862306a36Sopenharmony_ci}; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic const struct cal_data dra72x_es1_cal_data = { 19162306a36Sopenharmony_ci .camerarx = dra72x_cal_camerarx, 19262306a36Sopenharmony_ci .num_csi2_phy = ARRAY_SIZE(dra72x_cal_camerarx), 19362306a36Sopenharmony_ci .flags = DRA72_CAL_PRE_ES2_LDO_DISABLE, 19462306a36Sopenharmony_ci}; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic const struct cal_camerarx_data dra76x_cal_csi_phy[] = { 19762306a36Sopenharmony_ci { 19862306a36Sopenharmony_ci .fields = { 19962306a36Sopenharmony_ci [F_CTRLCLKEN] = { 8, 8 }, 20062306a36Sopenharmony_ci [F_CAMMODE] = { 9, 10 }, 20162306a36Sopenharmony_ci [F_CSI_MODE] = { 11, 11 }, 20262306a36Sopenharmony_ci [F_LANEENABLE] = { 27, 31 }, 20362306a36Sopenharmony_ci }, 20462306a36Sopenharmony_ci .num_lanes = 5, 20562306a36Sopenharmony_ci }, 20662306a36Sopenharmony_ci { 20762306a36Sopenharmony_ci .fields = { 20862306a36Sopenharmony_ci [F_CTRLCLKEN] = { 0, 0 }, 20962306a36Sopenharmony_ci [F_CAMMODE] = { 1, 2 }, 21062306a36Sopenharmony_ci [F_CSI_MODE] = { 3, 3 }, 21162306a36Sopenharmony_ci [F_LANEENABLE] = { 24, 26 }, 21262306a36Sopenharmony_ci }, 21362306a36Sopenharmony_ci .num_lanes = 3, 21462306a36Sopenharmony_ci }, 21562306a36Sopenharmony_ci}; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic const struct cal_data dra76x_cal_data = { 21862306a36Sopenharmony_ci .camerarx = dra76x_cal_csi_phy, 21962306a36Sopenharmony_ci .num_csi2_phy = ARRAY_SIZE(dra76x_cal_csi_phy), 22062306a36Sopenharmony_ci}; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic const struct cal_camerarx_data am654_cal_csi_phy[] = { 22362306a36Sopenharmony_ci { 22462306a36Sopenharmony_ci .fields = { 22562306a36Sopenharmony_ci [F_CTRLCLKEN] = { 15, 15 }, 22662306a36Sopenharmony_ci [F_CAMMODE] = { 24, 25 }, 22762306a36Sopenharmony_ci [F_LANEENABLE] = { 0, 4 }, 22862306a36Sopenharmony_ci }, 22962306a36Sopenharmony_ci .num_lanes = 5, 23062306a36Sopenharmony_ci }, 23162306a36Sopenharmony_ci}; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic const struct cal_data am654_cal_data = { 23462306a36Sopenharmony_ci .camerarx = am654_cal_csi_phy, 23562306a36Sopenharmony_ci .num_csi2_phy = ARRAY_SIZE(am654_cal_csi_phy), 23662306a36Sopenharmony_ci}; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci/* ------------------------------------------------------------------ 23962306a36Sopenharmony_ci * I/O Register Accessors 24062306a36Sopenharmony_ci * ------------------------------------------------------------------ 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_civoid cal_quickdump_regs(struct cal_dev *cal) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci unsigned int i; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci cal_info(cal, "CAL Registers @ 0x%pa:\n", &cal->res->start); 24862306a36Sopenharmony_ci print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, 24962306a36Sopenharmony_ci (__force const void *)cal->base, 25062306a36Sopenharmony_ci resource_size(cal->res), false); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci for (i = 0; i < cal->data->num_csi2_phy; ++i) { 25362306a36Sopenharmony_ci struct cal_camerarx *phy = cal->phy[i]; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci cal_info(cal, "CSI2 Core %u Registers @ %pa:\n", i, 25662306a36Sopenharmony_ci &phy->res->start); 25762306a36Sopenharmony_ci print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 4, 25862306a36Sopenharmony_ci (__force const void *)phy->base, 25962306a36Sopenharmony_ci resource_size(phy->res), 26062306a36Sopenharmony_ci false); 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci/* ------------------------------------------------------------------ 26562306a36Sopenharmony_ci * Context Management 26662306a36Sopenharmony_ci * ------------------------------------------------------------------ 26762306a36Sopenharmony_ci */ 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci#define CAL_MAX_PIX_PROC 4 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic int cal_reserve_pix_proc(struct cal_dev *cal) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci unsigned long ret; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci spin_lock(&cal->v4l2_dev.lock); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci ret = find_first_zero_bit(&cal->reserved_pix_proc_mask, CAL_MAX_PIX_PROC); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (ret == CAL_MAX_PIX_PROC) { 28062306a36Sopenharmony_ci spin_unlock(&cal->v4l2_dev.lock); 28162306a36Sopenharmony_ci return -ENOSPC; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci cal->reserved_pix_proc_mask |= BIT(ret); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci spin_unlock(&cal->v4l2_dev.lock); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci return ret; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic void cal_release_pix_proc(struct cal_dev *cal, unsigned int pix_proc_num) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci spin_lock(&cal->v4l2_dev.lock); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci cal->reserved_pix_proc_mask &= ~BIT(pix_proc_num); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci spin_unlock(&cal->v4l2_dev.lock); 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic void cal_ctx_csi2_config(struct cal_ctx *ctx) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci u32 val; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci val = cal_read(ctx->cal, CAL_CSI2_CTX(ctx->phy->instance, ctx->csi2_ctx)); 30562306a36Sopenharmony_ci cal_set_field(&val, ctx->cport, CAL_CSI2_CTX_CPORT_MASK); 30662306a36Sopenharmony_ci /* 30762306a36Sopenharmony_ci * DT type: MIPI CSI-2 Specs 30862306a36Sopenharmony_ci * 0x1: All - DT filter is disabled 30962306a36Sopenharmony_ci * 0x24: RGB888 1 pixel = 3 bytes 31062306a36Sopenharmony_ci * 0x2B: RAW10 4 pixels = 5 bytes 31162306a36Sopenharmony_ci * 0x2A: RAW8 1 pixel = 1 byte 31262306a36Sopenharmony_ci * 0x1E: YUV422 2 pixels = 4 bytes 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_ci cal_set_field(&val, ctx->datatype, CAL_CSI2_CTX_DT_MASK); 31562306a36Sopenharmony_ci cal_set_field(&val, ctx->vc, CAL_CSI2_CTX_VC_MASK); 31662306a36Sopenharmony_ci cal_set_field(&val, ctx->v_fmt.fmt.pix.height, CAL_CSI2_CTX_LINES_MASK); 31762306a36Sopenharmony_ci cal_set_field(&val, CAL_CSI2_CTX_ATT_PIX, CAL_CSI2_CTX_ATT_MASK); 31862306a36Sopenharmony_ci cal_set_field(&val, CAL_CSI2_CTX_PACK_MODE_LINE, 31962306a36Sopenharmony_ci CAL_CSI2_CTX_PACK_MODE_MASK); 32062306a36Sopenharmony_ci cal_write(ctx->cal, CAL_CSI2_CTX(ctx->phy->instance, ctx->csi2_ctx), val); 32162306a36Sopenharmony_ci ctx_dbg(3, ctx, "CAL_CSI2_CTX(%u, %u) = 0x%08x\n", 32262306a36Sopenharmony_ci ctx->phy->instance, ctx->csi2_ctx, 32362306a36Sopenharmony_ci cal_read(ctx->cal, CAL_CSI2_CTX(ctx->phy->instance, ctx->csi2_ctx))); 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic void cal_ctx_pix_proc_config(struct cal_ctx *ctx) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci u32 val, extract, pack; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci switch (ctx->fmtinfo->bpp) { 33162306a36Sopenharmony_ci case 8: 33262306a36Sopenharmony_ci extract = CAL_PIX_PROC_EXTRACT_B8; 33362306a36Sopenharmony_ci pack = CAL_PIX_PROC_PACK_B8; 33462306a36Sopenharmony_ci break; 33562306a36Sopenharmony_ci case 10: 33662306a36Sopenharmony_ci extract = CAL_PIX_PROC_EXTRACT_B10_MIPI; 33762306a36Sopenharmony_ci pack = CAL_PIX_PROC_PACK_B16; 33862306a36Sopenharmony_ci break; 33962306a36Sopenharmony_ci case 12: 34062306a36Sopenharmony_ci extract = CAL_PIX_PROC_EXTRACT_B12_MIPI; 34162306a36Sopenharmony_ci pack = CAL_PIX_PROC_PACK_B16; 34262306a36Sopenharmony_ci break; 34362306a36Sopenharmony_ci case 16: 34462306a36Sopenharmony_ci extract = CAL_PIX_PROC_EXTRACT_B16_LE; 34562306a36Sopenharmony_ci pack = CAL_PIX_PROC_PACK_B16; 34662306a36Sopenharmony_ci break; 34762306a36Sopenharmony_ci default: 34862306a36Sopenharmony_ci /* 34962306a36Sopenharmony_ci * If you see this warning then it means that you added 35062306a36Sopenharmony_ci * some new entry in the cal_formats[] array with a different 35162306a36Sopenharmony_ci * bit per pixel values then the one supported below. 35262306a36Sopenharmony_ci * Either add support for the new bpp value below or adjust 35362306a36Sopenharmony_ci * the new entry to use one of the value below. 35462306a36Sopenharmony_ci * 35562306a36Sopenharmony_ci * Instead of failing here just use 8 bpp as a default. 35662306a36Sopenharmony_ci */ 35762306a36Sopenharmony_ci dev_warn_once(ctx->cal->dev, 35862306a36Sopenharmony_ci "%s:%d:%s: bpp:%d unsupported! Overwritten with 8.\n", 35962306a36Sopenharmony_ci __FILE__, __LINE__, __func__, ctx->fmtinfo->bpp); 36062306a36Sopenharmony_ci extract = CAL_PIX_PROC_EXTRACT_B8; 36162306a36Sopenharmony_ci pack = CAL_PIX_PROC_PACK_B8; 36262306a36Sopenharmony_ci break; 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci val = cal_read(ctx->cal, CAL_PIX_PROC(ctx->pix_proc)); 36662306a36Sopenharmony_ci cal_set_field(&val, extract, CAL_PIX_PROC_EXTRACT_MASK); 36762306a36Sopenharmony_ci cal_set_field(&val, CAL_PIX_PROC_DPCMD_BYPASS, CAL_PIX_PROC_DPCMD_MASK); 36862306a36Sopenharmony_ci cal_set_field(&val, CAL_PIX_PROC_DPCME_BYPASS, CAL_PIX_PROC_DPCME_MASK); 36962306a36Sopenharmony_ci cal_set_field(&val, pack, CAL_PIX_PROC_PACK_MASK); 37062306a36Sopenharmony_ci cal_set_field(&val, ctx->cport, CAL_PIX_PROC_CPORT_MASK); 37162306a36Sopenharmony_ci cal_set_field(&val, 1, CAL_PIX_PROC_EN_MASK); 37262306a36Sopenharmony_ci cal_write(ctx->cal, CAL_PIX_PROC(ctx->pix_proc), val); 37362306a36Sopenharmony_ci ctx_dbg(3, ctx, "CAL_PIX_PROC(%u) = 0x%08x\n", ctx->pix_proc, 37462306a36Sopenharmony_ci cal_read(ctx->cal, CAL_PIX_PROC(ctx->pix_proc))); 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic void cal_ctx_wr_dma_config(struct cal_ctx *ctx) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci unsigned int stride = ctx->v_fmt.fmt.pix.bytesperline; 38062306a36Sopenharmony_ci u32 val; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx)); 38362306a36Sopenharmony_ci cal_set_field(&val, ctx->cport, CAL_WR_DMA_CTRL_CPORT_MASK); 38462306a36Sopenharmony_ci cal_set_field(&val, ctx->v_fmt.fmt.pix.height, 38562306a36Sopenharmony_ci CAL_WR_DMA_CTRL_YSIZE_MASK); 38662306a36Sopenharmony_ci cal_set_field(&val, CAL_WR_DMA_CTRL_DTAG_PIX_DAT, 38762306a36Sopenharmony_ci CAL_WR_DMA_CTRL_DTAG_MASK); 38862306a36Sopenharmony_ci cal_set_field(&val, CAL_WR_DMA_CTRL_PATTERN_LINEAR, 38962306a36Sopenharmony_ci CAL_WR_DMA_CTRL_PATTERN_MASK); 39062306a36Sopenharmony_ci cal_set_field(&val, 1, CAL_WR_DMA_CTRL_STALL_RD_MASK); 39162306a36Sopenharmony_ci cal_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx), val); 39262306a36Sopenharmony_ci ctx_dbg(3, ctx, "CAL_WR_DMA_CTRL(%d) = 0x%08x\n", ctx->dma_ctx, 39362306a36Sopenharmony_ci cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx))); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci cal_write_field(ctx->cal, CAL_WR_DMA_OFST(ctx->dma_ctx), 39662306a36Sopenharmony_ci stride / 16, CAL_WR_DMA_OFST_MASK); 39762306a36Sopenharmony_ci ctx_dbg(3, ctx, "CAL_WR_DMA_OFST(%d) = 0x%08x\n", ctx->dma_ctx, 39862306a36Sopenharmony_ci cal_read(ctx->cal, CAL_WR_DMA_OFST(ctx->dma_ctx))); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci val = cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->dma_ctx)); 40162306a36Sopenharmony_ci /* 64 bit word means no skipping */ 40262306a36Sopenharmony_ci cal_set_field(&val, 0, CAL_WR_DMA_XSIZE_XSKIP_MASK); 40362306a36Sopenharmony_ci /* 40462306a36Sopenharmony_ci * The XSIZE field is expressed in 64-bit units and prevents overflows 40562306a36Sopenharmony_ci * in case of synchronization issues by limiting the number of bytes 40662306a36Sopenharmony_ci * written per line. 40762306a36Sopenharmony_ci */ 40862306a36Sopenharmony_ci cal_set_field(&val, stride / 8, CAL_WR_DMA_XSIZE_MASK); 40962306a36Sopenharmony_ci cal_write(ctx->cal, CAL_WR_DMA_XSIZE(ctx->dma_ctx), val); 41062306a36Sopenharmony_ci ctx_dbg(3, ctx, "CAL_WR_DMA_XSIZE(%d) = 0x%08x\n", ctx->dma_ctx, 41162306a36Sopenharmony_ci cal_read(ctx->cal, CAL_WR_DMA_XSIZE(ctx->dma_ctx))); 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_civoid cal_ctx_set_dma_addr(struct cal_ctx *ctx, dma_addr_t addr) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci cal_write(ctx->cal, CAL_WR_DMA_ADDR(ctx->dma_ctx), addr); 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic void cal_ctx_wr_dma_enable(struct cal_ctx *ctx) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci u32 val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx)); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci cal_set_field(&val, CAL_WR_DMA_CTRL_MODE_CONST, 42462306a36Sopenharmony_ci CAL_WR_DMA_CTRL_MODE_MASK); 42562306a36Sopenharmony_ci cal_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx), val); 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic void cal_ctx_wr_dma_disable(struct cal_ctx *ctx) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci u32 val = cal_read(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx)); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci cal_set_field(&val, CAL_WR_DMA_CTRL_MODE_DIS, 43362306a36Sopenharmony_ci CAL_WR_DMA_CTRL_MODE_MASK); 43462306a36Sopenharmony_ci cal_write(ctx->cal, CAL_WR_DMA_CTRL(ctx->dma_ctx), val); 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic bool cal_ctx_wr_dma_stopped(struct cal_ctx *ctx) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci bool stopped; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci spin_lock_irq(&ctx->dma.lock); 44262306a36Sopenharmony_ci stopped = ctx->dma.state == CAL_DMA_STOPPED; 44362306a36Sopenharmony_ci spin_unlock_irq(&ctx->dma.lock); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci return stopped; 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic int 44962306a36Sopenharmony_cical_get_remote_frame_desc_entry(struct cal_ctx *ctx, 45062306a36Sopenharmony_ci struct v4l2_mbus_frame_desc_entry *entry) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci struct v4l2_mbus_frame_desc fd; 45362306a36Sopenharmony_ci struct media_pad *phy_source_pad; 45462306a36Sopenharmony_ci int ret; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci phy_source_pad = media_pad_remote_pad_first(&ctx->pad); 45762306a36Sopenharmony_ci if (!phy_source_pad) 45862306a36Sopenharmony_ci return -ENODEV; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci ret = v4l2_subdev_call(&ctx->phy->subdev, pad, get_frame_desc, 46162306a36Sopenharmony_ci phy_source_pad->index, &fd); 46262306a36Sopenharmony_ci if (ret) 46362306a36Sopenharmony_ci return ret; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (fd.num_entries != 1) 46662306a36Sopenharmony_ci return -EINVAL; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci *entry = fd.entry[0]; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci return 0; 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ciint cal_ctx_prepare(struct cal_ctx *ctx) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci struct v4l2_mbus_frame_desc_entry entry; 47662306a36Sopenharmony_ci int ret; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci ret = cal_get_remote_frame_desc_entry(ctx, &entry); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci if (ret == -ENOIOCTLCMD) { 48162306a36Sopenharmony_ci ctx->vc = 0; 48262306a36Sopenharmony_ci ctx->datatype = CAL_CSI2_CTX_DT_ANY; 48362306a36Sopenharmony_ci } else if (!ret) { 48462306a36Sopenharmony_ci ctx_dbg(2, ctx, "Framedesc: len %u, vc %u, dt %#x\n", 48562306a36Sopenharmony_ci entry.length, entry.bus.csi2.vc, entry.bus.csi2.dt); 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci ctx->vc = entry.bus.csi2.vc; 48862306a36Sopenharmony_ci ctx->datatype = entry.bus.csi2.dt; 48962306a36Sopenharmony_ci } else { 49062306a36Sopenharmony_ci return ret; 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci ctx->use_pix_proc = !ctx->fmtinfo->meta; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci if (ctx->use_pix_proc) { 49662306a36Sopenharmony_ci ret = cal_reserve_pix_proc(ctx->cal); 49762306a36Sopenharmony_ci if (ret < 0) { 49862306a36Sopenharmony_ci ctx_err(ctx, "Failed to reserve pix proc: %d\n", ret); 49962306a36Sopenharmony_ci return ret; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci ctx->pix_proc = ret; 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci return 0; 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_civoid cal_ctx_unprepare(struct cal_ctx *ctx) 50962306a36Sopenharmony_ci{ 51062306a36Sopenharmony_ci if (ctx->use_pix_proc) 51162306a36Sopenharmony_ci cal_release_pix_proc(ctx->cal, ctx->pix_proc); 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_civoid cal_ctx_start(struct cal_ctx *ctx) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci struct cal_camerarx *phy = ctx->phy; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci /* 51962306a36Sopenharmony_ci * Reset the frame number & sequence number, but only if the 52062306a36Sopenharmony_ci * virtual channel is not already in use. 52162306a36Sopenharmony_ci */ 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci spin_lock(&phy->vc_lock); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci if (phy->vc_enable_count[ctx->vc]++ == 0) { 52662306a36Sopenharmony_ci phy->vc_frame_number[ctx->vc] = 0; 52762306a36Sopenharmony_ci phy->vc_sequence[ctx->vc] = 0; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci spin_unlock(&phy->vc_lock); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci ctx->dma.state = CAL_DMA_RUNNING; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci /* Configure the CSI-2, pixel processing and write DMA contexts. */ 53562306a36Sopenharmony_ci cal_ctx_csi2_config(ctx); 53662306a36Sopenharmony_ci if (ctx->use_pix_proc) 53762306a36Sopenharmony_ci cal_ctx_pix_proc_config(ctx); 53862306a36Sopenharmony_ci cal_ctx_wr_dma_config(ctx); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci /* Enable IRQ_WDMA_END and IRQ_WDMA_START. */ 54162306a36Sopenharmony_ci cal_write(ctx->cal, CAL_HL_IRQENABLE_SET(1), 54262306a36Sopenharmony_ci CAL_HL_IRQ_WDMA_END_MASK(ctx->dma_ctx)); 54362306a36Sopenharmony_ci cal_write(ctx->cal, CAL_HL_IRQENABLE_SET(2), 54462306a36Sopenharmony_ci CAL_HL_IRQ_WDMA_START_MASK(ctx->dma_ctx)); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci cal_ctx_wr_dma_enable(ctx); 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_civoid cal_ctx_stop(struct cal_ctx *ctx) 55062306a36Sopenharmony_ci{ 55162306a36Sopenharmony_ci struct cal_camerarx *phy = ctx->phy; 55262306a36Sopenharmony_ci long timeout; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci WARN_ON(phy->vc_enable_count[ctx->vc] == 0); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci spin_lock(&phy->vc_lock); 55762306a36Sopenharmony_ci phy->vc_enable_count[ctx->vc]--; 55862306a36Sopenharmony_ci spin_unlock(&phy->vc_lock); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* 56162306a36Sopenharmony_ci * Request DMA stop and wait until it completes. If completion times 56262306a36Sopenharmony_ci * out, forcefully disable the DMA. 56362306a36Sopenharmony_ci */ 56462306a36Sopenharmony_ci spin_lock_irq(&ctx->dma.lock); 56562306a36Sopenharmony_ci ctx->dma.state = CAL_DMA_STOP_REQUESTED; 56662306a36Sopenharmony_ci spin_unlock_irq(&ctx->dma.lock); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci timeout = wait_event_timeout(ctx->dma.wait, cal_ctx_wr_dma_stopped(ctx), 56962306a36Sopenharmony_ci msecs_to_jiffies(500)); 57062306a36Sopenharmony_ci if (!timeout) { 57162306a36Sopenharmony_ci ctx_err(ctx, "failed to disable dma cleanly\n"); 57262306a36Sopenharmony_ci cal_ctx_wr_dma_disable(ctx); 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci /* Disable IRQ_WDMA_END and IRQ_WDMA_START. */ 57662306a36Sopenharmony_ci cal_write(ctx->cal, CAL_HL_IRQENABLE_CLR(1), 57762306a36Sopenharmony_ci CAL_HL_IRQ_WDMA_END_MASK(ctx->dma_ctx)); 57862306a36Sopenharmony_ci cal_write(ctx->cal, CAL_HL_IRQENABLE_CLR(2), 57962306a36Sopenharmony_ci CAL_HL_IRQ_WDMA_START_MASK(ctx->dma_ctx)); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci ctx->dma.state = CAL_DMA_STOPPED; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci /* Disable CSI2 context */ 58462306a36Sopenharmony_ci cal_write(ctx->cal, CAL_CSI2_CTX(ctx->phy->instance, ctx->csi2_ctx), 0); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci /* Disable pix proc */ 58762306a36Sopenharmony_ci if (ctx->use_pix_proc) 58862306a36Sopenharmony_ci cal_write(ctx->cal, CAL_PIX_PROC(ctx->pix_proc), 0); 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci/* ------------------------------------------------------------------ 59262306a36Sopenharmony_ci * IRQ Handling 59362306a36Sopenharmony_ci * ------------------------------------------------------------------ 59462306a36Sopenharmony_ci */ 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci/* 59762306a36Sopenharmony_ci * Track a sequence number for each virtual channel, which is shared by 59862306a36Sopenharmony_ci * all contexts using the same virtual channel. This is done using the 59962306a36Sopenharmony_ci * CSI-2 frame number as a base. 60062306a36Sopenharmony_ci */ 60162306a36Sopenharmony_cistatic void cal_update_seq_number(struct cal_ctx *ctx) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci struct cal_dev *cal = ctx->cal; 60462306a36Sopenharmony_ci struct cal_camerarx *phy = ctx->phy; 60562306a36Sopenharmony_ci u16 prev_frame_num, frame_num; 60662306a36Sopenharmony_ci u8 vc = ctx->vc; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci frame_num = 60962306a36Sopenharmony_ci cal_read(cal, CAL_CSI2_STATUS(phy->instance, ctx->csi2_ctx)) & 61062306a36Sopenharmony_ci 0xffff; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci if (phy->vc_frame_number[vc] != frame_num) { 61362306a36Sopenharmony_ci prev_frame_num = phy->vc_frame_number[vc]; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci if (prev_frame_num >= frame_num) 61662306a36Sopenharmony_ci phy->vc_sequence[vc] += 1; 61762306a36Sopenharmony_ci else 61862306a36Sopenharmony_ci phy->vc_sequence[vc] += frame_num - prev_frame_num; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci phy->vc_frame_number[vc] = frame_num; 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic inline void cal_irq_wdma_start(struct cal_ctx *ctx) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci spin_lock(&ctx->dma.lock); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci if (ctx->dma.state == CAL_DMA_STOP_REQUESTED) { 62962306a36Sopenharmony_ci /* 63062306a36Sopenharmony_ci * If a stop is requested, disable the write DMA context 63162306a36Sopenharmony_ci * immediately. The CAL_WR_DMA_CTRL_j.MODE field is shadowed, 63262306a36Sopenharmony_ci * the current frame will complete and the DMA will then stop. 63362306a36Sopenharmony_ci */ 63462306a36Sopenharmony_ci cal_ctx_wr_dma_disable(ctx); 63562306a36Sopenharmony_ci ctx->dma.state = CAL_DMA_STOP_PENDING; 63662306a36Sopenharmony_ci } else if (!list_empty(&ctx->dma.queue) && !ctx->dma.pending) { 63762306a36Sopenharmony_ci /* 63862306a36Sopenharmony_ci * Otherwise, if a new buffer is available, queue it to the 63962306a36Sopenharmony_ci * hardware. 64062306a36Sopenharmony_ci */ 64162306a36Sopenharmony_ci struct cal_buffer *buf; 64262306a36Sopenharmony_ci dma_addr_t addr; 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci buf = list_first_entry(&ctx->dma.queue, struct cal_buffer, 64562306a36Sopenharmony_ci list); 64662306a36Sopenharmony_ci addr = vb2_dma_contig_plane_dma_addr(&buf->vb.vb2_buf, 0); 64762306a36Sopenharmony_ci cal_ctx_set_dma_addr(ctx, addr); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci ctx->dma.pending = buf; 65062306a36Sopenharmony_ci list_del(&buf->list); 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci spin_unlock(&ctx->dma.lock); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci cal_update_seq_number(ctx); 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cistatic inline void cal_irq_wdma_end(struct cal_ctx *ctx) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci struct cal_buffer *buf = NULL; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci spin_lock(&ctx->dma.lock); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci /* If the DMA context was stopping, it is now stopped. */ 66562306a36Sopenharmony_ci if (ctx->dma.state == CAL_DMA_STOP_PENDING) { 66662306a36Sopenharmony_ci ctx->dma.state = CAL_DMA_STOPPED; 66762306a36Sopenharmony_ci wake_up(&ctx->dma.wait); 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci /* If a new buffer was queued, complete the current buffer. */ 67162306a36Sopenharmony_ci if (ctx->dma.pending) { 67262306a36Sopenharmony_ci buf = ctx->dma.active; 67362306a36Sopenharmony_ci ctx->dma.active = ctx->dma.pending; 67462306a36Sopenharmony_ci ctx->dma.pending = NULL; 67562306a36Sopenharmony_ci } 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci spin_unlock(&ctx->dma.lock); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci if (buf) { 68062306a36Sopenharmony_ci buf->vb.vb2_buf.timestamp = ktime_get_ns(); 68162306a36Sopenharmony_ci buf->vb.field = ctx->v_fmt.fmt.pix.field; 68262306a36Sopenharmony_ci buf->vb.sequence = ctx->phy->vc_sequence[ctx->vc]; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_DONE); 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic void cal_irq_handle_wdma(struct cal_ctx *ctx, bool start, bool end) 68962306a36Sopenharmony_ci{ 69062306a36Sopenharmony_ci /* 69162306a36Sopenharmony_ci * CAL HW interrupts are inherently racy. If we get both start and end 69262306a36Sopenharmony_ci * interrupts, we don't know what has happened: did the DMA for a single 69362306a36Sopenharmony_ci * frame start and end, or did one frame end and a new frame start? 69462306a36Sopenharmony_ci * 69562306a36Sopenharmony_ci * Usually for normal pixel frames we get the interrupts separately. If 69662306a36Sopenharmony_ci * we do get both, we have to guess. The assumption in the code below is 69762306a36Sopenharmony_ci * that the active vertical area is larger than the blanking vertical 69862306a36Sopenharmony_ci * area, and thus it is more likely that we get the end of the old frame 69962306a36Sopenharmony_ci * and the start of a new frame. 70062306a36Sopenharmony_ci * 70162306a36Sopenharmony_ci * However, for embedded data, which is only a few lines high, we always 70262306a36Sopenharmony_ci * get both interrupts. Here the assumption is that we get both for the 70362306a36Sopenharmony_ci * same frame. 70462306a36Sopenharmony_ci */ 70562306a36Sopenharmony_ci if (ctx->v_fmt.fmt.pix.height < 10) { 70662306a36Sopenharmony_ci if (start) 70762306a36Sopenharmony_ci cal_irq_wdma_start(ctx); 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci if (end) 71062306a36Sopenharmony_ci cal_irq_wdma_end(ctx); 71162306a36Sopenharmony_ci } else { 71262306a36Sopenharmony_ci if (end) 71362306a36Sopenharmony_ci cal_irq_wdma_end(ctx); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci if (start) 71662306a36Sopenharmony_ci cal_irq_wdma_start(ctx); 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci} 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_cistatic irqreturn_t cal_irq(int irq_cal, void *data) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci struct cal_dev *cal = data; 72362306a36Sopenharmony_ci u32 status[3]; 72462306a36Sopenharmony_ci unsigned int i; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci for (i = 0; i < 3; ++i) { 72762306a36Sopenharmony_ci status[i] = cal_read(cal, CAL_HL_IRQSTATUS(i)); 72862306a36Sopenharmony_ci if (status[i]) 72962306a36Sopenharmony_ci cal_write(cal, CAL_HL_IRQSTATUS(i), status[i]); 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci if (status[0]) { 73362306a36Sopenharmony_ci if (status[0] & CAL_HL_IRQ_OCPO_ERR_MASK) 73462306a36Sopenharmony_ci dev_err_ratelimited(cal->dev, "OCPO ERROR\n"); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci for (i = 0; i < cal->data->num_csi2_phy; ++i) { 73762306a36Sopenharmony_ci if (status[0] & CAL_HL_IRQ_CIO_MASK(i)) { 73862306a36Sopenharmony_ci u32 cio_stat = cal_read(cal, 73962306a36Sopenharmony_ci CAL_CSI2_COMPLEXIO_IRQSTATUS(i)); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci dev_err_ratelimited(cal->dev, 74262306a36Sopenharmony_ci "CIO%u error: %#08x\n", i, cio_stat); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci cal_write(cal, CAL_CSI2_COMPLEXIO_IRQSTATUS(i), 74562306a36Sopenharmony_ci cio_stat); 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci if (status[0] & CAL_HL_IRQ_VC_MASK(i)) { 74962306a36Sopenharmony_ci u32 vc_stat = cal_read(cal, CAL_CSI2_VC_IRQSTATUS(i)); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci dev_err_ratelimited(cal->dev, 75262306a36Sopenharmony_ci "CIO%u VC error: %#08x\n", 75362306a36Sopenharmony_ci i, vc_stat); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci cal_write(cal, CAL_CSI2_VC_IRQSTATUS(i), vc_stat); 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci } 75862306a36Sopenharmony_ci } 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci for (i = 0; i < cal->num_contexts; ++i) { 76162306a36Sopenharmony_ci bool end = !!(status[1] & CAL_HL_IRQ_WDMA_END_MASK(i)); 76262306a36Sopenharmony_ci bool start = !!(status[2] & CAL_HL_IRQ_WDMA_START_MASK(i)); 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci if (start || end) 76562306a36Sopenharmony_ci cal_irq_handle_wdma(cal->ctx[i], start, end); 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci return IRQ_HANDLED; 76962306a36Sopenharmony_ci} 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci/* ------------------------------------------------------------------ 77262306a36Sopenharmony_ci * Asynchronous V4L2 subdev binding 77362306a36Sopenharmony_ci * ------------------------------------------------------------------ 77462306a36Sopenharmony_ci */ 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_cistruct cal_v4l2_async_subdev { 77762306a36Sopenharmony_ci struct v4l2_async_connection asd; /* Must be first */ 77862306a36Sopenharmony_ci struct cal_camerarx *phy; 77962306a36Sopenharmony_ci}; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cistatic inline struct cal_v4l2_async_subdev * 78262306a36Sopenharmony_cito_cal_asd(struct v4l2_async_connection *asd) 78362306a36Sopenharmony_ci{ 78462306a36Sopenharmony_ci return container_of(asd, struct cal_v4l2_async_subdev, asd); 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_cistatic int cal_async_notifier_bound(struct v4l2_async_notifier *notifier, 78862306a36Sopenharmony_ci struct v4l2_subdev *subdev, 78962306a36Sopenharmony_ci struct v4l2_async_connection *asd) 79062306a36Sopenharmony_ci{ 79162306a36Sopenharmony_ci struct cal_camerarx *phy = to_cal_asd(asd)->phy; 79262306a36Sopenharmony_ci int pad; 79362306a36Sopenharmony_ci int ret; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci if (phy->source) { 79662306a36Sopenharmony_ci phy_info(phy, "Rejecting subdev %s (Already set!!)", 79762306a36Sopenharmony_ci subdev->name); 79862306a36Sopenharmony_ci return 0; 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci phy->source = subdev; 80262306a36Sopenharmony_ci phy_dbg(1, phy, "Using source %s for capture\n", subdev->name); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci pad = media_entity_get_fwnode_pad(&subdev->entity, 80562306a36Sopenharmony_ci of_fwnode_handle(phy->source_ep_node), 80662306a36Sopenharmony_ci MEDIA_PAD_FL_SOURCE); 80762306a36Sopenharmony_ci if (pad < 0) { 80862306a36Sopenharmony_ci phy_err(phy, "Source %s has no connected source pad\n", 80962306a36Sopenharmony_ci subdev->name); 81062306a36Sopenharmony_ci return pad; 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci ret = media_create_pad_link(&subdev->entity, pad, 81462306a36Sopenharmony_ci &phy->subdev.entity, CAL_CAMERARX_PAD_SINK, 81562306a36Sopenharmony_ci MEDIA_LNK_FL_IMMUTABLE | 81662306a36Sopenharmony_ci MEDIA_LNK_FL_ENABLED); 81762306a36Sopenharmony_ci if (ret) { 81862306a36Sopenharmony_ci phy_err(phy, "Failed to create media link for source %s\n", 81962306a36Sopenharmony_ci subdev->name); 82062306a36Sopenharmony_ci return ret; 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci return 0; 82462306a36Sopenharmony_ci} 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_cistatic int cal_async_notifier_complete(struct v4l2_async_notifier *notifier) 82762306a36Sopenharmony_ci{ 82862306a36Sopenharmony_ci struct cal_dev *cal = container_of(notifier, struct cal_dev, notifier); 82962306a36Sopenharmony_ci unsigned int i; 83062306a36Sopenharmony_ci int ret; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci for (i = 0; i < cal->num_contexts; ++i) { 83362306a36Sopenharmony_ci ret = cal_ctx_v4l2_register(cal->ctx[i]); 83462306a36Sopenharmony_ci if (ret) 83562306a36Sopenharmony_ci goto err_ctx_unreg; 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci if (!cal_mc_api) 83962306a36Sopenharmony_ci return 0; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci ret = v4l2_device_register_subdev_nodes(&cal->v4l2_dev); 84262306a36Sopenharmony_ci if (ret) 84362306a36Sopenharmony_ci goto err_ctx_unreg; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci return 0; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_cierr_ctx_unreg: 84862306a36Sopenharmony_ci for (; i > 0; --i) { 84962306a36Sopenharmony_ci if (!cal->ctx[i - 1]) 85062306a36Sopenharmony_ci continue; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci cal_ctx_v4l2_unregister(cal->ctx[i - 1]); 85362306a36Sopenharmony_ci } 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci return ret; 85662306a36Sopenharmony_ci} 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_cistatic const struct v4l2_async_notifier_operations cal_async_notifier_ops = { 85962306a36Sopenharmony_ci .bound = cal_async_notifier_bound, 86062306a36Sopenharmony_ci .complete = cal_async_notifier_complete, 86162306a36Sopenharmony_ci}; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_cistatic int cal_async_notifier_register(struct cal_dev *cal) 86462306a36Sopenharmony_ci{ 86562306a36Sopenharmony_ci unsigned int i; 86662306a36Sopenharmony_ci int ret; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci v4l2_async_nf_init(&cal->notifier, &cal->v4l2_dev); 86962306a36Sopenharmony_ci cal->notifier.ops = &cal_async_notifier_ops; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci for (i = 0; i < cal->data->num_csi2_phy; ++i) { 87262306a36Sopenharmony_ci struct cal_camerarx *phy = cal->phy[i]; 87362306a36Sopenharmony_ci struct cal_v4l2_async_subdev *casd; 87462306a36Sopenharmony_ci struct fwnode_handle *fwnode; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci if (!phy->source_node) 87762306a36Sopenharmony_ci continue; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci fwnode = of_fwnode_handle(phy->source_node); 88062306a36Sopenharmony_ci casd = v4l2_async_nf_add_fwnode(&cal->notifier, 88162306a36Sopenharmony_ci fwnode, 88262306a36Sopenharmony_ci struct cal_v4l2_async_subdev); 88362306a36Sopenharmony_ci if (IS_ERR(casd)) { 88462306a36Sopenharmony_ci phy_err(phy, "Failed to add subdev to notifier\n"); 88562306a36Sopenharmony_ci ret = PTR_ERR(casd); 88662306a36Sopenharmony_ci goto error; 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci casd->phy = phy; 89062306a36Sopenharmony_ci } 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci ret = v4l2_async_nf_register(&cal->notifier); 89362306a36Sopenharmony_ci if (ret) { 89462306a36Sopenharmony_ci cal_err(cal, "Error registering async notifier\n"); 89562306a36Sopenharmony_ci goto error; 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci return 0; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_cierror: 90162306a36Sopenharmony_ci v4l2_async_nf_cleanup(&cal->notifier); 90262306a36Sopenharmony_ci return ret; 90362306a36Sopenharmony_ci} 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_cistatic void cal_async_notifier_unregister(struct cal_dev *cal) 90662306a36Sopenharmony_ci{ 90762306a36Sopenharmony_ci v4l2_async_nf_unregister(&cal->notifier); 90862306a36Sopenharmony_ci v4l2_async_nf_cleanup(&cal->notifier); 90962306a36Sopenharmony_ci} 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci/* ------------------------------------------------------------------ 91262306a36Sopenharmony_ci * Media and V4L2 device handling 91362306a36Sopenharmony_ci * ------------------------------------------------------------------ 91462306a36Sopenharmony_ci */ 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci/* 91762306a36Sopenharmony_ci * Register user-facing devices. To be called at the end of the probe function 91862306a36Sopenharmony_ci * when all resources are initialized and ready. 91962306a36Sopenharmony_ci */ 92062306a36Sopenharmony_cistatic int cal_media_register(struct cal_dev *cal) 92162306a36Sopenharmony_ci{ 92262306a36Sopenharmony_ci int ret; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci ret = media_device_register(&cal->mdev); 92562306a36Sopenharmony_ci if (ret) { 92662306a36Sopenharmony_ci cal_err(cal, "Failed to register media device\n"); 92762306a36Sopenharmony_ci return ret; 92862306a36Sopenharmony_ci } 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci /* 93162306a36Sopenharmony_ci * Register the async notifier. This may trigger registration of the 93262306a36Sopenharmony_ci * V4L2 video devices if all subdevs are ready. 93362306a36Sopenharmony_ci */ 93462306a36Sopenharmony_ci ret = cal_async_notifier_register(cal); 93562306a36Sopenharmony_ci if (ret) { 93662306a36Sopenharmony_ci media_device_unregister(&cal->mdev); 93762306a36Sopenharmony_ci return ret; 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci return 0; 94162306a36Sopenharmony_ci} 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci/* 94462306a36Sopenharmony_ci * Unregister the user-facing devices, but don't free memory yet. To be called 94562306a36Sopenharmony_ci * at the beginning of the remove function, to disallow access from userspace. 94662306a36Sopenharmony_ci */ 94762306a36Sopenharmony_cistatic void cal_media_unregister(struct cal_dev *cal) 94862306a36Sopenharmony_ci{ 94962306a36Sopenharmony_ci unsigned int i; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci /* Unregister all the V4L2 video devices. */ 95262306a36Sopenharmony_ci for (i = 0; i < cal->num_contexts; i++) 95362306a36Sopenharmony_ci cal_ctx_v4l2_unregister(cal->ctx[i]); 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci cal_async_notifier_unregister(cal); 95662306a36Sopenharmony_ci media_device_unregister(&cal->mdev); 95762306a36Sopenharmony_ci} 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci/* 96062306a36Sopenharmony_ci * Initialize the in-kernel objects. To be called at the beginning of the probe 96162306a36Sopenharmony_ci * function, before the V4L2 device is used by the driver. 96262306a36Sopenharmony_ci */ 96362306a36Sopenharmony_cistatic int cal_media_init(struct cal_dev *cal) 96462306a36Sopenharmony_ci{ 96562306a36Sopenharmony_ci struct media_device *mdev = &cal->mdev; 96662306a36Sopenharmony_ci int ret; 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci mdev->dev = cal->dev; 96962306a36Sopenharmony_ci mdev->hw_revision = cal->revision; 97062306a36Sopenharmony_ci strscpy(mdev->model, "CAL", sizeof(mdev->model)); 97162306a36Sopenharmony_ci media_device_init(mdev); 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci /* 97462306a36Sopenharmony_ci * Initialize the V4L2 device (despite the function name, this performs 97562306a36Sopenharmony_ci * initialization, not registration). 97662306a36Sopenharmony_ci */ 97762306a36Sopenharmony_ci cal->v4l2_dev.mdev = mdev; 97862306a36Sopenharmony_ci ret = v4l2_device_register(cal->dev, &cal->v4l2_dev); 97962306a36Sopenharmony_ci if (ret) { 98062306a36Sopenharmony_ci cal_err(cal, "Failed to register V4L2 device\n"); 98162306a36Sopenharmony_ci return ret; 98262306a36Sopenharmony_ci } 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci vb2_dma_contig_set_max_seg_size(cal->dev, DMA_BIT_MASK(32)); 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci return 0; 98762306a36Sopenharmony_ci} 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci/* 99062306a36Sopenharmony_ci * Cleanup the in-kernel objects, freeing memory. To be called at the very end 99162306a36Sopenharmony_ci * of the remove sequence, when nothing (including userspace) can access the 99262306a36Sopenharmony_ci * objects anymore. 99362306a36Sopenharmony_ci */ 99462306a36Sopenharmony_cistatic void cal_media_cleanup(struct cal_dev *cal) 99562306a36Sopenharmony_ci{ 99662306a36Sopenharmony_ci v4l2_device_unregister(&cal->v4l2_dev); 99762306a36Sopenharmony_ci media_device_cleanup(&cal->mdev); 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci vb2_dma_contig_clear_max_seg_size(cal->dev); 100062306a36Sopenharmony_ci} 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci/* ------------------------------------------------------------------ 100362306a36Sopenharmony_ci * Initialization and module stuff 100462306a36Sopenharmony_ci * ------------------------------------------------------------------ 100562306a36Sopenharmony_ci */ 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_cistatic struct cal_ctx *cal_ctx_create(struct cal_dev *cal, int inst) 100862306a36Sopenharmony_ci{ 100962306a36Sopenharmony_ci struct cal_ctx *ctx; 101062306a36Sopenharmony_ci int ret; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 101362306a36Sopenharmony_ci if (!ctx) 101462306a36Sopenharmony_ci return NULL; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci ctx->cal = cal; 101762306a36Sopenharmony_ci ctx->phy = cal->phy[inst]; 101862306a36Sopenharmony_ci ctx->dma_ctx = inst; 101962306a36Sopenharmony_ci ctx->csi2_ctx = inst; 102062306a36Sopenharmony_ci ctx->cport = inst; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci ret = cal_ctx_v4l2_init(ctx); 102362306a36Sopenharmony_ci if (ret) { 102462306a36Sopenharmony_ci kfree(ctx); 102562306a36Sopenharmony_ci return NULL; 102662306a36Sopenharmony_ci } 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci return ctx; 102962306a36Sopenharmony_ci} 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_cistatic void cal_ctx_destroy(struct cal_ctx *ctx) 103262306a36Sopenharmony_ci{ 103362306a36Sopenharmony_ci cal_ctx_v4l2_cleanup(ctx); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci kfree(ctx); 103662306a36Sopenharmony_ci} 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_cistatic const struct of_device_id cal_of_match[] = { 103962306a36Sopenharmony_ci { 104062306a36Sopenharmony_ci .compatible = "ti,dra72-cal", 104162306a36Sopenharmony_ci .data = (void *)&dra72x_cal_data, 104262306a36Sopenharmony_ci }, 104362306a36Sopenharmony_ci { 104462306a36Sopenharmony_ci .compatible = "ti,dra72-pre-es2-cal", 104562306a36Sopenharmony_ci .data = (void *)&dra72x_es1_cal_data, 104662306a36Sopenharmony_ci }, 104762306a36Sopenharmony_ci { 104862306a36Sopenharmony_ci .compatible = "ti,dra76-cal", 104962306a36Sopenharmony_ci .data = (void *)&dra76x_cal_data, 105062306a36Sopenharmony_ci }, 105162306a36Sopenharmony_ci { 105262306a36Sopenharmony_ci .compatible = "ti,am654-cal", 105362306a36Sopenharmony_ci .data = (void *)&am654_cal_data, 105462306a36Sopenharmony_ci }, 105562306a36Sopenharmony_ci {}, 105662306a36Sopenharmony_ci}; 105762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, cal_of_match); 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci/* Get hardware revision and info. */ 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci#define CAL_HL_HWINFO_VALUE 0xa3c90469 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_cistatic void cal_get_hwinfo(struct cal_dev *cal) 106462306a36Sopenharmony_ci{ 106562306a36Sopenharmony_ci u32 hwinfo; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci cal->revision = cal_read(cal, CAL_HL_REVISION); 106862306a36Sopenharmony_ci switch (FIELD_GET(CAL_HL_REVISION_SCHEME_MASK, cal->revision)) { 106962306a36Sopenharmony_ci case CAL_HL_REVISION_SCHEME_H08: 107062306a36Sopenharmony_ci cal_dbg(3, cal, "CAL HW revision %lu.%lu.%lu (0x%08x)\n", 107162306a36Sopenharmony_ci FIELD_GET(CAL_HL_REVISION_MAJOR_MASK, cal->revision), 107262306a36Sopenharmony_ci FIELD_GET(CAL_HL_REVISION_MINOR_MASK, cal->revision), 107362306a36Sopenharmony_ci FIELD_GET(CAL_HL_REVISION_RTL_MASK, cal->revision), 107462306a36Sopenharmony_ci cal->revision); 107562306a36Sopenharmony_ci break; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci case CAL_HL_REVISION_SCHEME_LEGACY: 107862306a36Sopenharmony_ci default: 107962306a36Sopenharmony_ci cal_info(cal, "Unexpected CAL HW revision 0x%08x\n", 108062306a36Sopenharmony_ci cal->revision); 108162306a36Sopenharmony_ci break; 108262306a36Sopenharmony_ci } 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci hwinfo = cal_read(cal, CAL_HL_HWINFO); 108562306a36Sopenharmony_ci if (hwinfo != CAL_HL_HWINFO_VALUE) 108662306a36Sopenharmony_ci cal_info(cal, "CAL_HL_HWINFO = 0x%08x, expected 0x%08x\n", 108762306a36Sopenharmony_ci hwinfo, CAL_HL_HWINFO_VALUE); 108862306a36Sopenharmony_ci} 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_cistatic int cal_init_camerarx_regmap(struct cal_dev *cal) 109162306a36Sopenharmony_ci{ 109262306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(cal->dev); 109362306a36Sopenharmony_ci struct device_node *np = cal->dev->of_node; 109462306a36Sopenharmony_ci struct regmap_config config = { }; 109562306a36Sopenharmony_ci struct regmap *syscon; 109662306a36Sopenharmony_ci struct resource *res; 109762306a36Sopenharmony_ci unsigned int offset; 109862306a36Sopenharmony_ci void __iomem *base; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci syscon = syscon_regmap_lookup_by_phandle_args(np, "ti,camerrx-control", 110162306a36Sopenharmony_ci 1, &offset); 110262306a36Sopenharmony_ci if (!IS_ERR(syscon)) { 110362306a36Sopenharmony_ci cal->syscon_camerrx = syscon; 110462306a36Sopenharmony_ci cal->syscon_camerrx_offset = offset; 110562306a36Sopenharmony_ci return 0; 110662306a36Sopenharmony_ci } 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci dev_warn(cal->dev, "failed to get ti,camerrx-control: %ld\n", 110962306a36Sopenharmony_ci PTR_ERR(syscon)); 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci /* 111262306a36Sopenharmony_ci * Backward DTS compatibility. If syscon entry is not present then 111362306a36Sopenharmony_ci * check if the camerrx_control resource is present. 111462306a36Sopenharmony_ci */ 111562306a36Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 111662306a36Sopenharmony_ci "camerrx_control"); 111762306a36Sopenharmony_ci base = devm_ioremap_resource(cal->dev, res); 111862306a36Sopenharmony_ci if (IS_ERR(base)) { 111962306a36Sopenharmony_ci cal_err(cal, "failed to ioremap camerrx_control\n"); 112062306a36Sopenharmony_ci return PTR_ERR(base); 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", 112462306a36Sopenharmony_ci res->name, &res->start, &res->end); 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci config.reg_bits = 32; 112762306a36Sopenharmony_ci config.reg_stride = 4; 112862306a36Sopenharmony_ci config.val_bits = 32; 112962306a36Sopenharmony_ci config.max_register = resource_size(res) - 4; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci syscon = regmap_init_mmio(NULL, base, &config); 113262306a36Sopenharmony_ci if (IS_ERR(syscon)) { 113362306a36Sopenharmony_ci pr_err("regmap init failed\n"); 113462306a36Sopenharmony_ci return PTR_ERR(syscon); 113562306a36Sopenharmony_ci } 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci /* 113862306a36Sopenharmony_ci * In this case the base already point to the direct CM register so no 113962306a36Sopenharmony_ci * need for an offset. 114062306a36Sopenharmony_ci */ 114162306a36Sopenharmony_ci cal->syscon_camerrx = syscon; 114262306a36Sopenharmony_ci cal->syscon_camerrx_offset = 0; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci return 0; 114562306a36Sopenharmony_ci} 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_cistatic int cal_probe(struct platform_device *pdev) 114862306a36Sopenharmony_ci{ 114962306a36Sopenharmony_ci struct cal_dev *cal; 115062306a36Sopenharmony_ci bool connected = false; 115162306a36Sopenharmony_ci unsigned int i; 115262306a36Sopenharmony_ci int ret; 115362306a36Sopenharmony_ci int irq; 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci cal = devm_kzalloc(&pdev->dev, sizeof(*cal), GFP_KERNEL); 115662306a36Sopenharmony_ci if (!cal) 115762306a36Sopenharmony_ci return -ENOMEM; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci cal->data = of_device_get_match_data(&pdev->dev); 116062306a36Sopenharmony_ci if (!cal->data) { 116162306a36Sopenharmony_ci dev_err(&pdev->dev, "Could not get feature data based on compatible version\n"); 116262306a36Sopenharmony_ci return -ENODEV; 116362306a36Sopenharmony_ci } 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci cal->dev = &pdev->dev; 116662306a36Sopenharmony_ci platform_set_drvdata(pdev, cal); 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci /* Acquire resources: clocks, CAMERARX regmap, I/O memory and IRQ. */ 116962306a36Sopenharmony_ci cal->fclk = devm_clk_get(&pdev->dev, "fck"); 117062306a36Sopenharmony_ci if (IS_ERR(cal->fclk)) { 117162306a36Sopenharmony_ci dev_err(&pdev->dev, "cannot get CAL fclk\n"); 117262306a36Sopenharmony_ci return PTR_ERR(cal->fclk); 117362306a36Sopenharmony_ci } 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci ret = cal_init_camerarx_regmap(cal); 117662306a36Sopenharmony_ci if (ret < 0) 117762306a36Sopenharmony_ci return ret; 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci cal->res = platform_get_resource_byname(pdev, IORESOURCE_MEM, 118062306a36Sopenharmony_ci "cal_top"); 118162306a36Sopenharmony_ci cal->base = devm_ioremap_resource(&pdev->dev, cal->res); 118262306a36Sopenharmony_ci if (IS_ERR(cal->base)) 118362306a36Sopenharmony_ci return PTR_ERR(cal->base); 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci cal_dbg(1, cal, "ioresource %s at %pa - %pa\n", 118662306a36Sopenharmony_ci cal->res->name, &cal->res->start, &cal->res->end); 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 118962306a36Sopenharmony_ci cal_dbg(1, cal, "got irq# %d\n", irq); 119062306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, irq, cal_irq, 0, CAL_MODULE_NAME, 119162306a36Sopenharmony_ci cal); 119262306a36Sopenharmony_ci if (ret) 119362306a36Sopenharmony_ci return ret; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci /* Read the revision and hardware info to verify hardware access. */ 119662306a36Sopenharmony_ci pm_runtime_enable(&pdev->dev); 119762306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(&pdev->dev); 119862306a36Sopenharmony_ci if (ret) 119962306a36Sopenharmony_ci goto error_pm_runtime; 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci cal_get_hwinfo(cal); 120262306a36Sopenharmony_ci pm_runtime_put_sync(&pdev->dev); 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci /* Initialize the media device. */ 120562306a36Sopenharmony_ci ret = cal_media_init(cal); 120662306a36Sopenharmony_ci if (ret < 0) 120762306a36Sopenharmony_ci goto error_pm_runtime; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci /* Create CAMERARX PHYs. */ 121062306a36Sopenharmony_ci for (i = 0; i < cal->data->num_csi2_phy; ++i) { 121162306a36Sopenharmony_ci cal->phy[i] = cal_camerarx_create(cal, i); 121262306a36Sopenharmony_ci if (IS_ERR(cal->phy[i])) { 121362306a36Sopenharmony_ci ret = PTR_ERR(cal->phy[i]); 121462306a36Sopenharmony_ci cal->phy[i] = NULL; 121562306a36Sopenharmony_ci goto error_camerarx; 121662306a36Sopenharmony_ci } 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci if (cal->phy[i]->source_node) 121962306a36Sopenharmony_ci connected = true; 122062306a36Sopenharmony_ci } 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci if (!connected) { 122362306a36Sopenharmony_ci cal_err(cal, "Neither port is configured, no point in staying up\n"); 122462306a36Sopenharmony_ci ret = -ENODEV; 122562306a36Sopenharmony_ci goto error_camerarx; 122662306a36Sopenharmony_ci } 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci /* Create contexts. */ 122962306a36Sopenharmony_ci for (i = 0; i < cal->data->num_csi2_phy; ++i) { 123062306a36Sopenharmony_ci if (!cal->phy[i]->source_node) 123162306a36Sopenharmony_ci continue; 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci cal->ctx[cal->num_contexts] = cal_ctx_create(cal, i); 123462306a36Sopenharmony_ci if (!cal->ctx[cal->num_contexts]) { 123562306a36Sopenharmony_ci cal_err(cal, "Failed to create context %u\n", cal->num_contexts); 123662306a36Sopenharmony_ci ret = -ENODEV; 123762306a36Sopenharmony_ci goto error_context; 123862306a36Sopenharmony_ci } 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci cal->num_contexts++; 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci /* Register the media device. */ 124462306a36Sopenharmony_ci ret = cal_media_register(cal); 124562306a36Sopenharmony_ci if (ret) 124662306a36Sopenharmony_ci goto error_context; 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci return 0; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_cierror_context: 125162306a36Sopenharmony_ci for (i = 0; i < cal->num_contexts; i++) 125262306a36Sopenharmony_ci cal_ctx_destroy(cal->ctx[i]); 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_cierror_camerarx: 125562306a36Sopenharmony_ci for (i = 0; i < cal->data->num_csi2_phy; i++) 125662306a36Sopenharmony_ci cal_camerarx_destroy(cal->phy[i]); 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci cal_media_cleanup(cal); 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_cierror_pm_runtime: 126162306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 126262306a36Sopenharmony_ci 126362306a36Sopenharmony_ci return ret; 126462306a36Sopenharmony_ci} 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_cistatic void cal_remove(struct platform_device *pdev) 126762306a36Sopenharmony_ci{ 126862306a36Sopenharmony_ci struct cal_dev *cal = platform_get_drvdata(pdev); 126962306a36Sopenharmony_ci unsigned int i; 127062306a36Sopenharmony_ci int ret; 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci cal_dbg(1, cal, "Removing %s\n", CAL_MODULE_NAME); 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(&pdev->dev); 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci cal_media_unregister(cal); 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci for (i = 0; i < cal->data->num_csi2_phy; i++) 127962306a36Sopenharmony_ci cal_camerarx_disable(cal->phy[i]); 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci for (i = 0; i < cal->num_contexts; i++) 128262306a36Sopenharmony_ci cal_ctx_destroy(cal->ctx[i]); 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci for (i = 0; i < cal->data->num_csi2_phy; i++) 128562306a36Sopenharmony_ci cal_camerarx_destroy(cal->phy[i]); 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci cal_media_cleanup(cal); 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci if (ret >= 0) 129062306a36Sopenharmony_ci pm_runtime_put_sync(&pdev->dev); 129162306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 129262306a36Sopenharmony_ci} 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_cistatic int cal_runtime_resume(struct device *dev) 129562306a36Sopenharmony_ci{ 129662306a36Sopenharmony_ci struct cal_dev *cal = dev_get_drvdata(dev); 129762306a36Sopenharmony_ci unsigned int i; 129862306a36Sopenharmony_ci u32 val; 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci if (cal->data->flags & DRA72_CAL_PRE_ES2_LDO_DISABLE) { 130162306a36Sopenharmony_ci /* 130262306a36Sopenharmony_ci * Apply errata on both port everytime we (re-)enable 130362306a36Sopenharmony_ci * the clock 130462306a36Sopenharmony_ci */ 130562306a36Sopenharmony_ci for (i = 0; i < cal->data->num_csi2_phy; i++) 130662306a36Sopenharmony_ci cal_camerarx_i913_errata(cal->phy[i]); 130762306a36Sopenharmony_ci } 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci /* 131062306a36Sopenharmony_ci * Enable global interrupts that are not related to a particular 131162306a36Sopenharmony_ci * CAMERARAX or context. 131262306a36Sopenharmony_ci */ 131362306a36Sopenharmony_ci cal_write(cal, CAL_HL_IRQENABLE_SET(0), CAL_HL_IRQ_OCPO_ERR_MASK); 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci val = cal_read(cal, CAL_CTRL); 131662306a36Sopenharmony_ci cal_set_field(&val, CAL_CTRL_BURSTSIZE_BURST128, 131762306a36Sopenharmony_ci CAL_CTRL_BURSTSIZE_MASK); 131862306a36Sopenharmony_ci cal_set_field(&val, 0xf, CAL_CTRL_TAGCNT_MASK); 131962306a36Sopenharmony_ci cal_set_field(&val, CAL_CTRL_POSTED_WRITES_NONPOSTED, 132062306a36Sopenharmony_ci CAL_CTRL_POSTED_WRITES_MASK); 132162306a36Sopenharmony_ci cal_set_field(&val, 0xff, CAL_CTRL_MFLAGL_MASK); 132262306a36Sopenharmony_ci cal_set_field(&val, 0xff, CAL_CTRL_MFLAGH_MASK); 132362306a36Sopenharmony_ci cal_write(cal, CAL_CTRL, val); 132462306a36Sopenharmony_ci cal_dbg(3, cal, "CAL_CTRL = 0x%08x\n", cal_read(cal, CAL_CTRL)); 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci return 0; 132762306a36Sopenharmony_ci} 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_cistatic const struct dev_pm_ops cal_pm_ops = { 133062306a36Sopenharmony_ci .runtime_resume = cal_runtime_resume, 133162306a36Sopenharmony_ci}; 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_cistatic struct platform_driver cal_pdrv = { 133462306a36Sopenharmony_ci .probe = cal_probe, 133562306a36Sopenharmony_ci .remove_new = cal_remove, 133662306a36Sopenharmony_ci .driver = { 133762306a36Sopenharmony_ci .name = CAL_MODULE_NAME, 133862306a36Sopenharmony_ci .pm = &cal_pm_ops, 133962306a36Sopenharmony_ci .of_match_table = cal_of_match, 134062306a36Sopenharmony_ci }, 134162306a36Sopenharmony_ci}; 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_cimodule_platform_driver(cal_pdrv); 1344