162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * isphist.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * TI OMAP3 ISP - Histogram module 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2010 Nokia Corporation 862306a36Sopenharmony_ci * Copyright (C) 2009 Texas Instruments, Inc. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Contacts: David Cohen <dacohen@gmail.com> 1162306a36Sopenharmony_ci * Laurent Pinchart <laurent.pinchart@ideasonboard.com> 1262306a36Sopenharmony_ci * Sakari Ailus <sakari.ailus@iki.fi> 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/delay.h> 1662306a36Sopenharmony_ci#include <linux/device.h> 1762306a36Sopenharmony_ci#include <linux/dmaengine.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <linux/uaccess.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "isp.h" 2262306a36Sopenharmony_ci#include "ispreg.h" 2362306a36Sopenharmony_ci#include "isphist.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define HIST_CONFIG_DMA 1 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* 2862306a36Sopenharmony_ci * hist_reset_mem - clear Histogram memory before start stats engine. 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_cistatic void hist_reset_mem(struct ispstat *hist) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci struct isp_device *isp = hist->isp; 3362306a36Sopenharmony_ci struct omap3isp_hist_config *conf = hist->priv; 3462306a36Sopenharmony_ci unsigned int i; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci /* 3962306a36Sopenharmony_ci * By setting it, the histogram internal buffer is being cleared at the 4062306a36Sopenharmony_ci * same time it's being read. This bit must be cleared afterwards. 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_ci isp_reg_set(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR); 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci /* 4562306a36Sopenharmony_ci * We'll clear 4 words at each iteration for optimization. It avoids 4662306a36Sopenharmony_ci * 3/4 of the jumps. We also know HIST_MEM_SIZE is divisible by 4. 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_ci for (i = OMAP3ISP_HIST_MEM_SIZE / 4; i > 0; i--) { 4962306a36Sopenharmony_ci isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); 5062306a36Sopenharmony_ci isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); 5162306a36Sopenharmony_ci isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); 5262306a36Sopenharmony_ci isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci isp_reg_clr(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci hist->wait_acc_frames = conf->num_acc_frames; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* 6062306a36Sopenharmony_ci * hist_setup_regs - Helper function to update Histogram registers. 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_cistatic void hist_setup_regs(struct ispstat *hist, void *priv) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci struct isp_device *isp = hist->isp; 6562306a36Sopenharmony_ci struct omap3isp_hist_config *conf = priv; 6662306a36Sopenharmony_ci int c; 6762306a36Sopenharmony_ci u32 cnt; 6862306a36Sopenharmony_ci u32 wb_gain; 6962306a36Sopenharmony_ci u32 reg_hor[OMAP3ISP_HIST_MAX_REGIONS]; 7062306a36Sopenharmony_ci u32 reg_ver[OMAP3ISP_HIST_MAX_REGIONS]; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci if (!hist->update || hist->state == ISPSTAT_DISABLED || 7362306a36Sopenharmony_ci hist->state == ISPSTAT_DISABLING) 7462306a36Sopenharmony_ci return; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci cnt = conf->cfa << ISPHIST_CNT_CFA_SHIFT; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci wb_gain = conf->wg[0] << ISPHIST_WB_GAIN_WG00_SHIFT; 7962306a36Sopenharmony_ci wb_gain |= conf->wg[1] << ISPHIST_WB_GAIN_WG01_SHIFT; 8062306a36Sopenharmony_ci wb_gain |= conf->wg[2] << ISPHIST_WB_GAIN_WG02_SHIFT; 8162306a36Sopenharmony_ci if (conf->cfa == OMAP3ISP_HIST_CFA_BAYER) 8262306a36Sopenharmony_ci wb_gain |= conf->wg[3] << ISPHIST_WB_GAIN_WG03_SHIFT; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* Regions size and position */ 8562306a36Sopenharmony_ci for (c = 0; c < OMAP3ISP_HIST_MAX_REGIONS; c++) { 8662306a36Sopenharmony_ci if (c < conf->num_regions) { 8762306a36Sopenharmony_ci reg_hor[c] = (conf->region[c].h_start << 8862306a36Sopenharmony_ci ISPHIST_REG_START_SHIFT) 8962306a36Sopenharmony_ci | (conf->region[c].h_end << 9062306a36Sopenharmony_ci ISPHIST_REG_END_SHIFT); 9162306a36Sopenharmony_ci reg_ver[c] = (conf->region[c].v_start << 9262306a36Sopenharmony_ci ISPHIST_REG_START_SHIFT) 9362306a36Sopenharmony_ci | (conf->region[c].v_end << 9462306a36Sopenharmony_ci ISPHIST_REG_END_SHIFT); 9562306a36Sopenharmony_ci } else { 9662306a36Sopenharmony_ci reg_hor[c] = 0; 9762306a36Sopenharmony_ci reg_ver[c] = 0; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci } 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci cnt |= conf->hist_bins << ISPHIST_CNT_BINS_SHIFT; 10262306a36Sopenharmony_ci switch (conf->hist_bins) { 10362306a36Sopenharmony_ci case OMAP3ISP_HIST_BINS_256: 10462306a36Sopenharmony_ci cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 8) << 10562306a36Sopenharmony_ci ISPHIST_CNT_SHIFT_SHIFT; 10662306a36Sopenharmony_ci break; 10762306a36Sopenharmony_ci case OMAP3ISP_HIST_BINS_128: 10862306a36Sopenharmony_ci cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 7) << 10962306a36Sopenharmony_ci ISPHIST_CNT_SHIFT_SHIFT; 11062306a36Sopenharmony_ci break; 11162306a36Sopenharmony_ci case OMAP3ISP_HIST_BINS_64: 11262306a36Sopenharmony_ci cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 6) << 11362306a36Sopenharmony_ci ISPHIST_CNT_SHIFT_SHIFT; 11462306a36Sopenharmony_ci break; 11562306a36Sopenharmony_ci default: /* OMAP3ISP_HIST_BINS_32 */ 11662306a36Sopenharmony_ci cnt |= (ISPHIST_IN_BIT_WIDTH_CCDC - 5) << 11762306a36Sopenharmony_ci ISPHIST_CNT_SHIFT_SHIFT; 11862306a36Sopenharmony_ci break; 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci hist_reset_mem(hist); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci isp_reg_writel(isp, cnt, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT); 12462306a36Sopenharmony_ci isp_reg_writel(isp, wb_gain, OMAP3_ISP_IOMEM_HIST, ISPHIST_WB_GAIN); 12562306a36Sopenharmony_ci isp_reg_writel(isp, reg_hor[0], OMAP3_ISP_IOMEM_HIST, ISPHIST_R0_HORZ); 12662306a36Sopenharmony_ci isp_reg_writel(isp, reg_ver[0], OMAP3_ISP_IOMEM_HIST, ISPHIST_R0_VERT); 12762306a36Sopenharmony_ci isp_reg_writel(isp, reg_hor[1], OMAP3_ISP_IOMEM_HIST, ISPHIST_R1_HORZ); 12862306a36Sopenharmony_ci isp_reg_writel(isp, reg_ver[1], OMAP3_ISP_IOMEM_HIST, ISPHIST_R1_VERT); 12962306a36Sopenharmony_ci isp_reg_writel(isp, reg_hor[2], OMAP3_ISP_IOMEM_HIST, ISPHIST_R2_HORZ); 13062306a36Sopenharmony_ci isp_reg_writel(isp, reg_ver[2], OMAP3_ISP_IOMEM_HIST, ISPHIST_R2_VERT); 13162306a36Sopenharmony_ci isp_reg_writel(isp, reg_hor[3], OMAP3_ISP_IOMEM_HIST, ISPHIST_R3_HORZ); 13262306a36Sopenharmony_ci isp_reg_writel(isp, reg_ver[3], OMAP3_ISP_IOMEM_HIST, ISPHIST_R3_VERT); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci hist->update = 0; 13562306a36Sopenharmony_ci hist->config_counter += hist->inc_config; 13662306a36Sopenharmony_ci hist->inc_config = 0; 13762306a36Sopenharmony_ci hist->buf_size = conf->buf_size; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic void hist_enable(struct ispstat *hist, int enable) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci if (enable) { 14362306a36Sopenharmony_ci isp_reg_set(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR, 14462306a36Sopenharmony_ci ISPHIST_PCR_ENABLE); 14562306a36Sopenharmony_ci omap3isp_subclk_enable(hist->isp, OMAP3_ISP_SUBCLK_HIST); 14662306a36Sopenharmony_ci } else { 14762306a36Sopenharmony_ci isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR, 14862306a36Sopenharmony_ci ISPHIST_PCR_ENABLE); 14962306a36Sopenharmony_ci omap3isp_subclk_disable(hist->isp, OMAP3_ISP_SUBCLK_HIST); 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic int hist_busy(struct ispstat *hist) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci return isp_reg_readl(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_PCR) 15662306a36Sopenharmony_ci & ISPHIST_PCR_BUSY; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic void hist_dma_cb(void *data) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci struct ispstat *hist = data; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* FIXME: The DMA engine API can't report transfer errors :-/ */ 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, 16662306a36Sopenharmony_ci ISPHIST_CNT_CLEAR); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci omap3isp_stat_dma_isr(hist); 16962306a36Sopenharmony_ci if (hist->state != ISPSTAT_DISABLED) 17062306a36Sopenharmony_ci omap3isp_hist_dma_done(hist->isp); 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic int hist_buf_dma(struct ispstat *hist) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci dma_addr_t dma_addr = hist->active_buf->dma_addr; 17662306a36Sopenharmony_ci struct dma_async_tx_descriptor *tx; 17762306a36Sopenharmony_ci struct dma_slave_config cfg; 17862306a36Sopenharmony_ci dma_cookie_t cookie; 17962306a36Sopenharmony_ci int ret; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if (unlikely(!dma_addr)) { 18262306a36Sopenharmony_ci dev_dbg(hist->isp->dev, "hist: invalid DMA buffer address\n"); 18362306a36Sopenharmony_ci goto error; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci isp_reg_writel(hist->isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR); 18762306a36Sopenharmony_ci isp_reg_set(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, 18862306a36Sopenharmony_ci ISPHIST_CNT_CLEAR); 18962306a36Sopenharmony_ci omap3isp_flush(hist->isp); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci memset(&cfg, 0, sizeof(cfg)); 19262306a36Sopenharmony_ci cfg.src_addr = hist->isp->mmio_hist_base_phys + ISPHIST_DATA; 19362306a36Sopenharmony_ci cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 19462306a36Sopenharmony_ci cfg.src_maxburst = hist->buf_size / 4; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci ret = dmaengine_slave_config(hist->dma_ch, &cfg); 19762306a36Sopenharmony_ci if (ret < 0) { 19862306a36Sopenharmony_ci dev_dbg(hist->isp->dev, 19962306a36Sopenharmony_ci "hist: DMA slave configuration failed\n"); 20062306a36Sopenharmony_ci goto error; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci tx = dmaengine_prep_slave_single(hist->dma_ch, dma_addr, 20462306a36Sopenharmony_ci hist->buf_size, DMA_DEV_TO_MEM, 20562306a36Sopenharmony_ci DMA_CTRL_ACK); 20662306a36Sopenharmony_ci if (tx == NULL) { 20762306a36Sopenharmony_ci dev_dbg(hist->isp->dev, 20862306a36Sopenharmony_ci "hist: DMA slave preparation failed\n"); 20962306a36Sopenharmony_ci goto error; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci tx->callback = hist_dma_cb; 21362306a36Sopenharmony_ci tx->callback_param = hist; 21462306a36Sopenharmony_ci cookie = tx->tx_submit(tx); 21562306a36Sopenharmony_ci if (dma_submit_error(cookie)) { 21662306a36Sopenharmony_ci dev_dbg(hist->isp->dev, "hist: DMA submission failed\n"); 21762306a36Sopenharmony_ci goto error; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci dma_async_issue_pending(hist->dma_ch); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci return STAT_BUF_WAITING_DMA; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cierror: 22562306a36Sopenharmony_ci hist_reset_mem(hist); 22662306a36Sopenharmony_ci return STAT_NO_BUF; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic int hist_buf_pio(struct ispstat *hist) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct isp_device *isp = hist->isp; 23262306a36Sopenharmony_ci u32 *buf = hist->active_buf->virt_addr; 23362306a36Sopenharmony_ci unsigned int i; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (!buf) { 23662306a36Sopenharmony_ci dev_dbg(isp->dev, "hist: invalid PIO buffer address\n"); 23762306a36Sopenharmony_ci hist_reset_mem(hist); 23862306a36Sopenharmony_ci return STAT_NO_BUF; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci isp_reg_writel(isp, 0, OMAP3_ISP_IOMEM_HIST, ISPHIST_ADDR); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci /* 24462306a36Sopenharmony_ci * By setting it, the histogram internal buffer is being cleared at the 24562306a36Sopenharmony_ci * same time it's being read. This bit must be cleared just after all 24662306a36Sopenharmony_ci * data is acquired. 24762306a36Sopenharmony_ci */ 24862306a36Sopenharmony_ci isp_reg_set(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, ISPHIST_CNT_CLEAR); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* 25162306a36Sopenharmony_ci * We'll read 4 times a 4-bytes-word at each iteration for 25262306a36Sopenharmony_ci * optimization. It avoids 3/4 of the jumps. We also know buf_size is 25362306a36Sopenharmony_ci * divisible by 16. 25462306a36Sopenharmony_ci */ 25562306a36Sopenharmony_ci for (i = hist->buf_size / 16; i > 0; i--) { 25662306a36Sopenharmony_ci *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); 25762306a36Sopenharmony_ci *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); 25862306a36Sopenharmony_ci *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); 25962306a36Sopenharmony_ci *buf++ = isp_reg_readl(isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_DATA); 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci isp_reg_clr(hist->isp, OMAP3_ISP_IOMEM_HIST, ISPHIST_CNT, 26262306a36Sopenharmony_ci ISPHIST_CNT_CLEAR); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci return STAT_BUF_DONE; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci/* 26862306a36Sopenharmony_ci * hist_buf_process - Callback from ISP driver for HIST interrupt. 26962306a36Sopenharmony_ci */ 27062306a36Sopenharmony_cistatic int hist_buf_process(struct ispstat *hist) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci struct omap3isp_hist_config *user_cfg = hist->priv; 27362306a36Sopenharmony_ci int ret; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (atomic_read(&hist->buf_err) || hist->state != ISPSTAT_ENABLED) { 27662306a36Sopenharmony_ci hist_reset_mem(hist); 27762306a36Sopenharmony_ci return STAT_NO_BUF; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci if (--(hist->wait_acc_frames)) 28162306a36Sopenharmony_ci return STAT_NO_BUF; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (hist->dma_ch) 28462306a36Sopenharmony_ci ret = hist_buf_dma(hist); 28562306a36Sopenharmony_ci else 28662306a36Sopenharmony_ci ret = hist_buf_pio(hist); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci hist->wait_acc_frames = user_cfg->num_acc_frames; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci return ret; 29162306a36Sopenharmony_ci} 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic u32 hist_get_buf_size(struct omap3isp_hist_config *conf) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci return OMAP3ISP_HIST_MEM_SIZE_BINS(conf->hist_bins) * conf->num_regions; 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci/* 29962306a36Sopenharmony_ci * hist_validate_params - Helper function to check user given params. 30062306a36Sopenharmony_ci * @new_conf: Pointer to user configuration structure. 30162306a36Sopenharmony_ci * 30262306a36Sopenharmony_ci * Returns 0 on success configuration. 30362306a36Sopenharmony_ci */ 30462306a36Sopenharmony_cistatic int hist_validate_params(struct ispstat *hist, void *new_conf) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct omap3isp_hist_config *user_cfg = new_conf; 30762306a36Sopenharmony_ci int c; 30862306a36Sopenharmony_ci u32 buf_size; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (user_cfg->cfa > OMAP3ISP_HIST_CFA_FOVEONX3) 31162306a36Sopenharmony_ci return -EINVAL; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* Regions size and position */ 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if ((user_cfg->num_regions < OMAP3ISP_HIST_MIN_REGIONS) || 31662306a36Sopenharmony_ci (user_cfg->num_regions > OMAP3ISP_HIST_MAX_REGIONS)) 31762306a36Sopenharmony_ci return -EINVAL; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* Regions */ 32062306a36Sopenharmony_ci for (c = 0; c < user_cfg->num_regions; c++) { 32162306a36Sopenharmony_ci if (user_cfg->region[c].h_start & ~ISPHIST_REG_START_END_MASK) 32262306a36Sopenharmony_ci return -EINVAL; 32362306a36Sopenharmony_ci if (user_cfg->region[c].h_end & ~ISPHIST_REG_START_END_MASK) 32462306a36Sopenharmony_ci return -EINVAL; 32562306a36Sopenharmony_ci if (user_cfg->region[c].v_start & ~ISPHIST_REG_START_END_MASK) 32662306a36Sopenharmony_ci return -EINVAL; 32762306a36Sopenharmony_ci if (user_cfg->region[c].v_end & ~ISPHIST_REG_START_END_MASK) 32862306a36Sopenharmony_ci return -EINVAL; 32962306a36Sopenharmony_ci if (user_cfg->region[c].h_start > user_cfg->region[c].h_end) 33062306a36Sopenharmony_ci return -EINVAL; 33162306a36Sopenharmony_ci if (user_cfg->region[c].v_start > user_cfg->region[c].v_end) 33262306a36Sopenharmony_ci return -EINVAL; 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci switch (user_cfg->num_regions) { 33662306a36Sopenharmony_ci case 1: 33762306a36Sopenharmony_ci if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_256) 33862306a36Sopenharmony_ci return -EINVAL; 33962306a36Sopenharmony_ci break; 34062306a36Sopenharmony_ci case 2: 34162306a36Sopenharmony_ci if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_128) 34262306a36Sopenharmony_ci return -EINVAL; 34362306a36Sopenharmony_ci break; 34462306a36Sopenharmony_ci default: /* 3 or 4 */ 34562306a36Sopenharmony_ci if (user_cfg->hist_bins > OMAP3ISP_HIST_BINS_64) 34662306a36Sopenharmony_ci return -EINVAL; 34762306a36Sopenharmony_ci break; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci buf_size = hist_get_buf_size(user_cfg); 35162306a36Sopenharmony_ci if (buf_size > user_cfg->buf_size) 35262306a36Sopenharmony_ci /* User's buf_size request wasn't enough */ 35362306a36Sopenharmony_ci user_cfg->buf_size = buf_size; 35462306a36Sopenharmony_ci else if (user_cfg->buf_size > OMAP3ISP_HIST_MAX_BUF_SIZE) 35562306a36Sopenharmony_ci user_cfg->buf_size = OMAP3ISP_HIST_MAX_BUF_SIZE; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci return 0; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic int hist_comp_params(struct ispstat *hist, 36162306a36Sopenharmony_ci struct omap3isp_hist_config *user_cfg) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci struct omap3isp_hist_config *cur_cfg = hist->priv; 36462306a36Sopenharmony_ci int c; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (cur_cfg->cfa != user_cfg->cfa) 36762306a36Sopenharmony_ci return 1; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (cur_cfg->num_acc_frames != user_cfg->num_acc_frames) 37062306a36Sopenharmony_ci return 1; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (cur_cfg->hist_bins != user_cfg->hist_bins) 37362306a36Sopenharmony_ci return 1; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci for (c = 0; c < OMAP3ISP_HIST_MAX_WG; c++) { 37662306a36Sopenharmony_ci if (c == 3 && user_cfg->cfa == OMAP3ISP_HIST_CFA_FOVEONX3) 37762306a36Sopenharmony_ci break; 37862306a36Sopenharmony_ci else if (cur_cfg->wg[c] != user_cfg->wg[c]) 37962306a36Sopenharmony_ci return 1; 38062306a36Sopenharmony_ci } 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (cur_cfg->num_regions != user_cfg->num_regions) 38362306a36Sopenharmony_ci return 1; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci /* Regions */ 38662306a36Sopenharmony_ci for (c = 0; c < user_cfg->num_regions; c++) { 38762306a36Sopenharmony_ci if (cur_cfg->region[c].h_start != user_cfg->region[c].h_start) 38862306a36Sopenharmony_ci return 1; 38962306a36Sopenharmony_ci if (cur_cfg->region[c].h_end != user_cfg->region[c].h_end) 39062306a36Sopenharmony_ci return 1; 39162306a36Sopenharmony_ci if (cur_cfg->region[c].v_start != user_cfg->region[c].v_start) 39262306a36Sopenharmony_ci return 1; 39362306a36Sopenharmony_ci if (cur_cfg->region[c].v_end != user_cfg->region[c].v_end) 39462306a36Sopenharmony_ci return 1; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci return 0; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci/* 40162306a36Sopenharmony_ci * hist_update_params - Helper function to check and store user given params. 40262306a36Sopenharmony_ci * @new_conf: Pointer to user configuration structure. 40362306a36Sopenharmony_ci */ 40462306a36Sopenharmony_cistatic void hist_set_params(struct ispstat *hist, void *new_conf) 40562306a36Sopenharmony_ci{ 40662306a36Sopenharmony_ci struct omap3isp_hist_config *user_cfg = new_conf; 40762306a36Sopenharmony_ci struct omap3isp_hist_config *cur_cfg = hist->priv; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (!hist->configured || hist_comp_params(hist, user_cfg)) { 41062306a36Sopenharmony_ci memcpy(cur_cfg, user_cfg, sizeof(*user_cfg)); 41162306a36Sopenharmony_ci if (user_cfg->num_acc_frames == 0) 41262306a36Sopenharmony_ci user_cfg->num_acc_frames = 1; 41362306a36Sopenharmony_ci hist->inc_config++; 41462306a36Sopenharmony_ci hist->update = 1; 41562306a36Sopenharmony_ci /* 41662306a36Sopenharmony_ci * User might be asked for a bigger buffer than necessary for 41762306a36Sopenharmony_ci * this configuration. In order to return the right amount of 41862306a36Sopenharmony_ci * data during buffer request, let's calculate the size here 41962306a36Sopenharmony_ci * instead of stick with user_cfg->buf_size. 42062306a36Sopenharmony_ci */ 42162306a36Sopenharmony_ci cur_cfg->buf_size = hist_get_buf_size(cur_cfg); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic long hist_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci struct ispstat *stat = v4l2_get_subdevdata(sd); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci switch (cmd) { 43162306a36Sopenharmony_ci case VIDIOC_OMAP3ISP_HIST_CFG: 43262306a36Sopenharmony_ci return omap3isp_stat_config(stat, arg); 43362306a36Sopenharmony_ci case VIDIOC_OMAP3ISP_STAT_REQ: 43462306a36Sopenharmony_ci return omap3isp_stat_request_statistics(stat, arg); 43562306a36Sopenharmony_ci case VIDIOC_OMAP3ISP_STAT_REQ_TIME32: 43662306a36Sopenharmony_ci return omap3isp_stat_request_statistics_time32(stat, arg); 43762306a36Sopenharmony_ci case VIDIOC_OMAP3ISP_STAT_EN: { 43862306a36Sopenharmony_ci int *en = arg; 43962306a36Sopenharmony_ci return omap3isp_stat_enable(stat, !!*en); 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci return -ENOIOCTLCMD; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic const struct ispstat_ops hist_ops = { 44862306a36Sopenharmony_ci .validate_params = hist_validate_params, 44962306a36Sopenharmony_ci .set_params = hist_set_params, 45062306a36Sopenharmony_ci .setup_regs = hist_setup_regs, 45162306a36Sopenharmony_ci .enable = hist_enable, 45262306a36Sopenharmony_ci .busy = hist_busy, 45362306a36Sopenharmony_ci .buf_process = hist_buf_process, 45462306a36Sopenharmony_ci}; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic const struct v4l2_subdev_core_ops hist_subdev_core_ops = { 45762306a36Sopenharmony_ci .ioctl = hist_ioctl, 45862306a36Sopenharmony_ci .subscribe_event = omap3isp_stat_subscribe_event, 45962306a36Sopenharmony_ci .unsubscribe_event = omap3isp_stat_unsubscribe_event, 46062306a36Sopenharmony_ci}; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic const struct v4l2_subdev_video_ops hist_subdev_video_ops = { 46362306a36Sopenharmony_ci .s_stream = omap3isp_stat_s_stream, 46462306a36Sopenharmony_ci}; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_cistatic const struct v4l2_subdev_ops hist_subdev_ops = { 46762306a36Sopenharmony_ci .core = &hist_subdev_core_ops, 46862306a36Sopenharmony_ci .video = &hist_subdev_video_ops, 46962306a36Sopenharmony_ci}; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci/* 47262306a36Sopenharmony_ci * omap3isp_hist_init - Module Initialization. 47362306a36Sopenharmony_ci */ 47462306a36Sopenharmony_ciint omap3isp_hist_init(struct isp_device *isp) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci struct ispstat *hist = &isp->isp_hist; 47762306a36Sopenharmony_ci struct omap3isp_hist_config *hist_cfg; 47862306a36Sopenharmony_ci int ret; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci hist_cfg = kzalloc(sizeof(*hist_cfg), GFP_KERNEL); 48162306a36Sopenharmony_ci if (hist_cfg == NULL) 48262306a36Sopenharmony_ci return -ENOMEM; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci hist->isp = isp; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (HIST_CONFIG_DMA) { 48762306a36Sopenharmony_ci dma_cap_mask_t mask; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* 49062306a36Sopenharmony_ci * We need slave capable channel without DMA request line for 49162306a36Sopenharmony_ci * reading out the data. 49262306a36Sopenharmony_ci * For this we can use dma_request_chan_by_mask() as we are 49362306a36Sopenharmony_ci * happy with any channel as long as it is capable of slave 49462306a36Sopenharmony_ci * configuration. 49562306a36Sopenharmony_ci */ 49662306a36Sopenharmony_ci dma_cap_zero(mask); 49762306a36Sopenharmony_ci dma_cap_set(DMA_SLAVE, mask); 49862306a36Sopenharmony_ci hist->dma_ch = dma_request_chan_by_mask(&mask); 49962306a36Sopenharmony_ci if (IS_ERR(hist->dma_ch)) { 50062306a36Sopenharmony_ci ret = PTR_ERR(hist->dma_ch); 50162306a36Sopenharmony_ci if (ret == -EPROBE_DEFER) 50262306a36Sopenharmony_ci goto err; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci hist->dma_ch = NULL; 50562306a36Sopenharmony_ci dev_warn(isp->dev, 50662306a36Sopenharmony_ci "hist: DMA channel request failed, using PIO\n"); 50762306a36Sopenharmony_ci } else { 50862306a36Sopenharmony_ci dev_dbg(isp->dev, "hist: using DMA channel %s\n", 50962306a36Sopenharmony_ci dma_chan_name(hist->dma_ch)); 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci hist->ops = &hist_ops; 51462306a36Sopenharmony_ci hist->priv = hist_cfg; 51562306a36Sopenharmony_ci hist->event_type = V4L2_EVENT_OMAP3ISP_HIST; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci ret = omap3isp_stat_init(hist, "histogram", &hist_subdev_ops); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_cierr: 52062306a36Sopenharmony_ci if (ret) { 52162306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(hist->dma_ch)) 52262306a36Sopenharmony_ci dma_release_channel(hist->dma_ch); 52362306a36Sopenharmony_ci kfree(hist_cfg); 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci return ret; 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci/* 53062306a36Sopenharmony_ci * omap3isp_hist_cleanup - Module cleanup. 53162306a36Sopenharmony_ci */ 53262306a36Sopenharmony_civoid omap3isp_hist_cleanup(struct isp_device *isp) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci struct ispstat *hist = &isp->isp_hist; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if (hist->dma_ch) 53762306a36Sopenharmony_ci dma_release_channel(hist->dma_ch); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci omap3isp_stat_cleanup(hist); 54062306a36Sopenharmony_ci} 541