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