| /* |
| * ispresizer.c |
| * |
| * Driver Library for Resizer module in TI's OMAP3 Camera ISP |
| * |
| * Copyright (C)2009 Texas Instruments, Inc. |
| * |
| * Contributors: |
| * Sameer Venkatraman <sameerv@ti.com> |
| * Mohit Jalori |
| * Sergio Aguirre <saaguirre@ti.com> |
| * |
| * This package is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED |
| * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/device.h> |
| |
| #include "isp.h" |
| #include "ispreg.h" |
| #include "ispresizer.h" |
| |
| /* Default configuration of resizer,filter coefficients,yenh for camera isp */ |
| static struct isprsz_coef ispreszdefcoef = { |
| { |
| 0x0000, 0x0100, 0x0000, 0x0000, |
| 0x03FA, 0x00F6, 0x0010, 0x0000, |
| 0x03F9, 0x00DB, 0x002C, 0x0000, |
| 0x03FB, 0x00B3, 0x0053, 0x03FF, |
| 0x03FD, 0x0082, 0x0084, 0x03FD, |
| 0x03FF, 0x0053, 0x00B3, 0x03FB, |
| 0x0000, 0x002C, 0x00DB, 0x03F9, |
| 0x0000, 0x0010, 0x00F6, 0x03FA |
| }, |
| { |
| 0x0000, 0x0100, 0x0000, 0x0000, |
| 0x03FA, 0x00F6, 0x0010, 0x0000, |
| 0x03F9, 0x00DB, 0x002C, 0x0000, |
| 0x03FB, 0x00B3, 0x0053, 0x03FF, |
| 0x03FD, 0x0082, 0x0084, 0x03FD, |
| 0x03FF, 0x0053, 0x00B3, 0x03FB, |
| 0x0000, 0x002C, 0x00DB, 0x03F9, |
| 0x0000, 0x0010, 0x00F6, 0x03FA |
| }, |
| { |
| 0x0004, 0x0023, 0x005A, 0x0058, |
| 0x0023, 0x0004, 0x0000, 0x0002, |
| 0x0018, 0x004d, 0x0060, 0x0031, |
| 0x0008, 0x0000, 0x0001, 0x000f, |
| 0x003f, 0x0062, 0x003f, 0x000f, |
| 0x0001, 0x0000, 0x0008, 0x0031, |
| 0x0060, 0x004d, 0x0018, 0x0002 |
| }, |
| { |
| 0x0004, 0x0023, 0x005A, 0x0058, |
| 0x0023, 0x0004, 0x0000, 0x0002, |
| 0x0018, 0x004d, 0x0060, 0x0031, |
| 0x0008, 0x0000, 0x0001, 0x000f, |
| 0x003f, 0x0062, 0x003f, 0x000f, |
| 0x0001, 0x0000, 0x0008, 0x0031, |
| 0x0060, 0x004d, 0x0018, 0x0002 |
| } |
| }; |
| |
| /* Structure for saving/restoring resizer module registers */ |
| static struct isp_reg isprsz_reg_list[] = { |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_OUT_SIZE, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_START, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_SIZE, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INADD, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INOFF, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTADD, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTOFF, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT10, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT32, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT54, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT76, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT98, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT1110, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT1312, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT1514, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT1716, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT1918, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT2120, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT2322, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT2524, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT2726, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT2928, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_HFILT3130, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT10, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT32, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT54, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT76, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT98, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT1110, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT1312, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT1514, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT1716, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT1918, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT2120, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT2322, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT2524, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT2726, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT2928, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_VFILT3130, 0x0000}, |
| {OMAP3_ISP_IOMEM_RESZ, ISPRSZ_YENH, 0x0000}, |
| {0, ISP_TOK_TERM, 0x0000} |
| }; |
| |
| /** |
| * ispresizer_applycrop - Apply crop to input image. |
| **/ |
| void ispresizer_applycrop(struct isp_res_device *isp_res) |
| { |
| struct isp_device *isp = to_isp_device(isp_res); |
| |
| if (!isp_res->applycrop) |
| return; |
| |
| ispresizer_s_pipeline(isp_res, &isp->pipeline); |
| |
| isp_res->applycrop = 0; |
| |
| return; |
| } |
| |
| /** |
| * ispresizer_config_shadow_registers - Configure shadow registers. |
| **/ |
| void ispresizer_config_shadow_registers(struct isp_res_device *isp_res) |
| { |
| ispresizer_applycrop(isp_res); |
| |
| return; |
| } |
| |
| int ispresizer_config_crop(struct isp_res_device *isp_res, |
| struct v4l2_crop *a) |
| { |
| struct isp_device *isp = to_isp_device(isp_res); |
| struct v4l2_crop *crop = a; |
| int rval; |
| |
| if (crop->c.left < 0) |
| crop->c.left = 0; |
| if (crop->c.width < 0) |
| crop->c.width = 0; |
| if (crop->c.top < 0) |
| crop->c.top = 0; |
| if (crop->c.height < 0) |
| crop->c.height = 0; |
| |
| if (crop->c.left >= isp->pipeline.prv_out_w_img) |
| crop->c.left = isp->pipeline.prv_out_w_img - 1; |
| if (crop->c.top >= isp->pipeline.rsz_out_h) |
| crop->c.top = isp->pipeline.rsz_out_h - 1; |
| |
| /* Make sure the crop rectangle is never smaller than width |
| * and height divided by 4, since the resizer cannot upscale it |
| * by more than 4x. */ |
| |
| if (crop->c.width < (isp->pipeline.rsz_out_w + 3) / 4) |
| crop->c.width = (isp->pipeline.rsz_out_w + 3) / 4; |
| if (crop->c.height < (isp->pipeline.rsz_out_h + 3) / 4) |
| crop->c.height = (isp->pipeline.rsz_out_h + 3) / 4; |
| |
| if (crop->c.left + crop->c.width > isp->pipeline.prv_out_w_img) |
| crop->c.width = isp->pipeline.prv_out_w_img - crop->c.left; |
| if (crop->c.top + crop->c.height > isp->pipeline.prv_out_h_img) |
| crop->c.height = isp->pipeline.prv_out_h_img - crop->c.top; |
| |
| isp->pipeline.rsz_crop = crop->c; |
| |
| rval = ispresizer_try_pipeline(isp_res, &isp->pipeline); |
| if (rval) |
| return rval; |
| |
| isp_res->applycrop = 1; |
| |
| if (isp->running == ISP_STOPPED) |
| ispresizer_applycrop(isp_res); |
| |
| return 0; |
| } |
| |
| /** |
| * ispresizer_request - Reserves the Resizer module. |
| * |
| * Allows only one user at a time. |
| * |
| * Returns 0 if successful, or -EBUSY if resizer module was already requested. |
| **/ |
| int ispresizer_request(struct isp_res_device *isp_res) |
| { |
| struct device *dev = to_device(isp_res); |
| |
| mutex_lock(&isp_res->ispres_mutex); |
| if (!isp_res->res_inuse) { |
| isp_res->res_inuse = 1; |
| mutex_unlock(&isp_res->ispres_mutex); |
| isp_reg_writel(dev, |
| isp_reg_readl(dev, |
| OMAP3_ISP_IOMEM_MAIN, ISP_CTRL) | |
| ISPCTRL_SBL_WR0_RAM_EN | |
| ISPCTRL_RSZ_CLK_EN, |
| OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); |
| return 0; |
| } else { |
| mutex_unlock(&isp_res->ispres_mutex); |
| dev_err(dev, "resizer: Module Busy\n"); |
| return -EBUSY; |
| } |
| } |
| |
| /** |
| * ispresizer_free - Makes Resizer module free. |
| * |
| * Returns 0 if successful, or -EINVAL if resizer module was already freed. |
| **/ |
| int ispresizer_free(struct isp_res_device *isp_res) |
| { |
| struct device *dev = to_device(isp_res); |
| |
| mutex_lock(&isp_res->ispres_mutex); |
| if (isp_res->res_inuse) { |
| isp_res->res_inuse = 0; |
| mutex_unlock(&isp_res->ispres_mutex); |
| isp_reg_and(dev, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL, |
| ~(ISPCTRL_RSZ_CLK_EN | ISPCTRL_SBL_WR0_RAM_EN)); |
| return 0; |
| } else { |
| mutex_unlock(&isp_res->ispres_mutex); |
| DPRINTK_ISPRESZ("ISP_ERR : Resizer Module already freed\n"); |
| return -EINVAL; |
| } |
| } |
| |
| /** |
| * ispresizer_config_datapath - Specifies which input to use in resizer module |
| * @input: Indicates the module that gives the image to resizer. |
| * |
| * Sets up the default resizer configuration according to the arguments. |
| * |
| * Returns 0 if successful, or -EINVAL if an unsupported input was requested. |
| **/ |
| int ispresizer_config_datapath(struct isp_res_device *isp_res, |
| struct isp_pipeline *pipe) |
| { |
| struct device *dev = to_device(isp_res); |
| u32 cnt = 0; |
| |
| DPRINTK_ISPRESZ("ispresizer_config_datapath()+\n"); |
| |
| switch (pipe->rsz_in) { |
| case RSZ_OTFLY_YUV: |
| cnt &= ~ISPRSZ_CNT_INPTYP; |
| cnt &= ~ISPRSZ_CNT_INPSRC; |
| ispresizer_set_inaddr(isp_res, 0); |
| ispresizer_config_inlineoffset(isp_res, 0); |
| break; |
| case RSZ_MEM_YUV: |
| cnt |= ISPRSZ_CNT_INPSRC; |
| cnt &= ~ISPRSZ_CNT_INPTYP; |
| break; |
| case RSZ_MEM_COL8: |
| cnt |= ISPRSZ_CNT_INPSRC; |
| cnt |= ISPRSZ_CNT_INPTYP; |
| break; |
| default: |
| dev_err(dev, "resizer: Wrong Input\n"); |
| return -EINVAL; |
| } |
| isp_reg_or(dev, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, cnt); |
| ispresizer_config_ycpos(isp_res, 0); |
| ispresizer_config_filter_coef(isp_res, &ispreszdefcoef); |
| ispresizer_enable_cbilin(isp_res, 0); |
| ispresizer_config_luma_enhance(isp_res, &isp_res->defaultyenh); |
| DPRINTK_ISPRESZ("ispresizer_config_datapath()-\n"); |
| return 0; |
| } |
| |
| /** |
| * ispresizer_try_size - Validates input and output images size. |
| * @input_w: input width for the resizer in number of pixels per line |
| * @input_h: input height for the resizer in number of lines |
| * @output_w: output width from the resizer in number of pixels per line |
| * resizer when writing to memory needs this to be multiple of 16. |
| * @pipe->rsz_out_h: output height for the resizer in number of lines, must be |
| * even. |
| * |
| * Calculates the horizontal and vertical resize ratio, number of pixels to |
| * be cropped in the resizer module and checks the validity of various |
| * parameters. Formula used for calculation is:- |
| * |
| * 8-phase 4-tap mode :- |
| * inputwidth = (32 * sph + (ow - 1) * hrsz + 16) >> 8 + 7 |
| * inputheight = (32 * spv + (oh - 1) * vrsz + 16) >> 8 + 4 |
| * endpahse for width = ((32 * sph + (ow - 1) * hrsz + 16) >> 5) % 8 |
| * endphase for height = ((32 * sph + (oh - 1) * hrsz + 16) >> 5) % 8 |
| * |
| * 4-phase 7-tap mode :- |
| * inputwidth = (64 * sph + (ow - 1) * hrsz + 32) >> 8 + 7 |
| * inputheight = (64 * spv + (oh - 1) * vrsz + 32) >> 8 + 7 |
| * endpahse for width = ((64 * sph + (ow - 1) * hrsz + 32) >> 6) % 4 |
| * endphase for height = ((64 * sph + (oh - 1) * hrsz + 32) >> 6) % 4 |
| * |
| * Where: |
| * sph = Start phase horizontal |
| * spv = Start phase vertical |
| * ow = Output width |
| * oh = Output height |
| * hrsz = Horizontal resize value |
| * vrsz = Vertical resize value |
| * |
| * Fills up the output/input widht/height, horizontal/vertical resize ratio, |
| * horizontal/vertical crop variables in the isp_res structure. |
| **/ |
| int ispresizer_try_pipeline(struct isp_res_device *isp_res, |
| struct isp_pipeline *pipe) |
| { |
| struct device *dev = to_device(isp_res); |
| u32 rsz, rsz_7, rsz_4; |
| u32 sph; |
| int max_in_otf, max_out_7tap; |
| |
| if (pipe->rsz_crop.width < 32 || pipe->rsz_crop.height < 32) { |
| DPRINTK_ISPCCDC("ISP_ERR: RESIZER cannot handle input width" |
| " less than 32 pixels or height less than" |
| " 32\n"); |
| return -EINVAL; |
| } |
| |
| if (pipe->rsz_crop.height > MAX_IN_HEIGHT) |
| return -EINVAL; |
| |
| if (pipe->rsz_out_w < 16) |
| pipe->rsz_out_w = 16; |
| |
| if (pipe->rsz_out_h < 2) |
| pipe->rsz_out_h = 2; |
| |
| if (omap_rev() == OMAP3430_REV_ES1_0) { |
| max_in_otf = MAX_IN_WIDTH_ONTHEFLY_MODE; |
| max_out_7tap = MAX_7TAP_VRSZ_OUTWIDTH; |
| } else { |
| max_in_otf = MAX_IN_WIDTH_ONTHEFLY_MODE_ES2; |
| max_out_7tap = MAX_7TAP_VRSZ_OUTWIDTH_ES2; |
| } |
| |
| if (pipe->rsz_in == RSZ_OTFLY_YUV) { |
| if (pipe->rsz_crop.width > max_in_otf) |
| return -EINVAL; |
| } else { |
| if (pipe->rsz_crop.width > MAX_IN_WIDTH_MEMORY_MODE) |
| return -EINVAL; |
| } |
| |
| pipe->rsz_out_h &= 0xfffffffe; |
| sph = DEFAULTSTPHASE; |
| |
| rsz_7 = ((pipe->rsz_crop.height - 7) * 256) / (pipe->rsz_out_h - 1); |
| rsz_4 = ((pipe->rsz_crop.height - 4) * 256) / (pipe->rsz_out_h - 1); |
| |
| rsz = (pipe->rsz_crop.height * 256) / pipe->rsz_out_h; |
| |
| if (rsz <= MID_RESIZE_VALUE) { |
| rsz = rsz_4; |
| if (rsz < MINIMUM_RESIZE_VALUE) { |
| rsz = MINIMUM_RESIZE_VALUE; |
| pipe->rsz_out_h = |
| (((pipe->rsz_crop.height - 4) * 256) / rsz) + 1; |
| dev_dbg(dev, |
| "resizer: %s: using height %d instead\n", |
| __func__, pipe->rsz_out_h); |
| } |
| } else { |
| rsz = rsz_7; |
| if (pipe->rsz_out_w > max_out_7tap) |
| pipe->rsz_out_w = max_out_7tap; |
| if (rsz > MAXIMUM_RESIZE_VALUE) { |
| rsz = MAXIMUM_RESIZE_VALUE; |
| pipe->rsz_out_h = |
| (((pipe->rsz_crop.height - 7) * 256) / rsz) + 1; |
| dev_dbg(dev, |
| "resizer: %s: using height %d instead\n", |
| __func__, pipe->rsz_out_h); |
| } |
| } |
| |
| if (rsz > MID_RESIZE_VALUE) { |
| pipe->rsz_crop.height = |
| (((64 * sph) + ((pipe->rsz_out_h - 1) * rsz) + 32) |
| / 256) + 7; |
| } else { |
| pipe->rsz_crop.height = |
| (((32 * sph) + ((pipe->rsz_out_h - 1) * rsz) + 16) |
| / 256) + 4; |
| } |
| |
| isp_res->v_resz = rsz; |
| /* FIXME: pipe->rsz_crop.height here is the real input height! */ |
| isp_res->v_startphase = sph; |
| |
| pipe->rsz_out_w &= 0xfffffff0; |
| sph = DEFAULTSTPHASE; |
| |
| rsz_7 = ((pipe->rsz_crop.width - 7) * 256) / (pipe->rsz_out_w - 1); |
| rsz_4 = ((pipe->rsz_crop.width - 4) * 256) / (pipe->rsz_out_w - 1); |
| |
| rsz = (pipe->rsz_crop.width * 256) / pipe->rsz_out_w; |
| if (rsz > MID_RESIZE_VALUE) { |
| rsz = rsz_7; |
| if (rsz > MAXIMUM_RESIZE_VALUE) { |
| rsz = MAXIMUM_RESIZE_VALUE; |
| pipe->rsz_out_w = |
| (((pipe->rsz_crop.width - 7) * 256) / rsz) + 1; |
| pipe->rsz_out_w = (pipe->rsz_out_w + 0xf) & 0xfffffff0; |
| dev_dbg(dev, |
| "resizer: %s: using width %d instead\n", |
| __func__, pipe->rsz_out_w); |
| } |
| } else { |
| rsz = rsz_4; |
| if (rsz < MINIMUM_RESIZE_VALUE) { |
| rsz = MINIMUM_RESIZE_VALUE; |
| pipe->rsz_out_w = |
| (((pipe->rsz_crop.width - 4) * 256) / rsz) + 1; |
| pipe->rsz_out_w = (pipe->rsz_out_w + 0xf) & 0xfffffff0; |
| dev_dbg(dev, |
| "resizer: %s: using width %d instead\n", |
| __func__, pipe->rsz_out_w); |
| } |
| } |
| |
| /* Recalculate input based on TRM equations */ |
| if (rsz > MID_RESIZE_VALUE) { |
| pipe->rsz_crop.width = |
| (((64 * sph) + ((pipe->rsz_out_w - 1) * rsz) + 32) |
| / 256) + 7; |
| } else { |
| pipe->rsz_crop.width = |
| (((32 * sph) + ((pipe->rsz_out_w - 1) * rsz) + 16) |
| / 256) + 7; |
| } |
| |
| isp_res->h_resz = rsz; |
| /* FIXME: pipe->rsz_crop.width here is the real input width! */ |
| isp_res->h_startphase = sph; |
| |
| pipe->rsz_out_w_img = pipe->rsz_out_w; |
| |
| return 0; |
| } |
| |
| /** |
| * ispresizer_config_size - Configures input and output image size. |
| * @pipe->rsz_crop.width: input width for the resizer in number of pixels per |
| * line. |
| * @pipe->rsz_crop.height: input height for the resizer in number of lines. |
| * @pipe->rsz_out_w: output width from the resizer in number of pixels per line. |
| * @pipe->rsz_out_h: output height for the resizer in number of lines. |
| * |
| * Configures the appropriate values stored in the isp_res structure in the |
| * resizer registers. |
| * |
| * Returns 0 if successful, or -EINVAL if passed values haven't been verified |
| * with ispresizer_try_size() previously. |
| **/ |
| int ispresizer_s_pipeline(struct isp_res_device *isp_res, |
| struct isp_pipeline *pipe) |
| { |
| struct device *dev = to_device(isp_res); |
| int i, j; |
| u32 res; |
| int rval; |
| |
| rval = ispresizer_config_datapath(isp_res, pipe); |
| if (rval) |
| return rval; |
| |
| /* Set Resizer input address and offset adderss */ |
| ispresizer_config_inlineoffset(isp_res, |
| pipe->prv_out_w * ISP_BYTES_PER_PIXEL); |
| |
| res = isp_reg_readl(dev, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT) & |
| ~(ISPRSZ_CNT_HSTPH_MASK | ISPRSZ_CNT_VSTPH_MASK); |
| isp_reg_writel(dev, res | |
| (isp_res->h_startphase << ISPRSZ_CNT_HSTPH_SHIFT) | |
| (isp_res->v_startphase << ISPRSZ_CNT_VSTPH_SHIFT), |
| OMAP3_ISP_IOMEM_RESZ, |
| ISPRSZ_CNT); |
| |
| /* Set start address for cropping */ |
| ispresizer_set_inaddr(isp_res, |
| isp_res->tmp_buf + isp_get_buf_offset(dev)); |
| |
| isp_reg_writel(dev, |
| (pipe->rsz_crop.width << ISPRSZ_IN_SIZE_HORZ_SHIFT) | |
| (pipe->rsz_crop.height << |
| ISPRSZ_IN_SIZE_VERT_SHIFT), |
| OMAP3_ISP_IOMEM_RESZ, |
| ISPRSZ_IN_SIZE); |
| if (!isp_res->algo) { |
| isp_reg_writel(dev, |
| (pipe->rsz_out_w << ISPRSZ_OUT_SIZE_HORZ_SHIFT) | |
| (pipe->rsz_out_h << ISPRSZ_OUT_SIZE_VERT_SHIFT), |
| OMAP3_ISP_IOMEM_RESZ, |
| ISPRSZ_OUT_SIZE); |
| } else { |
| isp_reg_writel(dev, |
| ((pipe->rsz_out_w - 4) |
| << ISPRSZ_OUT_SIZE_HORZ_SHIFT) | |
| (pipe->rsz_out_h << ISPRSZ_OUT_SIZE_VERT_SHIFT), |
| OMAP3_ISP_IOMEM_RESZ, |
| ISPRSZ_OUT_SIZE); |
| } |
| |
| res = isp_reg_readl(dev, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT) & |
| ~(ISPRSZ_CNT_HRSZ_MASK | ISPRSZ_CNT_VRSZ_MASK); |
| isp_reg_writel(dev, res | |
| ((isp_res->h_resz - 1) << ISPRSZ_CNT_HRSZ_SHIFT) | |
| ((isp_res->v_resz - 1) << ISPRSZ_CNT_VRSZ_SHIFT), |
| OMAP3_ISP_IOMEM_RESZ, |
| ISPRSZ_CNT); |
| if (isp_res->h_resz <= MID_RESIZE_VALUE) { |
| j = 0; |
| for (i = 0; i < 16; i++) { |
| isp_reg_writel(dev, |
| (isp_res->coeflist.h_filter_coef_4tap[j] |
| << ISPRSZ_HFILT10_COEF0_SHIFT) | |
| (isp_res->coeflist.h_filter_coef_4tap[j + 1] |
| << ISPRSZ_HFILT10_COEF1_SHIFT), |
| OMAP3_ISP_IOMEM_RESZ, |
| ISPRSZ_HFILT10 + (i * 0x04)); |
| j += 2; |
| } |
| } else { |
| j = 0; |
| for (i = 0; i < 16; i++) { |
| if ((i + 1) % 4 == 0) { |
| isp_reg_writel(dev, |
| (isp_res->coeflist. |
| h_filter_coef_7tap[j] << |
| ISPRSZ_HFILT10_COEF0_SHIFT), |
| OMAP3_ISP_IOMEM_RESZ, |
| ISPRSZ_HFILT10 + (i * 0x04)); |
| j += 1; |
| } else { |
| isp_reg_writel(dev, |
| (isp_res->coeflist. |
| h_filter_coef_7tap[j] << |
| ISPRSZ_HFILT10_COEF0_SHIFT) | |
| (isp_res->coeflist. |
| h_filter_coef_7tap[j+1] << |
| ISPRSZ_HFILT10_COEF1_SHIFT), |
| OMAP3_ISP_IOMEM_RESZ, |
| ISPRSZ_HFILT10 + (i * 0x04)); |
| j += 2; |
| } |
| } |
| } |
| if (isp_res->v_resz <= MID_RESIZE_VALUE) { |
| j = 0; |
| for (i = 0; i < 16; i++) { |
| isp_reg_writel(dev, (isp_res->coeflist. |
| v_filter_coef_4tap[j] << |
| ISPRSZ_VFILT10_COEF0_SHIFT) | |
| (isp_res->coeflist. |
| v_filter_coef_4tap[j + 1] << |
| ISPRSZ_VFILT10_COEF1_SHIFT), |
| OMAP3_ISP_IOMEM_RESZ, |
| ISPRSZ_VFILT10 + (i * 0x04)); |
| j += 2; |
| } |
| } else { |
| j = 0; |
| for (i = 0; i < 16; i++) { |
| if ((i + 1) % 4 == 0) { |
| isp_reg_writel(dev, |
| (isp_res->coeflist. |
| v_filter_coef_7tap[j] << |
| ISPRSZ_VFILT10_COEF0_SHIFT), |
| OMAP3_ISP_IOMEM_RESZ, |
| ISPRSZ_VFILT10 + (i * 0x04)); |
| j += 1; |
| } else { |
| isp_reg_writel(dev, |
| (isp_res->coeflist. |
| v_filter_coef_7tap[j] << |
| ISPRSZ_VFILT10_COEF0_SHIFT) | |
| (isp_res->coeflist. |
| v_filter_coef_7tap[j+1] << |
| ISPRSZ_VFILT10_COEF1_SHIFT), |
| OMAP3_ISP_IOMEM_RESZ, |
| ISPRSZ_VFILT10 + (i * 0x04)); |
| j += 2; |
| } |
| } |
| } |
| |
| ispresizer_config_outlineoffset(isp_res, pipe->rsz_out_w*2); |
| |
| if (pipe->pix.pixelformat == V4L2_PIX_FMT_UYVY) |
| ispresizer_config_ycpos(isp_res, 0); |
| else |
| ispresizer_config_ycpos(isp_res, 1); |
| |
| DPRINTK_ISPRESZ("ispresizer_config_size()-\n"); |
| return 0; |
| } |
| |
| /** |
| * ispresizer_enable - Enables the resizer module. |
| * @enable: 1 - Enable, 0 - Disable |
| * |
| * Client should configure all the sub modules in resizer before this. |
| **/ |
| void ispresizer_enable(struct isp_res_device *isp_res, int enable) |
| { |
| struct device *dev = to_device(isp_res); |
| int val; |
| |
| DPRINTK_ISPRESZ("+ispresizer_enable()+\n"); |
| if (enable) { |
| val = (isp_reg_readl(dev, |
| OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR) & 0x2) | |
| ISPRSZ_PCR_ENABLE; |
| } else { |
| val = isp_reg_readl(dev, |
| OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR) & |
| ~ISPRSZ_PCR_ENABLE; |
| } |
| isp_reg_writel(dev, val, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR); |
| DPRINTK_ISPRESZ("+ispresizer_enable()-\n"); |
| } |
| |
| /** |
| * ispresizer_busy - Checks if ISP resizer is busy. |
| * |
| * Returns busy field from ISPRSZ_PCR register. |
| **/ |
| int ispresizer_busy(struct isp_res_device *isp_res) |
| { |
| struct device *dev = to_device(isp_res); |
| |
| return isp_reg_readl(dev, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR) & |
| ISPPRV_PCR_BUSY; |
| } |
| |
| /** |
| * ispresizer_config_startphase - Sets the horizontal and vertical start phase. |
| * @hstartphase: horizontal start phase (0 - 7). |
| * @vstartphase: vertical startphase (0 - 7). |
| * |
| * This API just updates the isp_res struct. Actual register write happens in |
| * ispresizer_config_size. |
| **/ |
| void ispresizer_config_startphase(struct isp_res_device *isp_res, |
| u8 hstartphase, u8 vstartphase) |
| { |
| DPRINTK_ISPRESZ("ispresizer_config_startphase()+\n"); |
| isp_res->h_startphase = hstartphase; |
| isp_res->v_startphase = vstartphase; |
| DPRINTK_ISPRESZ("ispresizer_config_startphase()-\n"); |
| } |
| |
| /** |
| * ispresizer_config_ycpos - Specifies if output should be in YC or CY format. |
| * @yc: 0 - YC format, 1 - CY format |
| **/ |
| void ispresizer_config_ycpos(struct isp_res_device *isp_res, u8 yc) |
| { |
| struct device *dev = to_device(isp_res); |
| |
| DPRINTK_ISPRESZ("ispresizer_config_ycpos()+\n"); |
| isp_reg_and_or(dev, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, |
| ~ISPRSZ_CNT_YCPOS, (yc ? ISPRSZ_CNT_YCPOS : 0)); |
| DPRINTK_ISPRESZ("ispresizer_config_ycpos()-\n"); |
| } |
| |
| /** |
| * Sets the chrominance algorithm |
| * @cbilin: 0 - chrominance uses same processing as luminance, |
| * 1 - bilinear interpolation processing |
| **/ |
| void ispresizer_enable_cbilin(struct isp_res_device *isp_res, u8 enable) |
| { |
| struct device *dev = to_device(isp_res); |
| |
| DPRINTK_ISPRESZ("ispresizer_enable_cbilin()+\n"); |
| isp_reg_and_or(dev, OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT, |
| ~ISPRSZ_CNT_CBILIN, (enable ? ISPRSZ_CNT_CBILIN : 0)); |
| DPRINTK_ISPRESZ("ispresizer_enable_cbilin()-\n"); |
| } |
| |
| /** |
| * ispresizer_config_luma_enhance - Configures luminance enhancer parameters. |
| * @yenh: Pointer to structure containing desired values for core, slope, gain |
| * and algo parameters. |
| **/ |
| void ispresizer_config_luma_enhance(struct isp_res_device *isp_res, |
| struct isprsz_yenh *yenh) |
| { |
| struct device *dev = to_device(isp_res); |
| |
| DPRINTK_ISPRESZ("ispresizer_config_luma_enhance()+\n"); |
| isp_res->algo = yenh->algo; |
| isp_reg_writel(dev, (yenh->algo << ISPRSZ_YENH_ALGO_SHIFT) | |
| (yenh->gain << ISPRSZ_YENH_GAIN_SHIFT) | |
| (yenh->slope << ISPRSZ_YENH_SLOP_SHIFT) | |
| (yenh->coreoffset << ISPRSZ_YENH_CORE_SHIFT), |
| OMAP3_ISP_IOMEM_RESZ, |
| ISPRSZ_YENH); |
| DPRINTK_ISPRESZ("ispresizer_config_luma_enhance()-\n"); |
| } |
| |
| /** |
| * ispresizer_config_filter_coef - Sets filter coefficients for 4 & 7-tap mode. |
| * This API just updates the isp_res struct.Actual register write happens in |
| * ispresizer_config_size. |
| * @coef: Structure containing horizontal and vertical filter coefficients for |
| * both 4-tap and 7-tap mode. |
| **/ |
| void ispresizer_config_filter_coef(struct isp_res_device *isp_res, |
| struct isprsz_coef *coef) |
| { |
| int i; |
| DPRINTK_ISPRESZ("ispresizer_config_filter_coef()+\n"); |
| for (i = 0; i < 32; i++) { |
| isp_res->coeflist.h_filter_coef_4tap[i] = |
| coef->h_filter_coef_4tap[i]; |
| isp_res->coeflist.v_filter_coef_4tap[i] = |
| coef->v_filter_coef_4tap[i]; |
| } |
| for (i = 0; i < 28; i++) { |
| isp_res->coeflist.h_filter_coef_7tap[i] = |
| coef->h_filter_coef_7tap[i]; |
| isp_res->coeflist.v_filter_coef_7tap[i] = |
| coef->v_filter_coef_7tap[i]; |
| } |
| DPRINTK_ISPRESZ("ispresizer_config_filter_coef()-\n"); |
| } |
| |
| /** |
| * ispresizer_config_inlineoffset - Configures the read address line offset. |
| * @offset: Line Offset for the input image. |
| * |
| * Returns 0 if successful, or -EINVAL if offset is not 32 bits aligned. |
| **/ |
| int ispresizer_config_inlineoffset(struct isp_res_device *isp_res, u32 offset) |
| { |
| struct device *dev = to_device(isp_res); |
| |
| DPRINTK_ISPRESZ("ispresizer_config_inlineoffset()+\n"); |
| if (offset % 32) |
| return -EINVAL; |
| isp_reg_writel(dev, offset << ISPRSZ_SDR_INOFF_OFFSET_SHIFT, |
| OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INOFF); |
| DPRINTK_ISPRESZ("ispresizer_config_inlineoffset()-\n"); |
| return 0; |
| } |
| |
| /** |
| * ispresizer_set_inaddr - Sets the memory address of the input frame. |
| * @addr: 32bit memory address aligned on 32byte boundary. |
| * |
| * Returns 0 if successful, or -EINVAL if address is not 32 bits aligned. |
| **/ |
| int ispresizer_set_inaddr(struct isp_res_device *isp_res, u32 addr) |
| { |
| struct isp_device *isp = to_isp_device(isp_res); |
| struct device *dev = to_device(isp_res); |
| |
| DPRINTK_ISPRESZ("ispresizer_set_inaddr()+\n"); |
| |
| if (addr % 32) |
| return -EINVAL; |
| isp_res->tmp_buf = addr; |
| /* FIXME: is this the right place to put crop-related junk? */ |
| isp_reg_writel(dev, |
| isp_res->tmp_buf + ISP_BYTES_PER_PIXEL |
| * ((isp->pipeline.rsz_crop.left & ~0xf) + |
| isp->pipeline.prv_out_w |
| * isp->pipeline.rsz_crop.top), |
| OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INADD); |
| /* Set the fractional part of the starting address. Needed for crop */ |
| isp_reg_writel(dev, ((isp->pipeline.rsz_crop.left & 0xf) << |
| ISPRSZ_IN_START_HORZ_ST_SHIFT) | |
| (0x00 << ISPRSZ_IN_START_VERT_ST_SHIFT), |
| OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_START); |
| |
| DPRINTK_ISPRESZ("ispresizer_set_inaddr()-\n"); |
| return 0; |
| } |
| |
| /** |
| * ispresizer_config_outlineoffset - Configures the write address line offset. |
| * @offset: Line offset for the preview output. |
| * |
| * Returns 0 if successful, or -EINVAL if address is not 32 bits aligned. |
| **/ |
| int ispresizer_config_outlineoffset(struct isp_res_device *isp_res, u32 offset) |
| { |
| struct device *dev = to_device(isp_res); |
| |
| DPRINTK_ISPRESZ("ispresizer_config_outlineoffset()+\n"); |
| if (offset % 32) |
| return -EINVAL; |
| isp_reg_writel(dev, offset << ISPRSZ_SDR_OUTOFF_OFFSET_SHIFT, |
| OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTOFF); |
| DPRINTK_ISPRESZ("ispresizer_config_outlineoffset()-\n"); |
| return 0; |
| } |
| |
| /** |
| * Configures the memory address to which the output frame is written. |
| * @addr: 32bit memory address aligned on 32byte boundary. |
| **/ |
| int ispresizer_set_outaddr(struct isp_res_device *isp_res, u32 addr) |
| { |
| struct device *dev = to_device(isp_res); |
| |
| DPRINTK_ISPRESZ("ispresizer_set_outaddr()+\n"); |
| if (addr % 32) |
| return -EINVAL; |
| isp_reg_writel(dev, addr << ISPRSZ_SDR_OUTADD_ADDR_SHIFT, |
| OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTADD); |
| DPRINTK_ISPRESZ("ispresizer_set_outaddr()-\n"); |
| return 0; |
| } |
| |
| /** |
| * ispresizer_save_context - Saves the values of the resizer module registers. |
| **/ |
| void ispresizer_save_context(struct device *dev) |
| { |
| DPRINTK_ISPRESZ("Saving context\n"); |
| isp_save_context(dev, isprsz_reg_list); |
| } |
| |
| /** |
| * ispresizer_restore_context - Restores resizer module register values. |
| **/ |
| void ispresizer_restore_context(struct device *dev) |
| { |
| DPRINTK_ISPRESZ("Restoring context\n"); |
| isp_restore_context(dev, isprsz_reg_list); |
| } |
| |
| /** |
| * ispresizer_print_status - Prints the values of the resizer module registers. |
| **/ |
| void ispresizer_print_status(struct isp_res_device *isp_res) |
| { |
| #ifdef OMAP_ISPRESZ_DEBUG |
| struct device *dev = to_device(isp_res); |
| #endif |
| |
| if (!is_ispresz_debug_enabled()) |
| return; |
| DPRINTK_ISPRESZ("###ISP_CTRL inresizer =0x%x\n", |
| isp_reg_readl(dev, |
| OMAP3_ISP_IOMEM_MAIN, ISP_CTRL)); |
| DPRINTK_ISPRESZ("###ISP_IRQ0ENABLE in resizer =0x%x\n", |
| isp_reg_readl(dev, |
| OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0ENABLE)); |
| DPRINTK_ISPRESZ("###ISP_IRQ0STATUS in resizer =0x%x\n", |
| isp_reg_readl(dev, |
| OMAP3_ISP_IOMEM_MAIN, ISP_IRQ0STATUS)); |
| DPRINTK_ISPRESZ("###RSZ PCR =0x%x\n", |
| isp_reg_readl(dev, |
| OMAP3_ISP_IOMEM_RESZ, ISPRSZ_PCR)); |
| DPRINTK_ISPRESZ("###RSZ CNT =0x%x\n", |
| isp_reg_readl(dev, |
| OMAP3_ISP_IOMEM_RESZ, ISPRSZ_CNT)); |
| DPRINTK_ISPRESZ("###RSZ OUT SIZE =0x%x\n", |
| isp_reg_readl(dev, |
| OMAP3_ISP_IOMEM_RESZ, ISPRSZ_OUT_SIZE)); |
| DPRINTK_ISPRESZ("###RSZ IN START =0x%x\n", |
| isp_reg_readl(dev, |
| OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_START)); |
| DPRINTK_ISPRESZ("###RSZ IN SIZE =0x%x\n", |
| isp_reg_readl(dev, |
| OMAP3_ISP_IOMEM_RESZ, ISPRSZ_IN_SIZE)); |
| DPRINTK_ISPRESZ("###RSZ SDR INADD =0x%x\n", |
| isp_reg_readl(dev, |
| OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INADD)); |
| DPRINTK_ISPRESZ("###RSZ SDR INOFF =0x%x\n", |
| isp_reg_readl(dev, |
| OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_INOFF)); |
| DPRINTK_ISPRESZ("###RSZ SDR OUTADD =0x%x\n", |
| isp_reg_readl(dev, |
| OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTADD)); |
| DPRINTK_ISPRESZ("###RSZ SDR OTOFF =0x%x\n", |
| isp_reg_readl(dev, |
| OMAP3_ISP_IOMEM_RESZ, ISPRSZ_SDR_OUTOFF)); |
| DPRINTK_ISPRESZ("###RSZ YENH =0x%x\n", |
| isp_reg_readl(dev, |
| OMAP3_ISP_IOMEM_RESZ, ISPRSZ_YENH)); |
| } |
| |
| /** |
| * isp_resizer_init - Module Initialisation. |
| * |
| * Always returns 0. |
| **/ |
| int __init isp_resizer_init(struct device *dev) |
| { |
| struct isp_device *isp = dev_get_drvdata(dev); |
| struct isp_res_device *isp_res = &isp->isp_res; |
| |
| mutex_init(&isp_res->ispres_mutex); |
| |
| return 0; |
| } |
| |
| /** |
| * isp_resizer_cleanup - Module Cleanup. |
| **/ |
| void isp_resizer_cleanup(struct device *dev) |
| { |
| } |