| /* |
| * Copyright (C) 2017 NXP |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License as published by the |
| * Free Software Foundation; either version 2 of the License, or (at your |
| * option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, but |
| * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
| * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| * for more details. |
| */ |
| #include <linux/device.h> |
| #include <linux/bitops.h> |
| #include <linux/platform_device.h> |
| #include <linux/io.h> |
| #include <drm/drm_fourcc.h> |
| |
| #include <video/imx-dcss.h> |
| #include "dcss-prv.h" |
| |
| #define USE_CTXLD |
| |
| #define DCSS_SCALER_CTRL 0x00 |
| #define SCALER_EN BIT(0) |
| #define REPEAT_EN BIT(4) |
| #define SCALE2MEM_EN BIT(8) |
| #define MEM2OFIFO_EN BIT(12) |
| #define DCSS_SCALER_OFIFO_CTRL 0x04 |
| #define OFIFO_LOW_THRES_POS 0 |
| #define OFIFO_LOW_THRES_MASK GENMASK(9, 0) |
| #define OFIFO_HIGH_THRES_POS 16 |
| #define OFIFO_HIGH_THRES_MASK GENMASK(25, 16) |
| #define UNDERRUN_DETECT_CLR BIT(26) |
| #define LOW_THRES_DETECT_CLR BIT(27) |
| #define HIGH_THRES_DETECT_CLR BIT(28) |
| #define UNDERRUN_DETECT_EN BIT(29) |
| #define LOW_THRES_DETECT_EN BIT(30) |
| #define HIGH_THRES_DETECT_EN BIT(31) |
| #define DCSS_SCALER_SDATA_CTRL 0x08 |
| #define YUV_EN BIT(0) |
| #define RTRAM_8LINES BIT(1) |
| #define Y_UV_BYTE_SWAP BIT(4) |
| #define A2R10G10B10_FORMAT_POS 8 |
| #define A2R10G10B10_FORMAT_MASK GENMASK(11, 8) |
| #define DCSS_SCALER_BIT_DEPTH 0x0C |
| #define LUM_BIT_DEPTH_POS 0 |
| #define LUM_BIT_DEPTH_MASK GENMASK(1, 0) |
| #define CHR_BIT_DEPTH_POS 4 |
| #define CHR_BIT_DEPTH_MASK GENMASK(5, 4) |
| #define DCSS_SCALER_SRC_FORMAT 0x10 |
| #define DCSS_SCALER_DST_FORMAT 0x14 |
| #define FORMAT_MASK GENMASK(1, 0) |
| #define DCSS_SCALER_SRC_LUM_RES 0x18 |
| #define DCSS_SCALER_SRC_CHR_RES 0x1C |
| #define DCSS_SCALER_DST_LUM_RES 0x20 |
| #define DCSS_SCALER_DST_CHR_RES 0x24 |
| #define WIDTH_POS 0 |
| #define WIDTH_MASK GENMASK(11, 0) |
| #define HEIGHT_POS 16 |
| #define HEIGHT_MASK GENMASK(27, 16) |
| #define DCSS_SCALER_V_LUM_START 0x48 |
| #define V_START_MASK GENMASK(15, 0) |
| #define DCSS_SCALER_V_LUM_INC 0x4C |
| #define V_INC_MASK GENMASK(15, 0) |
| #define DCSS_SCALER_H_LUM_START 0x50 |
| #define H_START_MASK GENMASK(18, 0) |
| #define DCSS_SCALER_H_LUM_INC 0x54 |
| #define H_INC_MASK GENMASK(15, 0) |
| #define DCSS_SCALER_V_CHR_START 0x58 |
| #define DCSS_SCALER_V_CHR_INC 0x5C |
| #define DCSS_SCALER_H_CHR_START 0x60 |
| #define DCSS_SCALER_H_CHR_INC 0x64 |
| #define DCSS_SCALER_COEF_VLUM 0x80 |
| #define DCSS_SCALER_COEF_HLUM 0x140 |
| #define DCSS_SCALER_COEF_VCHR 0x200 |
| #define DCSS_SCALER_COEF_HCHR 0x300 |
| |
| static struct dcss_debug_reg scaler_debug_reg[] = { |
| DCSS_DBG_REG(DCSS_SCALER_CTRL), |
| DCSS_DBG_REG(DCSS_SCALER_OFIFO_CTRL), |
| DCSS_DBG_REG(DCSS_SCALER_SDATA_CTRL), |
| DCSS_DBG_REG(DCSS_SCALER_BIT_DEPTH), |
| DCSS_DBG_REG(DCSS_SCALER_SRC_FORMAT), |
| DCSS_DBG_REG(DCSS_SCALER_DST_FORMAT), |
| DCSS_DBG_REG(DCSS_SCALER_SRC_LUM_RES), |
| DCSS_DBG_REG(DCSS_SCALER_SRC_CHR_RES), |
| DCSS_DBG_REG(DCSS_SCALER_DST_LUM_RES), |
| DCSS_DBG_REG(DCSS_SCALER_DST_CHR_RES), |
| DCSS_DBG_REG(DCSS_SCALER_V_LUM_START), |
| DCSS_DBG_REG(DCSS_SCALER_V_LUM_INC), |
| DCSS_DBG_REG(DCSS_SCALER_H_LUM_START), |
| DCSS_DBG_REG(DCSS_SCALER_H_LUM_INC), |
| DCSS_DBG_REG(DCSS_SCALER_V_CHR_START), |
| DCSS_DBG_REG(DCSS_SCALER_V_CHR_INC), |
| DCSS_DBG_REG(DCSS_SCALER_H_CHR_START), |
| DCSS_DBG_REG(DCSS_SCALER_H_CHR_INC), |
| }; |
| |
| struct dcss_scaler_ch { |
| void __iomem *base_reg; |
| u32 base_ofs; |
| |
| u32 ctx_id; |
| |
| u32 sdata_ctrl; |
| u32 scaler_ctrl; |
| |
| u32 pix_format; |
| }; |
| |
| struct dcss_scaler_priv { |
| struct dcss_soc *dcss; |
| struct dcss_scaler_ch ch[3]; |
| |
| int ch_using_wrscl; |
| }; |
| |
| static void dcss_scaler_write(struct dcss_scaler_priv *scl, int ch_num, |
| u32 val, u32 ofs) |
| { |
| #if !defined(USE_CTXLD) |
| dcss_writel(val, scl->ch[ch_num].base_reg + ofs); |
| #else |
| dcss_ctxld_write(scl->dcss, scl->ch[ch_num].ctx_id, |
| val, scl->ch[ch_num].base_ofs + ofs); |
| #endif |
| } |
| |
| #ifdef CONFIG_DEBUG_FS |
| void dcss_scaler_dump_regs(struct seq_file *s, void *data) |
| { |
| struct dcss_soc *dcss = data; |
| int i, j; |
| |
| for (i = 0; i < 3; i++) { |
| seq_printf(s, ">> Dumping SCALER CH %d:\n", i); |
| for (j = 0; j < ARRAY_SIZE(scaler_debug_reg); j++) { |
| seq_printf(s, "%-35s(0x%04x) -> 0x%08x\n", |
| scaler_debug_reg[j].name, |
| scaler_debug_reg[j].ofs, |
| dcss_readl(dcss->scaler_priv->ch[i].base_reg + |
| scaler_debug_reg[j].ofs)); |
| } |
| } |
| } |
| #endif |
| |
| static int dcss_scaler_ch_init_all(struct dcss_soc *dcss, |
| unsigned long scaler_base) |
| { |
| struct dcss_scaler_priv *scaler = dcss->scaler_priv; |
| struct dcss_scaler_ch *ch; |
| int i; |
| |
| for (i = 0; i < 3; i++) { |
| ch = &scaler->ch[i]; |
| |
| ch->base_ofs = scaler_base + i * 0x400; |
| |
| ch->base_reg = devm_ioremap(dcss->dev, ch->base_ofs, SZ_4K); |
| if (!ch->base_reg) { |
| dev_err(dcss->dev, "scaler: unable to remap ch base\n"); |
| return -ENOMEM; |
| } |
| |
| #if defined(USE_CTXLD) |
| ch->ctx_id = CTX_SB_HP; |
| #endif |
| } |
| |
| return 0; |
| } |
| |
| int dcss_scaler_init(struct dcss_soc *dcss, unsigned long scaler_base) |
| { |
| struct dcss_scaler_priv *scaler; |
| |
| scaler = devm_kzalloc(dcss->dev, sizeof(*scaler), GFP_KERNEL); |
| if (!scaler) |
| return -ENOMEM; |
| |
| dcss->scaler_priv = scaler; |
| scaler->dcss = dcss; |
| |
| scaler->ch_using_wrscl = -1; |
| |
| return dcss_scaler_ch_init_all(dcss, scaler_base); |
| } |
| |
| void dcss_scaler_exit(struct dcss_soc *dcss) |
| { |
| struct dcss_scaler_priv *scaler = dcss->scaler_priv; |
| int ch_no; |
| |
| for (ch_no = 0; ch_no < 3; ch_no++) { |
| struct dcss_scaler_ch *ch = &scaler->ch[ch_no]; |
| |
| dcss_writel(0, ch->base_reg + DCSS_SCALER_CTRL); |
| } |
| } |
| |
| void dcss_scaler_enable(struct dcss_soc *dcss, int ch_num, bool en) |
| { |
| struct dcss_scaler_priv *scaler = dcss->scaler_priv; |
| struct dcss_scaler_ch *ch = &scaler->ch[ch_num]; |
| u32 scaler_ctrl; |
| |
| if (scaler->ch_using_wrscl == ch_num) { |
| if (en) { |
| scaler_ctrl = SCALE2MEM_EN | MEM2OFIFO_EN | REPEAT_EN; |
| } else { |
| dcss_wrscl_enable(dcss, false); |
| dcss_rdsrc_enable(dcss, false); |
| |
| scaler->ch_using_wrscl = -1; |
| scaler_ctrl = 0; |
| } |
| } else { |
| scaler_ctrl = en ? SCALER_EN | REPEAT_EN : 0; |
| } |
| |
| if (en) |
| dcss_scaler_write(dcss->scaler_priv, ch_num, ch->sdata_ctrl, |
| DCSS_SCALER_SDATA_CTRL); |
| |
| if (ch->scaler_ctrl != scaler_ctrl) |
| dcss_scaler_write(dcss->scaler_priv, ch_num, scaler_ctrl, |
| DCSS_SCALER_CTRL); |
| |
| ch->scaler_ctrl = scaler_ctrl; |
| } |
| EXPORT_SYMBOL(dcss_scaler_enable); |
| |
| static void dcss_scaler_yuv_enable(struct dcss_soc *dcss, int ch_num, bool en) |
| { |
| struct dcss_scaler_ch *ch = &dcss->scaler_priv->ch[ch_num]; |
| |
| ch->sdata_ctrl &= ~YUV_EN; |
| ch->sdata_ctrl |= en ? YUV_EN : 0; |
| } |
| |
| static void dcss_scaler_rtr_8lines_enable(struct dcss_soc *dcss, int ch_num, |
| bool en) |
| { |
| struct dcss_scaler_ch *ch = &dcss->scaler_priv->ch[ch_num]; |
| |
| ch->sdata_ctrl &= ~RTRAM_8LINES; |
| ch->sdata_ctrl |= en ? RTRAM_8LINES : 0; |
| } |
| |
| static void dcss_scaler_bit_depth_set(struct dcss_soc *dcss, int ch_num, |
| int depth) |
| { |
| u32 val; |
| |
| val = depth == 30 ? 2 : 0; |
| |
| dcss_scaler_write(dcss->scaler_priv, ch_num, |
| ((val << CHR_BIT_DEPTH_POS) & CHR_BIT_DEPTH_MASK) | |
| ((val << LUM_BIT_DEPTH_POS) & LUM_BIT_DEPTH_MASK), |
| DCSS_SCALER_BIT_DEPTH); |
| } |
| |
| enum buffer_format { |
| BUF_FMT_YUV420, |
| BUF_FMT_YUV422, |
| BUF_FMT_ARGB8888_YUV444, |
| }; |
| |
| static void dcss_scaler_format_set(struct dcss_soc *dcss, int ch_num, |
| enum buffer_format src_fmt, |
| enum buffer_format dst_fmt) |
| { |
| dcss_scaler_write(dcss->scaler_priv, ch_num, src_fmt, |
| DCSS_SCALER_SRC_FORMAT); |
| dcss_scaler_write(dcss->scaler_priv, ch_num, dst_fmt, |
| DCSS_SCALER_DST_FORMAT); |
| } |
| |
| static void dcss_scaler_res_set(struct dcss_soc *dcss, int ch_num, |
| int src_xres, int src_yres, |
| int dst_xres, int dst_yres, |
| u32 pix_format) |
| { |
| u32 lsrc_xres, lsrc_yres, csrc_xres, csrc_yres; |
| u32 ldst_xres, ldst_yres, cdst_xres, cdst_yres; |
| |
| lsrc_xres = csrc_xres = src_xres; |
| lsrc_yres = csrc_yres = src_yres; |
| ldst_xres = cdst_xres = dst_xres; |
| ldst_yres = cdst_yres = dst_yres; |
| |
| if (pix_format == DRM_FORMAT_UYVY || pix_format == DRM_FORMAT_VYUY || |
| pix_format == DRM_FORMAT_YUYV || pix_format == DRM_FORMAT_YVYU) |
| csrc_xres >>= 1; |
| else if (pix_format == DRM_FORMAT_NV12 || |
| pix_format == DRM_FORMAT_NV21 || |
| pix_format == DRM_FORMAT_P010) { |
| csrc_xres >>= 1; |
| csrc_yres >>= 1; |
| } |
| |
| dcss_scaler_write(dcss->scaler_priv, ch_num, |
| (((lsrc_yres - 1) << HEIGHT_POS) & HEIGHT_MASK) | |
| (((lsrc_xres - 1) << WIDTH_POS) & WIDTH_MASK), |
| DCSS_SCALER_SRC_LUM_RES); |
| dcss_scaler_write(dcss->scaler_priv, ch_num, |
| (((csrc_yres - 1) << HEIGHT_POS) & HEIGHT_MASK) | |
| (((csrc_xres - 1) << WIDTH_POS) & WIDTH_MASK), |
| DCSS_SCALER_SRC_CHR_RES); |
| dcss_scaler_write(dcss->scaler_priv, ch_num, |
| (((ldst_yres - 1) << HEIGHT_POS) & HEIGHT_MASK) | |
| (((ldst_xres - 1) << WIDTH_POS) & WIDTH_MASK), |
| DCSS_SCALER_DST_LUM_RES); |
| dcss_scaler_write(dcss->scaler_priv, ch_num, |
| (((cdst_yres - 1) << HEIGHT_POS) & HEIGHT_MASK) | |
| (((cdst_xres - 1) << WIDTH_POS) & WIDTH_MASK), |
| DCSS_SCALER_DST_CHR_RES); |
| } |
| |
| #define max_downscale(ratio) ((ratio) << 13) |
| #define max_upscale(ratio) ((1 << 13) / (ratio)) |
| |
| struct dcss_scaler_ratios { |
| u16 downscale; |
| u16 upscale; |
| }; |
| |
| static const struct dcss_scaler_ratios dcss_scaler_ratios[] = { |
| {max_downscale(3), max_upscale(5)}, |
| {max_downscale(5), max_upscale(7)}, |
| {max_downscale(5), max_upscale(7)}, |
| }; |
| |
| static const struct dcss_scaler_ratios dcss_scaler_wrscl_ratios[] = { |
| {max_downscale(5), max_upscale(5)}, |
| {max_downscale(7), max_upscale(7)}, |
| {max_downscale(7), max_upscale(7)}, |
| }; |
| |
| static bool dcss_scaler_fractions_set(struct dcss_soc *dcss, int ch_num, |
| int src_xres, int src_yres, |
| int dst_xres, int dst_yres, |
| u32 pix_format) |
| { |
| u32 l_vinc, l_hinc, c_vinc, c_hinc; |
| |
| l_vinc = ((src_yres << 13) + (dst_yres >> 1)) / dst_yres; |
| c_vinc = ((src_yres << 13) + (dst_yres >> 1)) / dst_yres; |
| l_hinc = ((src_xres << 13) + (dst_xres >> 1)) / dst_xres; |
| c_hinc = ((src_xres << 13) + (dst_xres >> 1)) / dst_xres; |
| |
| if (pix_format == DRM_FORMAT_UYVY || pix_format == DRM_FORMAT_VYUY || |
| pix_format == DRM_FORMAT_YUYV || pix_format == DRM_FORMAT_YVYU) { |
| c_hinc >>= 1; |
| } else if (pix_format == DRM_FORMAT_NV12 || |
| pix_format == DRM_FORMAT_NV21 || |
| pix_format == DRM_FORMAT_P010) { |
| c_hinc >>= 1; |
| c_vinc >>= 1; |
| } |
| |
| dcss_scaler_write(dcss->scaler_priv, ch_num, 0, |
| DCSS_SCALER_V_LUM_START); |
| dcss_scaler_write(dcss->scaler_priv, ch_num, l_vinc, |
| DCSS_SCALER_V_LUM_INC); |
| |
| dcss_scaler_write(dcss->scaler_priv, ch_num, 0, |
| DCSS_SCALER_H_LUM_START); |
| dcss_scaler_write(dcss->scaler_priv, ch_num, l_hinc, |
| DCSS_SCALER_H_LUM_INC); |
| |
| dcss_scaler_write(dcss->scaler_priv, ch_num, 0, |
| DCSS_SCALER_V_CHR_START); |
| dcss_scaler_write(dcss->scaler_priv, ch_num, c_vinc, |
| DCSS_SCALER_V_CHR_INC); |
| |
| dcss_scaler_write(dcss->scaler_priv, ch_num, 0, |
| DCSS_SCALER_H_CHR_START); |
| dcss_scaler_write(dcss->scaler_priv, ch_num, c_hinc, |
| DCSS_SCALER_H_CHR_INC); |
| |
| /* return if WR_SCL is needed to scale */ |
| return l_vinc > dcss_scaler_ratios[ch_num].downscale || |
| l_vinc < dcss_scaler_ratios[ch_num].upscale || |
| l_hinc > dcss_scaler_ratios[ch_num].downscale || |
| l_hinc < dcss_scaler_ratios[ch_num].upscale; |
| } |
| |
| bool dcss_scaler_can_scale(struct dcss_soc *dcss, int ch_num, |
| int src_xres, int src_yres, |
| int dst_xres, int dst_yres) |
| { |
| struct dcss_scaler_priv *scaler = dcss->scaler_priv; |
| u32 vscale_fp, hscale_fp; |
| const struct dcss_scaler_ratios *ratios_map = dcss_scaler_ratios; |
| |
| /* Convert to fixed point. Easier to work with. */ |
| vscale_fp = ((src_yres << 13) + (dst_yres >> 1)) / dst_yres; |
| hscale_fp = ((src_xres << 13) + (dst_xres >> 1)) / dst_xres; |
| |
| if (scaler->ch_using_wrscl == -1 || scaler->ch_using_wrscl == ch_num) |
| ratios_map = dcss_scaler_wrscl_ratios; |
| |
| return vscale_fp <= ratios_map[ch_num].downscale && |
| vscale_fp >= ratios_map[ch_num].upscale && |
| hscale_fp <= ratios_map[ch_num].downscale && |
| hscale_fp >= ratios_map[ch_num].upscale; |
| } |
| EXPORT_SYMBOL(dcss_scaler_can_scale); |
| |
| static void dcss_scaler_coef_clr(struct dcss_soc *dcss, int ch_num) |
| { |
| int i; |
| |
| for (i = 0; i < 48; i++) { |
| dcss_scaler_write(dcss->scaler_priv, ch_num, |
| 0, DCSS_SCALER_COEF_VLUM + i * 4); |
| dcss_scaler_write(dcss->scaler_priv, ch_num, |
| 0, DCSS_SCALER_COEF_HLUM + i * 4); |
| dcss_scaler_write(dcss->scaler_priv, ch_num, |
| 0, DCSS_SCALER_COEF_VCHR + i * 4); |
| dcss_scaler_write(dcss->scaler_priv, ch_num, |
| 0, DCSS_SCALER_COEF_HCHR + i * 4); |
| } |
| } |
| |
| static void dcss_scaler_rgb_coef_set(struct dcss_soc *dcss, int ch_num) |
| { |
| int i; |
| |
| dcss_scaler_coef_clr(dcss, ch_num); |
| |
| for (i = 0; i < 16; i++) { |
| u32 ofs = (16 + i) * sizeof(u32); |
| |
| dcss_scaler_write(dcss->scaler_priv, ch_num, 0x40000, |
| DCSS_SCALER_COEF_VLUM + ofs); |
| dcss_scaler_write(dcss->scaler_priv, ch_num, 0x40000, |
| DCSS_SCALER_COEF_HLUM + ofs); |
| dcss_scaler_write(dcss->scaler_priv, ch_num, 0x40000, |
| DCSS_SCALER_COEF_VCHR + ofs); |
| dcss_scaler_write(dcss->scaler_priv, ch_num, 0x40000, |
| DCSS_SCALER_COEF_HCHR + ofs); |
| } |
| } |
| |
| static u32 dcss_scaler_yuv_coef[] = { |
| 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, |
| 0x00000000, 0x00000000, 0x00000000, 0x00000061, 0x00000041, 0x00000031, |
| 0x00000021, 0x00000010, 0x00000010, 0x00000000, 0x00040300, 0x05532208, |
| 0x0413120a, 0x0312f80d, 0x0242d310, 0x01a2a614, 0x01327117, 0x00d2361b, |
| 0x0091f81f, 0x0b923600, 0x07b27101, 0x0402a601, 0x00a2d302, 0x0da2f803, |
| 0x0af31204, 0x08b32205, 0x00000000, 0x0b000000, 0x0f001000, 0x0a001000, |
| 0x0a002000, 0x00003000, 0x0b004000, 0x09006000, 0x08009000, 0x0d000000, |
| 0x03000000, 0x0a000000, 0x04000000, 0x01000000, 0x01000000, 0x05000000, |
| 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, |
| 0x00000000, 0x00000000, 0x00000000, 0x00000061, 0x00000041, 0x00000031, |
| 0x00000021, 0x00000010, 0x00000010, 0x00000000, 0x00040000, 0x05432008, |
| 0x0403100a, 0x0302f50d, 0x0242d110, 0x01a2a413, 0x01326f17, 0x00d2351b, |
| 0x0091f71f, 0x0b823500, 0x07a26f01, 0x03f2a401, 0x0092d102, 0x0d92f503, |
| 0x0af31004, 0x08b32005, 0x00000000, 0x0b000000, 0x0f001000, 0x09001000, |
| 0x09002000, 0x0f003000, 0x0a004000, 0x08006000, 0x07009000, 0x0d000000, |
| 0x03000000, 0x0a000000, 0x04000000, 0x00000000, 0x00000000, 0x04000000, |
| 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, |
| 0x00000000, 0x00000000, 0x00000000, 0x00000061, 0x00000041, 0x00000031, |
| 0x00000021, 0x00000010, 0x00000010, 0x00000000, 0x00040300, 0x05532208, |
| 0x0413120a, 0x0312f80d, 0x0242d310, 0x01a2a614, 0x01327117, 0x00d2361b, |
| 0x0091f81f, 0x0b923600, 0x07b27101, 0x0402a601, 0x00a2d302, 0x0da2f803, |
| 0x0af31204, 0x08b32205, 0x00000000, 0x0b000000, 0x0f001000, 0x0a001000, |
| 0x0a002000, 0x00003000, 0x0b004000, 0x09006000, 0x08009000, 0x0d000000, |
| 0x03000000, 0x0a000000, 0x04000000, 0x01000000, 0x01000000, 0x05000000, |
| 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, |
| 0x00000000, 0x00000000, 0x00000000, 0x00000061, 0x00000041, 0x00000031, |
| 0x00000021, 0x00000010, 0x00000010, 0x00000000, 0x00040000, 0x05432008, |
| 0x0403100a, 0x0302f50d, 0x0242d110, 0x01a2a413, 0x01326f17, 0x00d2351b, |
| 0x0091f71f, 0x0b823500, 0x07a26f01, 0x03f2a401, 0x0092d102, 0x0d92f503, |
| 0x0af31004, 0x08b32005, 0x00000000, 0x0b000000, 0x0f001000, 0x09001000, |
| 0x09002000, 0x0f003000, 0x0a004000, 0x08006000, 0x07009000, 0x0d000000, |
| 0x03000000, 0x0a000000, 0x04000000, 0x00000000, 0x00000000, 0x04000000, |
| }; |
| |
| static void dcss_scaler_yuv_coef_set(struct dcss_soc *dcss, int ch_num) |
| { |
| int i; |
| |
| for (i = 0; i < 48; i++) { |
| dcss_scaler_write(dcss->scaler_priv, ch_num, |
| dcss_scaler_yuv_coef[i], |
| DCSS_SCALER_COEF_VLUM + i * sizeof(u32)); |
| dcss_scaler_write(dcss->scaler_priv, ch_num, |
| dcss_scaler_yuv_coef[48 + i], |
| DCSS_SCALER_COEF_HLUM + i * sizeof(u32)); |
| dcss_scaler_write(dcss->scaler_priv, ch_num, |
| dcss_scaler_yuv_coef[2 * 48 + i], |
| DCSS_SCALER_COEF_VCHR + i * sizeof(u32)); |
| dcss_scaler_write(dcss->scaler_priv, ch_num, |
| dcss_scaler_yuv_coef[3 * 48 + i], |
| DCSS_SCALER_COEF_HCHR + i * sizeof(u32)); |
| } |
| } |
| |
| static void dcss_scaler_set_rgb10_order(struct dcss_soc *dcss, int ch_num, |
| u32 pix_format) |
| { |
| struct dcss_scaler_ch *ch = &dcss->scaler_priv->ch[ch_num]; |
| enum dcss_color_space dcss_cs; |
| unsigned int pixel_depth; |
| int bpp; |
| u32 a2r10g10b10_format; |
| |
| dcss_cs = dcss_drm_fourcc_to_colorspace(pix_format); |
| |
| if (dcss_cs != DCSS_COLORSPACE_RGB) |
| return; |
| |
| drm_fb_get_bpp_depth(pix_format, &pixel_depth, &bpp); |
| |
| ch->sdata_ctrl &= ~A2R10G10B10_FORMAT_MASK; |
| |
| if (pixel_depth != 30) |
| return; |
| |
| switch (pix_format) { |
| case DRM_FORMAT_ARGB2101010: |
| case DRM_FORMAT_XRGB2101010: |
| a2r10g10b10_format = 0; |
| break; |
| |
| case DRM_FORMAT_ABGR2101010: |
| case DRM_FORMAT_XBGR2101010: |
| a2r10g10b10_format = 5; |
| break; |
| |
| case DRM_FORMAT_RGBA1010102: |
| case DRM_FORMAT_RGBX1010102: |
| a2r10g10b10_format = 6; |
| break; |
| |
| case DRM_FORMAT_BGRA1010102: |
| case DRM_FORMAT_BGRX1010102: |
| a2r10g10b10_format = 11; |
| break; |
| |
| default: |
| a2r10g10b10_format = 0; |
| break; |
| } |
| |
| ch->sdata_ctrl |= a2r10g10b10_format << A2R10G10B10_FORMAT_POS; |
| } |
| |
| static void dcss_scaler_setup_path(struct dcss_soc *dcss, int ch_num, |
| u32 pix_format, int dst_xres, |
| int dst_yres, u32 vrefresh_hz, |
| bool wrscl_needed) |
| { |
| struct dcss_scaler_priv *scaler = dcss->scaler_priv; |
| u32 base_addr; |
| |
| /* nothing to do if WRSCL path is needed but it's already used */ |
| if (wrscl_needed && scaler->ch_using_wrscl != -1 && |
| scaler->ch_using_wrscl != ch_num) |
| return; |
| |
| if (!wrscl_needed) { |
| /* Channel has finished using WRSCL. Release WRSCL/RDSRC. */ |
| if (scaler->ch_using_wrscl == ch_num) { |
| dcss_wrscl_enable(dcss, false); |
| dcss_rdsrc_enable(dcss, false); |
| |
| scaler->ch_using_wrscl = -1; |
| } |
| |
| return; |
| } |
| |
| base_addr = dcss_wrscl_setup(dcss, pix_format, vrefresh_hz, |
| dst_xres, dst_yres); |
| |
| dcss_rdsrc_setup(dcss, pix_format, dst_xres, dst_yres, |
| base_addr); |
| |
| dcss_wrscl_enable(dcss, true); |
| dcss_rdsrc_enable(dcss, true); |
| |
| scaler->ch_using_wrscl = ch_num; |
| } |
| |
| void dcss_scaler_setup(struct dcss_soc *dcss, int ch_num, u32 pix_format, |
| int src_xres, int src_yres, int dst_xres, int dst_yres, |
| u32 vrefresh_hz) |
| { |
| struct dcss_scaler_ch *ch = &dcss->scaler_priv->ch[ch_num]; |
| enum dcss_color_space dcss_cs; |
| int planes; |
| unsigned int pixel_depth; |
| bool rtr_8line_en = false; |
| u32 bpp; |
| enum buffer_format src_format = BUF_FMT_ARGB8888_YUV444; |
| enum buffer_format dst_format = BUF_FMT_ARGB8888_YUV444; |
| bool wrscl_needed = false; |
| |
| dcss_cs = dcss_drm_fourcc_to_colorspace(pix_format); |
| planes = drm_format_num_planes(pix_format); |
| |
| if (dcss_cs == DCSS_COLORSPACE_YUV) { |
| dcss_scaler_yuv_enable(dcss, ch_num, true); |
| |
| if (pix_format == DRM_FORMAT_NV12 || |
| pix_format == DRM_FORMAT_NV21 || |
| pix_format == DRM_FORMAT_P010) { |
| rtr_8line_en = true; |
| src_format = BUF_FMT_YUV420; |
| } else if (pix_format == DRM_FORMAT_UYVY || |
| pix_format == DRM_FORMAT_VYUY || |
| pix_format == DRM_FORMAT_YUYV || |
| pix_format == DRM_FORMAT_YVYU) { |
| src_format = BUF_FMT_YUV422; |
| } |
| |
| if (pix_format != ch->pix_format) |
| dcss_scaler_yuv_coef_set(dcss, ch_num); |
| |
| if (pix_format == DRM_FORMAT_P010) |
| pixel_depth = 30; |
| |
| } else if (dcss_cs == DCSS_COLORSPACE_RGB) { |
| dcss_scaler_yuv_enable(dcss, ch_num, false); |
| |
| drm_fb_get_bpp_depth(pix_format, &pixel_depth, &bpp); |
| |
| if (pix_format != ch->pix_format) |
| dcss_scaler_rgb_coef_set(dcss, ch_num); |
| } |
| |
| dcss_scaler_rtr_8lines_enable(dcss, ch_num, rtr_8line_en); |
| dcss_scaler_bit_depth_set(dcss, ch_num, pixel_depth); |
| dcss_scaler_set_rgb10_order(dcss, ch_num, pix_format); |
| dcss_scaler_format_set(dcss, ch_num, src_format, dst_format); |
| dcss_scaler_res_set(dcss, ch_num, src_xres, src_yres, |
| dst_xres, dst_yres, pix_format); |
| wrscl_needed = dcss_scaler_fractions_set(dcss, ch_num, src_xres, |
| src_yres, dst_xres, |
| dst_yres, pix_format); |
| |
| dcss_scaler_setup_path(dcss, ch_num, pix_format, dst_xres, |
| dst_yres, vrefresh_hz, wrscl_needed); |
| |
| ch->pix_format = pix_format; |
| } |
| EXPORT_SYMBOL(dcss_scaler_setup); |