1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2023, The Linux Foundation. All rights reserved. 4 */ 5 6 #include <linux/bitfield.h> 7 8 #include <drm/drm_managed.h> 9 10 #include "dpu_hw_mdss.h" 11 #include "dpu_hw_util.h" 12 #include "dpu_hw_catalog.h" 13 #include "dpu_hw_cdm.h" 14 #include "dpu_kms.h" 15 16 #define CDM_CSC_10_OPMODE 0x000 17 #define CDM_CSC_10_BASE 0x004 18 19 #define CDM_CDWN2_OP_MODE 0x100 20 #define CDM_CDWN2_CLAMP_OUT 0x104 21 #define CDM_CDWN2_PARAMS_3D_0 0x108 22 #define CDM_CDWN2_PARAMS_3D_1 0x10C 23 #define CDM_CDWN2_COEFF_COSITE_H_0 0x110 24 #define CDM_CDWN2_COEFF_COSITE_H_1 0x114 25 #define CDM_CDWN2_COEFF_COSITE_H_2 0x118 26 #define CDM_CDWN2_COEFF_OFFSITE_H_0 0x11C 27 #define CDM_CDWN2_COEFF_OFFSITE_H_1 0x120 28 #define CDM_CDWN2_COEFF_OFFSITE_H_2 0x124 29 #define CDM_CDWN2_COEFF_COSITE_V 0x128 30 #define CDM_CDWN2_COEFF_OFFSITE_V 0x12C 31 #define CDM_CDWN2_OUT_SIZE 0x130 32 33 #define CDM_HDMI_PACK_OP_MODE 0x200 34 #define CDM_CSC_10_MATRIX_COEFF_0 0x004 35 36 #define CDM_MUX 0x224 37 38 /* CDM CDWN2 sub-block bit definitions */ 39 #define CDM_CDWN2_OP_MODE_EN BIT(0) 40 #define CDM_CDWN2_OP_MODE_ENABLE_H BIT(1) 41 #define CDM_CDWN2_OP_MODE_ENABLE_V BIT(2) 42 #define CDM_CDWN2_OP_MODE_BITS_OUT_8BIT BIT(7) 43 #define CDM_CDWN2_V_PIXEL_METHOD_MASK GENMASK(6, 5) 44 #define CDM_CDWN2_H_PIXEL_METHOD_MASK GENMASK(4, 3) 45 46 /* CDM CSC10 sub-block bit definitions */ 47 #define CDM_CSC10_OP_MODE_EN BIT(0) 48 #define CDM_CSC10_OP_MODE_SRC_FMT_YUV BIT(1) 49 #define CDM_CSC10_OP_MODE_DST_FMT_YUV BIT(2) 50 51 /* CDM HDMI pack sub-block bit definitions */ 52 #define CDM_HDMI_PACK_OP_MODE_EN BIT(0) 53 54 /* 55 * Horizontal coefficients for cosite chroma downscale 56 * s13 representation of coefficients 57 */ 58 static u32 cosite_h_coeff[] = {0x00000016, 0x000001cc, 0x0100009e}; 59 60 /* 61 * Horizontal coefficients for offsite chroma downscale 62 */ 63 static u32 offsite_h_coeff[] = {0x000b0005, 0x01db01eb, 0x00e40046}; 64 65 /* 66 * Vertical coefficients for cosite chroma downscale 67 */ 68 static u32 cosite_v_coeff[] = {0x00080004}; 69 /* 70 * Vertical coefficients for offsite chroma downscale 71 */ 72 static u32 offsite_v_coeff[] = {0x00060002}; 73 74 static int dpu_hw_cdm_setup_cdwn(struct dpu_hw_cdm *ctx, struct dpu_hw_cdm_cfg *cfg) 75 { 76 struct dpu_hw_blk_reg_map *c = &ctx->hw; 77 u32 opmode; 78 u32 out_size; 79 80 switch (cfg->h_cdwn_type) { 81 case CDM_CDWN_DISABLE: 82 opmode = 0; 83 break; 84 case CDM_CDWN_PIXEL_DROP: 85 opmode = CDM_CDWN2_OP_MODE_ENABLE_H | 86 FIELD_PREP(CDM_CDWN2_H_PIXEL_METHOD_MASK, 87 CDM_CDWN2_METHOD_PIXEL_DROP); 88 break; 89 case CDM_CDWN_AVG: 90 opmode = CDM_CDWN2_OP_MODE_ENABLE_H | 91 FIELD_PREP(CDM_CDWN2_H_PIXEL_METHOD_MASK, 92 CDM_CDWN2_METHOD_AVG); 93 break; 94 case CDM_CDWN_COSITE: 95 opmode = CDM_CDWN2_OP_MODE_ENABLE_H | 96 FIELD_PREP(CDM_CDWN2_H_PIXEL_METHOD_MASK, 97 CDM_CDWN2_METHOD_COSITE); 98 DPU_REG_WRITE(c, CDM_CDWN2_COEFF_COSITE_H_0, 99 cosite_h_coeff[0]); 100 DPU_REG_WRITE(c, CDM_CDWN2_COEFF_COSITE_H_1, 101 cosite_h_coeff[1]); 102 DPU_REG_WRITE(c, CDM_CDWN2_COEFF_COSITE_H_2, 103 cosite_h_coeff[2]); 104 break; 105 case CDM_CDWN_OFFSITE: 106 opmode = CDM_CDWN2_OP_MODE_ENABLE_H | 107 FIELD_PREP(CDM_CDWN2_H_PIXEL_METHOD_MASK, CDM_CDWN2_METHOD_OFFSITE); 108 DPU_REG_WRITE(c, CDM_CDWN2_COEFF_OFFSITE_H_0, 109 offsite_h_coeff[0]); 110 DPU_REG_WRITE(c, CDM_CDWN2_COEFF_OFFSITE_H_1, 111 offsite_h_coeff[1]); 112 DPU_REG_WRITE(c, CDM_CDWN2_COEFF_OFFSITE_H_2, 113 offsite_h_coeff[2]); 114 break; 115 default: 116 DPU_ERROR("%s invalid horz down sampling type\n", __func__); 117 return -EINVAL; 118 } 119 120 switch (cfg->v_cdwn_type) { 121 case CDM_CDWN_DISABLE: 122 /* if its only Horizontal downsample, we dont need to do anything here */ 123 break; 124 case CDM_CDWN_PIXEL_DROP: 125 opmode |= CDM_CDWN2_OP_MODE_ENABLE_V | 126 FIELD_PREP(CDM_CDWN2_V_PIXEL_METHOD_MASK, 127 CDM_CDWN2_METHOD_PIXEL_DROP); 128 break; 129 case CDM_CDWN_AVG: 130 opmode |= CDM_CDWN2_OP_MODE_ENABLE_V | 131 FIELD_PREP(CDM_CDWN2_V_PIXEL_METHOD_MASK, 132 CDM_CDWN2_METHOD_AVG); 133 break; 134 case CDM_CDWN_COSITE: 135 opmode |= CDM_CDWN2_OP_MODE_ENABLE_V | 136 FIELD_PREP(CDM_CDWN2_V_PIXEL_METHOD_MASK, 137 CDM_CDWN2_METHOD_COSITE); 138 DPU_REG_WRITE(c, 139 CDM_CDWN2_COEFF_COSITE_V, 140 cosite_v_coeff[0]); 141 break; 142 case CDM_CDWN_OFFSITE: 143 opmode |= CDM_CDWN2_OP_MODE_ENABLE_V | 144 FIELD_PREP(CDM_CDWN2_V_PIXEL_METHOD_MASK, 145 CDM_CDWN2_METHOD_OFFSITE); 146 DPU_REG_WRITE(c, 147 CDM_CDWN2_COEFF_OFFSITE_V, 148 offsite_v_coeff[0]); 149 break; 150 default: 151 return -EINVAL; 152 } 153 154 if (cfg->output_bit_depth != CDM_CDWN_OUTPUT_10BIT) 155 opmode |= CDM_CDWN2_OP_MODE_BITS_OUT_8BIT; 156 157 if (cfg->v_cdwn_type || cfg->h_cdwn_type) 158 opmode |= CDM_CDWN2_OP_MODE_EN; /* EN CDWN module */ 159 else 160 opmode &= ~CDM_CDWN2_OP_MODE_EN; 161 162 out_size = (cfg->output_width & 0xFFFF) | ((cfg->output_height & 0xFFFF) << 16); 163 DPU_REG_WRITE(c, CDM_CDWN2_OUT_SIZE, out_size); 164 DPU_REG_WRITE(c, CDM_CDWN2_OP_MODE, opmode); 165 DPU_REG_WRITE(c, CDM_CDWN2_CLAMP_OUT, ((0x3FF << 16) | 0x0)); 166 167 return 0; 168 } 169 170 static int dpu_hw_cdm_enable(struct dpu_hw_cdm *ctx, struct dpu_hw_cdm_cfg *cdm) 171 { 172 struct dpu_hw_blk_reg_map *c = &ctx->hw; 173 const struct dpu_format *fmt; 174 u32 opmode = 0; 175 u32 csc = 0; 176 177 if (!ctx || !cdm) 178 return -EINVAL; 179 180 fmt = cdm->output_fmt; 181 182 if (!DPU_FORMAT_IS_YUV(fmt)) 183 return -EINVAL; 184 185 dpu_hw_csc_setup(&ctx->hw, CDM_CSC_10_MATRIX_COEFF_0, cdm->csc_cfg, true); 186 dpu_hw_cdm_setup_cdwn(ctx, cdm); 187 188 if (cdm->output_type == CDM_CDWN_OUTPUT_HDMI) { 189 if (fmt->chroma_sample == DPU_CHROMA_H1V2) 190 return -EINVAL; /*unsupported format */ 191 opmode = CDM_HDMI_PACK_OP_MODE_EN; 192 opmode |= (fmt->chroma_sample << 1); 193 } 194 195 csc |= CDM_CSC10_OP_MODE_DST_FMT_YUV; 196 csc &= ~CDM_CSC10_OP_MODE_SRC_FMT_YUV; 197 csc |= CDM_CSC10_OP_MODE_EN; 198 199 if (ctx && ctx->ops.bind_pingpong_blk) 200 ctx->ops.bind_pingpong_blk(ctx, cdm->pp_id); 201 202 DPU_REG_WRITE(c, CDM_CSC_10_OPMODE, csc); 203 DPU_REG_WRITE(c, CDM_HDMI_PACK_OP_MODE, opmode); 204 return 0; 205 } 206 207 static void dpu_hw_cdm_bind_pingpong_blk(struct dpu_hw_cdm *ctx, const enum dpu_pingpong pp) 208 { 209 struct dpu_hw_blk_reg_map *c; 210 int mux_cfg; 211 212 c = &ctx->hw; 213 214 mux_cfg = DPU_REG_READ(c, CDM_MUX); 215 mux_cfg &= ~0xf; 216 217 if (pp) 218 mux_cfg |= (pp - PINGPONG_0) & 0x7; 219 else 220 mux_cfg |= 0xf; 221 222 DPU_REG_WRITE(c, CDM_MUX, mux_cfg); 223 } 224 225 struct dpu_hw_cdm *dpu_hw_cdm_init(struct drm_device *dev, 226 const struct dpu_cdm_cfg *cfg, void __iomem *addr, 227 const struct dpu_mdss_version *mdss_rev) 228 { 229 struct dpu_hw_cdm *c; 230 231 c = drmm_kzalloc(dev, sizeof(*c), GFP_KERNEL); 232 if (!c) 233 return ERR_PTR(-ENOMEM); 234 235 c->hw.blk_addr = addr + cfg->base; 236 c->hw.log_mask = DPU_DBG_MASK_CDM; 237 238 /* Assign ops */ 239 c->idx = cfg->id; 240 c->caps = cfg; 241 242 c->ops.enable = dpu_hw_cdm_enable; 243 if (mdss_rev->core_major_ver >= 5) 244 c->ops.bind_pingpong_blk = dpu_hw_cdm_bind_pingpong_blk; 245 246 return c; 247 } 248