--- zzzz-none-000/linux-3.10.107/drivers/media/platform/omap3isp/ispccdc.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/media/platform/omap3isp/ispccdc.c 2021-02-04 17:41:59.000000000 +0000 @@ -12,16 +12,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA */ #include @@ -30,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -43,27 +32,27 @@ #define CCDC_MIN_HEIGHT 32 static struct v4l2_mbus_framefmt * -__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, +__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_pad_config *cfg, unsigned int pad, enum v4l2_subdev_format_whence which); static const unsigned int ccdc_fmts[] = { - V4L2_MBUS_FMT_Y8_1X8, - V4L2_MBUS_FMT_Y10_1X10, - V4L2_MBUS_FMT_Y12_1X12, - V4L2_MBUS_FMT_SGRBG8_1X8, - V4L2_MBUS_FMT_SRGGB8_1X8, - V4L2_MBUS_FMT_SBGGR8_1X8, - V4L2_MBUS_FMT_SGBRG8_1X8, - V4L2_MBUS_FMT_SGRBG10_1X10, - V4L2_MBUS_FMT_SRGGB10_1X10, - V4L2_MBUS_FMT_SBGGR10_1X10, - V4L2_MBUS_FMT_SGBRG10_1X10, - V4L2_MBUS_FMT_SGRBG12_1X12, - V4L2_MBUS_FMT_SRGGB12_1X12, - V4L2_MBUS_FMT_SBGGR12_1X12, - V4L2_MBUS_FMT_SGBRG12_1X12, - V4L2_MBUS_FMT_YUYV8_2X8, - V4L2_MBUS_FMT_UYVY8_2X8, + MEDIA_BUS_FMT_Y8_1X8, + MEDIA_BUS_FMT_Y10_1X10, + MEDIA_BUS_FMT_Y12_1X12, + MEDIA_BUS_FMT_SGRBG8_1X8, + MEDIA_BUS_FMT_SRGGB8_1X8, + MEDIA_BUS_FMT_SBGGR8_1X8, + MEDIA_BUS_FMT_SGBRG8_1X8, + MEDIA_BUS_FMT_SGRBG10_1X10, + MEDIA_BUS_FMT_SRGGB10_1X10, + MEDIA_BUS_FMT_SBGGR10_1X10, + MEDIA_BUS_FMT_SGBRG10_1X10, + MEDIA_BUS_FMT_SGRBG12_1X12, + MEDIA_BUS_FMT_SRGGB12_1X12, + MEDIA_BUS_FMT_SBGGR12_1X12, + MEDIA_BUS_FMT_SGBRG12_1X12, + MEDIA_BUS_FMT_YUYV8_2X8, + MEDIA_BUS_FMT_UYVY8_2X8, }; /* @@ -206,7 +195,8 @@ * ccdc_lsc_program_table - Program Lens Shading Compensation table address. * @ccdc: Pointer to ISP CCDC device. */ -static void ccdc_lsc_program_table(struct isp_ccdc_device *ccdc, u32 addr) +static void ccdc_lsc_program_table(struct isp_ccdc_device *ccdc, + dma_addr_t addr) { isp_reg_writel(to_isp_device(ccdc), addr, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_TABLE_BASE); @@ -276,10 +266,10 @@ __ccdc_get_format(ccdc, NULL, CCDC_PAD_SINK, V4L2_SUBDEV_FORMAT_ACTIVE); - if ((format->code != V4L2_MBUS_FMT_SGRBG10_1X10) && - (format->code != V4L2_MBUS_FMT_SRGGB10_1X10) && - (format->code != V4L2_MBUS_FMT_SBGGR10_1X10) && - (format->code != V4L2_MBUS_FMT_SGBRG10_1X10)) + if ((format->code != MEDIA_BUS_FMT_SGRBG10_1X10) && + (format->code != MEDIA_BUS_FMT_SRGGB10_1X10) && + (format->code != MEDIA_BUS_FMT_SBGGR10_1X10) && + (format->code != MEDIA_BUS_FMT_SGBRG10_1X10)) return -EINVAL; if (enable) @@ -293,7 +283,7 @@ isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_LSC_CONFIG, ISPCCDC_LSC_ENABLE); ccdc->lsc.state = LSC_STATE_STOPPED; - dev_warn(to_device(ccdc), "LSC prefecth timeout\n"); + dev_warn(to_device(ccdc), "LSC prefetch timeout\n"); return -ETIMEDOUT; } ccdc->lsc.state = LSC_STATE_RUNNING; @@ -333,7 +323,7 @@ return -EBUSY; ccdc_lsc_setup_regs(ccdc, &req->config); - ccdc_lsc_program_table(ccdc, req->table); + ccdc_lsc_program_table(ccdc, req->table.dma); return 0; } @@ -368,11 +358,12 @@ if (req == NULL) return; - if (req->iovm) - dma_unmap_sg(isp->dev, req->iovm->sgt->sgl, - req->iovm->sgt->nents, DMA_TO_DEVICE); - if (req->table) - omap_iommu_vfree(isp->domain, isp->dev, req->table); + if (req->table.addr) { + sg_free_table(&req->table.sgt); + dma_free_coherent(isp->dev, req->config.size, req->table.addr, + req->table.dma); + } + kfree(req); } @@ -416,7 +407,6 @@ struct isp_device *isp = to_isp_device(ccdc); struct ispccdc_lsc_config_req *req; unsigned long flags; - void *table; u16 update; int ret; @@ -444,38 +434,31 @@ req->enable = 1; - req->table = omap_iommu_vmalloc(isp->domain, isp->dev, 0, - req->config.size, IOMMU_FLAG); - if (IS_ERR_VALUE(req->table)) { - req->table = 0; - ret = -ENOMEM; - goto done; - } - - req->iovm = omap_find_iovm_area(isp->dev, req->table); - if (req->iovm == NULL) { + req->table.addr = dma_alloc_coherent(isp->dev, req->config.size, + &req->table.dma, + GFP_KERNEL); + if (req->table.addr == NULL) { ret = -ENOMEM; goto done; } - if (!dma_map_sg(isp->dev, req->iovm->sgt->sgl, - req->iovm->sgt->nents, DMA_TO_DEVICE)) { - ret = -ENOMEM; - req->iovm = NULL; + ret = dma_get_sgtable(isp->dev, &req->table.sgt, + req->table.addr, req->table.dma, + req->config.size); + if (ret < 0) goto done; - } - dma_sync_sg_for_cpu(isp->dev, req->iovm->sgt->sgl, - req->iovm->sgt->nents, DMA_TO_DEVICE); + dma_sync_sg_for_cpu(isp->dev, req->table.sgt.sgl, + req->table.sgt.nents, DMA_TO_DEVICE); - table = omap_da_to_va(isp->dev, req->table); - if (copy_from_user(table, config->lsc, req->config.size)) { + if (copy_from_user(req->table.addr, config->lsc, + req->config.size)) { ret = -EFAULT; goto done; } - dma_sync_sg_for_device(isp->dev, req->iovm->sgt->sgl, - req->iovm->sgt->nents, DMA_TO_DEVICE); + dma_sync_sg_for_device(isp->dev, req->table.sgt.sgl, + req->table.sgt.nents, DMA_TO_DEVICE); } spin_lock_irqsave(&ccdc->lsc.req_lock, flags); @@ -498,14 +481,13 @@ static inline int ccdc_lsc_is_configured(struct isp_ccdc_device *ccdc) { unsigned long flags; + int ret; spin_lock_irqsave(&ccdc->lsc.req_lock, flags); - if (ccdc->lsc.active) { - spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); - return 1; - } + ret = ccdc->lsc.active != NULL; spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); - return 0; + + return ret; } static int ccdc_lsc_enable(struct isp_ccdc_device *ccdc) @@ -584,7 +566,7 @@ if (!ccdc->fpc_en) return; - isp_reg_writel(isp, ccdc->fpc.fpcaddr, OMAP3_ISP_IOMEM_CCDC, + isp_reg_writel(isp, ccdc->fpc.dma, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FPC_ADDR); /* The FPNUM field must be set before enabling FPC. */ isp_reg_writel(isp, (ccdc->fpc.fpnum << ISPCCDC_FPC_FPNUM_SHIFT), @@ -674,7 +656,7 @@ /* * ccdc_config - Set CCDC configuration from userspace * @ccdc: Pointer to ISP CCDC device. - * @userspace_add: Structure containing CCDC configuration sent from userspace. + * @ccdc_struct: Structure containing CCDC configuration sent from userspace. * * Returns 0 if successful, -EINVAL if the pointer to the configuration * structure is null, or the copy_from_user function fails to copy user space @@ -724,8 +706,9 @@ ccdc->shadow_update = 0; if (OMAP3ISP_CCDC_FPC & ccdc_struct->update) { - u32 table_old = 0; - u32 table_new; + struct omap3isp_ccdc_fpc fpc; + struct ispccdc_fpc fpc_old = { .addr = NULL, }; + struct ispccdc_fpc fpc_new; u32 size; if (ccdc->state != ISP_PIPELINE_STREAM_STOPPED) @@ -734,35 +717,39 @@ ccdc->fpc_en = !!(OMAP3ISP_CCDC_FPC & ccdc_struct->flag); if (ccdc->fpc_en) { - if (copy_from_user(&ccdc->fpc, ccdc_struct->fpc, - sizeof(ccdc->fpc))) + if (copy_from_user(&fpc, ccdc_struct->fpc, sizeof(fpc))) return -EFAULT; + size = fpc.fpnum * 4; + /* - * table_new must be 64-bytes aligned, but it's - * already done by omap_iommu_vmalloc(). + * The table address must be 64-bytes aligned, which is + * guaranteed by dma_alloc_coherent(). */ - size = ccdc->fpc.fpnum * 4; - table_new = omap_iommu_vmalloc(isp->domain, isp->dev, - 0, size, IOMMU_FLAG); - if (IS_ERR_VALUE(table_new)) + fpc_new.fpnum = fpc.fpnum; + fpc_new.addr = dma_alloc_coherent(isp->dev, size, + &fpc_new.dma, + GFP_KERNEL); + if (fpc_new.addr == NULL) return -ENOMEM; - if (copy_from_user(omap_da_to_va(isp->dev, table_new), - (__force void __user *) - ccdc->fpc.fpcaddr, size)) { - omap_iommu_vfree(isp->domain, isp->dev, - table_new); + if (copy_from_user(fpc_new.addr, + (__force void __user *)fpc.fpcaddr, + size)) { + dma_free_coherent(isp->dev, size, fpc_new.addr, + fpc_new.dma); return -EFAULT; } - table_old = ccdc->fpc.fpcaddr; - ccdc->fpc.fpcaddr = table_new; + fpc_old = ccdc->fpc; + ccdc->fpc = fpc_new; } ccdc_configure_fpc(ccdc); - if (table_old != 0) - omap_iommu_vfree(isp->domain, isp->dev, table_old); + + if (fpc_old.addr != NULL) + dma_free_coherent(isp->dev, fpc_old.fpnum * 4, + fpc_old.addr, fpc_old.dma); } return ccdc_lsc_config(ccdc, ccdc_struct); @@ -793,7 +780,7 @@ /* * omap3isp_ccdc_restore_context - Restore values of the CCDC module registers - * @dev: Pointer to ISP device + * @isp: Pointer to ISP device */ void omap3isp_ccdc_restore_context(struct isp_device *isp) { @@ -820,29 +807,48 @@ struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity); struct isp_device *isp = to_isp_device(ccdc); const struct isp_format_info *info; + struct v4l2_mbus_framefmt *format; unsigned long l3_ick = pipe->l3_ick; unsigned int max_div = isp->revision == ISP_REVISION_15_0 ? 64 : 8; unsigned int div = 0; - u32 fmtcfg_vp; + u32 fmtcfg = ISPCCDC_FMTCFG_VPEN; + + format = &ccdc->formats[CCDC_PAD_SOURCE_VP]; + + if (!format->code) { + /* Disable the video port when the input format isn't supported. + * This is indicated by a pixel code set to 0. + */ + isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG); + return; + } + + isp_reg_writel(isp, (0 << ISPCCDC_FMT_HORZ_FMTSPH_SHIFT) | + (format->width << ISPCCDC_FMT_HORZ_FMTLNH_SHIFT), + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_HORZ); + isp_reg_writel(isp, (0 << ISPCCDC_FMT_VERT_FMTSLV_SHIFT) | + ((format->height + 1) << ISPCCDC_FMT_VERT_FMTLNV_SHIFT), + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_VERT); - fmtcfg_vp = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG) - & ~(ISPCCDC_FMTCFG_VPIN_MASK | ISPCCDC_FMTCFG_VPIF_FRQ_MASK); + isp_reg_writel(isp, (format->width << ISPCCDC_VP_OUT_HORZ_NUM_SHIFT) | + (format->height << ISPCCDC_VP_OUT_VERT_NUM_SHIFT), + OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VP_OUT); info = omap3isp_video_format_info(ccdc->formats[CCDC_PAD_SINK].code); switch (info->width) { case 8: case 10: - fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_9_0; + fmtcfg |= ISPCCDC_FMTCFG_VPIN_9_0; break; case 11: - fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_10_1; + fmtcfg |= ISPCCDC_FMTCFG_VPIN_10_1; break; case 12: - fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_11_2; + fmtcfg |= ISPCCDC_FMTCFG_VPIN_11_2; break; case 13: - fmtcfg_vp |= ISPCCDC_FMTCFG_VPIN_12_3; + fmtcfg |= ISPCCDC_FMTCFG_VPIN_12_3; break; } @@ -852,75 +858,59 @@ div = l3_ick / pipe->external_rate; div = clamp(div, 2U, max_div); - fmtcfg_vp |= (div - 2) << ISPCCDC_FMTCFG_VPIF_FRQ_SHIFT; + fmtcfg |= (div - 2) << ISPCCDC_FMTCFG_VPIF_FRQ_SHIFT; - isp_reg_writel(isp, fmtcfg_vp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG); -} - -/* - * ccdc_enable_vp - Enable Video Port. - * @ccdc: Pointer to ISP CCDC device. - * @enable: 0 Disables VP, 1 Enables VP - * - * This is needed for outputting image to Preview, H3A and HIST ISP submodules. - */ -static void ccdc_enable_vp(struct isp_ccdc_device *ccdc, u8 enable) -{ - struct isp_device *isp = to_isp_device(ccdc); - - isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG, - ISPCCDC_FMTCFG_VPEN, enable ? ISPCCDC_FMTCFG_VPEN : 0); + isp_reg_writel(isp, fmtcfg, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMTCFG); } /* * ccdc_config_outlineoffset - Configure memory saving output line offset * @ccdc: Pointer to ISP CCDC device. - * @offset: Address offset to start a new line. Must be twice the - * Output width and aligned on 32 byte boundary - * @oddeven: Specifies the odd/even line pattern to be chosen to store the - * output. - * @numlines: Set the value 0-3 for +1-4lines, 4-7 for -1-4lines. - * - * - Configures the output line offset when stored in memory - * - Sets the odd/even line pattern to store the output - * (EVENEVEN (1), ODDEVEN (2), EVENODD (3), ODDODD (4)) - * - Configures the number of even and odd line fields in case of rearranging - * the lines. + * @bpl: Number of bytes per line when stored in memory. + * @field: Field order when storing interlaced formats in memory. + * + * Configure the offsets for the line output control: + * + * - The horizontal line offset is defined as the number of bytes between the + * start of two consecutive lines in memory. Set it to the given bytes per + * line value. + * + * - The field offset value is defined as the number of lines to offset the + * start of the field identified by FID = 1. Set it to one. + * + * - The line offset values are defined as the number of lines (as defined by + * the horizontal line offset) between the start of two consecutive lines for + * all combinations of odd/even lines in odd/even fields. When interleaving + * fields set them all to two lines, and to one line otherwise. */ static void ccdc_config_outlineoffset(struct isp_ccdc_device *ccdc, - u32 offset, u8 oddeven, u8 numlines) + unsigned int bpl, + enum v4l2_field field) { struct isp_device *isp = to_isp_device(ccdc); + u32 sdofst = 0; - isp_reg_writel(isp, offset & 0xffff, - OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HSIZE_OFF); - - isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, - ISPCCDC_SDOFST_FINV); - - isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, - ISPCCDC_SDOFST_FOFST_4L); + isp_reg_writel(isp, bpl & 0xffff, OMAP3_ISP_IOMEM_CCDC, + ISPCCDC_HSIZE_OFF); - switch (oddeven) { - case EVENEVEN: - isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, - (numlines & 0x7) << ISPCCDC_SDOFST_LOFST0_SHIFT); - break; - case ODDEVEN: - isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, - (numlines & 0x7) << ISPCCDC_SDOFST_LOFST1_SHIFT); - break; - case EVENODD: - isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, - (numlines & 0x7) << ISPCCDC_SDOFST_LOFST2_SHIFT); - break; - case ODDODD: - isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST, - (numlines & 0x7) << ISPCCDC_SDOFST_LOFST3_SHIFT); + switch (field) { + case V4L2_FIELD_INTERLACED_TB: + case V4L2_FIELD_INTERLACED_BT: + /* When interleaving fields in memory offset field one by one + * line and set the line offset to two lines. + */ + sdofst |= (1 << ISPCCDC_SDOFST_LOFST0_SHIFT) + | (1 << ISPCCDC_SDOFST_LOFST1_SHIFT) + | (1 << ISPCCDC_SDOFST_LOFST2_SHIFT) + | (1 << ISPCCDC_SDOFST_LOFST3_SHIFT); break; + default: + /* In all other cases set the line offsets to one line. */ break; } + + isp_reg_writel(isp, sdofst, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SDOFST); } /* @@ -968,11 +958,11 @@ /* * ccdc_config_sync_if - Set CCDC sync interface configuration * @ccdc: Pointer to ISP CCDC device. - * @pdata: Parallel interface platform data (may be NULL) + * @parcfg: Parallel interface platform data (may be NULL) * @data_size: Data size */ static void ccdc_config_sync_if(struct isp_ccdc_device *ccdc, - struct isp_parallel_platform_data *pdata, + struct isp_parallel_cfg *parcfg, unsigned int data_size) { struct isp_device *isp = to_isp_device(ccdc); @@ -981,12 +971,18 @@ format = &ccdc->formats[CCDC_PAD_SINK]; - if (format->code == V4L2_MBUS_FMT_YUYV8_2X8 || - format->code == V4L2_MBUS_FMT_UYVY8_2X8) { - /* The bridge is enabled for YUV8 formats. Configure the input - * mode accordingly. + if (format->code == MEDIA_BUS_FMT_YUYV8_2X8 || + format->code == MEDIA_BUS_FMT_UYVY8_2X8) { + /* According to the OMAP3 TRM the input mode only affects SYNC + * mode, enabling BT.656 mode should take precedence. However, + * in practice setting the input mode to YCbCr data on 8 bits + * seems to be required in BT.656 mode. In SYNC mode set it to + * YCbCr on 16 bits as the bridge is enabled in that case. */ - syn_mode |= ISPCCDC_SYN_MODE_INPMOD_YCBCR16; + if (ccdc->bt656) + syn_mode |= ISPCCDC_SYN_MODE_INPMOD_YCBCR8; + else + syn_mode |= ISPCCDC_SYN_MODE_INPMOD_YCBCR16; } switch (data_size) { @@ -1004,29 +1000,43 @@ break; } - if (pdata && pdata->data_pol) + if (parcfg && parcfg->data_pol) syn_mode |= ISPCCDC_SYN_MODE_DATAPOL; - if (pdata && pdata->hs_pol) + if (parcfg && parcfg->hs_pol) syn_mode |= ISPCCDC_SYN_MODE_HDPOL; - if (pdata && pdata->vs_pol) + /* The polarity of the vertical sync signal output by the BT.656 + * decoder is not documented and seems to be active low. + */ + if ((parcfg && parcfg->vs_pol) || ccdc->bt656) syn_mode |= ISPCCDC_SYN_MODE_VDPOL; + if (parcfg && parcfg->fld_pol) + syn_mode |= ISPCCDC_SYN_MODE_FLDPOL; + isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); /* The CCDC_CFG.Y8POS bit is used in YCbCr8 input mode only. The * hardware seems to ignore it in all other input modes. */ - if (format->code == V4L2_MBUS_FMT_UYVY8_2X8) + if (format->code == MEDIA_BUS_FMT_UYVY8_2X8) isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, ISPCCDC_CFG_Y8POS); else isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, ISPCCDC_CFG_Y8POS); - isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_REC656IF, - ISPCCDC_REC656IF_R656ON); + /* Enable or disable BT.656 mode, including error correction for the + * synchronization codes. + */ + if (ccdc->bt656) + isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_REC656IF, + ISPCCDC_REC656IF_R656ON | ISPCCDC_REC656IF_ECCFVH); + else + isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_REC656IF, + ISPCCDC_REC656IF_R656ON | ISPCCDC_REC656IF_ECCFVH); + } /* CCDC formats descriptions */ @@ -1105,7 +1115,7 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) { struct isp_device *isp = to_isp_device(ccdc); - struct isp_parallel_platform_data *pdata = NULL; + struct isp_parallel_cfg *parcfg = NULL; struct v4l2_subdev *sensor; struct v4l2_mbus_framefmt *format; const struct v4l2_rect *crop; @@ -1117,17 +1127,33 @@ unsigned long flags; unsigned int bridge; unsigned int shift; + unsigned int nph; + unsigned int sph; u32 syn_mode; u32 ccdc_pattern; - pad = media_entity_remote_source(&ccdc->pads[CCDC_PAD_SINK]); + ccdc->bt656 = false; + ccdc->fields = 0; + + pad = media_entity_remote_pad(&ccdc->pads[CCDC_PAD_SINK]); sensor = media_entity_to_v4l2_subdev(pad->entity); - if (ccdc->input == CCDC_INPUT_PARALLEL) - pdata = &((struct isp_v4l2_subdevs_group *)sensor->host_priv) + if (ccdc->input == CCDC_INPUT_PARALLEL) { + struct v4l2_mbus_config cfg; + int ret; + + ret = v4l2_subdev_call(sensor, video, g_mbus_config, &cfg); + if (!ret) + ccdc->bt656 = cfg.type == V4L2_MBUS_BT656; + + parcfg = &((struct isp_bus_cfg *)sensor->host_priv) ->bus.parallel; + } + + /* CCDC_PAD_SINK */ + format = &ccdc->formats[CCDC_PAD_SINK]; /* Compute the lane shifter shift value and enable the bridge when the - * input format is YUV. + * input format is a non-BT.656 YUV variant. */ fmt_src.pad = pad->index; fmt_src.which = V4L2_SUBDEV_FORMAT_ACTIVE; @@ -1136,21 +1162,23 @@ depth_in = fmt_info->width; } - fmt_info = omap3isp_video_format_info - (isp->isp_ccdc.formats[CCDC_PAD_SINK].code); + fmt_info = omap3isp_video_format_info(format->code); depth_out = fmt_info->width; shift = depth_in - depth_out; - if (fmt_info->code == V4L2_MBUS_FMT_YUYV8_2X8) + if (ccdc->bt656) + bridge = ISPCTRL_PAR_BRIDGE_DISABLE; + else if (fmt_info->code == MEDIA_BUS_FMT_YUYV8_2X8) bridge = ISPCTRL_PAR_BRIDGE_LENDIAN; - else if (fmt_info->code == V4L2_MBUS_FMT_UYVY8_2X8) + else if (fmt_info->code == MEDIA_BUS_FMT_UYVY8_2X8) bridge = ISPCTRL_PAR_BRIDGE_BENDIAN; else bridge = ISPCTRL_PAR_BRIDGE_DISABLE; - omap3isp_configure_bridge(isp, ccdc->input, pdata, shift, bridge); + omap3isp_configure_bridge(isp, ccdc->input, parcfg, shift, bridge); - ccdc_config_sync_if(ccdc, pdata, depth_out); + /* Configure the sync interface. */ + ccdc_config_sync_if(ccdc, parcfg, depth_out); syn_mode = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); @@ -1169,21 +1197,18 @@ else syn_mode &= ~ISPCCDC_SYN_MODE_SDR2RSZ; - /* CCDC_PAD_SINK */ - format = &ccdc->formats[CCDC_PAD_SINK]; - /* Mosaic filter */ switch (format->code) { - case V4L2_MBUS_FMT_SRGGB10_1X10: - case V4L2_MBUS_FMT_SRGGB12_1X12: + case MEDIA_BUS_FMT_SRGGB10_1X10: + case MEDIA_BUS_FMT_SRGGB12_1X12: ccdc_pattern = ccdc_srggb_pattern; break; - case V4L2_MBUS_FMT_SBGGR10_1X10: - case V4L2_MBUS_FMT_SBGGR12_1X12: + case MEDIA_BUS_FMT_SBGGR10_1X10: + case MEDIA_BUS_FMT_SBGGR12_1X12: ccdc_pattern = ccdc_sbggr_pattern; break; - case V4L2_MBUS_FMT_SGBRG10_1X10: - case V4L2_MBUS_FMT_SGBRG12_1X12: + case MEDIA_BUS_FMT_SGBRG10_1X10: + case MEDIA_BUS_FMT_SGBRG12_1X12: ccdc_pattern = ccdc_sgbrg_pattern; break; default: @@ -1204,29 +1229,56 @@ format = &ccdc->formats[CCDC_PAD_SOURCE_OF]; crop = &ccdc->crop; - isp_reg_writel(isp, (crop->left << ISPCCDC_HORZ_INFO_SPH_SHIFT) | - ((crop->width - 1) << ISPCCDC_HORZ_INFO_NPH_SHIFT), + /* The horizontal coordinates are expressed in pixel clock cycles. We + * need two cycles per pixel in BT.656 mode, and one cycle per pixel in + * SYNC mode regardless of the format as the bridge is enabled for YUV + * formats in that case. + */ + if (ccdc->bt656) { + sph = crop->left * 2; + nph = crop->width * 2 - 1; + } else { + sph = crop->left; + nph = crop->width - 1; + } + + isp_reg_writel(isp, (sph << ISPCCDC_HORZ_INFO_SPH_SHIFT) | + (nph << ISPCCDC_HORZ_INFO_NPH_SHIFT), OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HORZ_INFO); - isp_reg_writel(isp, crop->top << ISPCCDC_VERT_START_SLV0_SHIFT, + isp_reg_writel(isp, (crop->top << ISPCCDC_VERT_START_SLV0_SHIFT) | + (crop->top << ISPCCDC_VERT_START_SLV1_SHIFT), OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_START); isp_reg_writel(isp, (crop->height - 1) << ISPCCDC_VERT_LINES_NLV_SHIFT, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_LINES); - ccdc_config_outlineoffset(ccdc, ccdc->video_out.bpl_value, 0, 0); + ccdc_config_outlineoffset(ccdc, ccdc->video_out.bpl_value, + format->field); + + /* When interleaving fields enable processing of the field input signal. + * This will cause the line output control module to apply the field + * offset to field 1. + */ + if (ccdc->formats[CCDC_PAD_SINK].field == V4L2_FIELD_ALTERNATE && + (format->field == V4L2_FIELD_INTERLACED_TB || + format->field == V4L2_FIELD_INTERLACED_BT)) + syn_mode |= ISPCCDC_SYN_MODE_FLDMODE; /* The CCDC outputs data in UYVY order by default. Swap bytes to get * YUYV. */ - if (format->code == V4L2_MBUS_FMT_YUYV8_1X16) + if (format->code == MEDIA_BUS_FMT_YUYV8_1X16) isp_reg_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, ISPCCDC_CFG_BSWD); else isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_CFG, ISPCCDC_CFG_BSWD); - /* Use PACK8 mode for 1byte per pixel formats. */ - if (omap3isp_video_format_info(format->code)->width <= 8) + /* Use PACK8 mode for 1byte per pixel formats. Check for BT.656 mode + * explicitly as the driver reports 1X16 instead of 2X8 at the OF pad + * for simplicity. + */ + if (omap3isp_video_format_info(format->code)->width <= 8 || ccdc->bt656) syn_mode |= ISPCCDC_SYN_MODE_PACK8; else syn_mode &= ~ISPCCDC_SYN_MODE_PACK8; @@ -1234,18 +1286,7 @@ isp_reg_writel(isp, syn_mode, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE); /* CCDC_PAD_SOURCE_VP */ - format = &ccdc->formats[CCDC_PAD_SOURCE_VP]; - - isp_reg_writel(isp, (0 << ISPCCDC_FMT_HORZ_FMTSPH_SHIFT) | - (format->width << ISPCCDC_FMT_HORZ_FMTLNH_SHIFT), - OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_HORZ); - isp_reg_writel(isp, (0 << ISPCCDC_FMT_VERT_FMTSLV_SHIFT) | - ((format->height + 1) << ISPCCDC_FMT_VERT_FMTLNV_SHIFT), - OMAP3_ISP_IOMEM_CCDC, ISPCCDC_FMT_VERT); - - isp_reg_writel(isp, (format->width << ISPCCDC_VP_OUT_HORZ_NUM_SHIFT) | - (format->height << ISPCCDC_VP_OUT_VERT_NUM_SHIFT), - OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VP_OUT); + ccdc_config_vp(ccdc); /* Lens shading correction. */ spin_lock_irqsave(&ccdc->lsc.req_lock, flags); @@ -1279,6 +1320,8 @@ isp_reg_clr_set(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_PCR, ISPCCDC_PCR_EN, enable ? ISPCCDC_PCR_EN : 0); + + ccdc->running = enable; } static int ccdc_disable(struct isp_ccdc_device *ccdc) @@ -1289,6 +1332,8 @@ spin_lock_irqsave(&ccdc->lock, flags); if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS) ccdc->stopping = CCDC_STOP_REQUEST; + if (!ccdc->running) + ccdc->stopping = CCDC_STOP_FINISHED; spin_unlock_irqrestore(&ccdc->lock, flags); ret = wait_event_timeout(ccdc->wait, @@ -1371,14 +1416,14 @@ return -EBUSY; } -/* __ccdc_handle_stopping - Handle CCDC and/or LSC stopping sequence +/* ccdc_handle_stopping - Handle CCDC and/or LSC stopping sequence * @ccdc: Pointer to ISP CCDC device. * @event: Pointing which event trigger handler * * Return 1 when the event and stopping request combination is satisfied, * zero otherwise. */ -static int __ccdc_handle_stopping(struct isp_ccdc_device *ccdc, u32 event) +static int ccdc_handle_stopping(struct isp_ccdc_device *ccdc, u32 event) { int rval = 0; @@ -1460,7 +1505,7 @@ if (ccdc->lsc.state == LSC_STATE_STOPPING) ccdc->lsc.state = LSC_STATE_STOPPED; - if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_LSC_DONE)) + if (ccdc_handle_stopping(ccdc, CCDC_EVENT_LSC_DONE)) goto done; if (ccdc->lsc.state != LSC_STATE_RECONFIG) @@ -1488,12 +1533,59 @@ spin_unlock_irqrestore(&ccdc->lsc.req_lock, flags); } +/* + * Check whether the CCDC has captured all fields necessary to complete the + * buffer. + */ +static bool ccdc_has_all_fields(struct isp_ccdc_device *ccdc) +{ + struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity); + struct isp_device *isp = to_isp_device(ccdc); + enum v4l2_field of_field = ccdc->formats[CCDC_PAD_SOURCE_OF].field; + enum v4l2_field field; + + /* When the input is progressive fields don't matter. */ + if (of_field == V4L2_FIELD_NONE) + return true; + + /* Read the current field identifier. */ + field = isp_reg_readl(isp, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_SYN_MODE) + & ISPCCDC_SYN_MODE_FLDSTAT + ? V4L2_FIELD_BOTTOM : V4L2_FIELD_TOP; + + /* When capturing fields in alternate order just store the current field + * identifier in the pipeline. + */ + if (of_field == V4L2_FIELD_ALTERNATE) { + pipe->field = field; + return true; + } + + /* The format is interlaced. Make sure we've captured both fields. */ + ccdc->fields |= field == V4L2_FIELD_BOTTOM + ? CCDC_FIELD_BOTTOM : CCDC_FIELD_TOP; + + if (ccdc->fields != CCDC_FIELD_BOTH) + return false; + + /* Verify that the field just captured corresponds to the last field + * needed based on the desired field order. + */ + if ((of_field == V4L2_FIELD_INTERLACED_TB && field == V4L2_FIELD_TOP) || + (of_field == V4L2_FIELD_INTERLACED_BT && field == V4L2_FIELD_BOTTOM)) + return false; + + /* The buffer can be completed, reset the fields for the next buffer. */ + ccdc->fields = 0; + + return true; +} + static int ccdc_isr_buffer(struct isp_ccdc_device *ccdc) { struct isp_pipeline *pipe = to_isp_pipeline(&ccdc->subdev.entity); struct isp_device *isp = to_isp_device(ccdc); struct isp_buffer *buffer; - int restart = 0; /* The CCDC generates VD0 interrupts even when disabled (the datasheet * doesn't explicitly state if that's supposed to happen or not, so it @@ -1502,28 +1594,31 @@ * would thus not be enough, we need to handle the situation explicitly. */ if (list_empty(&ccdc->video_out.dmaqueue)) - goto done; + return 0; /* We're in continuous mode, and memory writes were disabled due to a * buffer underrun. Reenable them now that we have a buffer. The buffer * address has been set in ccdc_video_queue. */ if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS && ccdc->underrun) { - restart = 1; ccdc->underrun = 0; - goto done; + return 1; } + /* Wait for the CCDC to become idle. */ if (ccdc_sbl_wait_idle(ccdc, 1000)) { dev_info(isp->dev, "CCDC won't become idle!\n"); - goto done; + isp->crashed |= 1U << ccdc->subdev.entity.id; + omap3isp_pipeline_cancel_stream(pipe); + return 0; } + if (!ccdc_has_all_fields(ccdc)) + return 1; + buffer = omap3isp_video_buffer_next(&ccdc->video_out); - if (buffer != NULL) { - ccdc_set_outaddr(ccdc, buffer->isp_addr); - restart = 1; - } + if (buffer != NULL) + ccdc_set_outaddr(ccdc, buffer->dma); pipe->state |= ISP_PIPELINE_IDLE_OUTPUT; @@ -1532,8 +1627,7 @@ omap3isp_pipeline_set_stream(pipe, ISP_PIPELINE_STREAM_SINGLESHOT); -done: - return restart; + return buffer != NULL; } /* @@ -1547,11 +1641,38 @@ unsigned long flags; int restart = 0; + /* In BT.656 mode the CCDC doesn't generate an HS/VS interrupt. We thus + * need to increment the frame counter here. + */ + if (ccdc->bt656) { + struct isp_pipeline *pipe = + to_isp_pipeline(&ccdc->subdev.entity); + + atomic_inc(&pipe->frame_number); + } + + /* Emulate a VD1 interrupt for BT.656 mode, as we can't stop the CCDC in + * the VD1 interrupt handler in that mode without risking a CCDC stall + * if a short frame is received. + */ + if (ccdc->bt656) { + spin_lock_irqsave(&ccdc->lock, flags); + if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS && + ccdc->output & CCDC_OUTPUT_MEMORY) { + if (ccdc->lsc.state != LSC_STATE_STOPPED) + __ccdc_lsc_enable(ccdc, 0); + __ccdc_enable(ccdc, 0); + } + ccdc_handle_stopping(ccdc, CCDC_EVENT_VD1); + spin_unlock_irqrestore(&ccdc->lock, flags); + } + if (ccdc->output & CCDC_OUTPUT_MEMORY) restart = ccdc_isr_buffer(ccdc); spin_lock_irqsave(&ccdc->lock, flags); - if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_VD0)) { + + if (ccdc_handle_stopping(ccdc, CCDC_EVENT_VD0)) { spin_unlock_irqrestore(&ccdc->lock, flags); return; } @@ -1572,6 +1693,18 @@ { unsigned long flags; + /* In BT.656 mode the synchronization signals are generated by the CCDC + * from the embedded sync codes. The VD0 and VD1 interrupts are thus + * only triggered when the CCDC is enabled, unlike external sync mode + * where the line counter runs even when the CCDC is stopped. We can't + * disable the CCDC at VD1 time, as no VD0 interrupt would be generated + * for a short frame, which would result in the CCDC being stopped and + * no VD interrupt generated anymore. The CCDC is stopped from the VD0 + * interrupt handler instead for BT.656. + */ + if (ccdc->bt656) + return; + spin_lock_irqsave(&ccdc->lsc.req_lock, flags); /* @@ -1601,7 +1734,7 @@ break; } - if (__ccdc_handle_stopping(ccdc, CCDC_EVENT_VD1)) + if (ccdc_handle_stopping(ccdc, CCDC_EVENT_VD1)) goto done; if (ccdc->lsc.request == NULL) @@ -1656,17 +1789,30 @@ static int ccdc_video_queue(struct isp_video *video, struct isp_buffer *buffer) { struct isp_ccdc_device *ccdc = &video->isp->isp_ccdc; + unsigned long flags; + bool restart = false; if (!(ccdc->output & CCDC_OUTPUT_MEMORY)) return -ENODEV; - ccdc_set_outaddr(ccdc, buffer->isp_addr); + ccdc_set_outaddr(ccdc, buffer->dma); /* We now have a buffer queued on the output, restart the pipeline * on the next CCDC interrupt if running in continuous mode (or when - * starting the stream). + * starting the stream) in external sync mode, or immediately in BT.656 + * sync mode as no CCDC interrupt is generated when the CCDC is stopped + * in that case. */ - ccdc->underrun = 1; + spin_lock_irqsave(&ccdc->lock, flags); + if (ccdc->state == ISP_PIPELINE_STREAM_CONTINUOUS && !ccdc->running && + ccdc->bt656) + restart = true; + else + ccdc->underrun = 1; + spin_unlock_irqrestore(&ccdc->lock, flags); + + if (restart) + ccdc_enable(ccdc); return 0; } @@ -1753,11 +1899,6 @@ ccdc_configure(ccdc); - /* TODO: Don't configure the video port if all of its output - * links are inactive. - */ - ccdc_config_vp(ccdc); - ccdc_enable_vp(ccdc, 1); ccdc_print_status(ccdc); } @@ -1794,21 +1935,21 @@ } static struct v4l2_mbus_framefmt * -__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, +__ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_pad_config *cfg, unsigned int pad, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_format(fh, pad); + return v4l2_subdev_get_try_format(&ccdc->subdev, cfg, pad); else return &ccdc->formats[pad]; } static struct v4l2_rect * -__ccdc_get_crop(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, +__ccdc_get_crop(struct isp_ccdc_device *ccdc, struct v4l2_subdev_pad_config *cfg, enum v4l2_subdev_format_whence which) { if (which == V4L2_SUBDEV_FORMAT_TRY) - return v4l2_subdev_get_try_crop(fh, CCDC_PAD_SOURCE_OF); + return v4l2_subdev_get_try_crop(&ccdc->subdev, cfg, CCDC_PAD_SOURCE_OF); else return &ccdc->crop; } @@ -1816,20 +1957,21 @@ /* * ccdc_try_format - Try video format on a pad * @ccdc: ISP CCDC device - * @fh : V4L2 subdev file handle + * @cfg : V4L2 subdev pad configuration * @pad: Pad number * @fmt: Format */ static void -ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, +ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_pad_config *cfg, unsigned int pad, struct v4l2_mbus_framefmt *fmt, enum v4l2_subdev_format_whence which) { const struct isp_format_info *info; - enum v4l2_mbus_pixelcode pixelcode; + u32 pixelcode; unsigned int width = fmt->width; unsigned int height = fmt->height; struct v4l2_rect *crop; + enum v4l2_field field; unsigned int i; switch (pad) { @@ -1841,51 +1983,72 @@ /* If not found, use SGRBG10 as default */ if (i >= ARRAY_SIZE(ccdc_fmts)) - fmt->code = V4L2_MBUS_FMT_SGRBG10_1X10; + fmt->code = MEDIA_BUS_FMT_SGRBG10_1X10; /* Clamp the input size. */ fmt->width = clamp_t(u32, width, 32, 4096); fmt->height = clamp_t(u32, height, 32, 4096); + + /* Default to progressive field order. */ + if (fmt->field == V4L2_FIELD_ANY) + fmt->field = V4L2_FIELD_NONE; + break; case CCDC_PAD_SOURCE_OF: pixelcode = fmt->code; - *fmt = *__ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which); + field = fmt->field; + *fmt = *__ccdc_get_format(ccdc, cfg, CCDC_PAD_SINK, which); - /* YUV formats are converted from 2X8 to 1X16 by the bridge and - * can be byte-swapped. + /* In SYNC mode the bridge converts YUV formats from 2X8 to + * 1X16. In BT.656 no such conversion occurs. As we don't know + * at this point whether the source will use SYNC or BT.656 mode + * let's pretend the conversion always occurs. The CCDC will be + * configured to pack bytes in BT.656, hiding the inaccuracy. + * In all cases bytes can be swapped. */ - if (fmt->code == V4L2_MBUS_FMT_YUYV8_2X8 || - fmt->code == V4L2_MBUS_FMT_UYVY8_2X8) { + if (fmt->code == MEDIA_BUS_FMT_YUYV8_2X8 || + fmt->code == MEDIA_BUS_FMT_UYVY8_2X8) { /* Use the user requested format if YUV. */ - if (pixelcode == V4L2_MBUS_FMT_YUYV8_2X8 || - pixelcode == V4L2_MBUS_FMT_UYVY8_2X8 || - pixelcode == V4L2_MBUS_FMT_YUYV8_1X16 || - pixelcode == V4L2_MBUS_FMT_UYVY8_1X16) + if (pixelcode == MEDIA_BUS_FMT_YUYV8_2X8 || + pixelcode == MEDIA_BUS_FMT_UYVY8_2X8 || + pixelcode == MEDIA_BUS_FMT_YUYV8_1X16 || + pixelcode == MEDIA_BUS_FMT_UYVY8_1X16) fmt->code = pixelcode; - if (fmt->code == V4L2_MBUS_FMT_YUYV8_2X8) - fmt->code = V4L2_MBUS_FMT_YUYV8_1X16; - else if (fmt->code == V4L2_MBUS_FMT_UYVY8_2X8) - fmt->code = V4L2_MBUS_FMT_UYVY8_1X16; + if (fmt->code == MEDIA_BUS_FMT_YUYV8_2X8) + fmt->code = MEDIA_BUS_FMT_YUYV8_1X16; + else if (fmt->code == MEDIA_BUS_FMT_UYVY8_2X8) + fmt->code = MEDIA_BUS_FMT_UYVY8_1X16; } /* Hardcode the output size to the crop rectangle size. */ - crop = __ccdc_get_crop(ccdc, fh, which); + crop = __ccdc_get_crop(ccdc, cfg, which); fmt->width = crop->width; fmt->height = crop->height; + + /* When input format is interlaced with alternating fields the + * CCDC can interleave the fields. + */ + if (fmt->field == V4L2_FIELD_ALTERNATE && + (field == V4L2_FIELD_INTERLACED_TB || + field == V4L2_FIELD_INTERLACED_BT)) { + fmt->field = field; + fmt->height *= 2; + } + break; case CCDC_PAD_SOURCE_VP: - *fmt = *__ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which); + *fmt = *__ccdc_get_format(ccdc, cfg, CCDC_PAD_SINK, which); /* The video port interface truncates the data to 10 bits. */ info = omap3isp_video_format_info(fmt->code); fmt->code = info->truncated; /* YUV formats are not supported by the video port. */ - if (fmt->code == V4L2_MBUS_FMT_YUYV8_2X8 || - fmt->code == V4L2_MBUS_FMT_UYVY8_2X8) + if (fmt->code == MEDIA_BUS_FMT_YUYV8_2X8 || + fmt->code == MEDIA_BUS_FMT_UYVY8_2X8) fmt->code = 0; /* The number of lines that can be clocked out from the video @@ -1901,7 +2064,6 @@ * stored on 2 bytes. */ fmt->colorspace = V4L2_COLORSPACE_SRGB; - fmt->field = V4L2_FIELD_NONE; } /* @@ -1921,7 +2083,7 @@ * to keep the Bayer pattern. */ info = omap3isp_video_format_info(sink->code); - if (info->flavor != V4L2_MBUS_FMT_Y8_1X8) { + if (info->flavor != MEDIA_BUS_FMT_Y8_1X8) { crop->left &= ~1; crop->top &= ~1; } @@ -1941,7 +2103,7 @@ sink->height - crop->top); /* Odd width/height values don't make sense for Bayer formats. */ - if (info->flavor != V4L2_MBUS_FMT_Y8_1X8) { + if (info->flavor != MEDIA_BUS_FMT_Y8_1X8) { crop->width &= ~1; crop->height &= ~1; } @@ -1950,12 +2112,12 @@ /* * ccdc_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure - * @fh : V4L2 subdev file handle + * @cfg : V4L2 subdev pad configuration * @code : pointer to v4l2_subdev_mbus_code_enum structure * return -EINVAL or zero on success */ static int ccdc_enum_mbus_code(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); @@ -1970,16 +2132,16 @@ break; case CCDC_PAD_SOURCE_OF: - format = __ccdc_get_format(ccdc, fh, code->pad, - V4L2_SUBDEV_FORMAT_TRY); + format = __ccdc_get_format(ccdc, cfg, code->pad, + code->which); - if (format->code == V4L2_MBUS_FMT_YUYV8_2X8 || - format->code == V4L2_MBUS_FMT_UYVY8_2X8) { + if (format->code == MEDIA_BUS_FMT_YUYV8_2X8 || + format->code == MEDIA_BUS_FMT_UYVY8_2X8) { /* In YUV mode the CCDC can swap bytes. */ if (code->index == 0) - code->code = V4L2_MBUS_FMT_YUYV8_1X16; + code->code = MEDIA_BUS_FMT_YUYV8_1X16; else if (code->index == 1) - code->code = V4L2_MBUS_FMT_UYVY8_1X16; + code->code = MEDIA_BUS_FMT_UYVY8_1X16; else return -EINVAL; } else { @@ -2001,8 +2163,8 @@ if (code->index != 0) return -EINVAL; - format = __ccdc_get_format(ccdc, fh, code->pad, - V4L2_SUBDEV_FORMAT_TRY); + format = __ccdc_get_format(ccdc, cfg, code->pad, + code->which); /* A pixel code equal to 0 means that the video port doesn't * support the input format. Don't enumerate any pixel code. @@ -2021,7 +2183,7 @@ } static int ccdc_enum_frame_size(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, + struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); @@ -2033,7 +2195,7 @@ format.code = fse->code; format.width = 1; format.height = 1; - ccdc_try_format(ccdc, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + ccdc_try_format(ccdc, cfg, fse->pad, &format, fse->which); fse->min_width = format.width; fse->min_height = format.height; @@ -2043,7 +2205,7 @@ format.code = fse->code; format.width = -1; format.height = -1; - ccdc_try_format(ccdc, fh, fse->pad, &format, V4L2_SUBDEV_FORMAT_TRY); + ccdc_try_format(ccdc, cfg, fse->pad, &format, fse->which); fse->max_width = format.width; fse->max_height = format.height; @@ -2053,7 +2215,7 @@ /* * ccdc_get_selection - Retrieve a selection rectangle on a pad * @sd: ISP CCDC V4L2 subdevice - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @sel: Selection rectangle * * The only supported rectangles are the crop rectangles on the output formatter @@ -2061,7 +2223,7 @@ * * Return 0 on success or a negative error code otherwise. */ -static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); @@ -2077,12 +2239,12 @@ sel->r.width = INT_MAX; sel->r.height = INT_MAX; - format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, sel->which); + format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SINK, sel->which); ccdc_try_crop(ccdc, format, &sel->r); break; case V4L2_SEL_TGT_CROP: - sel->r = *__ccdc_get_crop(ccdc, fh, sel->which); + sel->r = *__ccdc_get_crop(ccdc, cfg, sel->which); break; default: @@ -2095,7 +2257,7 @@ /* * ccdc_set_selection - Set a selection rectangle on a pad * @sd: ISP CCDC V4L2 subdevice - * @fh: V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @sel: Selection rectangle * * The only supported rectangle is the actual crop rectangle on the output @@ -2103,7 +2265,7 @@ * * Return 0 on success or a negative error code otherwise. */ -static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_selection *sel) { struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); @@ -2122,17 +2284,17 @@ * rectangle. */ if (sel->flags & V4L2_SEL_FLAG_KEEP_CONFIG) { - sel->r = *__ccdc_get_crop(ccdc, fh, sel->which); + sel->r = *__ccdc_get_crop(ccdc, cfg, sel->which); return 0; } - format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, sel->which); + format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SINK, sel->which); ccdc_try_crop(ccdc, format, &sel->r); - *__ccdc_get_crop(ccdc, fh, sel->which) = sel->r; + *__ccdc_get_crop(ccdc, cfg, sel->which) = sel->r; /* Update the source format. */ - format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF, sel->which); - ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_OF, format, sel->which); + format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SOURCE_OF, sel->which); + ccdc_try_format(ccdc, cfg, CCDC_PAD_SOURCE_OF, format, sel->which); return 0; } @@ -2140,19 +2302,19 @@ /* * ccdc_get_format - Retrieve the video format on a pad * @sd : ISP CCDC V4L2 subdevice - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @fmt: Format * * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond * to the format type. */ -static int ccdc_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int ccdc_get_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which); + format = __ccdc_get_format(ccdc, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; @@ -2163,30 +2325,30 @@ /* * ccdc_set_format - Set the video format on a pad * @sd : ISP CCDC V4L2 subdevice - * @fh : V4L2 subdev file handle + * @cfg: V4L2 subdev pad configuration * @fmt: Format * * Return 0 on success or -EINVAL if the pad is invalid or doesn't correspond * to the format type. */ -static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, +static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; struct v4l2_rect *crop; - format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which); + format = __ccdc_get_format(ccdc, cfg, fmt->pad, fmt->which); if (format == NULL) return -EINVAL; - ccdc_try_format(ccdc, fh, fmt->pad, &fmt->format, fmt->which); + ccdc_try_format(ccdc, cfg, fmt->pad, &fmt->format, fmt->which); *format = fmt->format; /* Propagate the format from sink to source */ if (fmt->pad == CCDC_PAD_SINK) { /* Reset the crop rectangle. */ - crop = __ccdc_get_crop(ccdc, fh, fmt->which); + crop = __ccdc_get_crop(ccdc, cfg, fmt->which); crop->left = 0; crop->top = 0; crop->width = fmt->format.width; @@ -2195,16 +2357,16 @@ ccdc_try_crop(ccdc, &fmt->format, crop); /* Update the source formats. */ - format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF, + format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SOURCE_OF, fmt->which); *format = fmt->format; - ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_OF, format, + ccdc_try_format(ccdc, cfg, CCDC_PAD_SOURCE_OF, format, fmt->which); - format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_VP, + format = __ccdc_get_format(ccdc, cfg, CCDC_PAD_SOURCE_VP, fmt->which); *format = fmt->format; - ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_VP, format, + ccdc_try_format(ccdc, cfg, CCDC_PAD_SOURCE_VP, format, fmt->which); } @@ -2221,9 +2383,7 @@ * return true if the combination is possible * return false otherwise */ -static bool ccdc_is_shiftable(enum v4l2_mbus_pixelcode in, - enum v4l2_mbus_pixelcode out, - unsigned int additional_shift) +static bool ccdc_is_shiftable(u32 in, u32 out, unsigned int additional_shift) { const struct isp_format_info *in_info, *out_info; @@ -2257,11 +2417,11 @@ /* We've got a parallel sensor here. */ if (ccdc->input == CCDC_INPUT_PARALLEL) { - struct isp_parallel_platform_data *pdata = - &((struct isp_v4l2_subdevs_group *) + struct isp_parallel_cfg *parcfg = + &((struct isp_bus_cfg *) media_entity_to_v4l2_subdev(link->source->entity) ->host_priv)->bus.parallel; - parallel_shift = pdata->data_lane_shift * 2; + parallel_shift = parcfg->data_lane_shift * 2; } else { parallel_shift = 0; } @@ -2290,10 +2450,10 @@ memset(&format, 0, sizeof(format)); format.pad = CCDC_PAD_SINK; format.which = fh ? V4L2_SUBDEV_FORMAT_TRY : V4L2_SUBDEV_FORMAT_ACTIVE; - format.format.code = V4L2_MBUS_FMT_SGRBG10_1X10; + format.format.code = MEDIA_BUS_FMT_SGRBG10_1X10; format.format.width = 4096; format.format.height = 4096; - ccdc_set_format(sd, fh, &format); + ccdc_set_format(sd, fh ? fh->pad : NULL, &format); return 0; } @@ -2484,7 +2644,8 @@ v4l2_set_subdevdata(sd, ccdc); sd->flags |= V4L2_SUBDEV_FL_HAS_EVENTS | V4L2_SUBDEV_FL_HAS_DEVNODE; - pads[CCDC_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + pads[CCDC_PAD_SINK].flags = MEDIA_PAD_FL_SINK + | MEDIA_PAD_FL_MUST_CONNECT; pads[CCDC_PAD_SOURCE_VP].flags = MEDIA_PAD_FL_SOURCE; pads[CCDC_PAD_SOURCE_OF].flags = MEDIA_PAD_FL_SOURCE; @@ -2522,7 +2683,7 @@ /* * omap3isp_ccdc_init - CCDC module initialization. - * @dev: Device pointer specific to the OMAP3 ISP. + * @isp: Device pointer specific to the OMAP3 ISP. * * TODO: Get the initialisation values from platform data. * @@ -2561,7 +2722,7 @@ /* * omap3isp_ccdc_cleanup - CCDC module cleanup. - * @dev: Device pointer specific to the OMAP3 ISP. + * @isp: Device pointer specific to the OMAP3 ISP. */ void omap3isp_ccdc_cleanup(struct isp_device *isp) { @@ -2577,8 +2738,9 @@ cancel_work_sync(&ccdc->lsc.table_work); ccdc_lsc_free_queue(ccdc, &ccdc->lsc.free_queue); - if (ccdc->fpc.fpcaddr != 0) - omap_iommu_vfree(isp->domain, isp->dev, ccdc->fpc.fpcaddr); + if (ccdc->fpc.addr != NULL) + dma_free_coherent(isp->dev, ccdc->fpc.fpnum * 4, ccdc->fpc.addr, + ccdc->fpc.dma); mutex_destroy(&ccdc->ioctl_lock); }