diff options
Diffstat (limited to 'drivers/mxc/ipu3/ipu_param_mem.h')
-rw-r--r-- | drivers/mxc/ipu3/ipu_param_mem.h | 1002 |
1 files changed, 1002 insertions, 0 deletions
diff --git a/drivers/mxc/ipu3/ipu_param_mem.h b/drivers/mxc/ipu3/ipu_param_mem.h new file mode 100644 index 000000000000..020f75ece50e --- /dev/null +++ b/drivers/mxc/ipu3/ipu_param_mem.h @@ -0,0 +1,1002 @@ +/* + * Copyright 2005-2015 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2019 NXP + */ + +/* + * 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 + */ +#ifndef __INCLUDE_IPU_PARAM_MEM_H__ +#define __INCLUDE_IPU_PARAM_MEM_H__ + +#include <linux/bitrev.h> +#include <linux/types.h> + +#include "ipu_prv.h" + +extern u32 *ipu_cpmem_base; + +struct ipu_ch_param_word { + uint32_t data[5]; + uint32_t res[3]; +}; + +struct ipu_ch_param { + struct ipu_ch_param_word word[2]; +}; + +#define ipu_ch_param_addr(ipu, ch) \ + (((struct ipu_ch_param *)(ipu)->cpmem_base) + (ch)) + +#define _param_word(base, w) \ + (((struct ipu_ch_param *)(base))->word[(w)].data) + +#define ipu_ch_param_set_field(base, w, bit, size, v) { \ + int i = (bit) / 32; \ + int off = (bit) % 32; \ + _param_word((base), (w))[i] |= (v) << off; \ + if (((bit)+(size)-1)/32 > i) { \ + _param_word((base), (w))[i + 1] |= \ + (v) >> (off ? (32 - off) : 0); \ + } \ +} + +#define ipu_ch_param_set_field_io(base, w, bit, size, v) { \ + int i = (bit) / 32; \ + int off = (bit) % 32; \ + unsigned reg_offset; \ + u32 temp; \ + reg_offset = sizeof(struct ipu_ch_param_word) * (w) / 4; \ + reg_offset += i; \ + temp = readl((u32 *)(base) + reg_offset); \ + temp |= (v) << off; \ + writel(temp, (u32 *)(base) + reg_offset); \ + if (((bit)+(size)-1)/32 > i) { \ + reg_offset++; \ + temp = readl((u32 *)(base) + reg_offset); \ + temp |= (v) >> (off ? (32 - off) : 0); \ + writel(temp, (u32 *)(base) + reg_offset); \ + } \ +} + +#define ipu_ch_param_mod_field(base, w, bit, size, v) { \ + int i = (bit) / 32; \ + int off = (bit) % 32; \ + u32 mask = (1UL << (size)) - 1; \ + u32 temp = _param_word((base), (w))[i]; \ + temp &= ~(mask << off); \ + _param_word((base), (w))[i] = temp | (v) << off; \ + if (((bit)+(size)-1)/32 > i) { \ + temp = _param_word((base), (w))[i + 1]; \ + temp &= ~(mask >> (32 - off)); \ + _param_word((base), (w))[i + 1] = \ + temp | ((v) >> (off ? (32 - off) : 0)); \ + } \ +} + +#define ipu_ch_param_mod_field_io(base, w, bit, size, v) { \ + int i = (bit) / 32; \ + int off = (bit) % 32; \ + u32 mask = (1UL << (size)) - 1; \ + unsigned reg_offset; \ + u32 temp; \ + reg_offset = sizeof(struct ipu_ch_param_word) * (w) / 4; \ + reg_offset += i; \ + temp = readl((u32 *)(base) + reg_offset); \ + temp &= ~(mask << off); \ + temp |= (v) << off; \ + writel(temp, (u32 *)(base) + reg_offset); \ + if (((bit)+(size)-1)/32 > i) { \ + reg_offset++; \ + temp = readl((u32 *)(base) + reg_offset); \ + temp &= ~(mask >> (32 - off)); \ + temp |= ((v) >> (off ? (32 - off) : 0)); \ + writel(temp, (u32 *)(base) + reg_offset); \ + } \ +} + +#define ipu_ch_param_read_field(base, w, bit, size) ({ \ + u32 temp2; \ + int i = (bit) / 32; \ + int off = (bit) % 32; \ + u32 mask = (1UL << (size)) - 1; \ + u32 temp1 = _param_word((base), (w))[i]; \ + temp1 = mask & (temp1 >> off); \ + if (((bit)+(size)-1)/32 > i) { \ + temp2 = _param_word((base), (w))[i + 1]; \ + temp2 &= mask >> (off ? (32 - off) : 0); \ + temp1 |= temp2 << (off ? (32 - off) : 0); \ + } \ + temp1; \ +}) + +#define ipu_ch_param_read_field_io(base, w, bit, size) ({ \ + u32 temp1, temp2; \ + int i = (bit) / 32; \ + int off = (bit) % 32; \ + u32 mask = (1UL << (size)) - 1; \ + unsigned reg_offset; \ + reg_offset = sizeof(struct ipu_ch_param_word) * (w) / 4; \ + reg_offset += i; \ + temp1 = readl((u32 *)(base) + reg_offset); \ + temp1 = mask & (temp1 >> off); \ + if (((bit)+(size)-1)/32 > i) { \ + reg_offset++; \ + temp2 = readl((u32 *)(base) + reg_offset); \ + temp2 &= mask >> (off ? (32 - off) : 0); \ + temp1 |= temp2 << (off ? (32 - off) : 0); \ + } \ + temp1; \ +}) + +static inline int __ipu_ch_get_third_buf_cpmem_num(int ch) +{ + switch (ch) { + case 8: + return 64; + case 9: + return 65; + case 10: + return 66; + case 13: + return 67; + case 21: + return 68; + case 23: + return 69; + case 27: + return 70; + case 28: + return 71; + default: + return -EINVAL; + } + return 0; +} + +static inline void _ipu_ch_params_set_packing(struct ipu_ch_param *p, + int red_width, int red_offset, + int green_width, int green_offset, + int blue_width, int blue_offset, + int alpha_width, int alpha_offset) +{ + /* Setup red width and offset */ + ipu_ch_param_set_field(p, 1, 116, 3, red_width - 1); + ipu_ch_param_set_field(p, 1, 128, 5, red_offset); + /* Setup green width and offset */ + ipu_ch_param_set_field(p, 1, 119, 3, green_width - 1); + ipu_ch_param_set_field(p, 1, 133, 5, green_offset); + /* Setup blue width and offset */ + ipu_ch_param_set_field(p, 1, 122, 3, blue_width - 1); + ipu_ch_param_set_field(p, 1, 138, 5, blue_offset); + /* Setup alpha width and offset */ + ipu_ch_param_set_field(p, 1, 125, 3, alpha_width - 1); + ipu_ch_param_set_field(p, 1, 143, 5, alpha_offset); +} + +static inline void _ipu_ch_param_dump(struct ipu_soc *ipu, int ch) +{ + struct ipu_ch_param *p = ipu_ch_param_addr(ipu, ch); + dev_dbg(ipu->dev, "ch %d word 0 - %08X %08X %08X %08X %08X\n", ch, + p->word[0].data[0], p->word[0].data[1], p->word[0].data[2], + p->word[0].data[3], p->word[0].data[4]); + dev_dbg(ipu->dev, "ch %d word 1 - %08X %08X %08X %08X %08X\n", ch, + p->word[1].data[0], p->word[1].data[1], p->word[1].data[2], + p->word[1].data[3], p->word[1].data[4]); + dev_dbg(ipu->dev, "PFS 0x%x, ", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 85, 4)); + dev_dbg(ipu->dev, "BPP 0x%x, ", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 107, 3)); + dev_dbg(ipu->dev, "NPB 0x%x\n", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 78, 7)); + + dev_dbg(ipu->dev, "FW %d, ", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 125, 13)); + dev_dbg(ipu->dev, "FH %d, ", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 138, 12)); + dev_dbg(ipu->dev, "EBA0 0x%x\n", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 0, 29) << 3); + dev_dbg(ipu->dev, "EBA1 0x%x\n", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 29, 29) << 3); + dev_dbg(ipu->dev, "Stride %d\n", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 102, 14)); + dev_dbg(ipu->dev, "scan_order %d\n", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 113, 1)); + dev_dbg(ipu->dev, "uv_stride %d\n", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 128, 14)); + dev_dbg(ipu->dev, "u_offset 0x%x\n", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 46, 22) << 3); + dev_dbg(ipu->dev, "v_offset 0x%x\n", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 68, 22) << 3); + + dev_dbg(ipu->dev, "Width0 %d+1, ", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 116, 3)); + dev_dbg(ipu->dev, "Width1 %d+1, ", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 119, 3)); + dev_dbg(ipu->dev, "Width2 %d+1, ", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 122, 3)); + dev_dbg(ipu->dev, "Width3 %d+1, ", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 125, 3)); + dev_dbg(ipu->dev, "Offset0 %d, ", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 128, 5)); + dev_dbg(ipu->dev, "Offset1 %d, ", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 133, 5)); + dev_dbg(ipu->dev, "Offset2 %d, ", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 138, 5)); + dev_dbg(ipu->dev, "Offset3 %d\n", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 143, 5)); +} + +static inline void fill_cpmem(struct ipu_soc *ipu, int ch, struct ipu_ch_param *params) +{ + int i, w; + void *addr = ipu_ch_param_addr(ipu, ch); + + /* 2 words, 5 valid data */ + for (w = 0; w < 2; w++) { + for (i = 0; i < 5; i++) { + writel(params->word[w].data[i], addr); + addr += 4; + } + addr += 12; + } +} + +static inline void _ipu_ch_param_init(struct ipu_soc *ipu, int ch, + uint32_t pixel_fmt, uint32_t width, + uint32_t height, uint32_t stride, + uint32_t u, uint32_t v, + uint32_t uv_stride, dma_addr_t addr0, + dma_addr_t addr1, dma_addr_t addr2) +{ + uint32_t u_offset = 0; + uint32_t v_offset = 0; + uint32_t bs = 0; + int32_t sub_ch = 0; + struct ipu_ch_param params; + + memset(¶ms, 0, sizeof(params)); + + ipu_ch_param_set_field(¶ms, 0, 125, 13, width - 1); + + if (((ch == 8) || (ch == 9) || (ch == 10)) && !ipu->vdoa_en) { + ipu_ch_param_set_field(¶ms, 0, 138, 12, (height / 2) - 1); + ipu_ch_param_set_field(¶ms, 1, 102, 14, (stride * 2) - 1); + } else { + /* note: for vdoa+vdi- ch8/9/10, always use band mode */ + ipu_ch_param_set_field(¶ms, 0, 138, 12, height - 1); + ipu_ch_param_set_field(¶ms, 1, 102, 14, stride - 1); + } + + /* EBA is 8-byte aligned */ + ipu_ch_param_set_field(¶ms, 1, 0, 29, addr0 >> 3); + ipu_ch_param_set_field(¶ms, 1, 29, 29, addr1 >> 3); + if (addr0%8) + dev_warn(ipu->dev, + "IDMAC%d's EBA0 is not 8-byte aligned\n", ch); + if (addr1%8) + dev_warn(ipu->dev, + "IDMAC%d's EBA1 is not 8-byte aligned\n", ch); + + switch (pixel_fmt) { + case IPU_PIX_FMT_GENERIC: + /*Represents 8-bit Generic data */ + ipu_ch_param_set_field(¶ms, 0, 107, 3, 5); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 6); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 63); /* burst size */ + + break; + case IPU_PIX_FMT_GENERIC_16: + /* Represents 16-bit generic data */ + ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 6); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + + break; + case IPU_PIX_FMT_GENERIC_32: + /*Represents 32-bit Generic data */ + break; + case IPU_PIX_FMT_RGB565: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + + _ipu_ch_params_set_packing(¶ms, 5, 0, 6, 5, 5, 11, 8, 16); + break; + case IPU_PIX_FMT_BGRA4444: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + + _ipu_ch_params_set_packing(¶ms, 4, 4, 4, 8, 4, 12, 4, 0); + break; + case IPU_PIX_FMT_BGRA5551: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + + _ipu_ch_params_set_packing(¶ms, 5, 1, 5, 6, 5, 11, 1, 0); + break; + case IPU_PIX_FMT_BGR24: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 1); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 19); /* burst size */ + + _ipu_ch_params_set_packing(¶ms, 8, 0, 8, 8, 8, 16, 8, 24); + break; + case IPU_PIX_FMT_RGB24: + case IPU_PIX_FMT_YUV444: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 1); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 19); /* burst size */ + + _ipu_ch_params_set_packing(¶ms, 8, 16, 8, 8, 8, 0, 8, 24); + break; + case IPU_PIX_FMT_VYU444: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 1); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 19); /* burst size */ + + _ipu_ch_params_set_packing(¶ms, 8, 8, 8, 0, 8, 16, 8, 24); + break; + case IPU_PIX_FMT_AYUV: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 0); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ + + _ipu_ch_params_set_packing(¶ms, 8, 8, 8, 16, 8, 24, 8, 0); + break; + case IPU_PIX_FMT_BGRA32: + case IPU_PIX_FMT_BGR32: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 0); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ + + _ipu_ch_params_set_packing(¶ms, 8, 8, 8, 16, 8, 24, 8, 0); + break; + case IPU_PIX_FMT_RGBA32: + case IPU_PIX_FMT_RGB32: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 0); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ + + _ipu_ch_params_set_packing(¶ms, 8, 24, 8, 16, 8, 8, 8, 0); + break; + case IPU_PIX_FMT_ABGR32: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 0); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 7); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ + + _ipu_ch_params_set_packing(¶ms, 8, 0, 8, 8, 8, 16, 8, 24); + break; + case IPU_PIX_FMT_UYVY: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 0xA); /* pix format */ + if ((ch == 8) || (ch == 9) || (ch == 10)) { + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ + } else { + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + } + break; + case IPU_PIX_FMT_YUYV: + ipu_ch_param_set_field(¶ms, 0, 107, 3, 3); /* bits/pixel */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 0x8); /* pix format */ + if ((ch == 8) || (ch == 9) || (ch == 10)) { + if (ipu->vdoa_en) { + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); + } else { + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); + } + } else { + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + } + break; + case IPU_PIX_FMT_YUV420P2: + case IPU_PIX_FMT_YUV420P: + ipu_ch_param_set_field(¶ms, 1, 85, 4, 2); /* pix format */ + + if (uv_stride < stride / 2) + uv_stride = stride / 2; + + u_offset = stride * height; + v_offset = u_offset + (uv_stride * height / 2); + if ((ch == 8) || (ch == 9) || (ch == 10)) { + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ + uv_stride = uv_stride*2; + } else { + if (_ipu_is_smfc_chan(ch) && + ipu->smfc_idmac_12bit_3planar_bs_fixup) + bs = 15; + else + bs = 31; + ipu_ch_param_set_field(¶ms, 1, 78, 7, bs); /* burst size */ + } + break; + case IPU_PIX_FMT_YVU420P: + ipu_ch_param_set_field(¶ms, 1, 85, 4, 2); /* pix format */ + + if (uv_stride < stride / 2) + uv_stride = stride / 2; + + v_offset = stride * height; + u_offset = v_offset + (uv_stride * height / 2); + if ((ch == 8) || (ch == 9) || (ch == 10)) { + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); /* burst size */ + uv_stride = uv_stride*2; + } else { + if (_ipu_is_smfc_chan(ch) && + ipu->smfc_idmac_12bit_3planar_bs_fixup) + bs = 15; + else + bs = 31; + ipu_ch_param_set_field(¶ms, 1, 78, 7, bs); /* burst size */ + } + break; + case IPU_PIX_FMT_YVU422P: + /* BPP & pixel format */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 1); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + + if (uv_stride < stride / 2) + uv_stride = stride / 2; + + v_offset = (v == 0) ? stride * height : v; + u_offset = (u == 0) ? v_offset + v_offset / 2 : u; + break; + case IPU_PIX_FMT_YUV422P: + /* BPP & pixel format */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 1); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + + if (uv_stride < stride / 2) + uv_stride = stride / 2; + + u_offset = (u == 0) ? stride * height : u; + v_offset = (v == 0) ? u_offset + u_offset / 2 : v; + break; + case IPU_PIX_FMT_YUV444P: + /* BPP & pixel format */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 0); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + uv_stride = stride; + u_offset = (u == 0) ? stride * height : u; + v_offset = (v == 0) ? u_offset * 2 : v; + break; + case IPU_PIX_FMT_NV16: + ipu_ch_param_set_field(¶ms, 1, 85, 4, 1); /* pix format */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + uv_stride = stride; + u_offset = (u == 0) ? stride * height : u; + break; + case IPU_PIX_FMT_NV12: + /* BPP & pixel format */ + ipu_ch_param_set_field(¶ms, 1, 85, 4, 4); /* pix format */ + uv_stride = stride; + u_offset = (u == 0) ? stride * height : u; + if ((ch == 8) || (ch == 9) || (ch == 10)) { + if (ipu->vdoa_en) { + /* one field buffer, memory width 64bits */ + ipu_ch_param_set_field(¶ms, 1, 78, 7, 63); + } else { + ipu_ch_param_set_field(¶ms, 1, 78, 7, 15); + /* top/bottom field in one buffer*/ + uv_stride = uv_stride*2; + } + } else { + ipu_ch_param_set_field(¶ms, 1, 78, 7, 31); /* burst size */ + } + break; + default: + dev_err(ipu->dev, "mxc ipu: unimplemented pixel format\n"); + break; + } + /*set burst size to 16*/ + + + if (uv_stride) + ipu_ch_param_set_field(¶ms, 1, 128, 14, uv_stride - 1); + + /* Get the uv offset from user when need cropping */ + if (u || v) { + u_offset = u; + v_offset = v; + } + + /* UBO and VBO are 22-bit and 8-byte aligned */ + if (u_offset/8 > 0x3fffff) + dev_warn(ipu->dev, + "IDMAC%d's U offset exceeds IPU limitation\n", ch); + if (v_offset/8 > 0x3fffff) + dev_warn(ipu->dev, + "IDMAC%d's V offset exceeds IPU limitation\n", ch); + if (u_offset%8) + dev_warn(ipu->dev, + "IDMAC%d's U offset is not 8-byte aligned\n", ch); + if (v_offset%8) + dev_warn(ipu->dev, + "IDMAC%d's V offset is not 8-byte aligned\n", ch); + + ipu_ch_param_set_field(¶ms, 0, 46, 22, u_offset / 8); + ipu_ch_param_set_field(¶ms, 0, 68, 22, v_offset / 8); + + dev_dbg(ipu->dev, "initializing idma ch %d @ %p\n", ch, ipu_ch_param_addr(ipu, ch)); + fill_cpmem(ipu, ch, ¶ms); + if (addr2) { + sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch); + if (sub_ch <= 0) + return; + + ipu_ch_param_set_field(¶ms, 1, 0, 29, addr2 >> 3); + ipu_ch_param_set_field(¶ms, 1, 29, 29, 0); + if (addr2%8) + dev_warn(ipu->dev, + "IDMAC%d's sub-CPMEM entry%d EBA0 is not " + "8-byte aligned\n", ch, sub_ch); + + dev_dbg(ipu->dev, "initializing idma ch %d @ %p sub cpmem\n", ch, + ipu_ch_param_addr(ipu, sub_ch)); + fill_cpmem(ipu, sub_ch, ¶ms); + } +}; + +static inline void _ipu_ch_param_set_burst_size(struct ipu_soc *ipu, + uint32_t ch, + uint16_t burst_pixels) +{ + int32_t sub_ch = 0; + + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 78, 7, + burst_pixels - 1); + + sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch); + if (sub_ch <= 0) + return; + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 78, 7, + burst_pixels - 1); +}; + +static inline int _ipu_ch_param_get_burst_size(struct ipu_soc *ipu, uint32_t ch) +{ + return ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 78, 7) + 1; +}; + +static inline int _ipu_ch_param_get_bpp(struct ipu_soc *ipu, uint32_t ch) +{ + return ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 107, 3); +}; + +static inline void _ipu_ch_param_set_buffer(struct ipu_soc *ipu, uint32_t ch, + int bufNum, dma_addr_t phyaddr) +{ + if (bufNum == 2) { + ch = __ipu_ch_get_third_buf_cpmem_num(ch); + if (ch <= 0) + return; + bufNum = 0; + } + + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 29 * bufNum, 29, + phyaddr / 8); +}; + +static inline void _ipu_ch_param_set_rotation(struct ipu_soc *ipu, uint32_t ch, + ipu_rotate_mode_t rot) +{ + u32 temp_rot = bitrev8(rot) >> 5; + int32_t sub_ch = 0; + + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 0, 119, 3, temp_rot); + + sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch); + if (sub_ch <= 0) + return; + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 119, 3, temp_rot); +}; + +static inline void _ipu_ch_param_set_block_mode(struct ipu_soc *ipu, uint32_t ch) +{ + int32_t sub_ch = 0; + + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 0, 117, 2, 1); + + sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch); + if (sub_ch <= 0) + return; + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 117, 2, 1); +}; + +static inline void _ipu_ch_param_set_alpha_use_separate_channel(struct ipu_soc *ipu, + uint32_t ch, + bool option) +{ + int32_t sub_ch = 0; + + if (option) { + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 89, 1, 1); + } else { + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 89, 1, 0); + } + + sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch); + if (sub_ch <= 0) + return; + + if (option) { + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 89, 1, 1); + } else { + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 89, 1, 0); + } +}; + +static inline void _ipu_ch_param_set_alpha_condition_read(struct ipu_soc *ipu, uint32_t ch) +{ + int32_t sub_ch = 0; + + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 149, 1, 1); + + sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch); + if (sub_ch <= 0) + return; + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 149, 1, 1); +}; + +static inline void _ipu_ch_param_set_alpha_buffer_memory(struct ipu_soc *ipu, uint32_t ch) +{ + int alp_mem_idx; + int32_t sub_ch = 0; + + switch (ch) { + case 14: /* PRP graphic */ + alp_mem_idx = 0; + break; + case 15: /* PP graphic */ + alp_mem_idx = 1; + break; + case 23: /* DP BG SYNC graphic */ + alp_mem_idx = 4; + break; + case 27: /* DP FG SYNC graphic */ + alp_mem_idx = 2; + break; + default: + dev_err(ipu->dev, "unsupported correlative channel of local " + "alpha channel\n"); + return; + } + + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 90, 3, alp_mem_idx); + + sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch); + if (sub_ch <= 0) + return; + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 90, 3, alp_mem_idx); +}; + +static inline void _ipu_ch_param_set_interlaced_scan(struct ipu_soc *ipu, uint32_t ch) +{ + u32 stride; + int32_t sub_ch = 0; + + sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch); + + ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, ch), 0, 113, 1, 1); + if (sub_ch > 0) + ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 113, 1, 1); + stride = ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 102, 14) + 1; + /* ILO is 20-bit and 8-byte aligned */ + if (stride/8 > 0xfffff) + dev_warn(ipu->dev, + "IDMAC%d's ILO exceeds IPU limitation\n", ch); + if (stride%8) + dev_warn(ipu->dev, + "IDMAC%d's ILO is not 8-byte aligned\n", ch); + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 58, 20, stride / 8); + if (sub_ch > 0) + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 58, 20, + stride / 8); + stride *= 2; + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 102, 14, stride - 1); + if (sub_ch > 0) + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 102, 14, + stride - 1); +}; + +static inline void _ipu_ch_param_set_axi_id(struct ipu_soc *ipu, uint32_t ch, uint32_t id) +{ + int32_t sub_ch = 0; + + id %= 4; + + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 1, 93, 2, id); + + sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch); + if (sub_ch <= 0) + return; + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 93, 2, id); +}; + +static inline int _ipu_ch_param_get_axi_id(struct ipu_soc *ipu, uint32_t ch) +{ + return ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 1, 93, 2); +} + +static inline int __ipu_ch_offset_calc(uint32_t pixel_fmt, + uint32_t width, + uint32_t height, + uint32_t stride, + uint32_t u, + uint32_t v, + uint32_t uv_stride, + uint32_t vertical_offset, + uint32_t horizontal_offset, + uint32_t *u_offset, + uint32_t *v_offset) +{ + uint32_t u_fix = 0; + uint32_t v_fix = 0; + + switch (pixel_fmt) { + case IPU_PIX_FMT_GENERIC: + case IPU_PIX_FMT_GENERIC_16: + case IPU_PIX_FMT_GENERIC_32: + case IPU_PIX_FMT_RGB565: + case IPU_PIX_FMT_BGRA4444: + case IPU_PIX_FMT_BGRA5551: + case IPU_PIX_FMT_BGR24: + case IPU_PIX_FMT_RGB24: + case IPU_PIX_FMT_YUV444: + case IPU_PIX_FMT_AYUV: + case IPU_PIX_FMT_BGRA32: + case IPU_PIX_FMT_BGR32: + case IPU_PIX_FMT_RGBA32: + case IPU_PIX_FMT_RGB32: + case IPU_PIX_FMT_ABGR32: + case IPU_PIX_FMT_UYVY: + case IPU_PIX_FMT_YUYV: + case IPU_PIX_FMT_GPU32_SB_ST: + case IPU_PIX_FMT_GPU32_SB_SRT: + case IPU_PIX_FMT_GPU32_ST: + case IPU_PIX_FMT_GPU32_SRT: + case IPU_PIX_FMT_GPU16_SB_ST: + case IPU_PIX_FMT_GPU16_SB_SRT: + case IPU_PIX_FMT_GPU16_ST: + case IPU_PIX_FMT_GPU16_SRT: + *u_offset = 0; + *v_offset = 0; + break; + case IPU_PIX_FMT_YUV420P2: + case IPU_PIX_FMT_YUV420P: + if (uv_stride < stride / 2) + uv_stride = stride / 2; + + *u_offset = stride * (height - vertical_offset - 1) + + (stride - horizontal_offset) + + (uv_stride * vertical_offset / 2) + + horizontal_offset / 2; + *v_offset = *u_offset + (uv_stride * height / 2); + u_fix = u ? (u + (uv_stride * vertical_offset / 2) + + (horizontal_offset / 2) - + (stride * vertical_offset) - (horizontal_offset)) : + *u_offset; + v_fix = v ? (v + (uv_stride * vertical_offset / 2) + + (horizontal_offset / 2) - + (stride * vertical_offset) - (horizontal_offset)) : + *v_offset; + break; + case IPU_PIX_FMT_YVU420P: + if (uv_stride < stride / 2) + uv_stride = stride / 2; + + *v_offset = stride * (height - vertical_offset - 1) + + (stride - horizontal_offset) + + (uv_stride * vertical_offset / 2) + + horizontal_offset / 2; + *u_offset = *v_offset + (uv_stride * height / 2); + u_fix = u ? (u + (uv_stride * vertical_offset / 2) + + (horizontal_offset / 2) - + (stride * vertical_offset) - (horizontal_offset)) : + *u_offset; + v_fix = v ? (v + (uv_stride * vertical_offset / 2) + + (horizontal_offset / 2) - + (stride * vertical_offset) - (horizontal_offset)) : + *v_offset; + break; + case IPU_PIX_FMT_YVU422P: + if (uv_stride < stride / 2) + uv_stride = stride / 2; + + *v_offset = stride * (height - vertical_offset - 1) + + (stride - horizontal_offset) + + (uv_stride * vertical_offset) + + horizontal_offset / 2; + *u_offset = *v_offset + uv_stride * height; + u_fix = u ? (u + (uv_stride * vertical_offset) + + horizontal_offset / 2 - + (stride * vertical_offset) - (horizontal_offset)) : + *u_offset; + v_fix = v ? (v + (uv_stride * vertical_offset) + + horizontal_offset / 2 - + (stride * vertical_offset) - (horizontal_offset)) : + *v_offset; + break; + case IPU_PIX_FMT_YUV422P: + if (uv_stride < stride / 2) + uv_stride = stride / 2; + + *u_offset = stride * (height - vertical_offset - 1) + + (stride - horizontal_offset) + + (uv_stride * vertical_offset) + + horizontal_offset / 2; + *v_offset = *u_offset + uv_stride * height; + u_fix = u ? (u + (uv_stride * vertical_offset) + + horizontal_offset / 2 - + (stride * vertical_offset) - (horizontal_offset)) : + *u_offset; + v_fix = v ? (v + (uv_stride * vertical_offset) + + horizontal_offset / 2 - + (stride * vertical_offset) - (horizontal_offset)) : + *v_offset; + break; + case IPU_PIX_FMT_YUV444P: + uv_stride = stride; + *u_offset = stride * (height - vertical_offset - 1) + + (stride - horizontal_offset) + + (uv_stride * vertical_offset) + + horizontal_offset; + *v_offset = *u_offset + uv_stride * height; + u_fix = u ? (u + (uv_stride * vertical_offset) + + horizontal_offset - + (stride * vertical_offset) - + (horizontal_offset)) : + *u_offset; + v_fix = v ? (v + (uv_stride * vertical_offset) + + horizontal_offset - + (stride * vertical_offset) - + (horizontal_offset)) : + *v_offset; + break; + case IPU_PIX_FMT_NV12: + case IPU_PIX_FMT_NV16: + case PRE_PIX_FMT_NV21: + case PRE_PIX_FMT_NV61: + uv_stride = stride; + *u_offset = stride * (height - vertical_offset - 1) + + (stride - horizontal_offset) + + (uv_stride * vertical_offset / 2) + + horizontal_offset; + *v_offset = 0; + u_fix = u ? (u + (uv_stride * vertical_offset / 2) + + horizontal_offset - + (stride * vertical_offset) - (horizontal_offset)) : + *u_offset; + break; + default: + return -EINVAL; + } + + if (u_fix > *u_offset) + *u_offset = u_fix; + + if (v_fix > *v_offset) + *v_offset = v_fix; + + return 0; +} + +/* IDMAC U/V offset changing support */ +/* U and V input is not affected, */ +/* the update is done by new calculation according to */ +/* vertical_offset and horizontal_offset */ +static inline void _ipu_ch_offset_update(struct ipu_soc *ipu, + int ch, + uint32_t pixel_fmt, + uint32_t width, + uint32_t height, + uint32_t stride, + uint32_t u, + uint32_t v, + uint32_t uv_stride, + uint32_t vertical_offset, + uint32_t horizontal_offset) +{ + uint32_t u_offset = 0; + uint32_t v_offset = 0; + uint32_t old_offset = 0; + int32_t sub_ch = 0; + int ret; + + ret = __ipu_ch_offset_calc(pixel_fmt, width, height, stride, + u, v, uv_stride, + vertical_offset, horizontal_offset, + &u_offset, &v_offset); + if (ret) { + dev_err(ipu->dev, "mxc ipu: unimplemented pixel format\n"); + return; + } + + /* UBO and VBO are 22-bit and 8-byte aligned */ + if (u_offset/8 > 0x3fffff) + dev_warn(ipu->dev, + "IDMAC%d's U offset exceeds IPU limitation\n", ch); + if (v_offset/8 > 0x3fffff) + dev_warn(ipu->dev, + "IDMAC%d's V offset exceeds IPU limitation\n", ch); + if (u_offset%8) + dev_warn(ipu->dev, + "IDMAC%d's U offset is not 8-byte aligned\n", ch); + if (v_offset%8) + dev_warn(ipu->dev, + "IDMAC%d's V offset is not 8-byte aligned\n", ch); + + old_offset = ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 46, 22); + if (old_offset != u_offset / 8) + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 0, 46, 22, u_offset / 8); + old_offset = ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 68, 22); + if (old_offset != v_offset / 8) + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, ch), 0, 68, 22, v_offset / 8); + + sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch); + if (sub_ch <= 0) + return; + old_offset = ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 46, 22); + if (old_offset != u_offset / 8) + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 46, 22, u_offset / 8); + old_offset = ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 68, 22); + if (old_offset != v_offset / 8) + ipu_ch_param_mod_field_io(ipu_ch_param_addr(ipu, sub_ch), 0, 68, 22, v_offset / 8); +}; + +static inline void _ipu_ch_params_set_alpha_width(struct ipu_soc *ipu, uint32_t ch, int alpha_width) +{ + int32_t sub_ch = 0; + + ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, ch), 1, 125, 3, alpha_width - 1); + + sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch); + if (sub_ch <= 0) + return; + ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, sub_ch), 1, 125, 3, alpha_width - 1); +}; + +static inline void _ipu_ch_param_set_bandmode(struct ipu_soc *ipu, + uint32_t ch, uint32_t band_height) +{ + int32_t sub_ch = 0; + + ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, ch), + 0, 114, 3, band_height - 1); + sub_ch = __ipu_ch_get_third_buf_cpmem_num(ch); + if (sub_ch <= 0) + return; + ipu_ch_param_set_field_io(ipu_ch_param_addr(ipu, sub_ch), + 0, 114, 3, band_height - 1); + + dev_dbg(ipu->dev, "BNDM 0x%x, ", + ipu_ch_param_read_field_io(ipu_ch_param_addr(ipu, ch), 0, 114, 3)); +} + +/* + * The IPUv3 IDMAC has a bug to read 32bpp pixels from a graphics plane + * whose alpha component is at the most significant 8 bits. The bug only + * impacts on cases in which the relevant separate alpha channel is enabled. + * + * Return true on bad alpha component position, otherwise, return false. + */ +static inline bool _ipu_ch_param_bad_alpha_pos(uint32_t pixel_fmt) +{ + switch (pixel_fmt) { + case IPU_PIX_FMT_BGRA32: + case IPU_PIX_FMT_BGR32: + case IPU_PIX_FMT_RGBA32: + case IPU_PIX_FMT_RGB32: + return true; + } + + return false; +} +#endif |