| /* Copyright (c) 2017, The Linux Foundation. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 and |
| * only version 2 as published by the Free Software Foundation. |
| * |
| * 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 <drm/msm_drm_pp.h> |
| #include "sde_hw_color_proc_common_v4.h" |
| #include "sde_hw_color_proc_v4.h" |
| |
| static int sde_write_3d_gamut(struct sde_hw_blk_reg_map *hw, |
| struct drm_msm_3d_gamut *payload, u32 base, |
| u32 *opcode) |
| { |
| u32 reg, tbl_len, tbl_off, scale_off, i, j; |
| u32 *scale_data; |
| |
| if (!payload || !opcode || !hw) { |
| DRM_ERROR("invalid payload %pK opcode %pK hw %pK\n", |
| payload, opcode, hw); |
| return -EINVAL; |
| } |
| |
| switch (payload->mode) { |
| case GAMUT_3D_MODE_17: |
| tbl_len = GAMUT_3D_MODE17_TBL_SZ; |
| tbl_off = 0; |
| scale_off = GAMUT_SCALEA_OFFSET_OFF; |
| *opcode = gamut_mode_17 << 2; |
| break; |
| case GAMUT_3D_MODE_13: |
| *opcode = (*opcode & (BIT(4) - 1)) >> 2; |
| if (*opcode == gamut_mode_13a) |
| *opcode = gamut_mode_13b; |
| else |
| *opcode = gamut_mode_13a; |
| tbl_len = GAMUT_3D_MODE13_TBL_SZ; |
| tbl_off = (*opcode == gamut_mode_13a) ? 0 : |
| GAMUT_MODE_13B_OFF; |
| scale_off = (*opcode == gamut_mode_13a) ? |
| GAMUT_SCALEA_OFFSET_OFF : GAMUT_SCALEB_OFFSET_OFF; |
| *opcode <<= 2; |
| break; |
| case GAMUT_3D_MODE_5: |
| *opcode = gamut_mode_5 << 2; |
| *opcode |= GAMUT_MAP_EN; |
| tbl_len = GAMUT_3D_MODE5_TBL_SZ; |
| tbl_off = 0; |
| scale_off = GAMUT_SCALEB_OFFSET_OFF; |
| break; |
| default: |
| DRM_ERROR("invalid mode %d\n", payload->mode); |
| return -EINVAL; |
| } |
| |
| if (payload->flags & GAMUT_3D_MAP_EN) |
| *opcode |= GAMUT_MAP_EN; |
| *opcode |= GAMUT_EN; |
| |
| for (i = 0; i < GAMUT_3D_TBL_NUM; i++) { |
| reg = GAMUT_TABLE0_SEL << i; |
| reg |= ((tbl_off) & (BIT(11) - 1)); |
| SDE_REG_WRITE(hw, base + GAMUT_TABLE_SEL_OFF, reg); |
| for (j = 0; j < tbl_len; j++) { |
| SDE_REG_WRITE(hw, base + GAMUT_LOWER_COLOR_OFF, |
| payload->col[i][j].c2_c1); |
| SDE_REG_WRITE(hw, base + GAMUT_UPPER_COLOR_OFF, |
| payload->col[i][j].c0); |
| } |
| } |
| |
| if ((*opcode & GAMUT_MAP_EN)) { |
| scale_data = &payload->scale_off[0][0]; |
| tbl_off = base + scale_off; |
| tbl_len = GAMUT_3D_SCALE_OFF_TBL_NUM * GAMUT_3D_SCALE_OFF_SZ; |
| for (i = 0; i < tbl_len; i++) |
| SDE_REG_WRITE(hw, tbl_off + (i * sizeof(u32)), |
| scale_data[i]); |
| } |
| SDE_REG_WRITE(hw, base, *opcode); |
| return 0; |
| } |
| |
| void sde_setup_dspp_3d_gamutv4(struct sde_hw_dspp *ctx, void *cfg) |
| { |
| struct drm_msm_3d_gamut *payload; |
| struct sde_hw_cp_cfg *hw_cfg = cfg; |
| u32 op_mode; |
| |
| if (!ctx || !cfg) { |
| DRM_ERROR("invalid param ctx %pK cfg %pK\n", ctx, cfg); |
| return; |
| } |
| |
| op_mode = SDE_REG_READ(&ctx->hw, ctx->cap->sblk->gamut.base); |
| if (!hw_cfg->payload) { |
| DRM_DEBUG_DRIVER("disable gamut feature\n"); |
| SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->gamut.base, 0); |
| return; |
| } |
| |
| payload = hw_cfg->payload; |
| sde_write_3d_gamut(&ctx->hw, payload, ctx->cap->sblk->gamut.base, |
| &op_mode); |
| |
| } |
| |
| void sde_setup_dspp_igcv3(struct sde_hw_dspp *ctx, void *cfg) |
| { |
| struct drm_msm_igc_lut *lut_cfg; |
| struct sde_hw_cp_cfg *hw_cfg = cfg; |
| int i = 0, j = 0; |
| u32 *addr = NULL; |
| u32 offset = 0; |
| |
| if (!ctx || !cfg) { |
| DRM_ERROR("invalid param ctx %pK cfg %pK\n", ctx, cfg); |
| return; |
| } |
| |
| if (!hw_cfg->payload) { |
| DRM_DEBUG_DRIVER("disable igc feature\n"); |
| SDE_REG_WRITE(&ctx->hw, IGC_OPMODE_OFF, 0); |
| return; |
| } |
| |
| if (hw_cfg->len != sizeof(struct drm_msm_igc_lut)) { |
| DRM_ERROR("invalid size of payload len %d exp %zd\n", |
| hw_cfg->len, sizeof(struct drm_msm_igc_lut)); |
| return; |
| } |
| |
| lut_cfg = hw_cfg->payload; |
| |
| for (i = 0; i < IGC_TBL_NUM; i++) { |
| addr = lut_cfg->c0 + (i * ARRAY_SIZE(lut_cfg->c0)); |
| offset = IGC_C0_OFF + (i * sizeof(u32)); |
| |
| for (j = 0; j < IGC_TBL_LEN; j++) { |
| addr[j] &= IGC_DATA_MASK; |
| addr[j] |= IGC_DSPP_SEL_MASK(ctx->idx - 1); |
| if (j == 0) |
| addr[j] |= IGC_INDEX_UPDATE; |
| /* IGC lut registers are part of DSPP Top HW block */ |
| SDE_REG_WRITE(&ctx->hw_top, offset, addr[j]); |
| } |
| } |
| |
| if (lut_cfg->flags & IGC_DITHER_ENABLE) { |
| SDE_REG_WRITE(&ctx->hw, IGC_DITHER_OFF, |
| lut_cfg->strength & IGC_DITHER_DATA_MASK); |
| } |
| |
| SDE_REG_WRITE(&ctx->hw, IGC_OPMODE_OFF, IGC_EN); |
| } |
| |
| void sde_setup_dspp_pccv4(struct sde_hw_dspp *ctx, void *cfg) |
| { |
| struct sde_hw_cp_cfg *hw_cfg = cfg; |
| struct drm_msm_pcc *pcc_cfg; |
| struct drm_msm_pcc_coeff *coeffs = NULL; |
| int i = 0; |
| u32 base = 0; |
| |
| if (!ctx || !cfg) { |
| DRM_ERROR("invalid param ctx %pK cfg %pK\n", ctx, cfg); |
| return; |
| } |
| |
| if (!hw_cfg->payload) { |
| DRM_DEBUG_DRIVER("disable pcc feature\n"); |
| SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base, 0); |
| return; |
| } |
| |
| if (hw_cfg->len != sizeof(struct drm_msm_pcc)) { |
| DRM_ERROR("invalid size of payload len %d exp %zd\n", |
| hw_cfg->len, sizeof(struct drm_msm_pcc)); |
| return; |
| } |
| |
| pcc_cfg = hw_cfg->payload; |
| |
| for (i = 0; i < PCC_NUM_PLANES; i++) { |
| base = ctx->cap->sblk->pcc.base + (i * sizeof(u32)); |
| switch (i) { |
| case 0: |
| coeffs = &pcc_cfg->r; |
| SDE_REG_WRITE(&ctx->hw, |
| base + PCC_RR_OFF, pcc_cfg->r_rr); |
| SDE_REG_WRITE(&ctx->hw, |
| base + PCC_GG_OFF, pcc_cfg->r_gg); |
| SDE_REG_WRITE(&ctx->hw, |
| base + PCC_BB_OFF, pcc_cfg->r_bb); |
| break; |
| case 1: |
| coeffs = &pcc_cfg->g; |
| SDE_REG_WRITE(&ctx->hw, |
| base + PCC_RR_OFF, pcc_cfg->g_rr); |
| SDE_REG_WRITE(&ctx->hw, |
| base + PCC_GG_OFF, pcc_cfg->g_gg); |
| SDE_REG_WRITE(&ctx->hw, |
| base + PCC_BB_OFF, pcc_cfg->g_bb); |
| break; |
| case 2: |
| coeffs = &pcc_cfg->b; |
| SDE_REG_WRITE(&ctx->hw, |
| base + PCC_RR_OFF, pcc_cfg->b_rr); |
| SDE_REG_WRITE(&ctx->hw, |
| base + PCC_GG_OFF, pcc_cfg->b_gg); |
| SDE_REG_WRITE(&ctx->hw, |
| base + PCC_BB_OFF, pcc_cfg->b_bb); |
| break; |
| default: |
| DRM_ERROR("invalid pcc plane: %d\n", i); |
| return; |
| } |
| |
| SDE_REG_WRITE(&ctx->hw, base + PCC_C_OFF, coeffs->c); |
| SDE_REG_WRITE(&ctx->hw, base + PCC_R_OFF, coeffs->r); |
| SDE_REG_WRITE(&ctx->hw, base + PCC_G_OFF, coeffs->g); |
| SDE_REG_WRITE(&ctx->hw, base + PCC_B_OFF, coeffs->b); |
| SDE_REG_WRITE(&ctx->hw, base + PCC_RG_OFF, coeffs->rg); |
| SDE_REG_WRITE(&ctx->hw, base + PCC_RB_OFF, coeffs->rb); |
| SDE_REG_WRITE(&ctx->hw, base + PCC_GB_OFF, coeffs->gb); |
| SDE_REG_WRITE(&ctx->hw, base + PCC_RGB_OFF, coeffs->rgb); |
| } |
| |
| SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->pcc.base, PCC_EN); |
| } |