162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ispcsi2.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * TI OMAP3 ISP - CSI2 module 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2010 Nokia Corporation 862306a36Sopenharmony_ci * Copyright (C) 2009 Texas Instruments, Inc. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Contacts: Laurent Pinchart <laurent.pinchart@ideasonboard.com> 1162306a36Sopenharmony_ci * Sakari Ailus <sakari.ailus@iki.fi> 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <media/v4l2-common.h> 1562306a36Sopenharmony_ci#include <linux/v4l2-mediabus.h> 1662306a36Sopenharmony_ci#include <linux/mm.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "isp.h" 1962306a36Sopenharmony_ci#include "ispreg.h" 2062306a36Sopenharmony_ci#include "ispcsi2.h" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* 2362306a36Sopenharmony_ci * csi2_if_enable - Enable CSI2 Receiver interface. 2462306a36Sopenharmony_ci * @enable: enable flag 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_cistatic void csi2_if_enable(struct isp_device *isp, 2862306a36Sopenharmony_ci struct isp_csi2_device *csi2, u8 enable) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci struct isp_csi2_ctrl_cfg *currctrl = &csi2->ctrl; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci isp_reg_clr_set(isp, csi2->regs1, ISPCSI2_CTRL, ISPCSI2_CTRL_IF_EN, 3362306a36Sopenharmony_ci enable ? ISPCSI2_CTRL_IF_EN : 0); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci currctrl->if_enable = enable; 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* 3962306a36Sopenharmony_ci * csi2_recv_config - CSI2 receiver module configuration. 4062306a36Sopenharmony_ci * @currctrl: isp_csi2_ctrl_cfg structure 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_cistatic void csi2_recv_config(struct isp_device *isp, 4462306a36Sopenharmony_ci struct isp_csi2_device *csi2, 4562306a36Sopenharmony_ci struct isp_csi2_ctrl_cfg *currctrl) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci u32 reg; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTRL); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci if (currctrl->frame_mode) 5262306a36Sopenharmony_ci reg |= ISPCSI2_CTRL_FRAME; 5362306a36Sopenharmony_ci else 5462306a36Sopenharmony_ci reg &= ~ISPCSI2_CTRL_FRAME; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (currctrl->vp_clk_enable) 5762306a36Sopenharmony_ci reg |= ISPCSI2_CTRL_VP_CLK_EN; 5862306a36Sopenharmony_ci else 5962306a36Sopenharmony_ci reg &= ~ISPCSI2_CTRL_VP_CLK_EN; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci if (currctrl->vp_only_enable) 6262306a36Sopenharmony_ci reg |= ISPCSI2_CTRL_VP_ONLY_EN; 6362306a36Sopenharmony_ci else 6462306a36Sopenharmony_ci reg &= ~ISPCSI2_CTRL_VP_ONLY_EN; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci reg &= ~ISPCSI2_CTRL_VP_OUT_CTRL_MASK; 6762306a36Sopenharmony_ci reg |= currctrl->vp_out_ctrl << ISPCSI2_CTRL_VP_OUT_CTRL_SHIFT; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (currctrl->ecc_enable) 7062306a36Sopenharmony_ci reg |= ISPCSI2_CTRL_ECC_EN; 7162306a36Sopenharmony_ci else 7262306a36Sopenharmony_ci reg &= ~ISPCSI2_CTRL_ECC_EN; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTRL); 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic const unsigned int csi2_input_fmts[] = { 7862306a36Sopenharmony_ci MEDIA_BUS_FMT_SGRBG10_1X10, 7962306a36Sopenharmony_ci MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8, 8062306a36Sopenharmony_ci MEDIA_BUS_FMT_SRGGB10_1X10, 8162306a36Sopenharmony_ci MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8, 8262306a36Sopenharmony_ci MEDIA_BUS_FMT_SBGGR10_1X10, 8362306a36Sopenharmony_ci MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8, 8462306a36Sopenharmony_ci MEDIA_BUS_FMT_SGBRG10_1X10, 8562306a36Sopenharmony_ci MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8, 8662306a36Sopenharmony_ci MEDIA_BUS_FMT_YUYV8_2X8, 8762306a36Sopenharmony_ci}; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci/* To set the format on the CSI2 requires a mapping function that takes 9062306a36Sopenharmony_ci * the following inputs: 9162306a36Sopenharmony_ci * - 3 different formats (at this time) 9262306a36Sopenharmony_ci * - 2 destinations (mem, vp+mem) (vp only handled separately) 9362306a36Sopenharmony_ci * - 2 decompression options (on, off) 9462306a36Sopenharmony_ci * - 2 isp revisions (certain format must be handled differently on OMAP3630) 9562306a36Sopenharmony_ci * Output should be CSI2 frame format code 9662306a36Sopenharmony_ci * Array indices as follows: [format][dest][decompr][is_3630] 9762306a36Sopenharmony_ci * Not all combinations are valid. 0 means invalid. 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_cistatic const u16 __csi2_fmt_map[3][2][2][2] = { 10062306a36Sopenharmony_ci /* RAW10 formats */ 10162306a36Sopenharmony_ci { 10262306a36Sopenharmony_ci /* Output to memory */ 10362306a36Sopenharmony_ci { 10462306a36Sopenharmony_ci /* No DPCM decompression */ 10562306a36Sopenharmony_ci { CSI2_PIX_FMT_RAW10_EXP16, CSI2_PIX_FMT_RAW10_EXP16 }, 10662306a36Sopenharmony_ci /* DPCM decompression */ 10762306a36Sopenharmony_ci { 0, 0 }, 10862306a36Sopenharmony_ci }, 10962306a36Sopenharmony_ci /* Output to both */ 11062306a36Sopenharmony_ci { 11162306a36Sopenharmony_ci /* No DPCM decompression */ 11262306a36Sopenharmony_ci { CSI2_PIX_FMT_RAW10_EXP16_VP, 11362306a36Sopenharmony_ci CSI2_PIX_FMT_RAW10_EXP16_VP }, 11462306a36Sopenharmony_ci /* DPCM decompression */ 11562306a36Sopenharmony_ci { 0, 0 }, 11662306a36Sopenharmony_ci }, 11762306a36Sopenharmony_ci }, 11862306a36Sopenharmony_ci /* RAW10 DPCM8 formats */ 11962306a36Sopenharmony_ci { 12062306a36Sopenharmony_ci /* Output to memory */ 12162306a36Sopenharmony_ci { 12262306a36Sopenharmony_ci /* No DPCM decompression */ 12362306a36Sopenharmony_ci { CSI2_PIX_FMT_RAW8, CSI2_USERDEF_8BIT_DATA1 }, 12462306a36Sopenharmony_ci /* DPCM decompression */ 12562306a36Sopenharmony_ci { CSI2_PIX_FMT_RAW8_DPCM10_EXP16, 12662306a36Sopenharmony_ci CSI2_USERDEF_8BIT_DATA1_DPCM10 }, 12762306a36Sopenharmony_ci }, 12862306a36Sopenharmony_ci /* Output to both */ 12962306a36Sopenharmony_ci { 13062306a36Sopenharmony_ci /* No DPCM decompression */ 13162306a36Sopenharmony_ci { CSI2_PIX_FMT_RAW8_VP, 13262306a36Sopenharmony_ci CSI2_PIX_FMT_RAW8_VP }, 13362306a36Sopenharmony_ci /* DPCM decompression */ 13462306a36Sopenharmony_ci { CSI2_PIX_FMT_RAW8_DPCM10_VP, 13562306a36Sopenharmony_ci CSI2_USERDEF_8BIT_DATA1_DPCM10_VP }, 13662306a36Sopenharmony_ci }, 13762306a36Sopenharmony_ci }, 13862306a36Sopenharmony_ci /* YUYV8 2X8 formats */ 13962306a36Sopenharmony_ci { 14062306a36Sopenharmony_ci /* Output to memory */ 14162306a36Sopenharmony_ci { 14262306a36Sopenharmony_ci /* No DPCM decompression */ 14362306a36Sopenharmony_ci { CSI2_PIX_FMT_YUV422_8BIT, 14462306a36Sopenharmony_ci CSI2_PIX_FMT_YUV422_8BIT }, 14562306a36Sopenharmony_ci /* DPCM decompression */ 14662306a36Sopenharmony_ci { 0, 0 }, 14762306a36Sopenharmony_ci }, 14862306a36Sopenharmony_ci /* Output to both */ 14962306a36Sopenharmony_ci { 15062306a36Sopenharmony_ci /* No DPCM decompression */ 15162306a36Sopenharmony_ci { CSI2_PIX_FMT_YUV422_8BIT_VP, 15262306a36Sopenharmony_ci CSI2_PIX_FMT_YUV422_8BIT_VP }, 15362306a36Sopenharmony_ci /* DPCM decompression */ 15462306a36Sopenharmony_ci { 0, 0 }, 15562306a36Sopenharmony_ci }, 15662306a36Sopenharmony_ci }, 15762306a36Sopenharmony_ci}; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* 16062306a36Sopenharmony_ci * csi2_ctx_map_format - Map CSI2 sink media bus format to CSI2 format ID 16162306a36Sopenharmony_ci * @csi2: ISP CSI2 device 16262306a36Sopenharmony_ci * 16362306a36Sopenharmony_ci * Returns CSI2 physical format id 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_cistatic u16 csi2_ctx_map_format(struct isp_csi2_device *csi2) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci const struct v4l2_mbus_framefmt *fmt = &csi2->formats[CSI2_PAD_SINK]; 16862306a36Sopenharmony_ci int fmtidx, destidx, is_3630; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci switch (fmt->code) { 17162306a36Sopenharmony_ci case MEDIA_BUS_FMT_SGRBG10_1X10: 17262306a36Sopenharmony_ci case MEDIA_BUS_FMT_SRGGB10_1X10: 17362306a36Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR10_1X10: 17462306a36Sopenharmony_ci case MEDIA_BUS_FMT_SGBRG10_1X10: 17562306a36Sopenharmony_ci fmtidx = 0; 17662306a36Sopenharmony_ci break; 17762306a36Sopenharmony_ci case MEDIA_BUS_FMT_SGRBG10_DPCM8_1X8: 17862306a36Sopenharmony_ci case MEDIA_BUS_FMT_SRGGB10_DPCM8_1X8: 17962306a36Sopenharmony_ci case MEDIA_BUS_FMT_SBGGR10_DPCM8_1X8: 18062306a36Sopenharmony_ci case MEDIA_BUS_FMT_SGBRG10_DPCM8_1X8: 18162306a36Sopenharmony_ci fmtidx = 1; 18262306a36Sopenharmony_ci break; 18362306a36Sopenharmony_ci case MEDIA_BUS_FMT_YUYV8_2X8: 18462306a36Sopenharmony_ci fmtidx = 2; 18562306a36Sopenharmony_ci break; 18662306a36Sopenharmony_ci default: 18762306a36Sopenharmony_ci WARN(1, KERN_ERR "CSI2: pixel format %08x unsupported!\n", 18862306a36Sopenharmony_ci fmt->code); 18962306a36Sopenharmony_ci return 0; 19062306a36Sopenharmony_ci } 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (!(csi2->output & CSI2_OUTPUT_CCDC) && 19362306a36Sopenharmony_ci !(csi2->output & CSI2_OUTPUT_MEMORY)) { 19462306a36Sopenharmony_ci /* Neither output enabled is a valid combination */ 19562306a36Sopenharmony_ci return CSI2_PIX_FMT_OTHERS; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* If we need to skip frames at the beginning of the stream disable the 19962306a36Sopenharmony_ci * video port to avoid sending the skipped frames to the CCDC. 20062306a36Sopenharmony_ci */ 20162306a36Sopenharmony_ci destidx = csi2->frame_skip ? 0 : !!(csi2->output & CSI2_OUTPUT_CCDC); 20262306a36Sopenharmony_ci is_3630 = csi2->isp->revision == ISP_REVISION_15_0; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci return __csi2_fmt_map[fmtidx][destidx][csi2->dpcm_decompress][is_3630]; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci/* 20862306a36Sopenharmony_ci * csi2_set_outaddr - Set memory address to save output image 20962306a36Sopenharmony_ci * @csi2: Pointer to ISP CSI2a device. 21062306a36Sopenharmony_ci * @addr: ISP MMU Mapped 32-bit memory address aligned on 32 byte boundary. 21162306a36Sopenharmony_ci * 21262306a36Sopenharmony_ci * Sets the memory address where the output will be saved. 21362306a36Sopenharmony_ci * 21462306a36Sopenharmony_ci * Returns 0 if successful, or -EINVAL if the address is not in the 32 byte 21562306a36Sopenharmony_ci * boundary. 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_cistatic void csi2_set_outaddr(struct isp_csi2_device *csi2, u32 addr) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci struct isp_device *isp = csi2->isp; 22062306a36Sopenharmony_ci struct isp_csi2_ctx_cfg *ctx = &csi2->contexts[0]; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci ctx->ping_addr = addr; 22362306a36Sopenharmony_ci ctx->pong_addr = addr; 22462306a36Sopenharmony_ci isp_reg_writel(isp, ctx->ping_addr, 22562306a36Sopenharmony_ci csi2->regs1, ISPCSI2_CTX_DAT_PING_ADDR(ctx->ctxnum)); 22662306a36Sopenharmony_ci isp_reg_writel(isp, ctx->pong_addr, 22762306a36Sopenharmony_ci csi2->regs1, ISPCSI2_CTX_DAT_PONG_ADDR(ctx->ctxnum)); 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci/* 23162306a36Sopenharmony_ci * is_usr_def_mapping - Checks whether USER_DEF_MAPPING should 23262306a36Sopenharmony_ci * be enabled by CSI2. 23362306a36Sopenharmony_ci * @format_id: mapped format id 23462306a36Sopenharmony_ci * 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_cistatic inline int is_usr_def_mapping(u32 format_id) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci return (format_id & 0x40) ? 1 : 0; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci/* 24262306a36Sopenharmony_ci * csi2_ctx_enable - Enable specified CSI2 context 24362306a36Sopenharmony_ci * @ctxnum: Context number, valid between 0 and 7 values. 24462306a36Sopenharmony_ci * @enable: enable 24562306a36Sopenharmony_ci * 24662306a36Sopenharmony_ci */ 24762306a36Sopenharmony_cistatic void csi2_ctx_enable(struct isp_device *isp, 24862306a36Sopenharmony_ci struct isp_csi2_device *csi2, u8 ctxnum, u8 enable) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci struct isp_csi2_ctx_cfg *ctx = &csi2->contexts[ctxnum]; 25162306a36Sopenharmony_ci unsigned int skip = 0; 25262306a36Sopenharmony_ci u32 reg; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL1(ctxnum)); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (enable) { 25762306a36Sopenharmony_ci if (csi2->frame_skip) 25862306a36Sopenharmony_ci skip = csi2->frame_skip; 25962306a36Sopenharmony_ci else if (csi2->output & CSI2_OUTPUT_MEMORY) 26062306a36Sopenharmony_ci skip = 1; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci reg &= ~ISPCSI2_CTX_CTRL1_COUNT_MASK; 26362306a36Sopenharmony_ci reg |= ISPCSI2_CTX_CTRL1_COUNT_UNLOCK 26462306a36Sopenharmony_ci | (skip << ISPCSI2_CTX_CTRL1_COUNT_SHIFT) 26562306a36Sopenharmony_ci | ISPCSI2_CTX_CTRL1_CTX_EN; 26662306a36Sopenharmony_ci } else { 26762306a36Sopenharmony_ci reg &= ~ISPCSI2_CTX_CTRL1_CTX_EN; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL1(ctxnum)); 27162306a36Sopenharmony_ci ctx->enabled = enable; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci/* 27562306a36Sopenharmony_ci * csi2_ctx_config - CSI2 context configuration. 27662306a36Sopenharmony_ci * @ctx: context configuration 27762306a36Sopenharmony_ci * 27862306a36Sopenharmony_ci */ 27962306a36Sopenharmony_cistatic void csi2_ctx_config(struct isp_device *isp, 28062306a36Sopenharmony_ci struct isp_csi2_device *csi2, 28162306a36Sopenharmony_ci struct isp_csi2_ctx_cfg *ctx) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci u32 reg; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* Set up CSI2_CTx_CTRL1 */ 28662306a36Sopenharmony_ci reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL1(ctx->ctxnum)); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (ctx->eof_enabled) 28962306a36Sopenharmony_ci reg |= ISPCSI2_CTX_CTRL1_EOF_EN; 29062306a36Sopenharmony_ci else 29162306a36Sopenharmony_ci reg &= ~ISPCSI2_CTX_CTRL1_EOF_EN; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (ctx->eol_enabled) 29462306a36Sopenharmony_ci reg |= ISPCSI2_CTX_CTRL1_EOL_EN; 29562306a36Sopenharmony_ci else 29662306a36Sopenharmony_ci reg &= ~ISPCSI2_CTX_CTRL1_EOL_EN; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (ctx->checksum_enabled) 29962306a36Sopenharmony_ci reg |= ISPCSI2_CTX_CTRL1_CS_EN; 30062306a36Sopenharmony_ci else 30162306a36Sopenharmony_ci reg &= ~ISPCSI2_CTX_CTRL1_CS_EN; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL1(ctx->ctxnum)); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* Set up CSI2_CTx_CTRL2 */ 30662306a36Sopenharmony_ci reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL2(ctx->ctxnum)); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci reg &= ~(ISPCSI2_CTX_CTRL2_VIRTUAL_ID_MASK); 30962306a36Sopenharmony_ci reg |= ctx->virtual_id << ISPCSI2_CTX_CTRL2_VIRTUAL_ID_SHIFT; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci reg &= ~(ISPCSI2_CTX_CTRL2_FORMAT_MASK); 31262306a36Sopenharmony_ci reg |= ctx->format_id << ISPCSI2_CTX_CTRL2_FORMAT_SHIFT; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (ctx->dpcm_decompress) { 31562306a36Sopenharmony_ci if (ctx->dpcm_predictor) 31662306a36Sopenharmony_ci reg |= ISPCSI2_CTX_CTRL2_DPCM_PRED; 31762306a36Sopenharmony_ci else 31862306a36Sopenharmony_ci reg &= ~ISPCSI2_CTX_CTRL2_DPCM_PRED; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (is_usr_def_mapping(ctx->format_id)) { 32262306a36Sopenharmony_ci reg &= ~ISPCSI2_CTX_CTRL2_USER_DEF_MAP_MASK; 32362306a36Sopenharmony_ci reg |= 2 << ISPCSI2_CTX_CTRL2_USER_DEF_MAP_SHIFT; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL2(ctx->ctxnum)); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* Set up CSI2_CTx_CTRL3 */ 32962306a36Sopenharmony_ci reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_CTRL3(ctx->ctxnum)); 33062306a36Sopenharmony_ci reg &= ~(ISPCSI2_CTX_CTRL3_ALPHA_MASK); 33162306a36Sopenharmony_ci reg |= (ctx->alpha << ISPCSI2_CTX_CTRL3_ALPHA_SHIFT); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_CTX_CTRL3(ctx->ctxnum)); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci /* Set up CSI2_CTx_DAT_OFST */ 33662306a36Sopenharmony_ci reg = isp_reg_readl(isp, csi2->regs1, 33762306a36Sopenharmony_ci ISPCSI2_CTX_DAT_OFST(ctx->ctxnum)); 33862306a36Sopenharmony_ci reg &= ~ISPCSI2_CTX_DAT_OFST_OFST_MASK; 33962306a36Sopenharmony_ci reg |= ctx->data_offset << ISPCSI2_CTX_DAT_OFST_OFST_SHIFT; 34062306a36Sopenharmony_ci isp_reg_writel(isp, reg, csi2->regs1, 34162306a36Sopenharmony_ci ISPCSI2_CTX_DAT_OFST(ctx->ctxnum)); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci isp_reg_writel(isp, ctx->ping_addr, 34462306a36Sopenharmony_ci csi2->regs1, ISPCSI2_CTX_DAT_PING_ADDR(ctx->ctxnum)); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci isp_reg_writel(isp, ctx->pong_addr, 34762306a36Sopenharmony_ci csi2->regs1, ISPCSI2_CTX_DAT_PONG_ADDR(ctx->ctxnum)); 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci/* 35162306a36Sopenharmony_ci * csi2_timing_config - CSI2 timing configuration. 35262306a36Sopenharmony_ci * @timing: csi2_timing_cfg structure 35362306a36Sopenharmony_ci */ 35462306a36Sopenharmony_cistatic void csi2_timing_config(struct isp_device *isp, 35562306a36Sopenharmony_ci struct isp_csi2_device *csi2, 35662306a36Sopenharmony_ci struct isp_csi2_timing_cfg *timing) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci u32 reg; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_TIMING); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (timing->force_rx_mode) 36362306a36Sopenharmony_ci reg |= ISPCSI2_TIMING_FORCE_RX_MODE_IO(timing->ionum); 36462306a36Sopenharmony_ci else 36562306a36Sopenharmony_ci reg &= ~ISPCSI2_TIMING_FORCE_RX_MODE_IO(timing->ionum); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if (timing->stop_state_16x) 36862306a36Sopenharmony_ci reg |= ISPCSI2_TIMING_STOP_STATE_X16_IO(timing->ionum); 36962306a36Sopenharmony_ci else 37062306a36Sopenharmony_ci reg &= ~ISPCSI2_TIMING_STOP_STATE_X16_IO(timing->ionum); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (timing->stop_state_4x) 37362306a36Sopenharmony_ci reg |= ISPCSI2_TIMING_STOP_STATE_X4_IO(timing->ionum); 37462306a36Sopenharmony_ci else 37562306a36Sopenharmony_ci reg &= ~ISPCSI2_TIMING_STOP_STATE_X4_IO(timing->ionum); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci reg &= ~ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_MASK(timing->ionum); 37862306a36Sopenharmony_ci reg |= timing->stop_state_counter << 37962306a36Sopenharmony_ci ISPCSI2_TIMING_STOP_STATE_COUNTER_IO_SHIFT(timing->ionum); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_TIMING); 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci/* 38562306a36Sopenharmony_ci * csi2_irq_ctx_set - Enables CSI2 Context IRQs. 38662306a36Sopenharmony_ci * @enable: Enable/disable CSI2 Context interrupts 38762306a36Sopenharmony_ci */ 38862306a36Sopenharmony_cistatic void csi2_irq_ctx_set(struct isp_device *isp, 38962306a36Sopenharmony_ci struct isp_csi2_device *csi2, int enable) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci int i; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 39462306a36Sopenharmony_ci isp_reg_writel(isp, ISPCSI2_CTX_IRQSTATUS_FE_IRQ, csi2->regs1, 39562306a36Sopenharmony_ci ISPCSI2_CTX_IRQSTATUS(i)); 39662306a36Sopenharmony_ci if (enable) 39762306a36Sopenharmony_ci isp_reg_set(isp, csi2->regs1, ISPCSI2_CTX_IRQENABLE(i), 39862306a36Sopenharmony_ci ISPCSI2_CTX_IRQSTATUS_FE_IRQ); 39962306a36Sopenharmony_ci else 40062306a36Sopenharmony_ci isp_reg_clr(isp, csi2->regs1, ISPCSI2_CTX_IRQENABLE(i), 40162306a36Sopenharmony_ci ISPCSI2_CTX_IRQSTATUS_FE_IRQ); 40262306a36Sopenharmony_ci } 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci/* 40662306a36Sopenharmony_ci * csi2_irq_complexio1_set - Enables CSI2 ComplexIO IRQs. 40762306a36Sopenharmony_ci * @enable: Enable/disable CSI2 ComplexIO #1 interrupts 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_cistatic void csi2_irq_complexio1_set(struct isp_device *isp, 41062306a36Sopenharmony_ci struct isp_csi2_device *csi2, int enable) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci u32 reg; 41362306a36Sopenharmony_ci reg = ISPCSI2_PHY_IRQENABLE_STATEALLULPMEXIT | 41462306a36Sopenharmony_ci ISPCSI2_PHY_IRQENABLE_STATEALLULPMENTER | 41562306a36Sopenharmony_ci ISPCSI2_PHY_IRQENABLE_STATEULPM5 | 41662306a36Sopenharmony_ci ISPCSI2_PHY_IRQENABLE_ERRCONTROL5 | 41762306a36Sopenharmony_ci ISPCSI2_PHY_IRQENABLE_ERRESC5 | 41862306a36Sopenharmony_ci ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS5 | 41962306a36Sopenharmony_ci ISPCSI2_PHY_IRQENABLE_ERRSOTHS5 | 42062306a36Sopenharmony_ci ISPCSI2_PHY_IRQENABLE_STATEULPM4 | 42162306a36Sopenharmony_ci ISPCSI2_PHY_IRQENABLE_ERRCONTROL4 | 42262306a36Sopenharmony_ci ISPCSI2_PHY_IRQENABLE_ERRESC4 | 42362306a36Sopenharmony_ci ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS4 | 42462306a36Sopenharmony_ci ISPCSI2_PHY_IRQENABLE_ERRSOTHS4 | 42562306a36Sopenharmony_ci ISPCSI2_PHY_IRQENABLE_STATEULPM3 | 42662306a36Sopenharmony_ci ISPCSI2_PHY_IRQENABLE_ERRCONTROL3 | 42762306a36Sopenharmony_ci ISPCSI2_PHY_IRQENABLE_ERRESC3 | 42862306a36Sopenharmony_ci ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS3 | 42962306a36Sopenharmony_ci ISPCSI2_PHY_IRQENABLE_ERRSOTHS3 | 43062306a36Sopenharmony_ci ISPCSI2_PHY_IRQENABLE_STATEULPM2 | 43162306a36Sopenharmony_ci ISPCSI2_PHY_IRQENABLE_ERRCONTROL2 | 43262306a36Sopenharmony_ci ISPCSI2_PHY_IRQENABLE_ERRESC2 | 43362306a36Sopenharmony_ci ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS2 | 43462306a36Sopenharmony_ci ISPCSI2_PHY_IRQENABLE_ERRSOTHS2 | 43562306a36Sopenharmony_ci ISPCSI2_PHY_IRQENABLE_STATEULPM1 | 43662306a36Sopenharmony_ci ISPCSI2_PHY_IRQENABLE_ERRCONTROL1 | 43762306a36Sopenharmony_ci ISPCSI2_PHY_IRQENABLE_ERRESC1 | 43862306a36Sopenharmony_ci ISPCSI2_PHY_IRQENABLE_ERRSOTSYNCHS1 | 43962306a36Sopenharmony_ci ISPCSI2_PHY_IRQENABLE_ERRSOTHS1; 44062306a36Sopenharmony_ci isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_PHY_IRQSTATUS); 44162306a36Sopenharmony_ci if (enable) 44262306a36Sopenharmony_ci reg |= isp_reg_readl(isp, csi2->regs1, ISPCSI2_PHY_IRQENABLE); 44362306a36Sopenharmony_ci else 44462306a36Sopenharmony_ci reg = 0; 44562306a36Sopenharmony_ci isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_PHY_IRQENABLE); 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci/* 44962306a36Sopenharmony_ci * csi2_irq_status_set - Enables CSI2 Status IRQs. 45062306a36Sopenharmony_ci * @enable: Enable/disable CSI2 Status interrupts 45162306a36Sopenharmony_ci */ 45262306a36Sopenharmony_cistatic void csi2_irq_status_set(struct isp_device *isp, 45362306a36Sopenharmony_ci struct isp_csi2_device *csi2, int enable) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci u32 reg; 45662306a36Sopenharmony_ci reg = ISPCSI2_IRQSTATUS_OCP_ERR_IRQ | 45762306a36Sopenharmony_ci ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ | 45862306a36Sopenharmony_ci ISPCSI2_IRQSTATUS_ECC_CORRECTION_IRQ | 45962306a36Sopenharmony_ci ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ | 46062306a36Sopenharmony_ci ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ | 46162306a36Sopenharmony_ci ISPCSI2_IRQSTATUS_COMPLEXIO1_ERR_IRQ | 46262306a36Sopenharmony_ci ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ | 46362306a36Sopenharmony_ci ISPCSI2_IRQSTATUS_CONTEXT(0); 46462306a36Sopenharmony_ci isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_IRQSTATUS); 46562306a36Sopenharmony_ci if (enable) 46662306a36Sopenharmony_ci reg |= isp_reg_readl(isp, csi2->regs1, ISPCSI2_IRQENABLE); 46762306a36Sopenharmony_ci else 46862306a36Sopenharmony_ci reg = 0; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci isp_reg_writel(isp, reg, csi2->regs1, ISPCSI2_IRQENABLE); 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci/* 47462306a36Sopenharmony_ci * omap3isp_csi2_reset - Resets the CSI2 module. 47562306a36Sopenharmony_ci * 47662306a36Sopenharmony_ci * Must be called with the phy lock held. 47762306a36Sopenharmony_ci * 47862306a36Sopenharmony_ci * Returns 0 if successful, or -EBUSY if power command didn't respond. 47962306a36Sopenharmony_ci */ 48062306a36Sopenharmony_ciint omap3isp_csi2_reset(struct isp_csi2_device *csi2) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci struct isp_device *isp = csi2->isp; 48362306a36Sopenharmony_ci u8 soft_reset_retries = 0; 48462306a36Sopenharmony_ci u32 reg; 48562306a36Sopenharmony_ci int i; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (!csi2->available) 48862306a36Sopenharmony_ci return -ENODEV; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (csi2->phy->entity) 49162306a36Sopenharmony_ci return -EBUSY; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci isp_reg_set(isp, csi2->regs1, ISPCSI2_SYSCONFIG, 49462306a36Sopenharmony_ci ISPCSI2_SYSCONFIG_SOFT_RESET); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci do { 49762306a36Sopenharmony_ci reg = isp_reg_readl(isp, csi2->regs1, ISPCSI2_SYSSTATUS) & 49862306a36Sopenharmony_ci ISPCSI2_SYSSTATUS_RESET_DONE; 49962306a36Sopenharmony_ci if (reg == ISPCSI2_SYSSTATUS_RESET_DONE) 50062306a36Sopenharmony_ci break; 50162306a36Sopenharmony_ci soft_reset_retries++; 50262306a36Sopenharmony_ci if (soft_reset_retries < 5) 50362306a36Sopenharmony_ci udelay(100); 50462306a36Sopenharmony_ci } while (soft_reset_retries < 5); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci if (soft_reset_retries == 5) { 50762306a36Sopenharmony_ci dev_err(isp->dev, "CSI2: Soft reset try count exceeded!\n"); 50862306a36Sopenharmony_ci return -EBUSY; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci if (isp->revision == ISP_REVISION_15_0) 51262306a36Sopenharmony_ci isp_reg_set(isp, csi2->regs1, ISPCSI2_PHY_CFG, 51362306a36Sopenharmony_ci ISPCSI2_PHY_CFG_RESET_CTRL); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci i = 100; 51662306a36Sopenharmony_ci do { 51762306a36Sopenharmony_ci reg = isp_reg_readl(isp, csi2->phy->phy_regs, ISPCSIPHY_REG1) 51862306a36Sopenharmony_ci & ISPCSIPHY_REG1_RESET_DONE_CTRLCLK; 51962306a36Sopenharmony_ci if (reg == ISPCSIPHY_REG1_RESET_DONE_CTRLCLK) 52062306a36Sopenharmony_ci break; 52162306a36Sopenharmony_ci udelay(100); 52262306a36Sopenharmony_ci } while (--i > 0); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci if (i == 0) { 52562306a36Sopenharmony_ci dev_err(isp->dev, 52662306a36Sopenharmony_ci "CSI2: Reset for CSI2_96M_FCLK domain Failed!\n"); 52762306a36Sopenharmony_ci return -EBUSY; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (isp->autoidle) 53162306a36Sopenharmony_ci isp_reg_clr_set(isp, csi2->regs1, ISPCSI2_SYSCONFIG, 53262306a36Sopenharmony_ci ISPCSI2_SYSCONFIG_MSTANDBY_MODE_MASK | 53362306a36Sopenharmony_ci ISPCSI2_SYSCONFIG_AUTO_IDLE, 53462306a36Sopenharmony_ci ISPCSI2_SYSCONFIG_MSTANDBY_MODE_SMART | 53562306a36Sopenharmony_ci ((isp->revision == ISP_REVISION_15_0) ? 53662306a36Sopenharmony_ci ISPCSI2_SYSCONFIG_AUTO_IDLE : 0)); 53762306a36Sopenharmony_ci else 53862306a36Sopenharmony_ci isp_reg_clr_set(isp, csi2->regs1, ISPCSI2_SYSCONFIG, 53962306a36Sopenharmony_ci ISPCSI2_SYSCONFIG_MSTANDBY_MODE_MASK | 54062306a36Sopenharmony_ci ISPCSI2_SYSCONFIG_AUTO_IDLE, 54162306a36Sopenharmony_ci ISPCSI2_SYSCONFIG_MSTANDBY_MODE_NO); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci return 0; 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cistatic int csi2_configure(struct isp_csi2_device *csi2) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci struct isp_pipeline *pipe = to_isp_pipeline(&csi2->subdev.entity); 54962306a36Sopenharmony_ci const struct isp_bus_cfg *buscfg; 55062306a36Sopenharmony_ci struct isp_device *isp = csi2->isp; 55162306a36Sopenharmony_ci struct isp_csi2_timing_cfg *timing = &csi2->timing[0]; 55262306a36Sopenharmony_ci struct v4l2_subdev *sensor; 55362306a36Sopenharmony_ci struct media_pad *pad; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci /* 55662306a36Sopenharmony_ci * CSI2 fields that can be updated while the context has 55762306a36Sopenharmony_ci * been enabled or the interface has been enabled are not 55862306a36Sopenharmony_ci * updated dynamically currently. So we do not allow to 55962306a36Sopenharmony_ci * reconfigure if either has been enabled 56062306a36Sopenharmony_ci */ 56162306a36Sopenharmony_ci if (csi2->contexts[0].enabled || csi2->ctrl.if_enable) 56262306a36Sopenharmony_ci return -EBUSY; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci pad = media_pad_remote_pad_first(&csi2->pads[CSI2_PAD_SINK]); 56562306a36Sopenharmony_ci sensor = media_entity_to_v4l2_subdev(pad->entity); 56662306a36Sopenharmony_ci buscfg = v4l2_subdev_to_bus_cfg(pipe->external); 56762306a36Sopenharmony_ci if (WARN_ON(!buscfg)) 56862306a36Sopenharmony_ci return -EPIPE; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci csi2->frame_skip = 0; 57162306a36Sopenharmony_ci v4l2_subdev_call(sensor, sensor, g_skip_frames, &csi2->frame_skip); 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci csi2->ctrl.vp_out_ctrl = 57462306a36Sopenharmony_ci clamp_t(unsigned int, pipe->l3_ick / pipe->external_rate - 1, 57562306a36Sopenharmony_ci 1, 3); 57662306a36Sopenharmony_ci dev_dbg(isp->dev, "%s: l3_ick %lu, external_rate %u, vp_out_ctrl %u\n", 57762306a36Sopenharmony_ci __func__, pipe->l3_ick, pipe->external_rate, 57862306a36Sopenharmony_ci csi2->ctrl.vp_out_ctrl); 57962306a36Sopenharmony_ci csi2->ctrl.frame_mode = ISP_CSI2_FRAME_IMMEDIATE; 58062306a36Sopenharmony_ci csi2->ctrl.ecc_enable = buscfg->bus.csi2.crc; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci timing->ionum = 1; 58362306a36Sopenharmony_ci timing->force_rx_mode = 1; 58462306a36Sopenharmony_ci timing->stop_state_16x = 1; 58562306a36Sopenharmony_ci timing->stop_state_4x = 1; 58662306a36Sopenharmony_ci timing->stop_state_counter = 0x1FF; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci /* 58962306a36Sopenharmony_ci * The CSI2 receiver can't do any format conversion except DPCM 59062306a36Sopenharmony_ci * decompression, so every set_format call configures both pads 59162306a36Sopenharmony_ci * and enables DPCM decompression as a special case: 59262306a36Sopenharmony_ci */ 59362306a36Sopenharmony_ci if (csi2->formats[CSI2_PAD_SINK].code != 59462306a36Sopenharmony_ci csi2->formats[CSI2_PAD_SOURCE].code) 59562306a36Sopenharmony_ci csi2->dpcm_decompress = true; 59662306a36Sopenharmony_ci else 59762306a36Sopenharmony_ci csi2->dpcm_decompress = false; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci csi2->contexts[0].format_id = csi2_ctx_map_format(csi2); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci if (csi2->video_out.bpl_padding == 0) 60262306a36Sopenharmony_ci csi2->contexts[0].data_offset = 0; 60362306a36Sopenharmony_ci else 60462306a36Sopenharmony_ci csi2->contexts[0].data_offset = csi2->video_out.bpl_value; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci /* 60762306a36Sopenharmony_ci * Enable end of frame and end of line signals generation for 60862306a36Sopenharmony_ci * context 0. These signals are generated from CSI2 receiver to 60962306a36Sopenharmony_ci * qualify the last pixel of a frame and the last pixel of a line. 61062306a36Sopenharmony_ci * Without enabling the signals CSI2 receiver writes data to memory 61162306a36Sopenharmony_ci * beyond buffer size and/or data line offset is not handled correctly. 61262306a36Sopenharmony_ci */ 61362306a36Sopenharmony_ci csi2->contexts[0].eof_enabled = 1; 61462306a36Sopenharmony_ci csi2->contexts[0].eol_enabled = 1; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci csi2_irq_complexio1_set(isp, csi2, 1); 61762306a36Sopenharmony_ci csi2_irq_ctx_set(isp, csi2, 1); 61862306a36Sopenharmony_ci csi2_irq_status_set(isp, csi2, 1); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci /* Set configuration (timings, format and links) */ 62162306a36Sopenharmony_ci csi2_timing_config(isp, csi2, timing); 62262306a36Sopenharmony_ci csi2_recv_config(isp, csi2, &csi2->ctrl); 62362306a36Sopenharmony_ci csi2_ctx_config(isp, csi2, &csi2->contexts[0]); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci return 0; 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci/* 62962306a36Sopenharmony_ci * csi2_print_status - Prints CSI2 debug information. 63062306a36Sopenharmony_ci */ 63162306a36Sopenharmony_ci#define CSI2_PRINT_REGISTER(isp, regs, name)\ 63262306a36Sopenharmony_ci dev_dbg(isp->dev, "###CSI2 " #name "=0x%08x\n", \ 63362306a36Sopenharmony_ci isp_reg_readl(isp, regs, ISPCSI2_##name)) 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_cistatic void csi2_print_status(struct isp_csi2_device *csi2) 63662306a36Sopenharmony_ci{ 63762306a36Sopenharmony_ci struct isp_device *isp = csi2->isp; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci if (!csi2->available) 64062306a36Sopenharmony_ci return; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci dev_dbg(isp->dev, "-------------CSI2 Register dump-------------\n"); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci CSI2_PRINT_REGISTER(isp, csi2->regs1, SYSCONFIG); 64562306a36Sopenharmony_ci CSI2_PRINT_REGISTER(isp, csi2->regs1, SYSSTATUS); 64662306a36Sopenharmony_ci CSI2_PRINT_REGISTER(isp, csi2->regs1, IRQENABLE); 64762306a36Sopenharmony_ci CSI2_PRINT_REGISTER(isp, csi2->regs1, IRQSTATUS); 64862306a36Sopenharmony_ci CSI2_PRINT_REGISTER(isp, csi2->regs1, CTRL); 64962306a36Sopenharmony_ci CSI2_PRINT_REGISTER(isp, csi2->regs1, DBG_H); 65062306a36Sopenharmony_ci CSI2_PRINT_REGISTER(isp, csi2->regs1, GNQ); 65162306a36Sopenharmony_ci CSI2_PRINT_REGISTER(isp, csi2->regs1, PHY_CFG); 65262306a36Sopenharmony_ci CSI2_PRINT_REGISTER(isp, csi2->regs1, PHY_IRQSTATUS); 65362306a36Sopenharmony_ci CSI2_PRINT_REGISTER(isp, csi2->regs1, SHORT_PACKET); 65462306a36Sopenharmony_ci CSI2_PRINT_REGISTER(isp, csi2->regs1, PHY_IRQENABLE); 65562306a36Sopenharmony_ci CSI2_PRINT_REGISTER(isp, csi2->regs1, DBG_P); 65662306a36Sopenharmony_ci CSI2_PRINT_REGISTER(isp, csi2->regs1, TIMING); 65762306a36Sopenharmony_ci CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_CTRL1(0)); 65862306a36Sopenharmony_ci CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_CTRL2(0)); 65962306a36Sopenharmony_ci CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_DAT_OFST(0)); 66062306a36Sopenharmony_ci CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_DAT_PING_ADDR(0)); 66162306a36Sopenharmony_ci CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_DAT_PONG_ADDR(0)); 66262306a36Sopenharmony_ci CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_IRQENABLE(0)); 66362306a36Sopenharmony_ci CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_IRQSTATUS(0)); 66462306a36Sopenharmony_ci CSI2_PRINT_REGISTER(isp, csi2->regs1, CTX_CTRL3(0)); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci dev_dbg(isp->dev, "--------------------------------------------\n"); 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 67062306a36Sopenharmony_ci * Interrupt handling 67162306a36Sopenharmony_ci */ 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci/* 67462306a36Sopenharmony_ci * csi2_isr_buffer - Does buffer handling at end-of-frame 67562306a36Sopenharmony_ci * when writing to memory. 67662306a36Sopenharmony_ci */ 67762306a36Sopenharmony_cistatic void csi2_isr_buffer(struct isp_csi2_device *csi2) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci struct isp_device *isp = csi2->isp; 68062306a36Sopenharmony_ci struct isp_buffer *buffer; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci csi2_ctx_enable(isp, csi2, 0, 0); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci buffer = omap3isp_video_buffer_next(&csi2->video_out); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci /* 68762306a36Sopenharmony_ci * Let video queue operation restart engine if there is an underrun 68862306a36Sopenharmony_ci * condition. 68962306a36Sopenharmony_ci */ 69062306a36Sopenharmony_ci if (buffer == NULL) 69162306a36Sopenharmony_ci return; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci csi2_set_outaddr(csi2, buffer->dma); 69462306a36Sopenharmony_ci csi2_ctx_enable(isp, csi2, 0, 1); 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_cistatic void csi2_isr_ctx(struct isp_csi2_device *csi2, 69862306a36Sopenharmony_ci struct isp_csi2_ctx_cfg *ctx) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci struct isp_device *isp = csi2->isp; 70162306a36Sopenharmony_ci unsigned int n = ctx->ctxnum; 70262306a36Sopenharmony_ci u32 status; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci status = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_IRQSTATUS(n)); 70562306a36Sopenharmony_ci isp_reg_writel(isp, status, csi2->regs1, ISPCSI2_CTX_IRQSTATUS(n)); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci if (!(status & ISPCSI2_CTX_IRQSTATUS_FE_IRQ)) 70862306a36Sopenharmony_ci return; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci /* Skip interrupts until we reach the frame skip count. The CSI2 will be 71162306a36Sopenharmony_ci * automatically disabled, as the frame skip count has been programmed 71262306a36Sopenharmony_ci * in the CSI2_CTx_CTRL1::COUNT field, so re-enable it. 71362306a36Sopenharmony_ci * 71462306a36Sopenharmony_ci * It would have been nice to rely on the FRAME_NUMBER interrupt instead 71562306a36Sopenharmony_ci * but it turned out that the interrupt is only generated when the CSI2 71662306a36Sopenharmony_ci * writes to memory (the CSI2_CTx_CTRL1::COUNT field is decreased 71762306a36Sopenharmony_ci * correctly and reaches 0 when data is forwarded to the video port only 71862306a36Sopenharmony_ci * but no interrupt arrives). Maybe a CSI2 hardware bug. 71962306a36Sopenharmony_ci */ 72062306a36Sopenharmony_ci if (csi2->frame_skip) { 72162306a36Sopenharmony_ci csi2->frame_skip--; 72262306a36Sopenharmony_ci if (csi2->frame_skip == 0) { 72362306a36Sopenharmony_ci ctx->format_id = csi2_ctx_map_format(csi2); 72462306a36Sopenharmony_ci csi2_ctx_config(isp, csi2, ctx); 72562306a36Sopenharmony_ci csi2_ctx_enable(isp, csi2, n, 1); 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci return; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci if (csi2->output & CSI2_OUTPUT_MEMORY) 73162306a36Sopenharmony_ci csi2_isr_buffer(csi2); 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci/* 73562306a36Sopenharmony_ci * omap3isp_csi2_isr - CSI2 interrupt handling. 73662306a36Sopenharmony_ci */ 73762306a36Sopenharmony_civoid omap3isp_csi2_isr(struct isp_csi2_device *csi2) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci struct isp_pipeline *pipe = to_isp_pipeline(&csi2->subdev.entity); 74062306a36Sopenharmony_ci u32 csi2_irqstatus, cpxio1_irqstatus; 74162306a36Sopenharmony_ci struct isp_device *isp = csi2->isp; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (!csi2->available) 74462306a36Sopenharmony_ci return; 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci csi2_irqstatus = isp_reg_readl(isp, csi2->regs1, ISPCSI2_IRQSTATUS); 74762306a36Sopenharmony_ci isp_reg_writel(isp, csi2_irqstatus, csi2->regs1, ISPCSI2_IRQSTATUS); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci /* Failure Cases */ 75062306a36Sopenharmony_ci if (csi2_irqstatus & ISPCSI2_IRQSTATUS_COMPLEXIO1_ERR_IRQ) { 75162306a36Sopenharmony_ci cpxio1_irqstatus = isp_reg_readl(isp, csi2->regs1, 75262306a36Sopenharmony_ci ISPCSI2_PHY_IRQSTATUS); 75362306a36Sopenharmony_ci isp_reg_writel(isp, cpxio1_irqstatus, 75462306a36Sopenharmony_ci csi2->regs1, ISPCSI2_PHY_IRQSTATUS); 75562306a36Sopenharmony_ci dev_dbg(isp->dev, "CSI2: ComplexIO Error IRQ %x\n", 75662306a36Sopenharmony_ci cpxio1_irqstatus); 75762306a36Sopenharmony_ci pipe->error = true; 75862306a36Sopenharmony_ci } 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci if (csi2_irqstatus & (ISPCSI2_IRQSTATUS_OCP_ERR_IRQ | 76162306a36Sopenharmony_ci ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ | 76262306a36Sopenharmony_ci ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ | 76362306a36Sopenharmony_ci ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ | 76462306a36Sopenharmony_ci ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ)) { 76562306a36Sopenharmony_ci dev_dbg(isp->dev, 76662306a36Sopenharmony_ci "CSI2 Err: OCP:%d, Short_pack:%d, ECC:%d, CPXIO2:%d, FIFO_OVF:%d,\n", 76762306a36Sopenharmony_ci (csi2_irqstatus & 76862306a36Sopenharmony_ci ISPCSI2_IRQSTATUS_OCP_ERR_IRQ) ? 1 : 0, 76962306a36Sopenharmony_ci (csi2_irqstatus & 77062306a36Sopenharmony_ci ISPCSI2_IRQSTATUS_SHORT_PACKET_IRQ) ? 1 : 0, 77162306a36Sopenharmony_ci (csi2_irqstatus & 77262306a36Sopenharmony_ci ISPCSI2_IRQSTATUS_ECC_NO_CORRECTION_IRQ) ? 1 : 0, 77362306a36Sopenharmony_ci (csi2_irqstatus & 77462306a36Sopenharmony_ci ISPCSI2_IRQSTATUS_COMPLEXIO2_ERR_IRQ) ? 1 : 0, 77562306a36Sopenharmony_ci (csi2_irqstatus & 77662306a36Sopenharmony_ci ISPCSI2_IRQSTATUS_FIFO_OVF_IRQ) ? 1 : 0); 77762306a36Sopenharmony_ci pipe->error = true; 77862306a36Sopenharmony_ci } 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (omap3isp_module_sync_is_stopping(&csi2->wait, &csi2->stopping)) 78162306a36Sopenharmony_ci return; 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci /* Successful cases */ 78462306a36Sopenharmony_ci if (csi2_irqstatus & ISPCSI2_IRQSTATUS_CONTEXT(0)) 78562306a36Sopenharmony_ci csi2_isr_ctx(csi2, &csi2->contexts[0]); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci if (csi2_irqstatus & ISPCSI2_IRQSTATUS_ECC_CORRECTION_IRQ) 78862306a36Sopenharmony_ci dev_dbg(isp->dev, "CSI2: ECC correction done\n"); 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 79262306a36Sopenharmony_ci * ISP video operations 79362306a36Sopenharmony_ci */ 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci/* 79662306a36Sopenharmony_ci * csi2_queue - Queues the first buffer when using memory output 79762306a36Sopenharmony_ci * @video: The video node 79862306a36Sopenharmony_ci * @buffer: buffer to queue 79962306a36Sopenharmony_ci */ 80062306a36Sopenharmony_cistatic int csi2_queue(struct isp_video *video, struct isp_buffer *buffer) 80162306a36Sopenharmony_ci{ 80262306a36Sopenharmony_ci struct isp_device *isp = video->isp; 80362306a36Sopenharmony_ci struct isp_csi2_device *csi2 = &isp->isp_csi2a; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci csi2_set_outaddr(csi2, buffer->dma); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci /* 80862306a36Sopenharmony_ci * If streaming was enabled before there was a buffer queued 80962306a36Sopenharmony_ci * or underrun happened in the ISR, the hardware was not enabled 81062306a36Sopenharmony_ci * and DMA queue flag ISP_VIDEO_DMAQUEUE_UNDERRUN is still set. 81162306a36Sopenharmony_ci * Enable it now. 81262306a36Sopenharmony_ci */ 81362306a36Sopenharmony_ci if (csi2->video_out.dmaqueue_flags & ISP_VIDEO_DMAQUEUE_UNDERRUN) { 81462306a36Sopenharmony_ci /* Enable / disable context 0 and IRQs */ 81562306a36Sopenharmony_ci csi2_if_enable(isp, csi2, 1); 81662306a36Sopenharmony_ci csi2_ctx_enable(isp, csi2, 0, 1); 81762306a36Sopenharmony_ci isp_video_dmaqueue_flags_clr(&csi2->video_out); 81862306a36Sopenharmony_ci } 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci return 0; 82162306a36Sopenharmony_ci} 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_cistatic const struct isp_video_operations csi2_ispvideo_ops = { 82462306a36Sopenharmony_ci .queue = csi2_queue, 82562306a36Sopenharmony_ci}; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 82862306a36Sopenharmony_ci * V4L2 subdev operations 82962306a36Sopenharmony_ci */ 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_cistatic struct v4l2_mbus_framefmt * 83262306a36Sopenharmony_ci__csi2_get_format(struct isp_csi2_device *csi2, 83362306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 83462306a36Sopenharmony_ci unsigned int pad, enum v4l2_subdev_format_whence which) 83562306a36Sopenharmony_ci{ 83662306a36Sopenharmony_ci if (which == V4L2_SUBDEV_FORMAT_TRY) 83762306a36Sopenharmony_ci return v4l2_subdev_get_try_format(&csi2->subdev, sd_state, 83862306a36Sopenharmony_ci pad); 83962306a36Sopenharmony_ci else 84062306a36Sopenharmony_ci return &csi2->formats[pad]; 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_cistatic void 84462306a36Sopenharmony_cicsi2_try_format(struct isp_csi2_device *csi2, 84562306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 84662306a36Sopenharmony_ci unsigned int pad, struct v4l2_mbus_framefmt *fmt, 84762306a36Sopenharmony_ci enum v4l2_subdev_format_whence which) 84862306a36Sopenharmony_ci{ 84962306a36Sopenharmony_ci u32 pixelcode; 85062306a36Sopenharmony_ci struct v4l2_mbus_framefmt *format; 85162306a36Sopenharmony_ci const struct isp_format_info *info; 85262306a36Sopenharmony_ci unsigned int i; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci switch (pad) { 85562306a36Sopenharmony_ci case CSI2_PAD_SINK: 85662306a36Sopenharmony_ci /* Clamp the width and height to valid range (1-8191). */ 85762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(csi2_input_fmts); i++) { 85862306a36Sopenharmony_ci if (fmt->code == csi2_input_fmts[i]) 85962306a36Sopenharmony_ci break; 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci /* If not found, use SGRBG10 as default */ 86362306a36Sopenharmony_ci if (i >= ARRAY_SIZE(csi2_input_fmts)) 86462306a36Sopenharmony_ci fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci fmt->width = clamp_t(u32, fmt->width, 1, 8191); 86762306a36Sopenharmony_ci fmt->height = clamp_t(u32, fmt->height, 1, 8191); 86862306a36Sopenharmony_ci break; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci case CSI2_PAD_SOURCE: 87162306a36Sopenharmony_ci /* Source format same as sink format, except for DPCM 87262306a36Sopenharmony_ci * compression. 87362306a36Sopenharmony_ci */ 87462306a36Sopenharmony_ci pixelcode = fmt->code; 87562306a36Sopenharmony_ci format = __csi2_get_format(csi2, sd_state, CSI2_PAD_SINK, 87662306a36Sopenharmony_ci which); 87762306a36Sopenharmony_ci memcpy(fmt, format, sizeof(*fmt)); 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci /* 88062306a36Sopenharmony_ci * Only Allow DPCM decompression, and check that the 88162306a36Sopenharmony_ci * pattern is preserved 88262306a36Sopenharmony_ci */ 88362306a36Sopenharmony_ci info = omap3isp_video_format_info(fmt->code); 88462306a36Sopenharmony_ci if (info->uncompressed == pixelcode) 88562306a36Sopenharmony_ci fmt->code = pixelcode; 88662306a36Sopenharmony_ci break; 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci /* RGB, non-interlaced */ 89062306a36Sopenharmony_ci fmt->colorspace = V4L2_COLORSPACE_SRGB; 89162306a36Sopenharmony_ci fmt->field = V4L2_FIELD_NONE; 89262306a36Sopenharmony_ci} 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci/* 89562306a36Sopenharmony_ci * csi2_enum_mbus_code - Handle pixel format enumeration 89662306a36Sopenharmony_ci * @sd : pointer to v4l2 subdev structure 89762306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 89862306a36Sopenharmony_ci * @code : pointer to v4l2_subdev_mbus_code_enum structure 89962306a36Sopenharmony_ci * return -EINVAL or zero on success 90062306a36Sopenharmony_ci */ 90162306a36Sopenharmony_cistatic int csi2_enum_mbus_code(struct v4l2_subdev *sd, 90262306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 90362306a36Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 90462306a36Sopenharmony_ci{ 90562306a36Sopenharmony_ci struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); 90662306a36Sopenharmony_ci struct v4l2_mbus_framefmt *format; 90762306a36Sopenharmony_ci const struct isp_format_info *info; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci if (code->pad == CSI2_PAD_SINK) { 91062306a36Sopenharmony_ci if (code->index >= ARRAY_SIZE(csi2_input_fmts)) 91162306a36Sopenharmony_ci return -EINVAL; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci code->code = csi2_input_fmts[code->index]; 91462306a36Sopenharmony_ci } else { 91562306a36Sopenharmony_ci format = __csi2_get_format(csi2, sd_state, CSI2_PAD_SINK, 91662306a36Sopenharmony_ci code->which); 91762306a36Sopenharmony_ci switch (code->index) { 91862306a36Sopenharmony_ci case 0: 91962306a36Sopenharmony_ci /* Passthrough sink pad code */ 92062306a36Sopenharmony_ci code->code = format->code; 92162306a36Sopenharmony_ci break; 92262306a36Sopenharmony_ci case 1: 92362306a36Sopenharmony_ci /* Uncompressed code */ 92462306a36Sopenharmony_ci info = omap3isp_video_format_info(format->code); 92562306a36Sopenharmony_ci if (info->uncompressed == format->code) 92662306a36Sopenharmony_ci return -EINVAL; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci code->code = info->uncompressed; 92962306a36Sopenharmony_ci break; 93062306a36Sopenharmony_ci default: 93162306a36Sopenharmony_ci return -EINVAL; 93262306a36Sopenharmony_ci } 93362306a36Sopenharmony_ci } 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci return 0; 93662306a36Sopenharmony_ci} 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_cistatic int csi2_enum_frame_size(struct v4l2_subdev *sd, 93962306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 94062306a36Sopenharmony_ci struct v4l2_subdev_frame_size_enum *fse) 94162306a36Sopenharmony_ci{ 94262306a36Sopenharmony_ci struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); 94362306a36Sopenharmony_ci struct v4l2_mbus_framefmt format; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci if (fse->index != 0) 94662306a36Sopenharmony_ci return -EINVAL; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci format.code = fse->code; 94962306a36Sopenharmony_ci format.width = 1; 95062306a36Sopenharmony_ci format.height = 1; 95162306a36Sopenharmony_ci csi2_try_format(csi2, sd_state, fse->pad, &format, fse->which); 95262306a36Sopenharmony_ci fse->min_width = format.width; 95362306a36Sopenharmony_ci fse->min_height = format.height; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci if (format.code != fse->code) 95662306a36Sopenharmony_ci return -EINVAL; 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci format.code = fse->code; 95962306a36Sopenharmony_ci format.width = -1; 96062306a36Sopenharmony_ci format.height = -1; 96162306a36Sopenharmony_ci csi2_try_format(csi2, sd_state, fse->pad, &format, fse->which); 96262306a36Sopenharmony_ci fse->max_width = format.width; 96362306a36Sopenharmony_ci fse->max_height = format.height; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci return 0; 96662306a36Sopenharmony_ci} 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci/* 96962306a36Sopenharmony_ci * csi2_get_format - Handle get format by pads subdev method 97062306a36Sopenharmony_ci * @sd : pointer to v4l2 subdev structure 97162306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 97262306a36Sopenharmony_ci * @fmt: pointer to v4l2 subdev format structure 97362306a36Sopenharmony_ci * return -EINVAL or zero on success 97462306a36Sopenharmony_ci */ 97562306a36Sopenharmony_cistatic int csi2_get_format(struct v4l2_subdev *sd, 97662306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 97762306a36Sopenharmony_ci struct v4l2_subdev_format *fmt) 97862306a36Sopenharmony_ci{ 97962306a36Sopenharmony_ci struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); 98062306a36Sopenharmony_ci struct v4l2_mbus_framefmt *format; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci format = __csi2_get_format(csi2, sd_state, fmt->pad, fmt->which); 98362306a36Sopenharmony_ci if (format == NULL) 98462306a36Sopenharmony_ci return -EINVAL; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci fmt->format = *format; 98762306a36Sopenharmony_ci return 0; 98862306a36Sopenharmony_ci} 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci/* 99162306a36Sopenharmony_ci * csi2_set_format - Handle set format by pads subdev method 99262306a36Sopenharmony_ci * @sd : pointer to v4l2 subdev structure 99362306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 99462306a36Sopenharmony_ci * @fmt: pointer to v4l2 subdev format structure 99562306a36Sopenharmony_ci * return -EINVAL or zero on success 99662306a36Sopenharmony_ci */ 99762306a36Sopenharmony_cistatic int csi2_set_format(struct v4l2_subdev *sd, 99862306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 99962306a36Sopenharmony_ci struct v4l2_subdev_format *fmt) 100062306a36Sopenharmony_ci{ 100162306a36Sopenharmony_ci struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); 100262306a36Sopenharmony_ci struct v4l2_mbus_framefmt *format; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci format = __csi2_get_format(csi2, sd_state, fmt->pad, fmt->which); 100562306a36Sopenharmony_ci if (format == NULL) 100662306a36Sopenharmony_ci return -EINVAL; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci csi2_try_format(csi2, sd_state, fmt->pad, &fmt->format, fmt->which); 100962306a36Sopenharmony_ci *format = fmt->format; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci /* Propagate the format from sink to source */ 101262306a36Sopenharmony_ci if (fmt->pad == CSI2_PAD_SINK) { 101362306a36Sopenharmony_ci format = __csi2_get_format(csi2, sd_state, CSI2_PAD_SOURCE, 101462306a36Sopenharmony_ci fmt->which); 101562306a36Sopenharmony_ci *format = fmt->format; 101662306a36Sopenharmony_ci csi2_try_format(csi2, sd_state, CSI2_PAD_SOURCE, format, 101762306a36Sopenharmony_ci fmt->which); 101862306a36Sopenharmony_ci } 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci return 0; 102162306a36Sopenharmony_ci} 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci/* 102462306a36Sopenharmony_ci * csi2_init_formats - Initialize formats on all pads 102562306a36Sopenharmony_ci * @sd: ISP CSI2 V4L2 subdevice 102662306a36Sopenharmony_ci * @fh: V4L2 subdev file handle 102762306a36Sopenharmony_ci * 102862306a36Sopenharmony_ci * Initialize all pad formats with default values. If fh is not NULL, try 102962306a36Sopenharmony_ci * formats are initialized on the file handle. Otherwise active formats are 103062306a36Sopenharmony_ci * initialized on the device. 103162306a36Sopenharmony_ci */ 103262306a36Sopenharmony_cistatic int csi2_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 103362306a36Sopenharmony_ci{ 103462306a36Sopenharmony_ci struct v4l2_subdev_format format; 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci memset(&format, 0, sizeof(format)); 103762306a36Sopenharmony_ci format.pad = CSI2_PAD_SINK; 103862306a36Sopenharmony_ci format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; 103962306a36Sopenharmony_ci format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10; 104062306a36Sopenharmony_ci format.format.width = 4096; 104162306a36Sopenharmony_ci format.format.height = 4096; 104262306a36Sopenharmony_ci csi2_set_format(sd, fh ? fh->state : NULL, &format); 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci return 0; 104562306a36Sopenharmony_ci} 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci/* 104862306a36Sopenharmony_ci * csi2_set_stream - Enable/Disable streaming on the CSI2 module 104962306a36Sopenharmony_ci * @sd: ISP CSI2 V4L2 subdevice 105062306a36Sopenharmony_ci * @enable: ISP pipeline stream state 105162306a36Sopenharmony_ci * 105262306a36Sopenharmony_ci * Return 0 on success or a negative error code otherwise. 105362306a36Sopenharmony_ci */ 105462306a36Sopenharmony_cistatic int csi2_set_stream(struct v4l2_subdev *sd, int enable) 105562306a36Sopenharmony_ci{ 105662306a36Sopenharmony_ci struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); 105762306a36Sopenharmony_ci struct isp_device *isp = csi2->isp; 105862306a36Sopenharmony_ci struct isp_video *video_out = &csi2->video_out; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci switch (enable) { 106162306a36Sopenharmony_ci case ISP_PIPELINE_STREAM_CONTINUOUS: 106262306a36Sopenharmony_ci if (omap3isp_csiphy_acquire(csi2->phy, &sd->entity) < 0) 106362306a36Sopenharmony_ci return -ENODEV; 106462306a36Sopenharmony_ci if (csi2->output & CSI2_OUTPUT_MEMORY) 106562306a36Sopenharmony_ci omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CSI2A_WRITE); 106662306a36Sopenharmony_ci csi2_configure(csi2); 106762306a36Sopenharmony_ci csi2_print_status(csi2); 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci /* 107062306a36Sopenharmony_ci * When outputting to memory with no buffer available, let the 107162306a36Sopenharmony_ci * buffer queue handler start the hardware. A DMA queue flag 107262306a36Sopenharmony_ci * ISP_VIDEO_DMAQUEUE_QUEUED will be set as soon as there is 107362306a36Sopenharmony_ci * a buffer available. 107462306a36Sopenharmony_ci */ 107562306a36Sopenharmony_ci if (csi2->output & CSI2_OUTPUT_MEMORY && 107662306a36Sopenharmony_ci !(video_out->dmaqueue_flags & ISP_VIDEO_DMAQUEUE_QUEUED)) 107762306a36Sopenharmony_ci break; 107862306a36Sopenharmony_ci /* Enable context 0 and IRQs */ 107962306a36Sopenharmony_ci atomic_set(&csi2->stopping, 0); 108062306a36Sopenharmony_ci csi2_ctx_enable(isp, csi2, 0, 1); 108162306a36Sopenharmony_ci csi2_if_enable(isp, csi2, 1); 108262306a36Sopenharmony_ci isp_video_dmaqueue_flags_clr(video_out); 108362306a36Sopenharmony_ci break; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci case ISP_PIPELINE_STREAM_STOPPED: 108662306a36Sopenharmony_ci if (csi2->state == ISP_PIPELINE_STREAM_STOPPED) 108762306a36Sopenharmony_ci return 0; 108862306a36Sopenharmony_ci if (omap3isp_module_sync_idle(&sd->entity, &csi2->wait, 108962306a36Sopenharmony_ci &csi2->stopping)) 109062306a36Sopenharmony_ci dev_dbg(isp->dev, "%s: module stop timeout.\n", 109162306a36Sopenharmony_ci sd->name); 109262306a36Sopenharmony_ci csi2_ctx_enable(isp, csi2, 0, 0); 109362306a36Sopenharmony_ci csi2_if_enable(isp, csi2, 0); 109462306a36Sopenharmony_ci csi2_irq_ctx_set(isp, csi2, 0); 109562306a36Sopenharmony_ci omap3isp_csiphy_release(csi2->phy); 109662306a36Sopenharmony_ci isp_video_dmaqueue_flags_clr(video_out); 109762306a36Sopenharmony_ci omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_CSI2A_WRITE); 109862306a36Sopenharmony_ci break; 109962306a36Sopenharmony_ci } 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci csi2->state = enable; 110262306a36Sopenharmony_ci return 0; 110362306a36Sopenharmony_ci} 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci/* subdev video operations */ 110662306a36Sopenharmony_cistatic const struct v4l2_subdev_video_ops csi2_video_ops = { 110762306a36Sopenharmony_ci .s_stream = csi2_set_stream, 110862306a36Sopenharmony_ci}; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci/* subdev pad operations */ 111162306a36Sopenharmony_cistatic const struct v4l2_subdev_pad_ops csi2_pad_ops = { 111262306a36Sopenharmony_ci .enum_mbus_code = csi2_enum_mbus_code, 111362306a36Sopenharmony_ci .enum_frame_size = csi2_enum_frame_size, 111462306a36Sopenharmony_ci .get_fmt = csi2_get_format, 111562306a36Sopenharmony_ci .set_fmt = csi2_set_format, 111662306a36Sopenharmony_ci}; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci/* subdev operations */ 111962306a36Sopenharmony_cistatic const struct v4l2_subdev_ops csi2_ops = { 112062306a36Sopenharmony_ci .video = &csi2_video_ops, 112162306a36Sopenharmony_ci .pad = &csi2_pad_ops, 112262306a36Sopenharmony_ci}; 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci/* subdev internal operations */ 112562306a36Sopenharmony_cistatic const struct v4l2_subdev_internal_ops csi2_internal_ops = { 112662306a36Sopenharmony_ci .open = csi2_init_formats, 112762306a36Sopenharmony_ci}; 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 113062306a36Sopenharmony_ci * Media entity operations 113162306a36Sopenharmony_ci */ 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci/* 113462306a36Sopenharmony_ci * csi2_link_setup - Setup CSI2 connections. 113562306a36Sopenharmony_ci * @entity : Pointer to media entity structure 113662306a36Sopenharmony_ci * @local : Pointer to local pad array 113762306a36Sopenharmony_ci * @remote : Pointer to remote pad array 113862306a36Sopenharmony_ci * @flags : Link flags 113962306a36Sopenharmony_ci * return -EINVAL or zero on success 114062306a36Sopenharmony_ci */ 114162306a36Sopenharmony_cistatic int csi2_link_setup(struct media_entity *entity, 114262306a36Sopenharmony_ci const struct media_pad *local, 114362306a36Sopenharmony_ci const struct media_pad *remote, u32 flags) 114462306a36Sopenharmony_ci{ 114562306a36Sopenharmony_ci struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); 114662306a36Sopenharmony_ci struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); 114762306a36Sopenharmony_ci struct isp_csi2_ctrl_cfg *ctrl = &csi2->ctrl; 114862306a36Sopenharmony_ci unsigned int index = local->index; 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci /* 115162306a36Sopenharmony_ci * The ISP core doesn't support pipelines with multiple video outputs. 115262306a36Sopenharmony_ci * Revisit this when it will be implemented, and return -EBUSY for now. 115362306a36Sopenharmony_ci */ 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci /* FIXME: this is actually a hack! */ 115662306a36Sopenharmony_ci if (is_media_entity_v4l2_subdev(remote->entity)) 115762306a36Sopenharmony_ci index |= 2 << 16; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci switch (index) { 116062306a36Sopenharmony_ci case CSI2_PAD_SOURCE: 116162306a36Sopenharmony_ci if (flags & MEDIA_LNK_FL_ENABLED) { 116262306a36Sopenharmony_ci if (csi2->output & ~CSI2_OUTPUT_MEMORY) 116362306a36Sopenharmony_ci return -EBUSY; 116462306a36Sopenharmony_ci csi2->output |= CSI2_OUTPUT_MEMORY; 116562306a36Sopenharmony_ci } else { 116662306a36Sopenharmony_ci csi2->output &= ~CSI2_OUTPUT_MEMORY; 116762306a36Sopenharmony_ci } 116862306a36Sopenharmony_ci break; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci case CSI2_PAD_SOURCE | 2 << 16: 117162306a36Sopenharmony_ci if (flags & MEDIA_LNK_FL_ENABLED) { 117262306a36Sopenharmony_ci if (csi2->output & ~CSI2_OUTPUT_CCDC) 117362306a36Sopenharmony_ci return -EBUSY; 117462306a36Sopenharmony_ci csi2->output |= CSI2_OUTPUT_CCDC; 117562306a36Sopenharmony_ci } else { 117662306a36Sopenharmony_ci csi2->output &= ~CSI2_OUTPUT_CCDC; 117762306a36Sopenharmony_ci } 117862306a36Sopenharmony_ci break; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci default: 118162306a36Sopenharmony_ci /* Link from camera to CSI2 is fixed... */ 118262306a36Sopenharmony_ci return -EINVAL; 118362306a36Sopenharmony_ci } 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci ctrl->vp_only_enable = 118662306a36Sopenharmony_ci (csi2->output & CSI2_OUTPUT_MEMORY) ? false : true; 118762306a36Sopenharmony_ci ctrl->vp_clk_enable = !!(csi2->output & CSI2_OUTPUT_CCDC); 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci return 0; 119062306a36Sopenharmony_ci} 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci/* media operations */ 119362306a36Sopenharmony_cistatic const struct media_entity_operations csi2_media_ops = { 119462306a36Sopenharmony_ci .link_setup = csi2_link_setup, 119562306a36Sopenharmony_ci .link_validate = v4l2_subdev_link_validate, 119662306a36Sopenharmony_ci}; 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_civoid omap3isp_csi2_unregister_entities(struct isp_csi2_device *csi2) 119962306a36Sopenharmony_ci{ 120062306a36Sopenharmony_ci v4l2_device_unregister_subdev(&csi2->subdev); 120162306a36Sopenharmony_ci omap3isp_video_unregister(&csi2->video_out); 120262306a36Sopenharmony_ci} 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ciint omap3isp_csi2_register_entities(struct isp_csi2_device *csi2, 120562306a36Sopenharmony_ci struct v4l2_device *vdev) 120662306a36Sopenharmony_ci{ 120762306a36Sopenharmony_ci int ret; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci /* Register the subdev and video nodes. */ 121062306a36Sopenharmony_ci csi2->subdev.dev = vdev->mdev->dev; 121162306a36Sopenharmony_ci ret = v4l2_device_register_subdev(vdev, &csi2->subdev); 121262306a36Sopenharmony_ci if (ret < 0) 121362306a36Sopenharmony_ci goto error; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci ret = omap3isp_video_register(&csi2->video_out, vdev); 121662306a36Sopenharmony_ci if (ret < 0) 121762306a36Sopenharmony_ci goto error; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci return 0; 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_cierror: 122262306a36Sopenharmony_ci omap3isp_csi2_unregister_entities(csi2); 122362306a36Sopenharmony_ci return ret; 122462306a36Sopenharmony_ci} 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 122762306a36Sopenharmony_ci * ISP CSI2 initialisation and cleanup 122862306a36Sopenharmony_ci */ 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci/* 123162306a36Sopenharmony_ci * csi2_init_entities - Initialize subdev and media entity. 123262306a36Sopenharmony_ci * @csi2: Pointer to csi2 structure. 123362306a36Sopenharmony_ci * return -ENOMEM or zero on success 123462306a36Sopenharmony_ci */ 123562306a36Sopenharmony_cistatic int csi2_init_entities(struct isp_csi2_device *csi2) 123662306a36Sopenharmony_ci{ 123762306a36Sopenharmony_ci struct v4l2_subdev *sd = &csi2->subdev; 123862306a36Sopenharmony_ci struct media_pad *pads = csi2->pads; 123962306a36Sopenharmony_ci struct media_entity *me = &sd->entity; 124062306a36Sopenharmony_ci int ret; 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci v4l2_subdev_init(sd, &csi2_ops); 124362306a36Sopenharmony_ci sd->internal_ops = &csi2_internal_ops; 124462306a36Sopenharmony_ci strscpy(sd->name, "OMAP3 ISP CSI2a", sizeof(sd->name)); 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci sd->grp_id = 1 << 16; /* group ID for isp subdevs */ 124762306a36Sopenharmony_ci v4l2_set_subdevdata(sd, csi2); 124862306a36Sopenharmony_ci sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci pads[CSI2_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; 125162306a36Sopenharmony_ci pads[CSI2_PAD_SINK].flags = MEDIA_PAD_FL_SINK 125262306a36Sopenharmony_ci | MEDIA_PAD_FL_MUST_CONNECT; 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci me->ops = &csi2_media_ops; 125562306a36Sopenharmony_ci ret = media_entity_pads_init(me, CSI2_PADS_NUM, pads); 125662306a36Sopenharmony_ci if (ret < 0) 125762306a36Sopenharmony_ci return ret; 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci csi2_init_formats(sd, NULL); 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci /* Video device node */ 126262306a36Sopenharmony_ci csi2->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 126362306a36Sopenharmony_ci csi2->video_out.ops = &csi2_ispvideo_ops; 126462306a36Sopenharmony_ci csi2->video_out.bpl_alignment = 32; 126562306a36Sopenharmony_ci csi2->video_out.bpl_zero_padding = 1; 126662306a36Sopenharmony_ci csi2->video_out.bpl_max = 0x1ffe0; 126762306a36Sopenharmony_ci csi2->video_out.isp = csi2->isp; 126862306a36Sopenharmony_ci csi2->video_out.capture_mem = PAGE_ALIGN(4096 * 4096) * 3; 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci ret = omap3isp_video_init(&csi2->video_out, "CSI2a"); 127162306a36Sopenharmony_ci if (ret < 0) 127262306a36Sopenharmony_ci goto error_video; 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci return 0; 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_cierror_video: 127762306a36Sopenharmony_ci media_entity_cleanup(&csi2->subdev.entity); 127862306a36Sopenharmony_ci return ret; 127962306a36Sopenharmony_ci} 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci/* 128262306a36Sopenharmony_ci * omap3isp_csi2_init - Routine for module driver init 128362306a36Sopenharmony_ci */ 128462306a36Sopenharmony_ciint omap3isp_csi2_init(struct isp_device *isp) 128562306a36Sopenharmony_ci{ 128662306a36Sopenharmony_ci struct isp_csi2_device *csi2a = &isp->isp_csi2a; 128762306a36Sopenharmony_ci struct isp_csi2_device *csi2c = &isp->isp_csi2c; 128862306a36Sopenharmony_ci int ret; 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci csi2a->isp = isp; 129162306a36Sopenharmony_ci csi2a->available = 1; 129262306a36Sopenharmony_ci csi2a->regs1 = OMAP3_ISP_IOMEM_CSI2A_REGS1; 129362306a36Sopenharmony_ci csi2a->regs2 = OMAP3_ISP_IOMEM_CSI2A_REGS2; 129462306a36Sopenharmony_ci csi2a->phy = &isp->isp_csiphy2; 129562306a36Sopenharmony_ci csi2a->state = ISP_PIPELINE_STREAM_STOPPED; 129662306a36Sopenharmony_ci init_waitqueue_head(&csi2a->wait); 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci ret = csi2_init_entities(csi2a); 129962306a36Sopenharmony_ci if (ret < 0) 130062306a36Sopenharmony_ci return ret; 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci if (isp->revision == ISP_REVISION_15_0) { 130362306a36Sopenharmony_ci csi2c->isp = isp; 130462306a36Sopenharmony_ci csi2c->available = 1; 130562306a36Sopenharmony_ci csi2c->regs1 = OMAP3_ISP_IOMEM_CSI2C_REGS1; 130662306a36Sopenharmony_ci csi2c->regs2 = OMAP3_ISP_IOMEM_CSI2C_REGS2; 130762306a36Sopenharmony_ci csi2c->phy = &isp->isp_csiphy1; 130862306a36Sopenharmony_ci csi2c->state = ISP_PIPELINE_STREAM_STOPPED; 130962306a36Sopenharmony_ci init_waitqueue_head(&csi2c->wait); 131062306a36Sopenharmony_ci } 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci return 0; 131362306a36Sopenharmony_ci} 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci/* 131662306a36Sopenharmony_ci * omap3isp_csi2_cleanup - Routine for module driver cleanup 131762306a36Sopenharmony_ci */ 131862306a36Sopenharmony_civoid omap3isp_csi2_cleanup(struct isp_device *isp) 131962306a36Sopenharmony_ci{ 132062306a36Sopenharmony_ci struct isp_csi2_device *csi2a = &isp->isp_csi2a; 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_ci omap3isp_video_cleanup(&csi2a->video_out); 132362306a36Sopenharmony_ci media_entity_cleanup(&csi2a->subdev.entity); 132462306a36Sopenharmony_ci} 1325