blob: 29519bd00d12bc8cea55f98bcced1ccb2ed4ee37 [file] [log] [blame]
/*
* Copyright 2005-2015 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
/*
* @file ipu_ic.c
*
* @brief IPU IC functions
*
* @ingroup IPU
*/
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/ipu-v3.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/videodev2.h>
#include "ipu_param_mem.h"
#include "ipu_regs.h"
enum {
IC_TASK_VIEWFINDER,
IC_TASK_ENCODER,
IC_TASK_POST_PROCESSOR
};
static void _init_csc(struct ipu_soc *ipu, uint8_t ic_task, ipu_color_space_t in_format,
ipu_color_space_t out_format, int csc_index);
static int _calc_resize_coeffs(struct ipu_soc *ipu,
uint32_t inSize, uint32_t outSize,
uint32_t *resizeCoeff,
uint32_t *downsizeCoeff);
void _ipu_vdi_set_top_field_man(struct ipu_soc *ipu, bool top_field_0)
{
uint32_t reg;
reg = ipu_vdi_read(ipu, VDI_C);
if (top_field_0)
reg &= ~VDI_C_TOP_FIELD_MAN_1;
else
reg |= VDI_C_TOP_FIELD_MAN_1;
ipu_vdi_write(ipu, reg, VDI_C);
}
void _ipu_vdi_set_motion(struct ipu_soc *ipu, ipu_motion_sel motion_sel)
{
uint32_t reg;
reg = ipu_vdi_read(ipu, VDI_C);
reg &= ~(VDI_C_MOT_SEL_FULL | VDI_C_MOT_SEL_MED | VDI_C_MOT_SEL_LOW);
if (motion_sel == HIGH_MOTION)
reg |= VDI_C_MOT_SEL_FULL;
else if (motion_sel == MED_MOTION)
reg |= VDI_C_MOT_SEL_MED;
else
reg |= VDI_C_MOT_SEL_LOW;
ipu_vdi_write(ipu, reg, VDI_C);
dev_dbg(ipu->dev, "VDI_C = \t0x%08X\n", reg);
}
void ic_dump_register(struct ipu_soc *ipu)
{
printk(KERN_DEBUG "IC_CONF = \t0x%08X\n", ipu_ic_read(ipu, IC_CONF));
printk(KERN_DEBUG "IC_PRP_ENC_RSC = \t0x%08X\n",
ipu_ic_read(ipu, IC_PRP_ENC_RSC));
printk(KERN_DEBUG "IC_PRP_VF_RSC = \t0x%08X\n",
ipu_ic_read(ipu, IC_PRP_VF_RSC));
printk(KERN_DEBUG "IC_PP_RSC = \t0x%08X\n", ipu_ic_read(ipu, IC_PP_RSC));
printk(KERN_DEBUG "IC_IDMAC_1 = \t0x%08X\n", ipu_ic_read(ipu, IC_IDMAC_1));
printk(KERN_DEBUG "IC_IDMAC_2 = \t0x%08X\n", ipu_ic_read(ipu, IC_IDMAC_2));
printk(KERN_DEBUG "IC_IDMAC_3 = \t0x%08X\n", ipu_ic_read(ipu, IC_IDMAC_3));
}
void _ipu_ic_enable_task(struct ipu_soc *ipu, ipu_channel_t channel)
{
uint32_t ic_conf;
ic_conf = ipu_ic_read(ipu, IC_CONF);
switch (channel) {
case CSI_PRP_VF_MEM:
case MEM_PRP_VF_MEM:
ic_conf |= IC_CONF_PRPVF_EN;
break;
case MEM_VDI_PRP_VF_MEM:
ic_conf |= IC_CONF_PRPVF_EN;
break;
case MEM_VDI_MEM:
ic_conf |= IC_CONF_PRPVF_EN | IC_CONF_RWS_EN ;
break;
case MEM_ROT_VF_MEM:
ic_conf |= IC_CONF_PRPVF_ROT_EN;
break;
case CSI_PRP_ENC_MEM:
case MEM_PRP_ENC_MEM:
ic_conf |= IC_CONF_PRPENC_EN;
break;
case MEM_ROT_ENC_MEM:
ic_conf |= IC_CONF_PRPENC_ROT_EN;
break;
case MEM_PP_MEM:
ic_conf |= IC_CONF_PP_EN;
break;
case MEM_ROT_PP_MEM:
ic_conf |= IC_CONF_PP_ROT_EN;
break;
default:
break;
}
ipu_ic_write(ipu, ic_conf, IC_CONF);
}
void _ipu_ic_disable_task(struct ipu_soc *ipu, ipu_channel_t channel)
{
uint32_t ic_conf;
ic_conf = ipu_ic_read(ipu, IC_CONF);
switch (channel) {
case CSI_PRP_VF_MEM:
case MEM_PRP_VF_MEM:
ic_conf &= ~IC_CONF_PRPVF_EN;
break;
case MEM_VDI_PRP_VF_MEM:
ic_conf &= ~IC_CONF_PRPVF_EN;
break;
case MEM_VDI_MEM:
ic_conf &= ~(IC_CONF_PRPVF_EN | IC_CONF_RWS_EN);
break;
case MEM_ROT_VF_MEM:
ic_conf &= ~IC_CONF_PRPVF_ROT_EN;
break;
case CSI_PRP_ENC_MEM:
case MEM_PRP_ENC_MEM:
ic_conf &= ~IC_CONF_PRPENC_EN;
break;
case MEM_ROT_ENC_MEM:
ic_conf &= ~IC_CONF_PRPENC_ROT_EN;
break;
case MEM_PP_MEM:
ic_conf &= ~IC_CONF_PP_EN;
break;
case MEM_ROT_PP_MEM:
ic_conf &= ~IC_CONF_PP_ROT_EN;
break;
default:
break;
}
ipu_ic_write(ipu, ic_conf, IC_CONF);
}
void _ipu_vdi_init(struct ipu_soc *ipu, ipu_channel_t channel, ipu_channel_params_t *params)
{
uint32_t reg;
uint32_t pixel_fmt;
uint32_t pix_per_burst;
reg = ((params->mem_prp_vf_mem.in_height-1) << 16) |
(params->mem_prp_vf_mem.in_width-1);
ipu_vdi_write(ipu, reg, VDI_FSIZE);
/* Full motion, only vertical filter is used
Burst size is 4 accesses */
if (params->mem_prp_vf_mem.in_pixel_fmt ==
IPU_PIX_FMT_UYVY ||
params->mem_prp_vf_mem.in_pixel_fmt ==
IPU_PIX_FMT_YUYV) {
pixel_fmt = VDI_C_CH_422;
pix_per_burst = 32;
} else {
pixel_fmt = VDI_C_CH_420;
pix_per_burst = 64;
}
reg = ipu_vdi_read(ipu, VDI_C);
reg |= pixel_fmt;
switch (channel) {
case MEM_VDI_PRP_VF_MEM:
reg |= VDI_C_BURST_SIZE2_4;
break;
case MEM_VDI_PRP_VF_MEM_P:
reg |= VDI_C_BURST_SIZE1_4 | VDI_C_VWM1_SET_1 | VDI_C_VWM1_CLR_2;
break;
case MEM_VDI_PRP_VF_MEM_N:
reg |= VDI_C_BURST_SIZE3_4 | VDI_C_VWM3_SET_1 | VDI_C_VWM3_CLR_2;
break;
case MEM_VDI_MEM:
reg |= (((pix_per_burst >> 2) - 1) & VDI_C_BURST_SIZE_MASK)
<< VDI_C_BURST_SIZE2_OFFSET;
break;
case MEM_VDI_MEM_P:
reg |= (((pix_per_burst >> 2) - 1) & VDI_C_BURST_SIZE_MASK)
<< VDI_C_BURST_SIZE1_OFFSET;
reg |= VDI_C_VWM1_SET_2 | VDI_C_VWM1_CLR_2;
break;
case MEM_VDI_MEM_N:
reg |= (((pix_per_burst >> 2) - 1) & VDI_C_BURST_SIZE_MASK)
<< VDI_C_BURST_SIZE3_OFFSET;
reg |= VDI_C_VWM3_SET_2 | VDI_C_VWM3_CLR_2;
break;
default:
break;
}
ipu_vdi_write(ipu, reg, VDI_C);
if (params->mem_prp_vf_mem.field_fmt == IPU_DEINTERLACE_FIELD_TOP)
_ipu_vdi_set_top_field_man(ipu, true);
else if (params->mem_prp_vf_mem.field_fmt == IPU_DEINTERLACE_FIELD_BOTTOM)
_ipu_vdi_set_top_field_man(ipu, false);
_ipu_vdi_set_motion(ipu, params->mem_prp_vf_mem.motion_sel);
reg = ipu_ic_read(ipu, IC_CONF);
reg &= ~IC_CONF_RWS_EN;
ipu_ic_write(ipu, reg, IC_CONF);
}
void _ipu_vdi_uninit(struct ipu_soc *ipu)
{
ipu_vdi_write(ipu, 0, VDI_FSIZE);
ipu_vdi_write(ipu, 0, VDI_C);
}
int _ipu_ic_init_prpvf(struct ipu_soc *ipu, ipu_channel_params_t *params,
bool src_is_csi)
{
uint32_t reg, ic_conf;
uint32_t downsizeCoeff, resizeCoeff;
ipu_color_space_t in_fmt, out_fmt;
int ret = 0;
/* Setup vertical resizing */
if (!params->mem_prp_vf_mem.outv_resize_ratio) {
ret = _calc_resize_coeffs(ipu, params->mem_prp_vf_mem.in_height,
params->mem_prp_vf_mem.out_height,
&resizeCoeff, &downsizeCoeff);
if (ret < 0) {
dev_err(ipu->dev, "failed to calculate prpvf height "
"scaling coefficients\n");
return ret;
}
reg = (downsizeCoeff << 30) | (resizeCoeff << 16);
} else
reg = (params->mem_prp_vf_mem.outv_resize_ratio) << 16;
/* Setup horizontal resizing */
if (!params->mem_prp_vf_mem.outh_resize_ratio) {
ret = _calc_resize_coeffs(ipu, params->mem_prp_vf_mem.in_width,
params->mem_prp_vf_mem.out_width,
&resizeCoeff, &downsizeCoeff);
if (ret < 0) {
dev_err(ipu->dev, "failed to calculate prpvf width "
"scaling coefficients\n");
return ret;
}
reg |= (downsizeCoeff << 14) | resizeCoeff;
} else
reg |= params->mem_prp_vf_mem.outh_resize_ratio;
ipu_ic_write(ipu, reg, IC_PRP_VF_RSC);
ic_conf = ipu_ic_read(ipu, IC_CONF);
/* Setup color space conversion */
in_fmt = format_to_colorspace(params->mem_prp_vf_mem.in_pixel_fmt);
out_fmt = format_to_colorspace(params->mem_prp_vf_mem.out_pixel_fmt);
if (in_fmt == RGB) {
if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
/* Enable RGB->YCBCR CSC1 */
_init_csc(ipu, IC_TASK_VIEWFINDER, RGB, out_fmt, 1);
ic_conf |= IC_CONF_PRPVF_CSC1;
}
}
if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
if (out_fmt == RGB) {
/* Enable YCBCR->RGB CSC1 */
_init_csc(ipu, IC_TASK_VIEWFINDER, YCbCr, RGB, 1);
ic_conf |= IC_CONF_PRPVF_CSC1;
} else {
/* TODO: Support YUV<->YCbCr conversion? */
}
}
if (params->mem_prp_vf_mem.graphics_combine_en) {
ic_conf |= IC_CONF_PRPVF_CMB;
if (!(ic_conf & IC_CONF_PRPVF_CSC1)) {
/* need transparent CSC1 conversion */
_init_csc(ipu, IC_TASK_VIEWFINDER, RGB, RGB, 1);
ic_conf |= IC_CONF_PRPVF_CSC1; /* Enable RGB->RGB CSC */
}
in_fmt = format_to_colorspace(params->mem_prp_vf_mem.in_g_pixel_fmt);
out_fmt = format_to_colorspace(params->mem_prp_vf_mem.out_pixel_fmt);
if (in_fmt == RGB) {
if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
/* Enable RGB->YCBCR CSC2 */
_init_csc(ipu, IC_TASK_VIEWFINDER, RGB, out_fmt, 2);
ic_conf |= IC_CONF_PRPVF_CSC2;
}
}
if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
if (out_fmt == RGB) {
/* Enable YCBCR->RGB CSC2 */
_init_csc(ipu, IC_TASK_VIEWFINDER, YCbCr, RGB, 2);
ic_conf |= IC_CONF_PRPVF_CSC2;
} else {
/* TODO: Support YUV<->YCbCr conversion? */
}
}
if (params->mem_prp_vf_mem.global_alpha_en) {
ic_conf |= IC_CONF_IC_GLB_LOC_A;
reg = ipu_ic_read(ipu, IC_CMBP_1);
reg &= ~(0xff);
reg |= params->mem_prp_vf_mem.alpha;
ipu_ic_write(ipu, reg, IC_CMBP_1);
} else
ic_conf &= ~IC_CONF_IC_GLB_LOC_A;
if (params->mem_prp_vf_mem.key_color_en) {
ic_conf |= IC_CONF_KEY_COLOR_EN;
ipu_ic_write(ipu, params->mem_prp_vf_mem.key_color,
IC_CMBP_2);
} else
ic_conf &= ~IC_CONF_KEY_COLOR_EN;
} else {
ic_conf &= ~IC_CONF_PRPVF_CMB;
}
if (src_is_csi)
ic_conf &= ~IC_CONF_RWS_EN;
else
ic_conf |= IC_CONF_RWS_EN;
ipu_ic_write(ipu, ic_conf, IC_CONF);
return ret;
}
void _ipu_ic_uninit_prpvf(struct ipu_soc *ipu)
{
uint32_t reg;
reg = ipu_ic_read(ipu, IC_CONF);
reg &= ~(IC_CONF_PRPVF_EN | IC_CONF_PRPVF_CMB |
IC_CONF_PRPVF_CSC2 | IC_CONF_PRPVF_CSC1);
ipu_ic_write(ipu, reg, IC_CONF);
}
void _ipu_ic_init_rotate_vf(struct ipu_soc *ipu, ipu_channel_params_t *params)
{
}
void _ipu_ic_uninit_rotate_vf(struct ipu_soc *ipu)
{
uint32_t reg;
reg = ipu_ic_read(ipu, IC_CONF);
reg &= ~IC_CONF_PRPVF_ROT_EN;
ipu_ic_write(ipu, reg, IC_CONF);
}
int _ipu_ic_init_prpenc(struct ipu_soc *ipu, ipu_channel_params_t *params,
bool src_is_csi)
{
uint32_t reg, ic_conf;
uint32_t downsizeCoeff, resizeCoeff;
ipu_color_space_t in_fmt, out_fmt;
int ret = 0;
/* Setup vertical resizing */
if (!params->mem_prp_enc_mem.outv_resize_ratio) {
ret = _calc_resize_coeffs(ipu,
params->mem_prp_enc_mem.in_height,
params->mem_prp_enc_mem.out_height,
&resizeCoeff, &downsizeCoeff);
if (ret < 0) {
dev_err(ipu->dev, "failed to calculate prpenc height "
"scaling coefficients\n");
return ret;
}
reg = (downsizeCoeff << 30) | (resizeCoeff << 16);
} else
reg = (params->mem_prp_enc_mem.outv_resize_ratio) << 16;
/* Setup horizontal resizing */
if (!params->mem_prp_enc_mem.outh_resize_ratio) {
ret = _calc_resize_coeffs(ipu, params->mem_prp_enc_mem.in_width,
params->mem_prp_enc_mem.out_width,
&resizeCoeff, &downsizeCoeff);
if (ret < 0) {
dev_err(ipu->dev, "failed to calculate prpenc width "
"scaling coefficients\n");
return ret;
}
reg |= (downsizeCoeff << 14) | resizeCoeff;
} else
reg |= params->mem_prp_enc_mem.outh_resize_ratio;
ipu_ic_write(ipu, reg, IC_PRP_ENC_RSC);
ic_conf = ipu_ic_read(ipu, IC_CONF);
/* Setup color space conversion */
in_fmt = format_to_colorspace(params->mem_prp_enc_mem.in_pixel_fmt);
out_fmt = format_to_colorspace(params->mem_prp_enc_mem.out_pixel_fmt);
if (in_fmt == RGB) {
if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
/* Enable RGB->YCBCR CSC1 */
_init_csc(ipu, IC_TASK_ENCODER, RGB, out_fmt, 1);
ic_conf |= IC_CONF_PRPENC_CSC1;
}
}
if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
if (out_fmt == RGB) {
/* Enable YCBCR->RGB CSC1 */
_init_csc(ipu, IC_TASK_ENCODER, YCbCr, RGB, 1);
ic_conf |= IC_CONF_PRPENC_CSC1;
} else {
/* TODO: Support YUV<->YCbCr conversion? */
}
}
if (src_is_csi)
ic_conf &= ~IC_CONF_RWS_EN;
else
ic_conf |= IC_CONF_RWS_EN;
ipu_ic_write(ipu, ic_conf, IC_CONF);
return ret;
}
void _ipu_ic_uninit_prpenc(struct ipu_soc *ipu)
{
uint32_t reg;
reg = ipu_ic_read(ipu, IC_CONF);
reg &= ~(IC_CONF_PRPENC_EN | IC_CONF_PRPENC_CSC1);
ipu_ic_write(ipu, reg, IC_CONF);
}
void _ipu_ic_init_rotate_enc(struct ipu_soc *ipu, ipu_channel_params_t *params)
{
}
void _ipu_ic_uninit_rotate_enc(struct ipu_soc *ipu)
{
uint32_t reg;
reg = ipu_ic_read(ipu, IC_CONF);
reg &= ~(IC_CONF_PRPENC_ROT_EN);
ipu_ic_write(ipu, reg, IC_CONF);
}
int _ipu_ic_init_pp(struct ipu_soc *ipu, ipu_channel_params_t *params)
{
uint32_t reg, ic_conf;
uint32_t downsizeCoeff, resizeCoeff;
ipu_color_space_t in_fmt, out_fmt;
int ret = 0;
/* Setup vertical resizing */
if (!params->mem_pp_mem.outv_resize_ratio) {
ret = _calc_resize_coeffs(ipu, params->mem_pp_mem.in_height,
params->mem_pp_mem.out_height,
&resizeCoeff, &downsizeCoeff);
if (ret < 0) {
dev_err(ipu->dev, "failed to calculate pp height "
"scaling coefficients\n");
return ret;
}
reg = (downsizeCoeff << 30) | (resizeCoeff << 16);
} else {
reg = (params->mem_pp_mem.outv_resize_ratio) << 16;
}
/* Setup horizontal resizing */
if (!params->mem_pp_mem.outh_resize_ratio) {
ret = _calc_resize_coeffs(ipu, params->mem_pp_mem.in_width,
params->mem_pp_mem.out_width,
&resizeCoeff, &downsizeCoeff);
if (ret < 0) {
dev_err(ipu->dev, "failed to calculate pp width "
"scaling coefficients\n");
return ret;
}
reg |= (downsizeCoeff << 14) | resizeCoeff;
} else {
reg |= params->mem_pp_mem.outh_resize_ratio;
}
ipu_ic_write(ipu, reg, IC_PP_RSC);
ic_conf = ipu_ic_read(ipu, IC_CONF);
/* Setup color space conversion */
in_fmt = format_to_colorspace(params->mem_pp_mem.in_pixel_fmt);
out_fmt = format_to_colorspace(params->mem_pp_mem.out_pixel_fmt);
if (in_fmt == RGB) {
if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
/* Enable RGB->YCBCR CSC1 */
_init_csc(ipu, IC_TASK_POST_PROCESSOR, RGB, out_fmt, 1);
ic_conf |= IC_CONF_PP_CSC1;
}
}
if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
if (out_fmt == RGB) {
/* Enable YCBCR->RGB CSC1 */
_init_csc(ipu, IC_TASK_POST_PROCESSOR, YCbCr, RGB, 1);
ic_conf |= IC_CONF_PP_CSC1;
} else {
/* TODO: Support YUV<->YCbCr conversion? */
}
}
if (params->mem_pp_mem.graphics_combine_en) {
ic_conf |= IC_CONF_PP_CMB;
if (!(ic_conf & IC_CONF_PP_CSC1)) {
/* need transparent CSC1 conversion */
_init_csc(ipu, IC_TASK_POST_PROCESSOR, RGB, RGB, 1);
ic_conf |= IC_CONF_PP_CSC1; /* Enable RGB->RGB CSC */
}
in_fmt = format_to_colorspace(params->mem_pp_mem.in_g_pixel_fmt);
out_fmt = format_to_colorspace(params->mem_pp_mem.out_pixel_fmt);
if (in_fmt == RGB) {
if ((out_fmt == YCbCr) || (out_fmt == YUV)) {
/* Enable RGB->YCBCR CSC2 */
_init_csc(ipu, IC_TASK_POST_PROCESSOR, RGB, out_fmt, 2);
ic_conf |= IC_CONF_PP_CSC2;
}
}
if ((in_fmt == YCbCr) || (in_fmt == YUV)) {
if (out_fmt == RGB) {
/* Enable YCBCR->RGB CSC2 */
_init_csc(ipu, IC_TASK_POST_PROCESSOR, YCbCr, RGB, 2);
ic_conf |= IC_CONF_PP_CSC2;
} else {
/* TODO: Support YUV<->YCbCr conversion? */
}
}
if (params->mem_pp_mem.global_alpha_en) {
ic_conf |= IC_CONF_IC_GLB_LOC_A;
reg = ipu_ic_read(ipu, IC_CMBP_1);
reg &= ~(0xff00);
reg |= (params->mem_pp_mem.alpha << 8);
ipu_ic_write(ipu, reg, IC_CMBP_1);
} else
ic_conf &= ~IC_CONF_IC_GLB_LOC_A;
if (params->mem_pp_mem.key_color_en) {
ic_conf |= IC_CONF_KEY_COLOR_EN;
ipu_ic_write(ipu, params->mem_pp_mem.key_color,
IC_CMBP_2);
} else
ic_conf &= ~IC_CONF_KEY_COLOR_EN;
} else {
ic_conf &= ~IC_CONF_PP_CMB;
}
ipu_ic_write(ipu, ic_conf, IC_CONF);
return ret;
}
void _ipu_ic_uninit_pp(struct ipu_soc *ipu)
{
uint32_t reg;
reg = ipu_ic_read(ipu, IC_CONF);
reg &= ~(IC_CONF_PP_EN | IC_CONF_PP_CSC1 | IC_CONF_PP_CSC2 |
IC_CONF_PP_CMB);
ipu_ic_write(ipu, reg, IC_CONF);
}
void _ipu_ic_init_rotate_pp(struct ipu_soc *ipu, ipu_channel_params_t *params)
{
}
void _ipu_ic_uninit_rotate_pp(struct ipu_soc *ipu)
{
uint32_t reg;
reg = ipu_ic_read(ipu, IC_CONF);
reg &= ~IC_CONF_PP_ROT_EN;
ipu_ic_write(ipu, reg, IC_CONF);
}
int _ipu_ic_idma_init(struct ipu_soc *ipu, int dma_chan,
uint16_t width, uint16_t height,
int burst_size, ipu_rotate_mode_t rot)
{
u32 ic_idmac_1, ic_idmac_2, ic_idmac_3;
u32 temp_rot = bitrev8(rot) >> 5;
bool need_hor_flip = false;
if ((burst_size != 8) && (burst_size != 16)) {
dev_dbg(ipu->dev, "Illegal burst length for IC\n");
return -EINVAL;
}
width--;
height--;
if (temp_rot & 0x2) /* Need horizontal flip */
need_hor_flip = true;
ic_idmac_1 = ipu_ic_read(ipu, IC_IDMAC_1);
ic_idmac_2 = ipu_ic_read(ipu, IC_IDMAC_2);
ic_idmac_3 = ipu_ic_read(ipu, IC_IDMAC_3);
if (dma_chan == 22) { /* PP output - CB2 */
if (burst_size == 16)
ic_idmac_1 |= IC_IDMAC_1_CB2_BURST_16;
else
ic_idmac_1 &= ~IC_IDMAC_1_CB2_BURST_16;
if (need_hor_flip)
ic_idmac_1 |= IC_IDMAC_1_PP_FLIP_RS;
else
ic_idmac_1 &= ~IC_IDMAC_1_PP_FLIP_RS;
ic_idmac_2 &= ~IC_IDMAC_2_PP_HEIGHT_MASK;
ic_idmac_2 |= height << IC_IDMAC_2_PP_HEIGHT_OFFSET;
ic_idmac_3 &= ~IC_IDMAC_3_PP_WIDTH_MASK;
ic_idmac_3 |= width << IC_IDMAC_3_PP_WIDTH_OFFSET;
} else if (dma_chan == 11) { /* PP Input - CB5 */
if (burst_size == 16)
ic_idmac_1 |= IC_IDMAC_1_CB5_BURST_16;
else
ic_idmac_1 &= ~IC_IDMAC_1_CB5_BURST_16;
} else if (dma_chan == 47) { /* PP Rot input */
ic_idmac_1 &= ~IC_IDMAC_1_PP_ROT_MASK;
ic_idmac_1 |= temp_rot << IC_IDMAC_1_PP_ROT_OFFSET;
}
if (dma_chan == 12) { /* PRP Input - CB6 */
if (burst_size == 16)
ic_idmac_1 |= IC_IDMAC_1_CB6_BURST_16;
else
ic_idmac_1 &= ~IC_IDMAC_1_CB6_BURST_16;
}
if (dma_chan == 20) { /* PRP ENC output - CB0 */
if (burst_size == 16)
ic_idmac_1 |= IC_IDMAC_1_CB0_BURST_16;
else
ic_idmac_1 &= ~IC_IDMAC_1_CB0_BURST_16;
if (need_hor_flip)
ic_idmac_1 |= IC_IDMAC_1_PRPENC_FLIP_RS;
else
ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_FLIP_RS;
ic_idmac_2 &= ~IC_IDMAC_2_PRPENC_HEIGHT_MASK;
ic_idmac_2 |= height << IC_IDMAC_2_PRPENC_HEIGHT_OFFSET;
ic_idmac_3 &= ~IC_IDMAC_3_PRPENC_WIDTH_MASK;
ic_idmac_3 |= width << IC_IDMAC_3_PRPENC_WIDTH_OFFSET;
} else if (dma_chan == 45) { /* PRP ENC Rot input */
ic_idmac_1 &= ~IC_IDMAC_1_PRPENC_ROT_MASK;
ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPENC_ROT_OFFSET;
}
if (dma_chan == 21) { /* PRP VF output - CB1 */
if (burst_size == 16)
ic_idmac_1 |= IC_IDMAC_1_CB1_BURST_16;
else
ic_idmac_1 &= ~IC_IDMAC_1_CB1_BURST_16;
if (need_hor_flip)
ic_idmac_1 |= IC_IDMAC_1_PRPVF_FLIP_RS;
else
ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_FLIP_RS;
ic_idmac_2 &= ~IC_IDMAC_2_PRPVF_HEIGHT_MASK;
ic_idmac_2 |= height << IC_IDMAC_2_PRPVF_HEIGHT_OFFSET;
ic_idmac_3 &= ~IC_IDMAC_3_PRPVF_WIDTH_MASK;
ic_idmac_3 |= width << IC_IDMAC_3_PRPVF_WIDTH_OFFSET;
} else if (dma_chan == 46) { /* PRP VF Rot input */
ic_idmac_1 &= ~IC_IDMAC_1_PRPVF_ROT_MASK;
ic_idmac_1 |= temp_rot << IC_IDMAC_1_PRPVF_ROT_OFFSET;
}
if (dma_chan == 14) { /* PRP VF graphics combining input - CB3 */
if (burst_size == 16)
ic_idmac_1 |= IC_IDMAC_1_CB3_BURST_16;
else
ic_idmac_1 &= ~IC_IDMAC_1_CB3_BURST_16;
} else if (dma_chan == 15) { /* PP graphics combining input - CB4 */
if (burst_size == 16)
ic_idmac_1 |= IC_IDMAC_1_CB4_BURST_16;
else
ic_idmac_1 &= ~IC_IDMAC_1_CB4_BURST_16;
} else if (dma_chan == 5) { /* VDIC OUTPUT - CB7 */
if (burst_size == 16)
ic_idmac_1 |= IC_IDMAC_1_CB7_BURST_16;
else
ic_idmac_1 &= ~IC_IDMAC_1_CB7_BURST_16;
}
ipu_ic_write(ipu, ic_idmac_1, IC_IDMAC_1);
ipu_ic_write(ipu, ic_idmac_2, IC_IDMAC_2);
ipu_ic_write(ipu, ic_idmac_3, IC_IDMAC_3);
return 0;
}
static void _init_csc(struct ipu_soc *ipu, uint8_t ic_task, ipu_color_space_t in_format,
ipu_color_space_t out_format, int csc_index)
{
/*
* Y = 0.257 * R + 0.504 * G + 0.098 * B + 16;
* U = -0.148 * R - 0.291 * G + 0.439 * B + 128;
* V = 0.439 * R - 0.368 * G - 0.071 * B + 128;
*/
static const uint32_t rgb2ycbcr_coeff[4][3] = {
{0x0042, 0x0081, 0x0019},
{0x01DA, 0x01B6, 0x0070},
{0x0070, 0x01A2, 0x01EE},
{0x0040, 0x0200, 0x0200}, /* A0, A1, A2 */
};
/* transparent RGB->RGB matrix for combining
*/
static const uint32_t rgb2rgb_coeff[4][3] = {
{0x0080, 0x0000, 0x0000},
{0x0000, 0x0080, 0x0000},
{0x0000, 0x0000, 0x0080},
{0x0000, 0x0000, 0x0000}, /* A0, A1, A2 */
};
/* R = (1.164 * (Y - 16)) + (1.596 * (Cr - 128));
G = (1.164 * (Y - 16)) - (0.392 * (Cb - 128)) - (0.813 * (Cr - 128));
B = (1.164 * (Y - 16)) + (2.017 * (Cb - 128); */
static const uint32_t ycbcr2rgb_coeff[4][3] = {
{149, 0, 204},
{149, 462, 408},
{149, 255, 0},
{8192 - 446, 266, 8192 - 554}, /* A0, A1, A2 */
};
uint32_t param;
uint32_t *base = NULL;
if (ic_task == IC_TASK_ENCODER) {
base = (uint32_t *)ipu->tpmem_base + 0x2008 / 4;
} else if (ic_task == IC_TASK_VIEWFINDER) {
if (csc_index == 1)
base = (uint32_t *)ipu->tpmem_base + 0x4028 / 4;
else
base = (uint32_t *)ipu->tpmem_base + 0x4040 / 4;
} else if (ic_task == IC_TASK_POST_PROCESSOR) {
if (csc_index == 1)
base = (uint32_t *)ipu->tpmem_base + 0x6060 / 4;
else
base = (uint32_t *)ipu->tpmem_base + 0x6078 / 4;
} else {
BUG();
}
if ((in_format == YCbCr) && (out_format == RGB)) {
/* Init CSC (YCbCr->RGB) */
param = (ycbcr2rgb_coeff[3][0] << 27) |
(ycbcr2rgb_coeff[0][0] << 18) |
(ycbcr2rgb_coeff[1][1] << 9) | ycbcr2rgb_coeff[2][2];
writel(param, base++);
/* scale = 2, sat = 0 */
param = (ycbcr2rgb_coeff[3][0] >> 5) | (2L << (40 - 32));
writel(param, base++);
param = (ycbcr2rgb_coeff[3][1] << 27) |
(ycbcr2rgb_coeff[0][1] << 18) |
(ycbcr2rgb_coeff[1][0] << 9) | ycbcr2rgb_coeff[2][0];
writel(param, base++);
param = (ycbcr2rgb_coeff[3][1] >> 5);
writel(param, base++);
param = (ycbcr2rgb_coeff[3][2] << 27) |
(ycbcr2rgb_coeff[0][2] << 18) |
(ycbcr2rgb_coeff[1][2] << 9) | ycbcr2rgb_coeff[2][1];
writel(param, base++);
param = (ycbcr2rgb_coeff[3][2] >> 5);
writel(param, base++);
} else if ((in_format == RGB) && (out_format == YCbCr)) {
/* Init CSC (RGB->YCbCr) */
param = (rgb2ycbcr_coeff[3][0] << 27) |
(rgb2ycbcr_coeff[0][0] << 18) |
(rgb2ycbcr_coeff[1][1] << 9) | rgb2ycbcr_coeff[2][2];
writel(param, base++);
/* scale = 1, sat = 0 */
param = (rgb2ycbcr_coeff[3][0] >> 5) | (1UL << 8);
writel(param, base++);
param = (rgb2ycbcr_coeff[3][1] << 27) |
(rgb2ycbcr_coeff[0][1] << 18) |
(rgb2ycbcr_coeff[1][0] << 9) | rgb2ycbcr_coeff[2][0];
writel(param, base++);
param = (rgb2ycbcr_coeff[3][1] >> 5);
writel(param, base++);
param = (rgb2ycbcr_coeff[3][2] << 27) |
(rgb2ycbcr_coeff[0][2] << 18) |
(rgb2ycbcr_coeff[1][2] << 9) | rgb2ycbcr_coeff[2][1];
writel(param, base++);
param = (rgb2ycbcr_coeff[3][2] >> 5);
writel(param, base++);
} else if ((in_format == RGB) && (out_format == RGB)) {
/* Init CSC */
param =
(rgb2rgb_coeff[3][0] << 27) | (rgb2rgb_coeff[0][0] << 18) |
(rgb2rgb_coeff[1][1] << 9) | rgb2rgb_coeff[2][2];
writel(param, base++);
/* scale = 2, sat = 0 */
param = (rgb2rgb_coeff[3][0] >> 5) | (2UL << 8);
writel(param, base++);
param =
(rgb2rgb_coeff[3][1] << 27) | (rgb2rgb_coeff[0][1] << 18) |
(rgb2rgb_coeff[1][0] << 9) | rgb2rgb_coeff[2][0];
writel(param, base++);
param = (rgb2rgb_coeff[3][1] >> 5);
writel(param, base++);
param =
(rgb2rgb_coeff[3][2] << 27) | (rgb2rgb_coeff[0][2] << 18) |
(rgb2rgb_coeff[1][2] << 9) | rgb2rgb_coeff[2][1];
writel(param, base++);
param = (rgb2rgb_coeff[3][2] >> 5);
writel(param, base++);
} else {
dev_err(ipu->dev, "Unsupported color space conversion\n");
}
}
static int _calc_resize_coeffs(struct ipu_soc *ipu,
uint32_t inSize, uint32_t outSize,
uint32_t *resizeCoeff,
uint32_t *downsizeCoeff)
{
uint32_t tempSize;
uint32_t tempDownsize;
if (inSize > 4096) {
dev_err(ipu->dev, "IC input size(%d) cannot exceed 4096\n",
inSize);
return -EINVAL;
}
if (outSize > 1024) {
dev_err(ipu->dev, "IC output size(%d) cannot exceed 1024\n",
outSize);
return -EINVAL;
}
if ((outSize << 3) < inSize) {
dev_err(ipu->dev, "IC cannot downsize more than 8:1\n");
return -EINVAL;
}
/* Compute downsizing coefficient */
/* Output of downsizing unit cannot be more than 1024 */
tempDownsize = 0;
tempSize = inSize;
while (((tempSize > 1024) || (tempSize >= outSize * 2)) &&
(tempDownsize < 2)) {
tempSize >>= 1;
tempDownsize++;
}
*downsizeCoeff = tempDownsize;
/* compute resizing coefficient using the following equation:
resizeCoeff = M*(SI -1)/(SO - 1)
where M = 2^13, SI - input size, SO - output size */
*resizeCoeff = (8192L * (tempSize - 1)) / (outSize - 1);
if (*resizeCoeff >= 16384L) {
dev_err(ipu->dev, "Overflow on IC resize coefficient.\n");
return -EINVAL;
}
dev_dbg(ipu->dev, "resizing from %u -> %u pixels, "
"downsize=%u, resize=%u.%lu (reg=%u)\n", inSize, outSize,
*downsizeCoeff, (*resizeCoeff >= 8192L) ? 1 : 0,
((*resizeCoeff & 0x1FFF) * 10000L) / 8192L, *resizeCoeff);
return 0;
}
void _ipu_vdi_toggle_top_field_man(struct ipu_soc *ipu)
{
uint32_t reg;
uint32_t mask_reg;
reg = ipu_vdi_read(ipu, VDI_C);
mask_reg = reg & VDI_C_TOP_FIELD_MAN_1;
if (mask_reg == VDI_C_TOP_FIELD_MAN_1)
reg &= ~VDI_C_TOP_FIELD_MAN_1;
else
reg |= VDI_C_TOP_FIELD_MAN_1;
ipu_vdi_write(ipu, reg, VDI_C);
}