162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Microchip Image Sensor Controller (ISC) common driver base 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2016-2019 Microchip Technology, Inc. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Songjun Wu 862306a36Sopenharmony_ci * Author: Eugen Hristev <eugen.hristev@microchip.com> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci#include <linux/delay.h> 1262306a36Sopenharmony_ci#include <linux/interrupt.h> 1362306a36Sopenharmony_ci#include <linux/math64.h> 1462306a36Sopenharmony_ci#include <linux/module.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include <linux/of_graph.h> 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1962306a36Sopenharmony_ci#include <linux/regmap.h> 2062306a36Sopenharmony_ci#include <linux/videodev2.h> 2162306a36Sopenharmony_ci#include <linux/atmel-isc-media.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#include <media/v4l2-ctrls.h> 2462306a36Sopenharmony_ci#include <media/v4l2-device.h> 2562306a36Sopenharmony_ci#include <media/v4l2-event.h> 2662306a36Sopenharmony_ci#include <media/v4l2-image-sizes.h> 2762306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 2862306a36Sopenharmony_ci#include <media/v4l2-fwnode.h> 2962306a36Sopenharmony_ci#include <media/v4l2-subdev.h> 3062306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#include "microchip-isc-regs.h" 3362306a36Sopenharmony_ci#include "microchip-isc.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define ISC_IS_FORMAT_RAW(mbus_code) \ 3662306a36Sopenharmony_ci (((mbus_code) & 0xf000) == 0x3000) 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define ISC_IS_FORMAT_GREY(mbus_code) \ 3962306a36Sopenharmony_ci (((mbus_code) == MEDIA_BUS_FMT_Y10_1X10) | \ 4062306a36Sopenharmony_ci (((mbus_code) == MEDIA_BUS_FMT_Y8_1X8))) 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic inline void isc_update_v4l2_ctrls(struct isc_device *isc) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci struct isc_ctrls *ctrls = &isc->ctrls; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci /* In here we set the v4l2 controls w.r.t. our pipeline config */ 4762306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(isc->r_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_R]); 4862306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(isc->b_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_B]); 4962306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(isc->gr_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GR]); 5062306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(isc->gb_gain_ctrl, ctrls->gain[ISC_HIS_CFG_MODE_GB]); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(isc->r_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_R]); 5362306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(isc->b_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_B]); 5462306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(isc->gr_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_GR]); 5562306a36Sopenharmony_ci v4l2_ctrl_s_ctrl(isc->gb_off_ctrl, ctrls->offset[ISC_HIS_CFG_MODE_GB]); 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic inline void isc_update_awb_ctrls(struct isc_device *isc) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct isc_ctrls *ctrls = &isc->ctrls; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci /* In here we set our actual hw pipeline config */ 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci regmap_write(isc->regmap, ISC_WB_O_RGR, 6562306a36Sopenharmony_ci ((ctrls->offset[ISC_HIS_CFG_MODE_R])) | 6662306a36Sopenharmony_ci ((ctrls->offset[ISC_HIS_CFG_MODE_GR]) << 16)); 6762306a36Sopenharmony_ci regmap_write(isc->regmap, ISC_WB_O_BGB, 6862306a36Sopenharmony_ci ((ctrls->offset[ISC_HIS_CFG_MODE_B])) | 6962306a36Sopenharmony_ci ((ctrls->offset[ISC_HIS_CFG_MODE_GB]) << 16)); 7062306a36Sopenharmony_ci regmap_write(isc->regmap, ISC_WB_G_RGR, 7162306a36Sopenharmony_ci ctrls->gain[ISC_HIS_CFG_MODE_R] | 7262306a36Sopenharmony_ci (ctrls->gain[ISC_HIS_CFG_MODE_GR] << 16)); 7362306a36Sopenharmony_ci regmap_write(isc->regmap, ISC_WB_G_BGB, 7462306a36Sopenharmony_ci ctrls->gain[ISC_HIS_CFG_MODE_B] | 7562306a36Sopenharmony_ci (ctrls->gain[ISC_HIS_CFG_MODE_GB] << 16)); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic inline void isc_reset_awb_ctrls(struct isc_device *isc) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci unsigned int c; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci for (c = ISC_HIS_CFG_MODE_GR; c <= ISC_HIS_CFG_MODE_B; c++) { 8362306a36Sopenharmony_ci /* gains have a fixed point at 9 decimals */ 8462306a36Sopenharmony_ci isc->ctrls.gain[c] = 1 << 9; 8562306a36Sopenharmony_ci /* offsets are in 2's complements */ 8662306a36Sopenharmony_ci isc->ctrls.offset[c] = 0; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic int isc_queue_setup(struct vb2_queue *vq, 9162306a36Sopenharmony_ci unsigned int *nbuffers, unsigned int *nplanes, 9262306a36Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci struct isc_device *isc = vb2_get_drv_priv(vq); 9562306a36Sopenharmony_ci unsigned int size = isc->fmt.fmt.pix.sizeimage; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (*nplanes) 9862306a36Sopenharmony_ci return sizes[0] < size ? -EINVAL : 0; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci *nplanes = 1; 10162306a36Sopenharmony_ci sizes[0] = size; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci return 0; 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic int isc_buffer_prepare(struct vb2_buffer *vb) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 10962306a36Sopenharmony_ci struct isc_device *isc = vb2_get_drv_priv(vb->vb2_queue); 11062306a36Sopenharmony_ci unsigned long size = isc->fmt.fmt.pix.sizeimage; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (vb2_plane_size(vb, 0) < size) { 11362306a36Sopenharmony_ci dev_err(isc->dev, "buffer too small (%lu < %lu)\n", 11462306a36Sopenharmony_ci vb2_plane_size(vb, 0), size); 11562306a36Sopenharmony_ci return -EINVAL; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci vb2_set_plane_payload(vb, 0, size); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci vbuf->field = isc->fmt.fmt.pix.field; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci return 0; 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic void isc_crop_pfe(struct isc_device *isc) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci struct regmap *regmap = isc->regmap; 12862306a36Sopenharmony_ci u32 h, w; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci h = isc->fmt.fmt.pix.height; 13162306a36Sopenharmony_ci w = isc->fmt.fmt.pix.width; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* 13462306a36Sopenharmony_ci * In case the sensor is not RAW, it will output a pixel (12-16 bits) 13562306a36Sopenharmony_ci * with two samples on the ISC Data bus (which is 8-12) 13662306a36Sopenharmony_ci * ISC will count each sample, so, we need to multiply these values 13762306a36Sopenharmony_ci * by two, to get the real number of samples for the required pixels. 13862306a36Sopenharmony_ci */ 13962306a36Sopenharmony_ci if (!ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) { 14062306a36Sopenharmony_ci h <<= 1; 14162306a36Sopenharmony_ci w <<= 1; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci /* 14562306a36Sopenharmony_ci * We limit the column/row count that the ISC will output according 14662306a36Sopenharmony_ci * to the configured resolution that we want. 14762306a36Sopenharmony_ci * This will avoid the situation where the sensor is misconfigured, 14862306a36Sopenharmony_ci * sending more data, and the ISC will just take it and DMA to memory, 14962306a36Sopenharmony_ci * causing corruption. 15062306a36Sopenharmony_ci */ 15162306a36Sopenharmony_ci regmap_write(regmap, ISC_PFE_CFG1, 15262306a36Sopenharmony_ci (ISC_PFE_CFG1_COLMIN(0) & ISC_PFE_CFG1_COLMIN_MASK) | 15362306a36Sopenharmony_ci (ISC_PFE_CFG1_COLMAX(w - 1) & ISC_PFE_CFG1_COLMAX_MASK)); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci regmap_write(regmap, ISC_PFE_CFG2, 15662306a36Sopenharmony_ci (ISC_PFE_CFG2_ROWMIN(0) & ISC_PFE_CFG2_ROWMIN_MASK) | 15762306a36Sopenharmony_ci (ISC_PFE_CFG2_ROWMAX(h - 1) & ISC_PFE_CFG2_ROWMAX_MASK)); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci regmap_update_bits(regmap, ISC_PFE_CFG0, 16062306a36Sopenharmony_ci ISC_PFE_CFG0_COLEN | ISC_PFE_CFG0_ROWEN, 16162306a36Sopenharmony_ci ISC_PFE_CFG0_COLEN | ISC_PFE_CFG0_ROWEN); 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic void isc_start_dma(struct isc_device *isc) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct regmap *regmap = isc->regmap; 16762306a36Sopenharmony_ci u32 sizeimage = isc->fmt.fmt.pix.sizeimage; 16862306a36Sopenharmony_ci u32 dctrl_dview; 16962306a36Sopenharmony_ci dma_addr_t addr0; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci addr0 = vb2_dma_contig_plane_dma_addr(&isc->cur_frm->vb.vb2_buf, 0); 17262306a36Sopenharmony_ci regmap_write(regmap, ISC_DAD0 + isc->offsets.dma, addr0); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci switch (isc->config.fourcc) { 17562306a36Sopenharmony_ci case V4L2_PIX_FMT_YUV420: 17662306a36Sopenharmony_ci regmap_write(regmap, ISC_DAD1 + isc->offsets.dma, 17762306a36Sopenharmony_ci addr0 + (sizeimage * 2) / 3); 17862306a36Sopenharmony_ci regmap_write(regmap, ISC_DAD2 + isc->offsets.dma, 17962306a36Sopenharmony_ci addr0 + (sizeimage * 5) / 6); 18062306a36Sopenharmony_ci break; 18162306a36Sopenharmony_ci case V4L2_PIX_FMT_YUV422P: 18262306a36Sopenharmony_ci regmap_write(regmap, ISC_DAD1 + isc->offsets.dma, 18362306a36Sopenharmony_ci addr0 + sizeimage / 2); 18462306a36Sopenharmony_ci regmap_write(regmap, ISC_DAD2 + isc->offsets.dma, 18562306a36Sopenharmony_ci addr0 + (sizeimage * 3) / 4); 18662306a36Sopenharmony_ci break; 18762306a36Sopenharmony_ci default: 18862306a36Sopenharmony_ci break; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci dctrl_dview = isc->config.dctrl_dview; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci regmap_write(regmap, ISC_DCTRL + isc->offsets.dma, 19462306a36Sopenharmony_ci dctrl_dview | ISC_DCTRL_IE_IS); 19562306a36Sopenharmony_ci spin_lock(&isc->awb_lock); 19662306a36Sopenharmony_ci regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_CAPTURE); 19762306a36Sopenharmony_ci spin_unlock(&isc->awb_lock); 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic void isc_set_pipeline(struct isc_device *isc, u32 pipeline) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci struct regmap *regmap = isc->regmap; 20362306a36Sopenharmony_ci struct isc_ctrls *ctrls = &isc->ctrls; 20462306a36Sopenharmony_ci u32 val, bay_cfg; 20562306a36Sopenharmony_ci const u32 *gamma; 20662306a36Sopenharmony_ci unsigned int i; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* WB-->CFA-->CC-->GAM-->CSC-->CBC-->SUB422-->SUB420 */ 20962306a36Sopenharmony_ci for (i = 0; i < ISC_PIPE_LINE_NODE_NUM; i++) { 21062306a36Sopenharmony_ci val = pipeline & BIT(i) ? 1 : 0; 21162306a36Sopenharmony_ci regmap_field_write(isc->pipeline[i], val); 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (!pipeline) 21562306a36Sopenharmony_ci return; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci bay_cfg = isc->config.sd_format->cfa_baycfg; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci regmap_write(regmap, ISC_WB_CFG, bay_cfg); 22062306a36Sopenharmony_ci isc_update_awb_ctrls(isc); 22162306a36Sopenharmony_ci isc_update_v4l2_ctrls(isc); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci regmap_write(regmap, ISC_CFA_CFG, bay_cfg | ISC_CFA_CFG_EITPOL); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci gamma = &isc->gamma_table[ctrls->gamma_index][0]; 22662306a36Sopenharmony_ci regmap_bulk_write(regmap, ISC_GAM_BENTRY, gamma, GAMMA_ENTRIES); 22762306a36Sopenharmony_ci regmap_bulk_write(regmap, ISC_GAM_GENTRY, gamma, GAMMA_ENTRIES); 22862306a36Sopenharmony_ci regmap_bulk_write(regmap, ISC_GAM_RENTRY, gamma, GAMMA_ENTRIES); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci isc->config_dpc(isc); 23162306a36Sopenharmony_ci isc->config_csc(isc); 23262306a36Sopenharmony_ci isc->config_cbc(isc); 23362306a36Sopenharmony_ci isc->config_cc(isc); 23462306a36Sopenharmony_ci isc->config_gam(isc); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic int isc_update_profile(struct isc_device *isc) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci struct regmap *regmap = isc->regmap; 24062306a36Sopenharmony_ci u32 sr; 24162306a36Sopenharmony_ci int counter = 100; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_UPPRO); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci regmap_read(regmap, ISC_CTRLSR, &sr); 24662306a36Sopenharmony_ci while ((sr & ISC_CTRL_UPPRO) && counter--) { 24762306a36Sopenharmony_ci usleep_range(1000, 2000); 24862306a36Sopenharmony_ci regmap_read(regmap, ISC_CTRLSR, &sr); 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (counter < 0) { 25262306a36Sopenharmony_ci v4l2_warn(&isc->v4l2_dev, "Time out to update profile\n"); 25362306a36Sopenharmony_ci return -ETIMEDOUT; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return 0; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic void isc_set_histogram(struct isc_device *isc, bool enable) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci struct regmap *regmap = isc->regmap; 26262306a36Sopenharmony_ci struct isc_ctrls *ctrls = &isc->ctrls; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (enable) { 26562306a36Sopenharmony_ci regmap_write(regmap, ISC_HIS_CFG + isc->offsets.his, 26662306a36Sopenharmony_ci ISC_HIS_CFG_MODE_GR | 26762306a36Sopenharmony_ci (isc->config.sd_format->cfa_baycfg 26862306a36Sopenharmony_ci << ISC_HIS_CFG_BAYSEL_SHIFT) | 26962306a36Sopenharmony_ci ISC_HIS_CFG_RAR); 27062306a36Sopenharmony_ci regmap_write(regmap, ISC_HIS_CTRL + isc->offsets.his, 27162306a36Sopenharmony_ci ISC_HIS_CTRL_EN); 27262306a36Sopenharmony_ci regmap_write(regmap, ISC_INTEN, ISC_INT_HISDONE); 27362306a36Sopenharmony_ci ctrls->hist_id = ISC_HIS_CFG_MODE_GR; 27462306a36Sopenharmony_ci isc_update_profile(isc); 27562306a36Sopenharmony_ci regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci ctrls->hist_stat = HIST_ENABLED; 27862306a36Sopenharmony_ci } else { 27962306a36Sopenharmony_ci regmap_write(regmap, ISC_INTDIS, ISC_INT_HISDONE); 28062306a36Sopenharmony_ci regmap_write(regmap, ISC_HIS_CTRL + isc->offsets.his, 28162306a36Sopenharmony_ci ISC_HIS_CTRL_DIS); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci ctrls->hist_stat = HIST_DISABLED; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic int isc_configure(struct isc_device *isc) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci struct regmap *regmap = isc->regmap; 29062306a36Sopenharmony_ci u32 pfe_cfg0, dcfg, mask, pipeline; 29162306a36Sopenharmony_ci struct isc_subdev_entity *subdev = isc->current_subdev; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci pfe_cfg0 = isc->config.sd_format->pfe_cfg0_bps; 29462306a36Sopenharmony_ci pipeline = isc->config.bits_pipeline; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci dcfg = isc->config.dcfg_imode | isc->dcfg; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci pfe_cfg0 |= subdev->pfe_cfg0 | ISC_PFE_CFG0_MODE_PROGRESSIVE; 29962306a36Sopenharmony_ci mask = ISC_PFE_CFG0_BPS_MASK | ISC_PFE_CFG0_HPOL_LOW | 30062306a36Sopenharmony_ci ISC_PFE_CFG0_VPOL_LOW | ISC_PFE_CFG0_PPOL_LOW | 30162306a36Sopenharmony_ci ISC_PFE_CFG0_MODE_MASK | ISC_PFE_CFG0_CCIR_CRC | 30262306a36Sopenharmony_ci ISC_PFE_CFG0_CCIR656 | ISC_PFE_CFG0_MIPI; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci regmap_update_bits(regmap, ISC_PFE_CFG0, mask, pfe_cfg0); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci isc->config_rlp(isc); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci regmap_write(regmap, ISC_DCFG + isc->offsets.dma, dcfg); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci /* Set the pipeline */ 31162306a36Sopenharmony_ci isc_set_pipeline(isc, pipeline); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* 31462306a36Sopenharmony_ci * The current implemented histogram is available for RAW R, B, GB, GR 31562306a36Sopenharmony_ci * channels. We need to check if sensor is outputting RAW BAYER 31662306a36Sopenharmony_ci */ 31762306a36Sopenharmony_ci if (isc->ctrls.awb && 31862306a36Sopenharmony_ci ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) 31962306a36Sopenharmony_ci isc_set_histogram(isc, true); 32062306a36Sopenharmony_ci else 32162306a36Sopenharmony_ci isc_set_histogram(isc, false); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* Update profile */ 32462306a36Sopenharmony_ci return isc_update_profile(isc); 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic int isc_prepare_streaming(struct vb2_queue *vq) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci struct isc_device *isc = vb2_get_drv_priv(vq); 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci return media_pipeline_start(isc->video_dev.entity.pads, &isc->mpipe); 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic int isc_start_streaming(struct vb2_queue *vq, unsigned int count) 33562306a36Sopenharmony_ci{ 33662306a36Sopenharmony_ci struct isc_device *isc = vb2_get_drv_priv(vq); 33762306a36Sopenharmony_ci struct regmap *regmap = isc->regmap; 33862306a36Sopenharmony_ci struct isc_buffer *buf; 33962306a36Sopenharmony_ci unsigned long flags; 34062306a36Sopenharmony_ci int ret; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci /* Enable stream on the sub device */ 34362306a36Sopenharmony_ci ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 1); 34462306a36Sopenharmony_ci if (ret && ret != -ENOIOCTLCMD) { 34562306a36Sopenharmony_ci dev_err(isc->dev, "stream on failed in subdev %d\n", ret); 34662306a36Sopenharmony_ci goto err_start_stream; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(isc->dev); 35062306a36Sopenharmony_ci if (ret < 0) { 35162306a36Sopenharmony_ci dev_err(isc->dev, "RPM resume failed in subdev %d\n", 35262306a36Sopenharmony_ci ret); 35362306a36Sopenharmony_ci goto err_pm_get; 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci ret = isc_configure(isc); 35762306a36Sopenharmony_ci if (unlikely(ret)) 35862306a36Sopenharmony_ci goto err_configure; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* Enable DMA interrupt */ 36162306a36Sopenharmony_ci regmap_write(regmap, ISC_INTEN, ISC_INT_DDONE); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci spin_lock_irqsave(&isc->dma_queue_lock, flags); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci isc->sequence = 0; 36662306a36Sopenharmony_ci isc->stop = false; 36762306a36Sopenharmony_ci reinit_completion(&isc->comp); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci isc->cur_frm = list_first_entry(&isc->dma_queue, 37062306a36Sopenharmony_ci struct isc_buffer, list); 37162306a36Sopenharmony_ci list_del(&isc->cur_frm->list); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci isc_crop_pfe(isc); 37462306a36Sopenharmony_ci isc_start_dma(isc); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci spin_unlock_irqrestore(&isc->dma_queue_lock, flags); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci /* if we streaming from RAW, we can do one-shot white balance adj */ 37962306a36Sopenharmony_ci if (ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) 38062306a36Sopenharmony_ci v4l2_ctrl_activate(isc->do_wb_ctrl, true); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci return 0; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cierr_configure: 38562306a36Sopenharmony_ci pm_runtime_put_sync(isc->dev); 38662306a36Sopenharmony_cierr_pm_get: 38762306a36Sopenharmony_ci v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 0); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cierr_start_stream: 39062306a36Sopenharmony_ci spin_lock_irqsave(&isc->dma_queue_lock, flags); 39162306a36Sopenharmony_ci list_for_each_entry(buf, &isc->dma_queue, list) 39262306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); 39362306a36Sopenharmony_ci INIT_LIST_HEAD(&isc->dma_queue); 39462306a36Sopenharmony_ci spin_unlock_irqrestore(&isc->dma_queue_lock, flags); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci return ret; 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic void isc_unprepare_streaming(struct vb2_queue *vq) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci struct isc_device *isc = vb2_get_drv_priv(vq); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci /* Stop media pipeline */ 40462306a36Sopenharmony_ci media_pipeline_stop(isc->video_dev.entity.pads); 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_cistatic void isc_stop_streaming(struct vb2_queue *vq) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci struct isc_device *isc = vb2_get_drv_priv(vq); 41062306a36Sopenharmony_ci unsigned long flags; 41162306a36Sopenharmony_ci struct isc_buffer *buf; 41262306a36Sopenharmony_ci int ret; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci mutex_lock(&isc->awb_mutex); 41562306a36Sopenharmony_ci v4l2_ctrl_activate(isc->do_wb_ctrl, false); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci isc->stop = true; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* Wait until the end of the current frame */ 42062306a36Sopenharmony_ci if (isc->cur_frm && !wait_for_completion_timeout(&isc->comp, 5 * HZ)) 42162306a36Sopenharmony_ci dev_err(isc->dev, "Timeout waiting for end of the capture\n"); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci mutex_unlock(&isc->awb_mutex); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci /* Disable DMA interrupt */ 42662306a36Sopenharmony_ci regmap_write(isc->regmap, ISC_INTDIS, ISC_INT_DDONE); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci pm_runtime_put_sync(isc->dev); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* Disable stream on the sub device */ 43162306a36Sopenharmony_ci ret = v4l2_subdev_call(isc->current_subdev->sd, video, s_stream, 0); 43262306a36Sopenharmony_ci if (ret && ret != -ENOIOCTLCMD) 43362306a36Sopenharmony_ci dev_err(isc->dev, "stream off failed in subdev\n"); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci /* Release all active buffers */ 43662306a36Sopenharmony_ci spin_lock_irqsave(&isc->dma_queue_lock, flags); 43762306a36Sopenharmony_ci if (unlikely(isc->cur_frm)) { 43862306a36Sopenharmony_ci vb2_buffer_done(&isc->cur_frm->vb.vb2_buf, 43962306a36Sopenharmony_ci VB2_BUF_STATE_ERROR); 44062306a36Sopenharmony_ci isc->cur_frm = NULL; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci list_for_each_entry(buf, &isc->dma_queue, list) 44362306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 44462306a36Sopenharmony_ci INIT_LIST_HEAD(&isc->dma_queue); 44562306a36Sopenharmony_ci spin_unlock_irqrestore(&isc->dma_queue_lock, flags); 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic void isc_buffer_queue(struct vb2_buffer *vb) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 45162306a36Sopenharmony_ci struct isc_buffer *buf = container_of(vbuf, struct isc_buffer, vb); 45262306a36Sopenharmony_ci struct isc_device *isc = vb2_get_drv_priv(vb->vb2_queue); 45362306a36Sopenharmony_ci unsigned long flags; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci spin_lock_irqsave(&isc->dma_queue_lock, flags); 45662306a36Sopenharmony_ci if (!isc->cur_frm && list_empty(&isc->dma_queue) && 45762306a36Sopenharmony_ci vb2_start_streaming_called(vb->vb2_queue)) { 45862306a36Sopenharmony_ci isc->cur_frm = buf; 45962306a36Sopenharmony_ci isc_start_dma(isc); 46062306a36Sopenharmony_ci } else { 46162306a36Sopenharmony_ci list_add_tail(&buf->list, &isc->dma_queue); 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci spin_unlock_irqrestore(&isc->dma_queue_lock, flags); 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic const struct vb2_ops isc_vb2_ops = { 46762306a36Sopenharmony_ci .queue_setup = isc_queue_setup, 46862306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 46962306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 47062306a36Sopenharmony_ci .buf_prepare = isc_buffer_prepare, 47162306a36Sopenharmony_ci .start_streaming = isc_start_streaming, 47262306a36Sopenharmony_ci .stop_streaming = isc_stop_streaming, 47362306a36Sopenharmony_ci .buf_queue = isc_buffer_queue, 47462306a36Sopenharmony_ci .prepare_streaming = isc_prepare_streaming, 47562306a36Sopenharmony_ci .unprepare_streaming = isc_unprepare_streaming, 47662306a36Sopenharmony_ci}; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic int isc_querycap(struct file *file, void *priv, 47962306a36Sopenharmony_ci struct v4l2_capability *cap) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct isc_device *isc = video_drvdata(file); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci strscpy(cap->driver, "microchip-isc", sizeof(cap->driver)); 48462306a36Sopenharmony_ci strscpy(cap->card, "Microchip Image Sensor Controller", sizeof(cap->card)); 48562306a36Sopenharmony_ci snprintf(cap->bus_info, sizeof(cap->bus_info), 48662306a36Sopenharmony_ci "platform:%s", isc->v4l2_dev.name); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci return 0; 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic int isc_enum_fmt_vid_cap(struct file *file, void *priv, 49262306a36Sopenharmony_ci struct v4l2_fmtdesc *f) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci struct isc_device *isc = video_drvdata(file); 49562306a36Sopenharmony_ci u32 index = f->index; 49662306a36Sopenharmony_ci u32 i, supported_index = 0; 49762306a36Sopenharmony_ci struct isc_format *fmt; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci /* 50062306a36Sopenharmony_ci * If we are not asked a specific mbus_code, we have to report all 50162306a36Sopenharmony_ci * the formats that we can output. 50262306a36Sopenharmony_ci */ 50362306a36Sopenharmony_ci if (!f->mbus_code) { 50462306a36Sopenharmony_ci if (index >= isc->controller_formats_size) 50562306a36Sopenharmony_ci return -EINVAL; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci f->pixelformat = isc->controller_formats[index].fourcc; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci return 0; 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* 51362306a36Sopenharmony_ci * If a specific mbus_code is requested, check if we support 51462306a36Sopenharmony_ci * this mbus_code as input for the ISC. 51562306a36Sopenharmony_ci * If it's supported, then we report the corresponding pixelformat 51662306a36Sopenharmony_ci * as first possible option for the ISC. 51762306a36Sopenharmony_ci * E.g. mbus MEDIA_BUS_FMT_YUYV8_2X8 and report 51862306a36Sopenharmony_ci * 'YUYV' (YUYV 4:2:2) 51962306a36Sopenharmony_ci */ 52062306a36Sopenharmony_ci fmt = isc_find_format_by_code(isc, f->mbus_code, &i); 52162306a36Sopenharmony_ci if (!fmt) 52262306a36Sopenharmony_ci return -EINVAL; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci if (!index) { 52562306a36Sopenharmony_ci f->pixelformat = fmt->fourcc; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci return 0; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci supported_index++; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci /* If the index is not raw, we don't have anymore formats to report */ 53362306a36Sopenharmony_ci if (!ISC_IS_FORMAT_RAW(f->mbus_code)) 53462306a36Sopenharmony_ci return -EINVAL; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci /* 53762306a36Sopenharmony_ci * We are asked for a specific mbus code, which is raw. 53862306a36Sopenharmony_ci * We have to search through the formats we can convert to. 53962306a36Sopenharmony_ci * We have to skip the raw formats, we cannot convert to raw. 54062306a36Sopenharmony_ci * E.g. 'AR12' (16-bit ARGB 4-4-4-4), 'AR15' (16-bit ARGB 1-5-5-5), etc. 54162306a36Sopenharmony_ci */ 54262306a36Sopenharmony_ci for (i = 0; i < isc->controller_formats_size; i++) { 54362306a36Sopenharmony_ci if (isc->controller_formats[i].raw) 54462306a36Sopenharmony_ci continue; 54562306a36Sopenharmony_ci if (index == supported_index) { 54662306a36Sopenharmony_ci f->pixelformat = isc->controller_formats[i].fourcc; 54762306a36Sopenharmony_ci return 0; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci supported_index++; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci return -EINVAL; 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic int isc_g_fmt_vid_cap(struct file *file, void *priv, 55662306a36Sopenharmony_ci struct v4l2_format *fmt) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci struct isc_device *isc = video_drvdata(file); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci *fmt = isc->fmt; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci return 0; 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci/* 56662306a36Sopenharmony_ci * Checks the current configured format, if ISC can output it, 56762306a36Sopenharmony_ci * considering which type of format the ISC receives from the sensor 56862306a36Sopenharmony_ci */ 56962306a36Sopenharmony_cistatic int isc_try_validate_formats(struct isc_device *isc) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci int ret; 57262306a36Sopenharmony_ci bool bayer = false, yuv = false, rgb = false, grey = false; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* all formats supported by the RLP module are OK */ 57562306a36Sopenharmony_ci switch (isc->try_config.fourcc) { 57662306a36Sopenharmony_ci case V4L2_PIX_FMT_SBGGR8: 57762306a36Sopenharmony_ci case V4L2_PIX_FMT_SGBRG8: 57862306a36Sopenharmony_ci case V4L2_PIX_FMT_SGRBG8: 57962306a36Sopenharmony_ci case V4L2_PIX_FMT_SRGGB8: 58062306a36Sopenharmony_ci case V4L2_PIX_FMT_SBGGR10: 58162306a36Sopenharmony_ci case V4L2_PIX_FMT_SGBRG10: 58262306a36Sopenharmony_ci case V4L2_PIX_FMT_SGRBG10: 58362306a36Sopenharmony_ci case V4L2_PIX_FMT_SRGGB10: 58462306a36Sopenharmony_ci case V4L2_PIX_FMT_SBGGR12: 58562306a36Sopenharmony_ci case V4L2_PIX_FMT_SGBRG12: 58662306a36Sopenharmony_ci case V4L2_PIX_FMT_SGRBG12: 58762306a36Sopenharmony_ci case V4L2_PIX_FMT_SRGGB12: 58862306a36Sopenharmony_ci ret = 0; 58962306a36Sopenharmony_ci bayer = true; 59062306a36Sopenharmony_ci break; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci case V4L2_PIX_FMT_YUV420: 59362306a36Sopenharmony_ci case V4L2_PIX_FMT_YUV422P: 59462306a36Sopenharmony_ci case V4L2_PIX_FMT_YUYV: 59562306a36Sopenharmony_ci case V4L2_PIX_FMT_UYVY: 59662306a36Sopenharmony_ci case V4L2_PIX_FMT_VYUY: 59762306a36Sopenharmony_ci ret = 0; 59862306a36Sopenharmony_ci yuv = true; 59962306a36Sopenharmony_ci break; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci case V4L2_PIX_FMT_RGB565: 60262306a36Sopenharmony_ci case V4L2_PIX_FMT_ABGR32: 60362306a36Sopenharmony_ci case V4L2_PIX_FMT_XBGR32: 60462306a36Sopenharmony_ci case V4L2_PIX_FMT_ARGB444: 60562306a36Sopenharmony_ci case V4L2_PIX_FMT_ARGB555: 60662306a36Sopenharmony_ci ret = 0; 60762306a36Sopenharmony_ci rgb = true; 60862306a36Sopenharmony_ci break; 60962306a36Sopenharmony_ci case V4L2_PIX_FMT_GREY: 61062306a36Sopenharmony_ci case V4L2_PIX_FMT_Y10: 61162306a36Sopenharmony_ci case V4L2_PIX_FMT_Y16: 61262306a36Sopenharmony_ci ret = 0; 61362306a36Sopenharmony_ci grey = true; 61462306a36Sopenharmony_ci break; 61562306a36Sopenharmony_ci default: 61662306a36Sopenharmony_ci /* any other different formats are not supported */ 61762306a36Sopenharmony_ci dev_err(isc->dev, "Requested unsupported format.\n"); 61862306a36Sopenharmony_ci ret = -EINVAL; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci dev_dbg(isc->dev, 62162306a36Sopenharmony_ci "Format validation, requested rgb=%u, yuv=%u, grey=%u, bayer=%u\n", 62262306a36Sopenharmony_ci rgb, yuv, grey, bayer); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci if (bayer && 62562306a36Sopenharmony_ci !ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) { 62662306a36Sopenharmony_ci dev_err(isc->dev, "Cannot output RAW if we do not receive RAW.\n"); 62762306a36Sopenharmony_ci return -EINVAL; 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci if (grey && !ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code) && 63162306a36Sopenharmony_ci !ISC_IS_FORMAT_GREY(isc->try_config.sd_format->mbus_code)) { 63262306a36Sopenharmony_ci dev_err(isc->dev, "Cannot output GREY if we do not receive RAW/GREY.\n"); 63362306a36Sopenharmony_ci return -EINVAL; 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci if ((rgb || bayer || yuv) && 63762306a36Sopenharmony_ci ISC_IS_FORMAT_GREY(isc->try_config.sd_format->mbus_code)) { 63862306a36Sopenharmony_ci dev_err(isc->dev, "Cannot convert GREY to another format.\n"); 63962306a36Sopenharmony_ci return -EINVAL; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci return ret; 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci/* 64662306a36Sopenharmony_ci * Configures the RLP and DMA modules, depending on the output format 64762306a36Sopenharmony_ci * configured for the ISC. 64862306a36Sopenharmony_ci * If direct_dump == true, just dump raw data 8/16 bits depending on format. 64962306a36Sopenharmony_ci */ 65062306a36Sopenharmony_cistatic int isc_try_configure_rlp_dma(struct isc_device *isc, bool direct_dump) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci isc->try_config.rlp_cfg_mode = 0; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci switch (isc->try_config.fourcc) { 65562306a36Sopenharmony_ci case V4L2_PIX_FMT_SBGGR8: 65662306a36Sopenharmony_ci case V4L2_PIX_FMT_SGBRG8: 65762306a36Sopenharmony_ci case V4L2_PIX_FMT_SGRBG8: 65862306a36Sopenharmony_ci case V4L2_PIX_FMT_SRGGB8: 65962306a36Sopenharmony_ci isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT8; 66062306a36Sopenharmony_ci isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8; 66162306a36Sopenharmony_ci isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; 66262306a36Sopenharmony_ci isc->try_config.bpp = 8; 66362306a36Sopenharmony_ci isc->try_config.bpp_v4l2 = 8; 66462306a36Sopenharmony_ci break; 66562306a36Sopenharmony_ci case V4L2_PIX_FMT_SBGGR10: 66662306a36Sopenharmony_ci case V4L2_PIX_FMT_SGBRG10: 66762306a36Sopenharmony_ci case V4L2_PIX_FMT_SGRBG10: 66862306a36Sopenharmony_ci case V4L2_PIX_FMT_SRGGB10: 66962306a36Sopenharmony_ci isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT10; 67062306a36Sopenharmony_ci isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; 67162306a36Sopenharmony_ci isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; 67262306a36Sopenharmony_ci isc->try_config.bpp = 16; 67362306a36Sopenharmony_ci isc->try_config.bpp_v4l2 = 16; 67462306a36Sopenharmony_ci break; 67562306a36Sopenharmony_ci case V4L2_PIX_FMT_SBGGR12: 67662306a36Sopenharmony_ci case V4L2_PIX_FMT_SGBRG12: 67762306a36Sopenharmony_ci case V4L2_PIX_FMT_SGRBG12: 67862306a36Sopenharmony_ci case V4L2_PIX_FMT_SRGGB12: 67962306a36Sopenharmony_ci isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT12; 68062306a36Sopenharmony_ci isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; 68162306a36Sopenharmony_ci isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; 68262306a36Sopenharmony_ci isc->try_config.bpp = 16; 68362306a36Sopenharmony_ci isc->try_config.bpp_v4l2 = 16; 68462306a36Sopenharmony_ci break; 68562306a36Sopenharmony_ci case V4L2_PIX_FMT_RGB565: 68662306a36Sopenharmony_ci isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_RGB565; 68762306a36Sopenharmony_ci isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; 68862306a36Sopenharmony_ci isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; 68962306a36Sopenharmony_ci isc->try_config.bpp = 16; 69062306a36Sopenharmony_ci isc->try_config.bpp_v4l2 = 16; 69162306a36Sopenharmony_ci break; 69262306a36Sopenharmony_ci case V4L2_PIX_FMT_ARGB444: 69362306a36Sopenharmony_ci isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB444; 69462306a36Sopenharmony_ci isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; 69562306a36Sopenharmony_ci isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; 69662306a36Sopenharmony_ci isc->try_config.bpp = 16; 69762306a36Sopenharmony_ci isc->try_config.bpp_v4l2 = 16; 69862306a36Sopenharmony_ci break; 69962306a36Sopenharmony_ci case V4L2_PIX_FMT_ARGB555: 70062306a36Sopenharmony_ci isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB555; 70162306a36Sopenharmony_ci isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; 70262306a36Sopenharmony_ci isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; 70362306a36Sopenharmony_ci isc->try_config.bpp = 16; 70462306a36Sopenharmony_ci isc->try_config.bpp_v4l2 = 16; 70562306a36Sopenharmony_ci break; 70662306a36Sopenharmony_ci case V4L2_PIX_FMT_ABGR32: 70762306a36Sopenharmony_ci case V4L2_PIX_FMT_XBGR32: 70862306a36Sopenharmony_ci isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_ARGB32; 70962306a36Sopenharmony_ci isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32; 71062306a36Sopenharmony_ci isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; 71162306a36Sopenharmony_ci isc->try_config.bpp = 32; 71262306a36Sopenharmony_ci isc->try_config.bpp_v4l2 = 32; 71362306a36Sopenharmony_ci break; 71462306a36Sopenharmony_ci case V4L2_PIX_FMT_YUV420: 71562306a36Sopenharmony_ci isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YYCC; 71662306a36Sopenharmony_ci isc->try_config.dcfg_imode = ISC_DCFG_IMODE_YC420P; 71762306a36Sopenharmony_ci isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PLANAR; 71862306a36Sopenharmony_ci isc->try_config.bpp = 12; 71962306a36Sopenharmony_ci isc->try_config.bpp_v4l2 = 8; /* only first plane */ 72062306a36Sopenharmony_ci break; 72162306a36Sopenharmony_ci case V4L2_PIX_FMT_YUV422P: 72262306a36Sopenharmony_ci isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YYCC; 72362306a36Sopenharmony_ci isc->try_config.dcfg_imode = ISC_DCFG_IMODE_YC422P; 72462306a36Sopenharmony_ci isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PLANAR; 72562306a36Sopenharmony_ci isc->try_config.bpp = 16; 72662306a36Sopenharmony_ci isc->try_config.bpp_v4l2 = 8; /* only first plane */ 72762306a36Sopenharmony_ci break; 72862306a36Sopenharmony_ci case V4L2_PIX_FMT_YUYV: 72962306a36Sopenharmony_ci isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_YUYV; 73062306a36Sopenharmony_ci isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32; 73162306a36Sopenharmony_ci isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; 73262306a36Sopenharmony_ci isc->try_config.bpp = 16; 73362306a36Sopenharmony_ci isc->try_config.bpp_v4l2 = 16; 73462306a36Sopenharmony_ci break; 73562306a36Sopenharmony_ci case V4L2_PIX_FMT_UYVY: 73662306a36Sopenharmony_ci isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_UYVY; 73762306a36Sopenharmony_ci isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32; 73862306a36Sopenharmony_ci isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; 73962306a36Sopenharmony_ci isc->try_config.bpp = 16; 74062306a36Sopenharmony_ci isc->try_config.bpp_v4l2 = 16; 74162306a36Sopenharmony_ci break; 74262306a36Sopenharmony_ci case V4L2_PIX_FMT_VYUY: 74362306a36Sopenharmony_ci isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_YCYC | ISC_RLP_CFG_YMODE_VYUY; 74462306a36Sopenharmony_ci isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED32; 74562306a36Sopenharmony_ci isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; 74662306a36Sopenharmony_ci isc->try_config.bpp = 16; 74762306a36Sopenharmony_ci isc->try_config.bpp_v4l2 = 16; 74862306a36Sopenharmony_ci break; 74962306a36Sopenharmony_ci case V4L2_PIX_FMT_GREY: 75062306a36Sopenharmony_ci isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DATY8; 75162306a36Sopenharmony_ci isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8; 75262306a36Sopenharmony_ci isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; 75362306a36Sopenharmony_ci isc->try_config.bpp = 8; 75462306a36Sopenharmony_ci isc->try_config.bpp_v4l2 = 8; 75562306a36Sopenharmony_ci break; 75662306a36Sopenharmony_ci case V4L2_PIX_FMT_Y16: 75762306a36Sopenharmony_ci isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DATY10 | ISC_RLP_CFG_LSH; 75862306a36Sopenharmony_ci fallthrough; 75962306a36Sopenharmony_ci case V4L2_PIX_FMT_Y10: 76062306a36Sopenharmony_ci isc->try_config.rlp_cfg_mode |= ISC_RLP_CFG_MODE_DATY10; 76162306a36Sopenharmony_ci isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED16; 76262306a36Sopenharmony_ci isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; 76362306a36Sopenharmony_ci isc->try_config.bpp = 16; 76462306a36Sopenharmony_ci isc->try_config.bpp_v4l2 = 16; 76562306a36Sopenharmony_ci break; 76662306a36Sopenharmony_ci default: 76762306a36Sopenharmony_ci return -EINVAL; 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci if (direct_dump) { 77162306a36Sopenharmony_ci isc->try_config.rlp_cfg_mode = ISC_RLP_CFG_MODE_DAT8; 77262306a36Sopenharmony_ci isc->try_config.dcfg_imode = ISC_DCFG_IMODE_PACKED8; 77362306a36Sopenharmony_ci isc->try_config.dctrl_dview = ISC_DCTRL_DVIEW_PACKED; 77462306a36Sopenharmony_ci return 0; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci return 0; 77862306a36Sopenharmony_ci} 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci/* 78162306a36Sopenharmony_ci * Configuring pipeline modules, depending on which format the ISC outputs 78262306a36Sopenharmony_ci * and considering which format it has as input from the sensor. 78362306a36Sopenharmony_ci */ 78462306a36Sopenharmony_cistatic int isc_try_configure_pipeline(struct isc_device *isc) 78562306a36Sopenharmony_ci{ 78662306a36Sopenharmony_ci switch (isc->try_config.fourcc) { 78762306a36Sopenharmony_ci case V4L2_PIX_FMT_RGB565: 78862306a36Sopenharmony_ci case V4L2_PIX_FMT_ARGB555: 78962306a36Sopenharmony_ci case V4L2_PIX_FMT_ARGB444: 79062306a36Sopenharmony_ci case V4L2_PIX_FMT_ABGR32: 79162306a36Sopenharmony_ci case V4L2_PIX_FMT_XBGR32: 79262306a36Sopenharmony_ci /* if sensor format is RAW, we convert inside ISC */ 79362306a36Sopenharmony_ci if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) { 79462306a36Sopenharmony_ci isc->try_config.bits_pipeline = CFA_ENABLE | 79562306a36Sopenharmony_ci WB_ENABLE | GAM_ENABLES | DPC_BLCENABLE | 79662306a36Sopenharmony_ci CC_ENABLE; 79762306a36Sopenharmony_ci } else { 79862306a36Sopenharmony_ci isc->try_config.bits_pipeline = 0x0; 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci break; 80162306a36Sopenharmony_ci case V4L2_PIX_FMT_YUV420: 80262306a36Sopenharmony_ci /* if sensor format is RAW, we convert inside ISC */ 80362306a36Sopenharmony_ci if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) { 80462306a36Sopenharmony_ci isc->try_config.bits_pipeline = CFA_ENABLE | 80562306a36Sopenharmony_ci CSC_ENABLE | GAM_ENABLES | WB_ENABLE | 80662306a36Sopenharmony_ci SUB420_ENABLE | SUB422_ENABLE | CBC_ENABLE | 80762306a36Sopenharmony_ci DPC_BLCENABLE; 80862306a36Sopenharmony_ci } else { 80962306a36Sopenharmony_ci isc->try_config.bits_pipeline = 0x0; 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci break; 81262306a36Sopenharmony_ci case V4L2_PIX_FMT_YUV422P: 81362306a36Sopenharmony_ci /* if sensor format is RAW, we convert inside ISC */ 81462306a36Sopenharmony_ci if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) { 81562306a36Sopenharmony_ci isc->try_config.bits_pipeline = CFA_ENABLE | 81662306a36Sopenharmony_ci CSC_ENABLE | WB_ENABLE | GAM_ENABLES | 81762306a36Sopenharmony_ci SUB422_ENABLE | CBC_ENABLE | DPC_BLCENABLE; 81862306a36Sopenharmony_ci } else { 81962306a36Sopenharmony_ci isc->try_config.bits_pipeline = 0x0; 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci break; 82262306a36Sopenharmony_ci case V4L2_PIX_FMT_YUYV: 82362306a36Sopenharmony_ci case V4L2_PIX_FMT_UYVY: 82462306a36Sopenharmony_ci case V4L2_PIX_FMT_VYUY: 82562306a36Sopenharmony_ci /* if sensor format is RAW, we convert inside ISC */ 82662306a36Sopenharmony_ci if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) { 82762306a36Sopenharmony_ci isc->try_config.bits_pipeline = CFA_ENABLE | 82862306a36Sopenharmony_ci CSC_ENABLE | WB_ENABLE | GAM_ENABLES | 82962306a36Sopenharmony_ci SUB422_ENABLE | CBC_ENABLE | DPC_BLCENABLE; 83062306a36Sopenharmony_ci } else { 83162306a36Sopenharmony_ci isc->try_config.bits_pipeline = 0x0; 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci break; 83462306a36Sopenharmony_ci case V4L2_PIX_FMT_GREY: 83562306a36Sopenharmony_ci case V4L2_PIX_FMT_Y16: 83662306a36Sopenharmony_ci /* if sensor format is RAW, we convert inside ISC */ 83762306a36Sopenharmony_ci if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) { 83862306a36Sopenharmony_ci isc->try_config.bits_pipeline = CFA_ENABLE | 83962306a36Sopenharmony_ci CSC_ENABLE | WB_ENABLE | GAM_ENABLES | 84062306a36Sopenharmony_ci CBC_ENABLE | DPC_BLCENABLE; 84162306a36Sopenharmony_ci } else { 84262306a36Sopenharmony_ci isc->try_config.bits_pipeline = 0x0; 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci break; 84562306a36Sopenharmony_ci default: 84662306a36Sopenharmony_ci if (ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) 84762306a36Sopenharmony_ci isc->try_config.bits_pipeline = WB_ENABLE | DPC_BLCENABLE; 84862306a36Sopenharmony_ci else 84962306a36Sopenharmony_ci isc->try_config.bits_pipeline = 0x0; 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci /* Tune the pipeline to product specific */ 85362306a36Sopenharmony_ci isc->adapt_pipeline(isc); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci return 0; 85662306a36Sopenharmony_ci} 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_cistatic void isc_try_fse(struct isc_device *isc, 85962306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state) 86062306a36Sopenharmony_ci{ 86162306a36Sopenharmony_ci struct v4l2_subdev_frame_size_enum fse = { 86262306a36Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_TRY, 86362306a36Sopenharmony_ci }; 86462306a36Sopenharmony_ci int ret; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci /* 86762306a36Sopenharmony_ci * If we do not know yet which format the subdev is using, we cannot 86862306a36Sopenharmony_ci * do anything. 86962306a36Sopenharmony_ci */ 87062306a36Sopenharmony_ci if (!isc->config.sd_format) 87162306a36Sopenharmony_ci return; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci fse.code = isc->try_config.sd_format->mbus_code; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci ret = v4l2_subdev_call(isc->current_subdev->sd, pad, enum_frame_size, 87662306a36Sopenharmony_ci sd_state, &fse); 87762306a36Sopenharmony_ci /* 87862306a36Sopenharmony_ci * Attempt to obtain format size from subdev. If not available, 87962306a36Sopenharmony_ci * just use the maximum ISC can receive. 88062306a36Sopenharmony_ci */ 88162306a36Sopenharmony_ci if (ret) { 88262306a36Sopenharmony_ci sd_state->pads->try_crop.width = isc->max_width; 88362306a36Sopenharmony_ci sd_state->pads->try_crop.height = isc->max_height; 88462306a36Sopenharmony_ci } else { 88562306a36Sopenharmony_ci sd_state->pads->try_crop.width = fse.max_width; 88662306a36Sopenharmony_ci sd_state->pads->try_crop.height = fse.max_height; 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci} 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_cistatic int isc_try_fmt(struct isc_device *isc, struct v4l2_format *f) 89162306a36Sopenharmony_ci{ 89262306a36Sopenharmony_ci struct v4l2_pix_format *pixfmt = &f->fmt.pix; 89362306a36Sopenharmony_ci unsigned int i; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) 89662306a36Sopenharmony_ci return -EINVAL; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci isc->try_config.fourcc = isc->controller_formats[0].fourcc; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci /* find if the format requested is supported */ 90162306a36Sopenharmony_ci for (i = 0; i < isc->controller_formats_size; i++) 90262306a36Sopenharmony_ci if (isc->controller_formats[i].fourcc == pixfmt->pixelformat) { 90362306a36Sopenharmony_ci isc->try_config.fourcc = pixfmt->pixelformat; 90462306a36Sopenharmony_ci break; 90562306a36Sopenharmony_ci } 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci isc_try_configure_rlp_dma(isc, false); 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci /* Limit to Microchip ISC hardware capabilities */ 91062306a36Sopenharmony_ci v4l_bound_align_image(&pixfmt->width, 16, isc->max_width, 0, 91162306a36Sopenharmony_ci &pixfmt->height, 16, isc->max_height, 0, 0); 91262306a36Sopenharmony_ci /* If we did not find the requested format, we will fallback here */ 91362306a36Sopenharmony_ci pixfmt->pixelformat = isc->try_config.fourcc; 91462306a36Sopenharmony_ci pixfmt->colorspace = V4L2_COLORSPACE_SRGB; 91562306a36Sopenharmony_ci pixfmt->field = V4L2_FIELD_NONE; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci pixfmt->bytesperline = (pixfmt->width * isc->try_config.bpp_v4l2) >> 3; 91862306a36Sopenharmony_ci pixfmt->sizeimage = ((pixfmt->width * isc->try_config.bpp) >> 3) * 91962306a36Sopenharmony_ci pixfmt->height; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci isc->try_fmt = *f; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci return 0; 92462306a36Sopenharmony_ci} 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_cistatic int isc_set_fmt(struct isc_device *isc, struct v4l2_format *f) 92762306a36Sopenharmony_ci{ 92862306a36Sopenharmony_ci isc_try_fmt(isc, f); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci /* make the try configuration active */ 93162306a36Sopenharmony_ci isc->config = isc->try_config; 93262306a36Sopenharmony_ci isc->fmt = isc->try_fmt; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci dev_dbg(isc->dev, "ISC set_fmt to %.4s @%dx%d\n", 93562306a36Sopenharmony_ci (char *)&f->fmt.pix.pixelformat, 93662306a36Sopenharmony_ci f->fmt.pix.width, f->fmt.pix.height); 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci return 0; 93962306a36Sopenharmony_ci} 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_cistatic int isc_validate(struct isc_device *isc) 94262306a36Sopenharmony_ci{ 94362306a36Sopenharmony_ci int ret; 94462306a36Sopenharmony_ci int i; 94562306a36Sopenharmony_ci struct isc_format *sd_fmt = NULL; 94662306a36Sopenharmony_ci struct v4l2_pix_format *pixfmt = &isc->fmt.fmt.pix; 94762306a36Sopenharmony_ci struct v4l2_subdev_format format = { 94862306a36Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 94962306a36Sopenharmony_ci .pad = isc->remote_pad, 95062306a36Sopenharmony_ci }; 95162306a36Sopenharmony_ci struct v4l2_subdev_pad_config pad_cfg = {}; 95262306a36Sopenharmony_ci struct v4l2_subdev_state pad_state = { 95362306a36Sopenharmony_ci .pads = &pad_cfg, 95462306a36Sopenharmony_ci }; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci /* Get current format from subdev */ 95762306a36Sopenharmony_ci ret = v4l2_subdev_call(isc->current_subdev->sd, pad, get_fmt, NULL, 95862306a36Sopenharmony_ci &format); 95962306a36Sopenharmony_ci if (ret) 96062306a36Sopenharmony_ci return ret; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci /* Identify the subdev's format configuration */ 96362306a36Sopenharmony_ci for (i = 0; i < isc->formats_list_size; i++) 96462306a36Sopenharmony_ci if (isc->formats_list[i].mbus_code == format.format.code) { 96562306a36Sopenharmony_ci sd_fmt = &isc->formats_list[i]; 96662306a36Sopenharmony_ci break; 96762306a36Sopenharmony_ci } 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci /* Check if the format is not supported */ 97062306a36Sopenharmony_ci if (!sd_fmt) { 97162306a36Sopenharmony_ci dev_err(isc->dev, 97262306a36Sopenharmony_ci "Current subdevice is streaming a media bus code that is not supported 0x%x\n", 97362306a36Sopenharmony_ci format.format.code); 97462306a36Sopenharmony_ci return -EPIPE; 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci /* At this moment we know which format the subdev will use */ 97862306a36Sopenharmony_ci isc->try_config.sd_format = sd_fmt; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci /* If the sensor is not RAW, we can only do a direct dump */ 98162306a36Sopenharmony_ci if (!ISC_IS_FORMAT_RAW(isc->try_config.sd_format->mbus_code)) 98262306a36Sopenharmony_ci isc_try_configure_rlp_dma(isc, true); 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci /* Limit to Microchip ISC hardware capabilities */ 98562306a36Sopenharmony_ci v4l_bound_align_image(&format.format.width, 16, isc->max_width, 0, 98662306a36Sopenharmony_ci &format.format.height, 16, isc->max_height, 0, 0); 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci /* Check if the frame size is the same. Otherwise we may overflow */ 98962306a36Sopenharmony_ci if (pixfmt->height != format.format.height || 99062306a36Sopenharmony_ci pixfmt->width != format.format.width) { 99162306a36Sopenharmony_ci dev_err(isc->dev, 99262306a36Sopenharmony_ci "ISC not configured with the proper frame size: %dx%d\n", 99362306a36Sopenharmony_ci format.format.width, format.format.height); 99462306a36Sopenharmony_ci return -EPIPE; 99562306a36Sopenharmony_ci } 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci dev_dbg(isc->dev, 99862306a36Sopenharmony_ci "Identified subdev using format %.4s with %dx%d %d bpp\n", 99962306a36Sopenharmony_ci (char *)&sd_fmt->fourcc, pixfmt->width, pixfmt->height, 100062306a36Sopenharmony_ci isc->try_config.bpp); 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci /* Reset and restart AWB if the subdevice changed the format */ 100362306a36Sopenharmony_ci if (isc->try_config.sd_format && isc->config.sd_format && 100462306a36Sopenharmony_ci isc->try_config.sd_format != isc->config.sd_format) { 100562306a36Sopenharmony_ci isc->ctrls.hist_stat = HIST_INIT; 100662306a36Sopenharmony_ci isc_reset_awb_ctrls(isc); 100762306a36Sopenharmony_ci isc_update_v4l2_ctrls(isc); 100862306a36Sopenharmony_ci } 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci /* Validate formats */ 101162306a36Sopenharmony_ci ret = isc_try_validate_formats(isc); 101262306a36Sopenharmony_ci if (ret) 101362306a36Sopenharmony_ci return ret; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci /* Obtain frame sizes if possible to have crop requirements ready */ 101662306a36Sopenharmony_ci isc_try_fse(isc, &pad_state); 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci /* Configure ISC pipeline for the config */ 101962306a36Sopenharmony_ci ret = isc_try_configure_pipeline(isc); 102062306a36Sopenharmony_ci if (ret) 102162306a36Sopenharmony_ci return ret; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci isc->config = isc->try_config; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci dev_dbg(isc->dev, "New ISC configuration in place\n"); 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci return 0; 102862306a36Sopenharmony_ci} 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_cistatic int isc_s_fmt_vid_cap(struct file *file, void *priv, 103162306a36Sopenharmony_ci struct v4l2_format *f) 103262306a36Sopenharmony_ci{ 103362306a36Sopenharmony_ci struct isc_device *isc = video_drvdata(file); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci if (vb2_is_busy(&isc->vb2_vidq)) 103662306a36Sopenharmony_ci return -EBUSY; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci return isc_set_fmt(isc, f); 103962306a36Sopenharmony_ci} 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_cistatic int isc_try_fmt_vid_cap(struct file *file, void *priv, 104262306a36Sopenharmony_ci struct v4l2_format *f) 104362306a36Sopenharmony_ci{ 104462306a36Sopenharmony_ci struct isc_device *isc = video_drvdata(file); 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci return isc_try_fmt(isc, f); 104762306a36Sopenharmony_ci} 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_cistatic int isc_enum_input(struct file *file, void *priv, 105062306a36Sopenharmony_ci struct v4l2_input *inp) 105162306a36Sopenharmony_ci{ 105262306a36Sopenharmony_ci if (inp->index != 0) 105362306a36Sopenharmony_ci return -EINVAL; 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci inp->type = V4L2_INPUT_TYPE_CAMERA; 105662306a36Sopenharmony_ci inp->std = 0; 105762306a36Sopenharmony_ci strscpy(inp->name, "Camera", sizeof(inp->name)); 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci return 0; 106062306a36Sopenharmony_ci} 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_cistatic int isc_g_input(struct file *file, void *priv, unsigned int *i) 106362306a36Sopenharmony_ci{ 106462306a36Sopenharmony_ci *i = 0; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci return 0; 106762306a36Sopenharmony_ci} 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_cistatic int isc_s_input(struct file *file, void *priv, unsigned int i) 107062306a36Sopenharmony_ci{ 107162306a36Sopenharmony_ci if (i > 0) 107262306a36Sopenharmony_ci return -EINVAL; 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci return 0; 107562306a36Sopenharmony_ci} 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_cistatic int isc_g_parm(struct file *file, void *fh, struct v4l2_streamparm *a) 107862306a36Sopenharmony_ci{ 107962306a36Sopenharmony_ci struct isc_device *isc = video_drvdata(file); 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci return v4l2_g_parm_cap(video_devdata(file), isc->current_subdev->sd, a); 108262306a36Sopenharmony_ci} 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_cistatic int isc_s_parm(struct file *file, void *fh, struct v4l2_streamparm *a) 108562306a36Sopenharmony_ci{ 108662306a36Sopenharmony_ci struct isc_device *isc = video_drvdata(file); 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci return v4l2_s_parm_cap(video_devdata(file), isc->current_subdev->sd, a); 108962306a36Sopenharmony_ci} 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_cistatic int isc_enum_framesizes(struct file *file, void *fh, 109262306a36Sopenharmony_ci struct v4l2_frmsizeenum *fsize) 109362306a36Sopenharmony_ci{ 109462306a36Sopenharmony_ci struct isc_device *isc = video_drvdata(file); 109562306a36Sopenharmony_ci int ret = -EINVAL; 109662306a36Sopenharmony_ci int i; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci if (fsize->index) 109962306a36Sopenharmony_ci return -EINVAL; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci for (i = 0; i < isc->controller_formats_size; i++) 110262306a36Sopenharmony_ci if (isc->controller_formats[i].fourcc == fsize->pixel_format) 110362306a36Sopenharmony_ci ret = 0; 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci if (ret) 110662306a36Sopenharmony_ci return ret; 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci fsize->stepwise.min_width = 16; 111162306a36Sopenharmony_ci fsize->stepwise.max_width = isc->max_width; 111262306a36Sopenharmony_ci fsize->stepwise.min_height = 16; 111362306a36Sopenharmony_ci fsize->stepwise.max_height = isc->max_height; 111462306a36Sopenharmony_ci fsize->stepwise.step_width = 1; 111562306a36Sopenharmony_ci fsize->stepwise.step_height = 1; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci return 0; 111862306a36Sopenharmony_ci} 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops isc_ioctl_ops = { 112162306a36Sopenharmony_ci .vidioc_querycap = isc_querycap, 112262306a36Sopenharmony_ci .vidioc_enum_fmt_vid_cap = isc_enum_fmt_vid_cap, 112362306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap = isc_g_fmt_vid_cap, 112462306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap = isc_s_fmt_vid_cap, 112562306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap = isc_try_fmt_vid_cap, 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci .vidioc_enum_input = isc_enum_input, 112862306a36Sopenharmony_ci .vidioc_g_input = isc_g_input, 112962306a36Sopenharmony_ci .vidioc_s_input = isc_s_input, 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci .vidioc_reqbufs = vb2_ioctl_reqbufs, 113262306a36Sopenharmony_ci .vidioc_querybuf = vb2_ioctl_querybuf, 113362306a36Sopenharmony_ci .vidioc_qbuf = vb2_ioctl_qbuf, 113462306a36Sopenharmony_ci .vidioc_expbuf = vb2_ioctl_expbuf, 113562306a36Sopenharmony_ci .vidioc_dqbuf = vb2_ioctl_dqbuf, 113662306a36Sopenharmony_ci .vidioc_create_bufs = vb2_ioctl_create_bufs, 113762306a36Sopenharmony_ci .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 113862306a36Sopenharmony_ci .vidioc_streamon = vb2_ioctl_streamon, 113962306a36Sopenharmony_ci .vidioc_streamoff = vb2_ioctl_streamoff, 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci .vidioc_g_parm = isc_g_parm, 114262306a36Sopenharmony_ci .vidioc_s_parm = isc_s_parm, 114362306a36Sopenharmony_ci .vidioc_enum_framesizes = isc_enum_framesizes, 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci .vidioc_log_status = v4l2_ctrl_log_status, 114662306a36Sopenharmony_ci .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 114762306a36Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 114862306a36Sopenharmony_ci}; 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_cistatic int isc_open(struct file *file) 115162306a36Sopenharmony_ci{ 115262306a36Sopenharmony_ci struct isc_device *isc = video_drvdata(file); 115362306a36Sopenharmony_ci struct v4l2_subdev *sd = isc->current_subdev->sd; 115462306a36Sopenharmony_ci int ret; 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci if (mutex_lock_interruptible(&isc->lock)) 115762306a36Sopenharmony_ci return -ERESTARTSYS; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci ret = v4l2_fh_open(file); 116062306a36Sopenharmony_ci if (ret < 0) 116162306a36Sopenharmony_ci goto unlock; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci if (!v4l2_fh_is_singular_file(file)) 116462306a36Sopenharmony_ci goto unlock; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci ret = v4l2_subdev_call(sd, core, s_power, 1); 116762306a36Sopenharmony_ci if (ret < 0 && ret != -ENOIOCTLCMD) { 116862306a36Sopenharmony_ci v4l2_fh_release(file); 116962306a36Sopenharmony_ci goto unlock; 117062306a36Sopenharmony_ci } 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci ret = isc_set_fmt(isc, &isc->fmt); 117362306a36Sopenharmony_ci if (ret) { 117462306a36Sopenharmony_ci v4l2_subdev_call(sd, core, s_power, 0); 117562306a36Sopenharmony_ci v4l2_fh_release(file); 117662306a36Sopenharmony_ci } 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ciunlock: 117962306a36Sopenharmony_ci mutex_unlock(&isc->lock); 118062306a36Sopenharmony_ci return ret; 118162306a36Sopenharmony_ci} 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_cistatic int isc_release(struct file *file) 118462306a36Sopenharmony_ci{ 118562306a36Sopenharmony_ci struct isc_device *isc = video_drvdata(file); 118662306a36Sopenharmony_ci struct v4l2_subdev *sd = isc->current_subdev->sd; 118762306a36Sopenharmony_ci bool fh_singular; 118862306a36Sopenharmony_ci int ret; 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci mutex_lock(&isc->lock); 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci fh_singular = v4l2_fh_is_singular_file(file); 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci ret = _vb2_fop_release(file, NULL); 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci if (fh_singular) 119762306a36Sopenharmony_ci v4l2_subdev_call(sd, core, s_power, 0); 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci mutex_unlock(&isc->lock); 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci return ret; 120262306a36Sopenharmony_ci} 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_cistatic const struct v4l2_file_operations isc_fops = { 120562306a36Sopenharmony_ci .owner = THIS_MODULE, 120662306a36Sopenharmony_ci .open = isc_open, 120762306a36Sopenharmony_ci .release = isc_release, 120862306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 120962306a36Sopenharmony_ci .read = vb2_fop_read, 121062306a36Sopenharmony_ci .mmap = vb2_fop_mmap, 121162306a36Sopenharmony_ci .poll = vb2_fop_poll, 121262306a36Sopenharmony_ci}; 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ciirqreturn_t microchip_isc_interrupt(int irq, void *dev_id) 121562306a36Sopenharmony_ci{ 121662306a36Sopenharmony_ci struct isc_device *isc = (struct isc_device *)dev_id; 121762306a36Sopenharmony_ci struct regmap *regmap = isc->regmap; 121862306a36Sopenharmony_ci u32 isc_intsr, isc_intmask, pending; 121962306a36Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci regmap_read(regmap, ISC_INTSR, &isc_intsr); 122262306a36Sopenharmony_ci regmap_read(regmap, ISC_INTMASK, &isc_intmask); 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci pending = isc_intsr & isc_intmask; 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_ci if (likely(pending & ISC_INT_DDONE)) { 122762306a36Sopenharmony_ci spin_lock(&isc->dma_queue_lock); 122862306a36Sopenharmony_ci if (isc->cur_frm) { 122962306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = &isc->cur_frm->vb; 123062306a36Sopenharmony_ci struct vb2_buffer *vb = &vbuf->vb2_buf; 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci vb->timestamp = ktime_get_ns(); 123362306a36Sopenharmony_ci vbuf->sequence = isc->sequence++; 123462306a36Sopenharmony_ci vb2_buffer_done(vb, VB2_BUF_STATE_DONE); 123562306a36Sopenharmony_ci isc->cur_frm = NULL; 123662306a36Sopenharmony_ci } 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci if (!list_empty(&isc->dma_queue) && !isc->stop) { 123962306a36Sopenharmony_ci isc->cur_frm = list_first_entry(&isc->dma_queue, 124062306a36Sopenharmony_ci struct isc_buffer, list); 124162306a36Sopenharmony_ci list_del(&isc->cur_frm->list); 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci isc_start_dma(isc); 124462306a36Sopenharmony_ci } 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci if (isc->stop) 124762306a36Sopenharmony_ci complete(&isc->comp); 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci ret = IRQ_HANDLED; 125062306a36Sopenharmony_ci spin_unlock(&isc->dma_queue_lock); 125162306a36Sopenharmony_ci } 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci if (pending & ISC_INT_HISDONE) { 125462306a36Sopenharmony_ci schedule_work(&isc->awb_work); 125562306a36Sopenharmony_ci ret = IRQ_HANDLED; 125662306a36Sopenharmony_ci } 125762306a36Sopenharmony_ci 125862306a36Sopenharmony_ci return ret; 125962306a36Sopenharmony_ci} 126062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(microchip_isc_interrupt); 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_cistatic void isc_hist_count(struct isc_device *isc, u32 *min, u32 *max) 126362306a36Sopenharmony_ci{ 126462306a36Sopenharmony_ci struct regmap *regmap = isc->regmap; 126562306a36Sopenharmony_ci struct isc_ctrls *ctrls = &isc->ctrls; 126662306a36Sopenharmony_ci u32 *hist_count = &ctrls->hist_count[ctrls->hist_id]; 126762306a36Sopenharmony_ci u32 *hist_entry = &ctrls->hist_entry[0]; 126862306a36Sopenharmony_ci u32 i; 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci *min = 0; 127162306a36Sopenharmony_ci *max = HIST_ENTRIES; 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci regmap_bulk_read(regmap, ISC_HIS_ENTRY + isc->offsets.his_entry, 127462306a36Sopenharmony_ci hist_entry, HIST_ENTRIES); 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci *hist_count = 0; 127762306a36Sopenharmony_ci /* 127862306a36Sopenharmony_ci * we deliberately ignore the end of the histogram, 127962306a36Sopenharmony_ci * the most white pixels 128062306a36Sopenharmony_ci */ 128162306a36Sopenharmony_ci for (i = 1; i < HIST_ENTRIES; i++) { 128262306a36Sopenharmony_ci if (*hist_entry && !*min) 128362306a36Sopenharmony_ci *min = i; 128462306a36Sopenharmony_ci if (*hist_entry) 128562306a36Sopenharmony_ci *max = i; 128662306a36Sopenharmony_ci *hist_count += i * (*hist_entry++); 128762306a36Sopenharmony_ci } 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci if (!*min) 129062306a36Sopenharmony_ci *min = 1; 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci dev_dbg(isc->dev, "isc wb: hist_id %u, hist_count %u", 129362306a36Sopenharmony_ci ctrls->hist_id, *hist_count); 129462306a36Sopenharmony_ci} 129562306a36Sopenharmony_ci 129662306a36Sopenharmony_cistatic void isc_wb_update(struct isc_ctrls *ctrls) 129762306a36Sopenharmony_ci{ 129862306a36Sopenharmony_ci struct isc_device *isc = container_of(ctrls, struct isc_device, ctrls); 129962306a36Sopenharmony_ci u32 *hist_count = &ctrls->hist_count[0]; 130062306a36Sopenharmony_ci u32 c, offset[4]; 130162306a36Sopenharmony_ci u64 avg = 0; 130262306a36Sopenharmony_ci /* We compute two gains, stretch gain and grey world gain */ 130362306a36Sopenharmony_ci u32 s_gain[4], gw_gain[4]; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci /* 130662306a36Sopenharmony_ci * According to Grey World, we need to set gains for R/B to normalize 130762306a36Sopenharmony_ci * them towards the green channel. 130862306a36Sopenharmony_ci * Thus we want to keep Green as fixed and adjust only Red/Blue 130962306a36Sopenharmony_ci * Compute the average of the both green channels first 131062306a36Sopenharmony_ci */ 131162306a36Sopenharmony_ci avg = (u64)hist_count[ISC_HIS_CFG_MODE_GR] + 131262306a36Sopenharmony_ci (u64)hist_count[ISC_HIS_CFG_MODE_GB]; 131362306a36Sopenharmony_ci avg >>= 1; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci dev_dbg(isc->dev, "isc wb: green components average %llu\n", avg); 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci /* Green histogram is null, nothing to do */ 131862306a36Sopenharmony_ci if (!avg) 131962306a36Sopenharmony_ci return; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci for (c = ISC_HIS_CFG_MODE_GR; c <= ISC_HIS_CFG_MODE_B; c++) { 132262306a36Sopenharmony_ci /* 132362306a36Sopenharmony_ci * the color offset is the minimum value of the histogram. 132462306a36Sopenharmony_ci * we stretch this color to the full range by substracting 132562306a36Sopenharmony_ci * this value from the color component. 132662306a36Sopenharmony_ci */ 132762306a36Sopenharmony_ci offset[c] = ctrls->hist_minmax[c][HIST_MIN_INDEX]; 132862306a36Sopenharmony_ci /* 132962306a36Sopenharmony_ci * The offset is always at least 1. If the offset is 1, we do 133062306a36Sopenharmony_ci * not need to adjust it, so our result must be zero. 133162306a36Sopenharmony_ci * the offset is computed in a histogram on 9 bits (0..512) 133262306a36Sopenharmony_ci * but the offset in register is based on 133362306a36Sopenharmony_ci * 12 bits pipeline (0..4096). 133462306a36Sopenharmony_ci * we need to shift with the 3 bits that the histogram is 133562306a36Sopenharmony_ci * ignoring 133662306a36Sopenharmony_ci */ 133762306a36Sopenharmony_ci ctrls->offset[c] = (offset[c] - 1) << 3; 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci /* 134062306a36Sopenharmony_ci * the offset is then taken and converted to 2's complements, 134162306a36Sopenharmony_ci * and must be negative, as we subtract this value from the 134262306a36Sopenharmony_ci * color components 134362306a36Sopenharmony_ci */ 134462306a36Sopenharmony_ci ctrls->offset[c] = -ctrls->offset[c]; 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci /* 134762306a36Sopenharmony_ci * the stretch gain is the total number of histogram bins 134862306a36Sopenharmony_ci * divided by the actual range of color component (Max - Min) 134962306a36Sopenharmony_ci * If we compute gain like this, the actual color component 135062306a36Sopenharmony_ci * will be stretched to the full histogram. 135162306a36Sopenharmony_ci * We need to shift 9 bits for precision, we have 9 bits for 135262306a36Sopenharmony_ci * decimals 135362306a36Sopenharmony_ci */ 135462306a36Sopenharmony_ci s_gain[c] = (HIST_ENTRIES << 9) / 135562306a36Sopenharmony_ci (ctrls->hist_minmax[c][HIST_MAX_INDEX] - 135662306a36Sopenharmony_ci ctrls->hist_minmax[c][HIST_MIN_INDEX] + 1); 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci /* 135962306a36Sopenharmony_ci * Now we have to compute the gain w.r.t. the average. 136062306a36Sopenharmony_ci * Add/lose gain to the component towards the average. 136162306a36Sopenharmony_ci * If it happens that the component is zero, use the 136262306a36Sopenharmony_ci * fixed point value : 1.0 gain. 136362306a36Sopenharmony_ci */ 136462306a36Sopenharmony_ci if (hist_count[c]) 136562306a36Sopenharmony_ci gw_gain[c] = div_u64(avg << 9, hist_count[c]); 136662306a36Sopenharmony_ci else 136762306a36Sopenharmony_ci gw_gain[c] = 1 << 9; 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci dev_dbg(isc->dev, 137062306a36Sopenharmony_ci "isc wb: component %d, s_gain %u, gw_gain %u\n", 137162306a36Sopenharmony_ci c, s_gain[c], gw_gain[c]); 137262306a36Sopenharmony_ci /* multiply both gains and adjust for decimals */ 137362306a36Sopenharmony_ci ctrls->gain[c] = s_gain[c] * gw_gain[c]; 137462306a36Sopenharmony_ci ctrls->gain[c] >>= 9; 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci /* make sure we are not out of range */ 137762306a36Sopenharmony_ci ctrls->gain[c] = clamp_val(ctrls->gain[c], 0, GENMASK(12, 0)); 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci dev_dbg(isc->dev, "isc wb: component %d, final gain %u\n", 138062306a36Sopenharmony_ci c, ctrls->gain[c]); 138162306a36Sopenharmony_ci } 138262306a36Sopenharmony_ci} 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_cistatic void isc_awb_work(struct work_struct *w) 138562306a36Sopenharmony_ci{ 138662306a36Sopenharmony_ci struct isc_device *isc = 138762306a36Sopenharmony_ci container_of(w, struct isc_device, awb_work); 138862306a36Sopenharmony_ci struct regmap *regmap = isc->regmap; 138962306a36Sopenharmony_ci struct isc_ctrls *ctrls = &isc->ctrls; 139062306a36Sopenharmony_ci u32 hist_id = ctrls->hist_id; 139162306a36Sopenharmony_ci u32 baysel; 139262306a36Sopenharmony_ci unsigned long flags; 139362306a36Sopenharmony_ci u32 min, max; 139462306a36Sopenharmony_ci int ret; 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci if (ctrls->hist_stat != HIST_ENABLED) 139762306a36Sopenharmony_ci return; 139862306a36Sopenharmony_ci 139962306a36Sopenharmony_ci isc_hist_count(isc, &min, &max); 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci dev_dbg(isc->dev, 140262306a36Sopenharmony_ci "isc wb mode %d: hist min %u , max %u\n", hist_id, min, max); 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci ctrls->hist_minmax[hist_id][HIST_MIN_INDEX] = min; 140562306a36Sopenharmony_ci ctrls->hist_minmax[hist_id][HIST_MAX_INDEX] = max; 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci if (hist_id != ISC_HIS_CFG_MODE_B) { 140862306a36Sopenharmony_ci hist_id++; 140962306a36Sopenharmony_ci } else { 141062306a36Sopenharmony_ci isc_wb_update(ctrls); 141162306a36Sopenharmony_ci hist_id = ISC_HIS_CFG_MODE_GR; 141262306a36Sopenharmony_ci } 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci ctrls->hist_id = hist_id; 141562306a36Sopenharmony_ci baysel = isc->config.sd_format->cfa_baycfg << ISC_HIS_CFG_BAYSEL_SHIFT; 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(isc->dev); 141862306a36Sopenharmony_ci if (ret < 0) 141962306a36Sopenharmony_ci return; 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci /* 142262306a36Sopenharmony_ci * only update if we have all the required histograms and controls 142362306a36Sopenharmony_ci * if awb has been disabled, we need to reset registers as well. 142462306a36Sopenharmony_ci */ 142562306a36Sopenharmony_ci if (hist_id == ISC_HIS_CFG_MODE_GR || ctrls->awb == ISC_WB_NONE) { 142662306a36Sopenharmony_ci /* 142762306a36Sopenharmony_ci * It may happen that DMA Done IRQ will trigger while we are 142862306a36Sopenharmony_ci * updating white balance registers here. 142962306a36Sopenharmony_ci * In that case, only parts of the controls have been updated. 143062306a36Sopenharmony_ci * We can avoid that by locking the section. 143162306a36Sopenharmony_ci */ 143262306a36Sopenharmony_ci spin_lock_irqsave(&isc->awb_lock, flags); 143362306a36Sopenharmony_ci isc_update_awb_ctrls(isc); 143462306a36Sopenharmony_ci spin_unlock_irqrestore(&isc->awb_lock, flags); 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci /* 143762306a36Sopenharmony_ci * if we are doing just the one time white balance adjustment, 143862306a36Sopenharmony_ci * we are basically done. 143962306a36Sopenharmony_ci */ 144062306a36Sopenharmony_ci if (ctrls->awb == ISC_WB_ONETIME) { 144162306a36Sopenharmony_ci dev_info(isc->dev, 144262306a36Sopenharmony_ci "Completed one time white-balance adjustment.\n"); 144362306a36Sopenharmony_ci /* update the v4l2 controls values */ 144462306a36Sopenharmony_ci isc_update_v4l2_ctrls(isc); 144562306a36Sopenharmony_ci ctrls->awb = ISC_WB_NONE; 144662306a36Sopenharmony_ci } 144762306a36Sopenharmony_ci } 144862306a36Sopenharmony_ci regmap_write(regmap, ISC_HIS_CFG + isc->offsets.his, 144962306a36Sopenharmony_ci hist_id | baysel | ISC_HIS_CFG_RAR); 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci /* 145262306a36Sopenharmony_ci * We have to make sure the streaming has not stopped meanwhile. 145362306a36Sopenharmony_ci * ISC requires a frame to clock the internal profile update. 145462306a36Sopenharmony_ci * To avoid issues, lock the sequence with a mutex 145562306a36Sopenharmony_ci */ 145662306a36Sopenharmony_ci mutex_lock(&isc->awb_mutex); 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci /* streaming is not active anymore */ 145962306a36Sopenharmony_ci if (isc->stop) { 146062306a36Sopenharmony_ci mutex_unlock(&isc->awb_mutex); 146162306a36Sopenharmony_ci return; 146262306a36Sopenharmony_ci } 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci isc_update_profile(isc); 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci mutex_unlock(&isc->awb_mutex); 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci /* if awb has been disabled, we don't need to start another histogram */ 146962306a36Sopenharmony_ci if (ctrls->awb) 147062306a36Sopenharmony_ci regmap_write(regmap, ISC_CTRLEN, ISC_CTRL_HISREQ); 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci pm_runtime_put_sync(isc->dev); 147362306a36Sopenharmony_ci} 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_cistatic int isc_s_ctrl(struct v4l2_ctrl *ctrl) 147662306a36Sopenharmony_ci{ 147762306a36Sopenharmony_ci struct isc_device *isc = container_of(ctrl->handler, 147862306a36Sopenharmony_ci struct isc_device, ctrls.handler); 147962306a36Sopenharmony_ci struct isc_ctrls *ctrls = &isc->ctrls; 148062306a36Sopenharmony_ci 148162306a36Sopenharmony_ci if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) 148262306a36Sopenharmony_ci return 0; 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci switch (ctrl->id) { 148562306a36Sopenharmony_ci case V4L2_CID_BRIGHTNESS: 148662306a36Sopenharmony_ci ctrls->brightness = ctrl->val & ISC_CBC_BRIGHT_MASK; 148762306a36Sopenharmony_ci break; 148862306a36Sopenharmony_ci case V4L2_CID_CONTRAST: 148962306a36Sopenharmony_ci ctrls->contrast = ctrl->val & ISC_CBC_CONTRAST_MASK; 149062306a36Sopenharmony_ci break; 149162306a36Sopenharmony_ci case V4L2_CID_GAMMA: 149262306a36Sopenharmony_ci ctrls->gamma_index = ctrl->val; 149362306a36Sopenharmony_ci break; 149462306a36Sopenharmony_ci default: 149562306a36Sopenharmony_ci return -EINVAL; 149662306a36Sopenharmony_ci } 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci return 0; 149962306a36Sopenharmony_ci} 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops isc_ctrl_ops = { 150262306a36Sopenharmony_ci .s_ctrl = isc_s_ctrl, 150362306a36Sopenharmony_ci}; 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_cistatic int isc_s_awb_ctrl(struct v4l2_ctrl *ctrl) 150662306a36Sopenharmony_ci{ 150762306a36Sopenharmony_ci struct isc_device *isc = container_of(ctrl->handler, 150862306a36Sopenharmony_ci struct isc_device, ctrls.handler); 150962306a36Sopenharmony_ci struct isc_ctrls *ctrls = &isc->ctrls; 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) 151262306a36Sopenharmony_ci return 0; 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci switch (ctrl->id) { 151562306a36Sopenharmony_ci case V4L2_CID_AUTO_WHITE_BALANCE: 151662306a36Sopenharmony_ci if (ctrl->val == 1) 151762306a36Sopenharmony_ci ctrls->awb = ISC_WB_AUTO; 151862306a36Sopenharmony_ci else 151962306a36Sopenharmony_ci ctrls->awb = ISC_WB_NONE; 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci /* configure the controls with new values from v4l2 */ 152262306a36Sopenharmony_ci if (ctrl->cluster[ISC_CTRL_R_GAIN]->is_new) 152362306a36Sopenharmony_ci ctrls->gain[ISC_HIS_CFG_MODE_R] = isc->r_gain_ctrl->val; 152462306a36Sopenharmony_ci if (ctrl->cluster[ISC_CTRL_B_GAIN]->is_new) 152562306a36Sopenharmony_ci ctrls->gain[ISC_HIS_CFG_MODE_B] = isc->b_gain_ctrl->val; 152662306a36Sopenharmony_ci if (ctrl->cluster[ISC_CTRL_GR_GAIN]->is_new) 152762306a36Sopenharmony_ci ctrls->gain[ISC_HIS_CFG_MODE_GR] = isc->gr_gain_ctrl->val; 152862306a36Sopenharmony_ci if (ctrl->cluster[ISC_CTRL_GB_GAIN]->is_new) 152962306a36Sopenharmony_ci ctrls->gain[ISC_HIS_CFG_MODE_GB] = isc->gb_gain_ctrl->val; 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci if (ctrl->cluster[ISC_CTRL_R_OFF]->is_new) 153262306a36Sopenharmony_ci ctrls->offset[ISC_HIS_CFG_MODE_R] = isc->r_off_ctrl->val; 153362306a36Sopenharmony_ci if (ctrl->cluster[ISC_CTRL_B_OFF]->is_new) 153462306a36Sopenharmony_ci ctrls->offset[ISC_HIS_CFG_MODE_B] = isc->b_off_ctrl->val; 153562306a36Sopenharmony_ci if (ctrl->cluster[ISC_CTRL_GR_OFF]->is_new) 153662306a36Sopenharmony_ci ctrls->offset[ISC_HIS_CFG_MODE_GR] = isc->gr_off_ctrl->val; 153762306a36Sopenharmony_ci if (ctrl->cluster[ISC_CTRL_GB_OFF]->is_new) 153862306a36Sopenharmony_ci ctrls->offset[ISC_HIS_CFG_MODE_GB] = isc->gb_off_ctrl->val; 153962306a36Sopenharmony_ci 154062306a36Sopenharmony_ci isc_update_awb_ctrls(isc); 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci mutex_lock(&isc->awb_mutex); 154362306a36Sopenharmony_ci if (vb2_is_streaming(&isc->vb2_vidq)) { 154462306a36Sopenharmony_ci /* 154562306a36Sopenharmony_ci * If we are streaming, we can update profile to 154662306a36Sopenharmony_ci * have the new settings in place. 154762306a36Sopenharmony_ci */ 154862306a36Sopenharmony_ci isc_update_profile(isc); 154962306a36Sopenharmony_ci } else { 155062306a36Sopenharmony_ci /* 155162306a36Sopenharmony_ci * The auto cluster will activate automatically this 155262306a36Sopenharmony_ci * control. This has to be deactivated when not 155362306a36Sopenharmony_ci * streaming. 155462306a36Sopenharmony_ci */ 155562306a36Sopenharmony_ci v4l2_ctrl_activate(isc->do_wb_ctrl, false); 155662306a36Sopenharmony_ci } 155762306a36Sopenharmony_ci mutex_unlock(&isc->awb_mutex); 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci /* if we have autowhitebalance on, start histogram procedure */ 156062306a36Sopenharmony_ci if (ctrls->awb == ISC_WB_AUTO && 156162306a36Sopenharmony_ci vb2_is_streaming(&isc->vb2_vidq) && 156262306a36Sopenharmony_ci ISC_IS_FORMAT_RAW(isc->config.sd_format->mbus_code)) 156362306a36Sopenharmony_ci isc_set_histogram(isc, true); 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci /* 156662306a36Sopenharmony_ci * for one time whitebalance adjustment, check the button, 156762306a36Sopenharmony_ci * if it's pressed, perform the one time operation. 156862306a36Sopenharmony_ci */ 156962306a36Sopenharmony_ci if (ctrls->awb == ISC_WB_NONE && 157062306a36Sopenharmony_ci ctrl->cluster[ISC_CTRL_DO_WB]->is_new && 157162306a36Sopenharmony_ci !(ctrl->cluster[ISC_CTRL_DO_WB]->flags & 157262306a36Sopenharmony_ci V4L2_CTRL_FLAG_INACTIVE)) { 157362306a36Sopenharmony_ci ctrls->awb = ISC_WB_ONETIME; 157462306a36Sopenharmony_ci isc_set_histogram(isc, true); 157562306a36Sopenharmony_ci dev_dbg(isc->dev, "One time white-balance started.\n"); 157662306a36Sopenharmony_ci } 157762306a36Sopenharmony_ci return 0; 157862306a36Sopenharmony_ci } 157962306a36Sopenharmony_ci return 0; 158062306a36Sopenharmony_ci} 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_cistatic int isc_g_volatile_awb_ctrl(struct v4l2_ctrl *ctrl) 158362306a36Sopenharmony_ci{ 158462306a36Sopenharmony_ci struct isc_device *isc = container_of(ctrl->handler, 158562306a36Sopenharmony_ci struct isc_device, ctrls.handler); 158662306a36Sopenharmony_ci struct isc_ctrls *ctrls = &isc->ctrls; 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci switch (ctrl->id) { 158962306a36Sopenharmony_ci /* being a cluster, this id will be called for every control */ 159062306a36Sopenharmony_ci case V4L2_CID_AUTO_WHITE_BALANCE: 159162306a36Sopenharmony_ci ctrl->cluster[ISC_CTRL_R_GAIN]->val = 159262306a36Sopenharmony_ci ctrls->gain[ISC_HIS_CFG_MODE_R]; 159362306a36Sopenharmony_ci ctrl->cluster[ISC_CTRL_B_GAIN]->val = 159462306a36Sopenharmony_ci ctrls->gain[ISC_HIS_CFG_MODE_B]; 159562306a36Sopenharmony_ci ctrl->cluster[ISC_CTRL_GR_GAIN]->val = 159662306a36Sopenharmony_ci ctrls->gain[ISC_HIS_CFG_MODE_GR]; 159762306a36Sopenharmony_ci ctrl->cluster[ISC_CTRL_GB_GAIN]->val = 159862306a36Sopenharmony_ci ctrls->gain[ISC_HIS_CFG_MODE_GB]; 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci ctrl->cluster[ISC_CTRL_R_OFF]->val = 160162306a36Sopenharmony_ci ctrls->offset[ISC_HIS_CFG_MODE_R]; 160262306a36Sopenharmony_ci ctrl->cluster[ISC_CTRL_B_OFF]->val = 160362306a36Sopenharmony_ci ctrls->offset[ISC_HIS_CFG_MODE_B]; 160462306a36Sopenharmony_ci ctrl->cluster[ISC_CTRL_GR_OFF]->val = 160562306a36Sopenharmony_ci ctrls->offset[ISC_HIS_CFG_MODE_GR]; 160662306a36Sopenharmony_ci ctrl->cluster[ISC_CTRL_GB_OFF]->val = 160762306a36Sopenharmony_ci ctrls->offset[ISC_HIS_CFG_MODE_GB]; 160862306a36Sopenharmony_ci break; 160962306a36Sopenharmony_ci } 161062306a36Sopenharmony_ci return 0; 161162306a36Sopenharmony_ci} 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops isc_awb_ops = { 161462306a36Sopenharmony_ci .s_ctrl = isc_s_awb_ctrl, 161562306a36Sopenharmony_ci .g_volatile_ctrl = isc_g_volatile_awb_ctrl, 161662306a36Sopenharmony_ci}; 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci#define ISC_CTRL_OFF(_name, _id, _name_str) \ 161962306a36Sopenharmony_ci static const struct v4l2_ctrl_config _name = { \ 162062306a36Sopenharmony_ci .ops = &isc_awb_ops, \ 162162306a36Sopenharmony_ci .id = _id, \ 162262306a36Sopenharmony_ci .name = _name_str, \ 162362306a36Sopenharmony_ci .type = V4L2_CTRL_TYPE_INTEGER, \ 162462306a36Sopenharmony_ci .flags = V4L2_CTRL_FLAG_SLIDER, \ 162562306a36Sopenharmony_ci .min = -4095, \ 162662306a36Sopenharmony_ci .max = 4095, \ 162762306a36Sopenharmony_ci .step = 1, \ 162862306a36Sopenharmony_ci .def = 0, \ 162962306a36Sopenharmony_ci } 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ciISC_CTRL_OFF(isc_r_off_ctrl, ISC_CID_R_OFFSET, "Red Component Offset"); 163262306a36Sopenharmony_ciISC_CTRL_OFF(isc_b_off_ctrl, ISC_CID_B_OFFSET, "Blue Component Offset"); 163362306a36Sopenharmony_ciISC_CTRL_OFF(isc_gr_off_ctrl, ISC_CID_GR_OFFSET, "Green Red Component Offset"); 163462306a36Sopenharmony_ciISC_CTRL_OFF(isc_gb_off_ctrl, ISC_CID_GB_OFFSET, "Green Blue Component Offset"); 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_ci#define ISC_CTRL_GAIN(_name, _id, _name_str) \ 163762306a36Sopenharmony_ci static const struct v4l2_ctrl_config _name = { \ 163862306a36Sopenharmony_ci .ops = &isc_awb_ops, \ 163962306a36Sopenharmony_ci .id = _id, \ 164062306a36Sopenharmony_ci .name = _name_str, \ 164162306a36Sopenharmony_ci .type = V4L2_CTRL_TYPE_INTEGER, \ 164262306a36Sopenharmony_ci .flags = V4L2_CTRL_FLAG_SLIDER, \ 164362306a36Sopenharmony_ci .min = 0, \ 164462306a36Sopenharmony_ci .max = 8191, \ 164562306a36Sopenharmony_ci .step = 1, \ 164662306a36Sopenharmony_ci .def = 512, \ 164762306a36Sopenharmony_ci } 164862306a36Sopenharmony_ci 164962306a36Sopenharmony_ciISC_CTRL_GAIN(isc_r_gain_ctrl, ISC_CID_R_GAIN, "Red Component Gain"); 165062306a36Sopenharmony_ciISC_CTRL_GAIN(isc_b_gain_ctrl, ISC_CID_B_GAIN, "Blue Component Gain"); 165162306a36Sopenharmony_ciISC_CTRL_GAIN(isc_gr_gain_ctrl, ISC_CID_GR_GAIN, "Green Red Component Gain"); 165262306a36Sopenharmony_ciISC_CTRL_GAIN(isc_gb_gain_ctrl, ISC_CID_GB_GAIN, "Green Blue Component Gain"); 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_cistatic int isc_ctrl_init(struct isc_device *isc) 165562306a36Sopenharmony_ci{ 165662306a36Sopenharmony_ci const struct v4l2_ctrl_ops *ops = &isc_ctrl_ops; 165762306a36Sopenharmony_ci struct isc_ctrls *ctrls = &isc->ctrls; 165862306a36Sopenharmony_ci struct v4l2_ctrl_handler *hdl = &ctrls->handler; 165962306a36Sopenharmony_ci int ret; 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci ctrls->hist_stat = HIST_INIT; 166262306a36Sopenharmony_ci isc_reset_awb_ctrls(isc); 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci ret = v4l2_ctrl_handler_init(hdl, 13); 166562306a36Sopenharmony_ci if (ret < 0) 166662306a36Sopenharmony_ci return ret; 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ci /* Initialize product specific controls. For example, contrast */ 166962306a36Sopenharmony_ci isc->config_ctrls(isc, ops); 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci ctrls->brightness = 0; 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, ops, V4L2_CID_BRIGHTNESS, -1024, 1023, 1, 0); 167462306a36Sopenharmony_ci v4l2_ctrl_new_std(hdl, ops, V4L2_CID_GAMMA, 0, isc->gamma_max, 1, 167562306a36Sopenharmony_ci isc->gamma_max); 167662306a36Sopenharmony_ci isc->awb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops, 167762306a36Sopenharmony_ci V4L2_CID_AUTO_WHITE_BALANCE, 167862306a36Sopenharmony_ci 0, 1, 1, 1); 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci /* do_white_balance is a button, so min,max,step,default are ignored */ 168162306a36Sopenharmony_ci isc->do_wb_ctrl = v4l2_ctrl_new_std(hdl, &isc_awb_ops, 168262306a36Sopenharmony_ci V4L2_CID_DO_WHITE_BALANCE, 168362306a36Sopenharmony_ci 0, 0, 0, 0); 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ci if (!isc->do_wb_ctrl) { 168662306a36Sopenharmony_ci ret = hdl->error; 168762306a36Sopenharmony_ci v4l2_ctrl_handler_free(hdl); 168862306a36Sopenharmony_ci return ret; 168962306a36Sopenharmony_ci } 169062306a36Sopenharmony_ci 169162306a36Sopenharmony_ci v4l2_ctrl_activate(isc->do_wb_ctrl, false); 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci isc->r_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_r_gain_ctrl, NULL); 169462306a36Sopenharmony_ci isc->b_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_b_gain_ctrl, NULL); 169562306a36Sopenharmony_ci isc->gr_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gr_gain_ctrl, NULL); 169662306a36Sopenharmony_ci isc->gb_gain_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gb_gain_ctrl, NULL); 169762306a36Sopenharmony_ci isc->r_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_r_off_ctrl, NULL); 169862306a36Sopenharmony_ci isc->b_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_b_off_ctrl, NULL); 169962306a36Sopenharmony_ci isc->gr_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gr_off_ctrl, NULL); 170062306a36Sopenharmony_ci isc->gb_off_ctrl = v4l2_ctrl_new_custom(hdl, &isc_gb_off_ctrl, NULL); 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci /* 170362306a36Sopenharmony_ci * The cluster is in auto mode with autowhitebalance enabled 170462306a36Sopenharmony_ci * and manual mode otherwise. 170562306a36Sopenharmony_ci */ 170662306a36Sopenharmony_ci v4l2_ctrl_auto_cluster(10, &isc->awb_ctrl, 0, true); 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci v4l2_ctrl_handler_setup(hdl); 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci return 0; 171162306a36Sopenharmony_ci} 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_cistatic int isc_async_bound(struct v4l2_async_notifier *notifier, 171462306a36Sopenharmony_ci struct v4l2_subdev *subdev, 171562306a36Sopenharmony_ci struct v4l2_async_connection *asd) 171662306a36Sopenharmony_ci{ 171762306a36Sopenharmony_ci struct isc_device *isc = container_of(notifier->v4l2_dev, 171862306a36Sopenharmony_ci struct isc_device, v4l2_dev); 171962306a36Sopenharmony_ci struct isc_subdev_entity *subdev_entity = 172062306a36Sopenharmony_ci container_of(notifier, struct isc_subdev_entity, notifier); 172162306a36Sopenharmony_ci int pad; 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci if (video_is_registered(&isc->video_dev)) { 172462306a36Sopenharmony_ci dev_err(isc->dev, "only supports one sub-device.\n"); 172562306a36Sopenharmony_ci return -EBUSY; 172662306a36Sopenharmony_ci } 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ci subdev_entity->sd = subdev; 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci pad = media_entity_get_fwnode_pad(&subdev->entity, asd->match.fwnode, 173162306a36Sopenharmony_ci MEDIA_PAD_FL_SOURCE); 173262306a36Sopenharmony_ci if (pad < 0) { 173362306a36Sopenharmony_ci dev_err(isc->dev, "failed to find pad for %s\n", subdev->name); 173462306a36Sopenharmony_ci return pad; 173562306a36Sopenharmony_ci } 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_ci isc->remote_pad = pad; 173862306a36Sopenharmony_ci 173962306a36Sopenharmony_ci return 0; 174062306a36Sopenharmony_ci} 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_cistatic void isc_async_unbind(struct v4l2_async_notifier *notifier, 174362306a36Sopenharmony_ci struct v4l2_subdev *subdev, 174462306a36Sopenharmony_ci struct v4l2_async_connection *asd) 174562306a36Sopenharmony_ci{ 174662306a36Sopenharmony_ci struct isc_device *isc = container_of(notifier->v4l2_dev, 174762306a36Sopenharmony_ci struct isc_device, v4l2_dev); 174862306a36Sopenharmony_ci mutex_destroy(&isc->awb_mutex); 174962306a36Sopenharmony_ci cancel_work_sync(&isc->awb_work); 175062306a36Sopenharmony_ci video_unregister_device(&isc->video_dev); 175162306a36Sopenharmony_ci v4l2_ctrl_handler_free(&isc->ctrls.handler); 175262306a36Sopenharmony_ci} 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_cistruct isc_format *isc_find_format_by_code(struct isc_device *isc, 175562306a36Sopenharmony_ci unsigned int code, int *index) 175662306a36Sopenharmony_ci{ 175762306a36Sopenharmony_ci struct isc_format *fmt = &isc->formats_list[0]; 175862306a36Sopenharmony_ci unsigned int i; 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci for (i = 0; i < isc->formats_list_size; i++) { 176162306a36Sopenharmony_ci if (fmt->mbus_code == code) { 176262306a36Sopenharmony_ci *index = i; 176362306a36Sopenharmony_ci return fmt; 176462306a36Sopenharmony_ci } 176562306a36Sopenharmony_ci 176662306a36Sopenharmony_ci fmt++; 176762306a36Sopenharmony_ci } 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci return NULL; 177062306a36Sopenharmony_ci} 177162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(isc_find_format_by_code); 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_cistatic int isc_set_default_fmt(struct isc_device *isc) 177462306a36Sopenharmony_ci{ 177562306a36Sopenharmony_ci struct v4l2_format f = { 177662306a36Sopenharmony_ci .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, 177762306a36Sopenharmony_ci .fmt.pix = { 177862306a36Sopenharmony_ci .width = VGA_WIDTH, 177962306a36Sopenharmony_ci .height = VGA_HEIGHT, 178062306a36Sopenharmony_ci .field = V4L2_FIELD_NONE, 178162306a36Sopenharmony_ci .pixelformat = isc->controller_formats[0].fourcc, 178262306a36Sopenharmony_ci }, 178362306a36Sopenharmony_ci }; 178462306a36Sopenharmony_ci int ret; 178562306a36Sopenharmony_ci 178662306a36Sopenharmony_ci ret = isc_try_fmt(isc, &f); 178762306a36Sopenharmony_ci if (ret) 178862306a36Sopenharmony_ci return ret; 178962306a36Sopenharmony_ci 179062306a36Sopenharmony_ci isc->fmt = f; 179162306a36Sopenharmony_ci return 0; 179262306a36Sopenharmony_ci} 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_cistatic int isc_async_complete(struct v4l2_async_notifier *notifier) 179562306a36Sopenharmony_ci{ 179662306a36Sopenharmony_ci struct isc_device *isc = container_of(notifier->v4l2_dev, 179762306a36Sopenharmony_ci struct isc_device, v4l2_dev); 179862306a36Sopenharmony_ci struct video_device *vdev = &isc->video_dev; 179962306a36Sopenharmony_ci struct vb2_queue *q = &isc->vb2_vidq; 180062306a36Sopenharmony_ci int ret = 0; 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci INIT_WORK(&isc->awb_work, isc_awb_work); 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci ret = v4l2_device_register_subdev_nodes(&isc->v4l2_dev); 180562306a36Sopenharmony_ci if (ret < 0) { 180662306a36Sopenharmony_ci dev_err(isc->dev, "Failed to register subdev nodes\n"); 180762306a36Sopenharmony_ci return ret; 180862306a36Sopenharmony_ci } 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci isc->current_subdev = container_of(notifier, 181162306a36Sopenharmony_ci struct isc_subdev_entity, notifier); 181262306a36Sopenharmony_ci mutex_init(&isc->lock); 181362306a36Sopenharmony_ci mutex_init(&isc->awb_mutex); 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_ci init_completion(&isc->comp); 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_ci /* Initialize videobuf2 queue */ 181862306a36Sopenharmony_ci q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 181962306a36Sopenharmony_ci q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; 182062306a36Sopenharmony_ci q->drv_priv = isc; 182162306a36Sopenharmony_ci q->buf_struct_size = sizeof(struct isc_buffer); 182262306a36Sopenharmony_ci q->ops = &isc_vb2_ops; 182362306a36Sopenharmony_ci q->mem_ops = &vb2_dma_contig_memops; 182462306a36Sopenharmony_ci q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 182562306a36Sopenharmony_ci q->lock = &isc->lock; 182662306a36Sopenharmony_ci q->min_buffers_needed = 1; 182762306a36Sopenharmony_ci q->dev = isc->dev; 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci ret = vb2_queue_init(q); 183062306a36Sopenharmony_ci if (ret < 0) { 183162306a36Sopenharmony_ci dev_err(isc->dev, "vb2_queue_init() failed: %d\n", ret); 183262306a36Sopenharmony_ci goto isc_async_complete_err; 183362306a36Sopenharmony_ci } 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci /* Init video dma queues */ 183662306a36Sopenharmony_ci INIT_LIST_HEAD(&isc->dma_queue); 183762306a36Sopenharmony_ci spin_lock_init(&isc->dma_queue_lock); 183862306a36Sopenharmony_ci spin_lock_init(&isc->awb_lock); 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_ci ret = isc_set_default_fmt(isc); 184162306a36Sopenharmony_ci if (ret) { 184262306a36Sopenharmony_ci dev_err(isc->dev, "Could not set default format\n"); 184362306a36Sopenharmony_ci goto isc_async_complete_err; 184462306a36Sopenharmony_ci } 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_ci ret = isc_ctrl_init(isc); 184762306a36Sopenharmony_ci if (ret) { 184862306a36Sopenharmony_ci dev_err(isc->dev, "Init isc ctrols failed: %d\n", ret); 184962306a36Sopenharmony_ci goto isc_async_complete_err; 185062306a36Sopenharmony_ci } 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ci /* Register video device */ 185362306a36Sopenharmony_ci strscpy(vdev->name, KBUILD_MODNAME, sizeof(vdev->name)); 185462306a36Sopenharmony_ci vdev->release = video_device_release_empty; 185562306a36Sopenharmony_ci vdev->fops = &isc_fops; 185662306a36Sopenharmony_ci vdev->ioctl_ops = &isc_ioctl_ops; 185762306a36Sopenharmony_ci vdev->v4l2_dev = &isc->v4l2_dev; 185862306a36Sopenharmony_ci vdev->vfl_dir = VFL_DIR_RX; 185962306a36Sopenharmony_ci vdev->queue = q; 186062306a36Sopenharmony_ci vdev->lock = &isc->lock; 186162306a36Sopenharmony_ci vdev->ctrl_handler = &isc->ctrls.handler; 186262306a36Sopenharmony_ci vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE | 186362306a36Sopenharmony_ci V4L2_CAP_IO_MC; 186462306a36Sopenharmony_ci video_set_drvdata(vdev, isc); 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci ret = video_register_device(vdev, VFL_TYPE_VIDEO, -1); 186762306a36Sopenharmony_ci if (ret < 0) { 186862306a36Sopenharmony_ci dev_err(isc->dev, "video_register_device failed: %d\n", ret); 186962306a36Sopenharmony_ci goto isc_async_complete_err; 187062306a36Sopenharmony_ci } 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_ci ret = isc_scaler_link(isc); 187362306a36Sopenharmony_ci if (ret < 0) 187462306a36Sopenharmony_ci goto isc_async_complete_unregister_device; 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci ret = media_device_register(&isc->mdev); 187762306a36Sopenharmony_ci if (ret < 0) 187862306a36Sopenharmony_ci goto isc_async_complete_unregister_device; 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ci return 0; 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_ciisc_async_complete_unregister_device: 188362306a36Sopenharmony_ci video_unregister_device(vdev); 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ciisc_async_complete_err: 188662306a36Sopenharmony_ci mutex_destroy(&isc->awb_mutex); 188762306a36Sopenharmony_ci mutex_destroy(&isc->lock); 188862306a36Sopenharmony_ci return ret; 188962306a36Sopenharmony_ci} 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_ciconst struct v4l2_async_notifier_operations microchip_isc_async_ops = { 189262306a36Sopenharmony_ci .bound = isc_async_bound, 189362306a36Sopenharmony_ci .unbind = isc_async_unbind, 189462306a36Sopenharmony_ci .complete = isc_async_complete, 189562306a36Sopenharmony_ci}; 189662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(microchip_isc_async_ops); 189762306a36Sopenharmony_ci 189862306a36Sopenharmony_civoid microchip_isc_subdev_cleanup(struct isc_device *isc) 189962306a36Sopenharmony_ci{ 190062306a36Sopenharmony_ci struct isc_subdev_entity *subdev_entity; 190162306a36Sopenharmony_ci 190262306a36Sopenharmony_ci list_for_each_entry(subdev_entity, &isc->subdev_entities, list) { 190362306a36Sopenharmony_ci v4l2_async_nf_unregister(&subdev_entity->notifier); 190462306a36Sopenharmony_ci v4l2_async_nf_cleanup(&subdev_entity->notifier); 190562306a36Sopenharmony_ci } 190662306a36Sopenharmony_ci 190762306a36Sopenharmony_ci INIT_LIST_HEAD(&isc->subdev_entities); 190862306a36Sopenharmony_ci} 190962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(microchip_isc_subdev_cleanup); 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ciint microchip_isc_pipeline_init(struct isc_device *isc) 191262306a36Sopenharmony_ci{ 191362306a36Sopenharmony_ci struct device *dev = isc->dev; 191462306a36Sopenharmony_ci struct regmap *regmap = isc->regmap; 191562306a36Sopenharmony_ci struct regmap_field *regs; 191662306a36Sopenharmony_ci unsigned int i; 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci /* 191962306a36Sopenharmony_ci * DPCEN-->GDCEN-->BLCEN-->WB-->CFA-->CC--> 192062306a36Sopenharmony_ci * GAM-->VHXS-->CSC-->CBC-->SUB422-->SUB420 192162306a36Sopenharmony_ci */ 192262306a36Sopenharmony_ci const struct reg_field regfields[ISC_PIPE_LINE_NODE_NUM] = { 192362306a36Sopenharmony_ci REG_FIELD(ISC_DPC_CTRL, 0, 0), 192462306a36Sopenharmony_ci REG_FIELD(ISC_DPC_CTRL, 1, 1), 192562306a36Sopenharmony_ci REG_FIELD(ISC_DPC_CTRL, 2, 2), 192662306a36Sopenharmony_ci REG_FIELD(ISC_WB_CTRL, 0, 0), 192762306a36Sopenharmony_ci REG_FIELD(ISC_CFA_CTRL, 0, 0), 192862306a36Sopenharmony_ci REG_FIELD(ISC_CC_CTRL, 0, 0), 192962306a36Sopenharmony_ci REG_FIELD(ISC_GAM_CTRL, 0, 0), 193062306a36Sopenharmony_ci REG_FIELD(ISC_GAM_CTRL, 1, 1), 193162306a36Sopenharmony_ci REG_FIELD(ISC_GAM_CTRL, 2, 2), 193262306a36Sopenharmony_ci REG_FIELD(ISC_GAM_CTRL, 3, 3), 193362306a36Sopenharmony_ci REG_FIELD(ISC_VHXS_CTRL, 0, 0), 193462306a36Sopenharmony_ci REG_FIELD(ISC_CSC_CTRL + isc->offsets.csc, 0, 0), 193562306a36Sopenharmony_ci REG_FIELD(ISC_CBC_CTRL + isc->offsets.cbc, 0, 0), 193662306a36Sopenharmony_ci REG_FIELD(ISC_SUB422_CTRL + isc->offsets.sub422, 0, 0), 193762306a36Sopenharmony_ci REG_FIELD(ISC_SUB420_CTRL + isc->offsets.sub420, 0, 0), 193862306a36Sopenharmony_ci }; 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci for (i = 0; i < ISC_PIPE_LINE_NODE_NUM; i++) { 194162306a36Sopenharmony_ci regs = devm_regmap_field_alloc(dev, regmap, regfields[i]); 194262306a36Sopenharmony_ci if (IS_ERR(regs)) 194362306a36Sopenharmony_ci return PTR_ERR(regs); 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_ci isc->pipeline[i] = regs; 194662306a36Sopenharmony_ci } 194762306a36Sopenharmony_ci 194862306a36Sopenharmony_ci return 0; 194962306a36Sopenharmony_ci} 195062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(microchip_isc_pipeline_init); 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_cistatic int isc_link_validate(struct media_link *link) 195362306a36Sopenharmony_ci{ 195462306a36Sopenharmony_ci struct video_device *vdev = 195562306a36Sopenharmony_ci media_entity_to_video_device(link->sink->entity); 195662306a36Sopenharmony_ci struct isc_device *isc = video_get_drvdata(vdev); 195762306a36Sopenharmony_ci int ret; 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci ret = v4l2_subdev_link_validate(link); 196062306a36Sopenharmony_ci if (ret) 196162306a36Sopenharmony_ci return ret; 196262306a36Sopenharmony_ci 196362306a36Sopenharmony_ci return isc_validate(isc); 196462306a36Sopenharmony_ci} 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_cistatic const struct media_entity_operations isc_entity_operations = { 196762306a36Sopenharmony_ci .link_validate = isc_link_validate, 196862306a36Sopenharmony_ci}; 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_ciint isc_mc_init(struct isc_device *isc, u32 ver) 197162306a36Sopenharmony_ci{ 197262306a36Sopenharmony_ci const struct of_device_id *match; 197362306a36Sopenharmony_ci int ret; 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_ci isc->video_dev.entity.function = MEDIA_ENT_F_IO_V4L; 197662306a36Sopenharmony_ci isc->video_dev.entity.flags = MEDIA_ENT_FL_DEFAULT; 197762306a36Sopenharmony_ci isc->video_dev.entity.ops = &isc_entity_operations; 197862306a36Sopenharmony_ci 197962306a36Sopenharmony_ci isc->pads[ISC_PAD_SINK].flags = MEDIA_PAD_FL_SINK; 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_ci ret = media_entity_pads_init(&isc->video_dev.entity, ISC_PADS_NUM, 198262306a36Sopenharmony_ci isc->pads); 198362306a36Sopenharmony_ci if (ret < 0) { 198462306a36Sopenharmony_ci dev_err(isc->dev, "media entity init failed\n"); 198562306a36Sopenharmony_ci return ret; 198662306a36Sopenharmony_ci } 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_ci isc->mdev.dev = isc->dev; 198962306a36Sopenharmony_ci 199062306a36Sopenharmony_ci match = of_match_node(isc->dev->driver->of_match_table, 199162306a36Sopenharmony_ci isc->dev->of_node); 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_ci strscpy(isc->mdev.driver_name, KBUILD_MODNAME, 199462306a36Sopenharmony_ci sizeof(isc->mdev.driver_name)); 199562306a36Sopenharmony_ci strscpy(isc->mdev.model, match->compatible, sizeof(isc->mdev.model)); 199662306a36Sopenharmony_ci snprintf(isc->mdev.bus_info, sizeof(isc->mdev.bus_info), "platform:%s", 199762306a36Sopenharmony_ci isc->v4l2_dev.name); 199862306a36Sopenharmony_ci isc->mdev.hw_revision = ver; 199962306a36Sopenharmony_ci 200062306a36Sopenharmony_ci media_device_init(&isc->mdev); 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ci isc->v4l2_dev.mdev = &isc->mdev; 200362306a36Sopenharmony_ci 200462306a36Sopenharmony_ci return isc_scaler_init(isc); 200562306a36Sopenharmony_ci} 200662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(isc_mc_init); 200762306a36Sopenharmony_ci 200862306a36Sopenharmony_civoid isc_mc_cleanup(struct isc_device *isc) 200962306a36Sopenharmony_ci{ 201062306a36Sopenharmony_ci media_entity_cleanup(&isc->video_dev.entity); 201162306a36Sopenharmony_ci media_device_cleanup(&isc->mdev); 201262306a36Sopenharmony_ci} 201362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(isc_mc_cleanup); 201462306a36Sopenharmony_ci 201562306a36Sopenharmony_ci/* regmap configuration */ 201662306a36Sopenharmony_ci#define MICROCHIP_ISC_REG_MAX 0xd5c 201762306a36Sopenharmony_ciconst struct regmap_config microchip_isc_regmap_config = { 201862306a36Sopenharmony_ci .reg_bits = 32, 201962306a36Sopenharmony_ci .reg_stride = 4, 202062306a36Sopenharmony_ci .val_bits = 32, 202162306a36Sopenharmony_ci .max_register = MICROCHIP_ISC_REG_MAX, 202262306a36Sopenharmony_ci}; 202362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(microchip_isc_regmap_config); 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_ciMODULE_AUTHOR("Songjun Wu"); 202662306a36Sopenharmony_ciMODULE_AUTHOR("Eugen Hristev"); 202762306a36Sopenharmony_ciMODULE_DESCRIPTION("Microchip ISC common code base"); 202862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2029