linux/drivers/gpu/drm/sun4i/sun8i_csc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net>
   4 */
   5
   6#include <drm/drm_print.h>
   7
   8#include "sun8i_csc.h"
   9#include "sun8i_mixer.h"
  10
  11static const u32 ccsc_base[2][2] = {
  12        {CCSC00_OFFSET, CCSC01_OFFSET},
  13        {CCSC10_OFFSET, CCSC11_OFFSET},
  14};
  15
  16/*
  17 * Factors are in two's complement format, 10 bits for fractinal part.
  18 * First tree values in each line are multiplication factor and last
  19 * value is constant, which is added at the end.
  20 */
  21
  22static const u32 yuv2rgb[2][2][12] = {
  23        [DRM_COLOR_YCBCR_LIMITED_RANGE] = {
  24                [DRM_COLOR_YCBCR_BT601] = {
  25                        0x000004A8, 0x00000000, 0x00000662, 0xFFFC8451,
  26                        0x000004A8, 0xFFFFFE6F, 0xFFFFFCC0, 0x00021E4D,
  27                        0x000004A8, 0x00000811, 0x00000000, 0xFFFBACA9,
  28                },
  29                [DRM_COLOR_YCBCR_BT709] = {
  30                        0x000004A8, 0x00000000, 0x0000072B, 0xFFFC1F99,
  31                        0x000004A8, 0xFFFFFF26, 0xFFFFFDDF, 0x00013383,
  32                        0x000004A8, 0x00000873, 0x00000000, 0xFFFB7BEF,
  33                }
  34        },
  35        [DRM_COLOR_YCBCR_FULL_RANGE] = {
  36                [DRM_COLOR_YCBCR_BT601] = {
  37                        0x00000400, 0x00000000, 0x0000059B, 0xFFFD322E,
  38                        0x00000400, 0xFFFFFEA0, 0xFFFFFD25, 0x00021DD5,
  39                        0x00000400, 0x00000716, 0x00000000, 0xFFFC74BD,
  40                },
  41                [DRM_COLOR_YCBCR_BT709] = {
  42                        0x00000400, 0x00000000, 0x0000064C, 0xFFFCD9B4,
  43                        0x00000400, 0xFFFFFF41, 0xFFFFFE21, 0x00014F96,
  44                        0x00000400, 0x0000076C, 0x00000000, 0xFFFC49EF,
  45                }
  46        },
  47};
  48
  49/*
  50 * DE3 has a bit different CSC units. Factors are in two's complement format.
  51 * First three factors in a row are multiplication factors which have 17 bits
  52 * for fractional part. Fourth value in a row is comprised of two factors.
  53 * Upper 16 bits represents difference, which is subtracted from the input
  54 * value before multiplication and lower 16 bits represents constant, which
  55 * is addes at the end.
  56 *
  57 * x' = c00 * (x + d0) + c01 * (y + d1) + c02 * (z + d2) + const0
  58 * y' = c10 * (x + d0) + c11 * (y + d1) + c12 * (z + d2) + const1
  59 * z' = c20 * (x + d0) + c21 * (y + d1) + c22 * (z + d2) + const2
  60 *
  61 * Please note that above formula is true only for Blender CSC. Other DE3 CSC
  62 * units takes only positive value for difference. From what can be deducted
  63 * from BSP driver code, those units probably automatically assume that
  64 * difference has to be subtracted.
  65 *
  66 * Layout of factors in table:
  67 * c00 c01 c02 [d0 const0]
  68 * c10 c11 c12 [d1 const1]
  69 * c20 c21 c22 [d2 const2]
  70 */
  71
  72static const u32 yuv2rgb_de3[2][3][12] = {
  73        [DRM_COLOR_YCBCR_LIMITED_RANGE] = {
  74                [DRM_COLOR_YCBCR_BT601] = {
  75                        0x0002542A, 0x00000000, 0x0003312A, 0xFFC00000,
  76                        0x0002542A, 0xFFFF376B, 0xFFFE5FC3, 0xFE000000,
  77                        0x0002542A, 0x000408D2, 0x00000000, 0xFE000000,
  78                },
  79                [DRM_COLOR_YCBCR_BT709] = {
  80                        0x0002542A, 0x00000000, 0x000395E2, 0xFFC00000,
  81                        0x0002542A, 0xFFFF92D2, 0xFFFEEF27, 0xFE000000,
  82                        0x0002542A, 0x0004398C, 0x00000000, 0xFE000000,
  83                },
  84                [DRM_COLOR_YCBCR_BT2020] = {
  85                        0x0002542A, 0x00000000, 0x00035B7B, 0xFFC00000,
  86                        0x0002542A, 0xFFFFA017, 0xFFFEB2FC, 0xFE000000,
  87                        0x0002542A, 0x00044896, 0x00000000, 0xFE000000,
  88                }
  89        },
  90        [DRM_COLOR_YCBCR_FULL_RANGE] = {
  91                [DRM_COLOR_YCBCR_BT601] = {
  92                        0x00020000, 0x00000000, 0x0002CDD2, 0x00000000,
  93                        0x00020000, 0xFFFF4FCE, 0xFFFE925D, 0xFE000000,
  94                        0x00020000, 0x00038B43, 0x00000000, 0xFE000000,
  95                },
  96                [DRM_COLOR_YCBCR_BT709] = {
  97                        0x00020000, 0x00000000, 0x0003264C, 0x00000000,
  98                        0x00020000, 0xFFFFA018, 0xFFFF1053, 0xFE000000,
  99                        0x00020000, 0x0003B611, 0x00000000, 0xFE000000,
 100                },
 101                [DRM_COLOR_YCBCR_BT2020] = {
 102                        0x00020000, 0x00000000, 0x0002F2FE, 0x00000000,
 103                        0x00020000, 0xFFFFABC0, 0xFFFEDB78, 0xFE000000,
 104                        0x00020000, 0x0003C346, 0x00000000, 0xFE000000,
 105                }
 106        },
 107};
 108
 109static void sun8i_csc_set_coefficients(struct regmap *map, u32 base,
 110                                       enum sun8i_csc_mode mode,
 111                                       enum drm_color_encoding encoding,
 112                                       enum drm_color_range range)
 113{
 114        const u32 *table;
 115        u32 base_reg;
 116        int i;
 117
 118        table = yuv2rgb[range][encoding];
 119
 120        switch (mode) {
 121        case SUN8I_CSC_MODE_YUV2RGB:
 122                base_reg = SUN8I_CSC_COEFF(base, 0);
 123                regmap_bulk_write(map, base_reg, table, 12);
 124                break;
 125        case SUN8I_CSC_MODE_YVU2RGB:
 126                for (i = 0; i < 12; i++) {
 127                        if ((i & 3) == 1)
 128                                base_reg = SUN8I_CSC_COEFF(base, i + 1);
 129                        else if ((i & 3) == 2)
 130                                base_reg = SUN8I_CSC_COEFF(base, i - 1);
 131                        else
 132                                base_reg = SUN8I_CSC_COEFF(base, i);
 133                        regmap_write(map, base_reg, table[i]);
 134                }
 135                break;
 136        default:
 137                DRM_WARN("Wrong CSC mode specified.\n");
 138                return;
 139        }
 140}
 141
 142static void sun8i_de3_ccsc_set_coefficients(struct regmap *map, int layer,
 143                                            enum sun8i_csc_mode mode,
 144                                            enum drm_color_encoding encoding,
 145                                            enum drm_color_range range)
 146{
 147        const u32 *table;
 148        u32 addr;
 149        int i;
 150
 151        table = yuv2rgb_de3[range][encoding];
 152
 153        switch (mode) {
 154        case SUN8I_CSC_MODE_YUV2RGB:
 155                addr = SUN50I_MIXER_BLEND_CSC_COEFF(DE3_BLD_BASE, layer, 0);
 156                regmap_bulk_write(map, addr, table, 12);
 157                break;
 158        case SUN8I_CSC_MODE_YVU2RGB:
 159                for (i = 0; i < 12; i++) {
 160                        if ((i & 3) == 1)
 161                                addr = SUN50I_MIXER_BLEND_CSC_COEFF(DE3_BLD_BASE,
 162                                                                    layer,
 163                                                                    i + 1);
 164                        else if ((i & 3) == 2)
 165                                addr = SUN50I_MIXER_BLEND_CSC_COEFF(DE3_BLD_BASE,
 166                                                                    layer,
 167                                                                    i - 1);
 168                        else
 169                                addr = SUN50I_MIXER_BLEND_CSC_COEFF(DE3_BLD_BASE,
 170                                                                    layer, i);
 171                        regmap_write(map, addr, table[i]);
 172                }
 173                break;
 174        default:
 175                DRM_WARN("Wrong CSC mode specified.\n");
 176                return;
 177        }
 178}
 179
 180static void sun8i_csc_enable(struct regmap *map, u32 base, bool enable)
 181{
 182        u32 val;
 183
 184        if (enable)
 185                val = SUN8I_CSC_CTRL_EN;
 186        else
 187                val = 0;
 188
 189        regmap_update_bits(map, SUN8I_CSC_CTRL(base), SUN8I_CSC_CTRL_EN, val);
 190}
 191
 192static void sun8i_de3_ccsc_enable(struct regmap *map, int layer, bool enable)
 193{
 194        u32 val, mask;
 195
 196        mask = SUN50I_MIXER_BLEND_CSC_CTL_EN(layer);
 197
 198        if (enable)
 199                val = mask;
 200        else
 201                val = 0;
 202
 203        regmap_update_bits(map, SUN50I_MIXER_BLEND_CSC_CTL(DE3_BLD_BASE),
 204                           mask, val);
 205}
 206
 207void sun8i_csc_set_ccsc_coefficients(struct sun8i_mixer *mixer, int layer,
 208                                     enum sun8i_csc_mode mode,
 209                                     enum drm_color_encoding encoding,
 210                                     enum drm_color_range range)
 211{
 212        u32 base;
 213
 214        if (mixer->cfg->is_de3) {
 215                sun8i_de3_ccsc_set_coefficients(mixer->engine.regs, layer,
 216                                                mode, encoding, range);
 217                return;
 218        }
 219
 220        base = ccsc_base[mixer->cfg->ccsc][layer];
 221
 222        sun8i_csc_set_coefficients(mixer->engine.regs, base,
 223                                   mode, encoding, range);
 224}
 225
 226void sun8i_csc_enable_ccsc(struct sun8i_mixer *mixer, int layer, bool enable)
 227{
 228        u32 base;
 229
 230        if (mixer->cfg->is_de3) {
 231                sun8i_de3_ccsc_enable(mixer->engine.regs, layer, enable);
 232                return;
 233        }
 234
 235        base = ccsc_base[mixer->cfg->ccsc][layer];
 236
 237        sun8i_csc_enable(mixer->engine.regs, base, enable);
 238}
 239