]>
Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
88302939 JS |
2 | /* |
3 | * Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net> | |
88302939 JS |
4 | */ |
5 | ||
6 | #include <drm/drmP.h> | |
7 | ||
8 | #include "sun8i_csc.h" | |
9 | #include "sun8i_mixer.h" | |
10 | ||
11 | static 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 | static const u32 yuv2rgb[] = { | |
22 | 0x000004A8, 0x00000000, 0x00000662, 0xFFFC845A, | |
23 | 0x000004A8, 0xFFFFFE6F, 0xFFFFFCBF, 0x00021DF4, | |
24 | 0x000004A8, 0x00000813, 0x00000000, 0xFFFBAC4A, | |
25 | }; | |
26 | ||
27 | static const u32 yvu2rgb[] = { | |
28 | 0x000004A8, 0x00000662, 0x00000000, 0xFFFC845A, | |
29 | 0x000004A8, 0xFFFFFCBF, 0xFFFFFE6F, 0x00021DF4, | |
30 | 0x000004A8, 0x00000000, 0x00000813, 0xFFFBAC4A, | |
31 | }; | |
32 | ||
c50519e6 JS |
33 | /* |
34 | * DE3 has a bit different CSC units. Factors are in two's complement format. | |
35 | * First three factors in a row are multiplication factors which have 17 bits | |
36 | * for fractional part. Fourth value in a row is comprised of two factors. | |
37 | * Upper 16 bits represents difference, which is subtracted from the input | |
38 | * value before multiplication and lower 16 bits represents constant, which | |
39 | * is addes at the end. | |
40 | * | |
41 | * x' = c00 * (x + d0) + c01 * (y + d1) + c02 * (z + d2) + const0 | |
42 | * y' = c10 * (x + d0) + c11 * (y + d1) + c12 * (z + d2) + const1 | |
43 | * z' = c20 * (x + d0) + c21 * (y + d1) + c22 * (z + d2) + const2 | |
44 | * | |
45 | * Please note that above formula is true only for Blender CSC. Other DE3 CSC | |
46 | * units takes only positive value for difference. From what can be deducted | |
47 | * from BSP driver code, those units probably automatically assume that | |
48 | * difference has to be subtracted. | |
49 | * | |
50 | * Layout of factors in table: | |
51 | * c00 c01 c02 [d0 const0] | |
52 | * c10 c11 c12 [d1 const1] | |
53 | * c20 c21 c22 [d2 const2] | |
54 | */ | |
55 | ||
56 | static const u32 yuv2rgb_de3[] = { | |
57 | 0x0002542a, 0x00000000, 0x0003312a, 0xffc00000, | |
58 | 0x0002542a, 0xffff376b, 0xfffe5fc3, 0xfe000000, | |
59 | 0x0002542a, 0x000408d3, 0x00000000, 0xfe000000, | |
60 | }; | |
61 | ||
62 | static const u32 yvu2rgb_de3[] = { | |
63 | 0x0002542a, 0x0003312a, 0x00000000, 0xffc00000, | |
64 | 0x0002542a, 0xfffe5fc3, 0xffff376b, 0xfe000000, | |
65 | 0x0002542a, 0x00000000, 0x000408d3, 0xfe000000, | |
66 | }; | |
67 | ||
88302939 JS |
68 | static void sun8i_csc_set_coefficients(struct regmap *map, u32 base, |
69 | enum sun8i_csc_mode mode) | |
70 | { | |
71 | const u32 *table; | |
72 | int i, data; | |
73 | ||
74 | switch (mode) { | |
75 | case SUN8I_CSC_MODE_YUV2RGB: | |
76 | table = yuv2rgb; | |
77 | break; | |
78 | case SUN8I_CSC_MODE_YVU2RGB: | |
79 | table = yvu2rgb; | |
80 | break; | |
81 | default: | |
82 | DRM_WARN("Wrong CSC mode specified.\n"); | |
83 | return; | |
84 | } | |
85 | ||
86 | for (i = 0; i < 12; i++) { | |
87 | data = table[i]; | |
88 | /* For some reason, 0x200 must be added to constant parts */ | |
89 | if (((i + 1) & 3) == 0) | |
90 | data += 0x200; | |
91 | regmap_write(map, SUN8I_CSC_COEFF(base, i), data); | |
92 | } | |
93 | } | |
94 | ||
c50519e6 JS |
95 | static void sun8i_de3_ccsc_set_coefficients(struct regmap *map, int layer, |
96 | enum sun8i_csc_mode mode) | |
97 | { | |
98 | const u32 *table; | |
99 | u32 base_reg; | |
100 | ||
101 | switch (mode) { | |
102 | case SUN8I_CSC_MODE_YUV2RGB: | |
103 | table = yuv2rgb_de3; | |
104 | break; | |
105 | case SUN8I_CSC_MODE_YVU2RGB: | |
106 | table = yvu2rgb_de3; | |
107 | break; | |
108 | default: | |
109 | DRM_WARN("Wrong CSC mode specified.\n"); | |
110 | return; | |
111 | } | |
112 | ||
113 | base_reg = SUN50I_MIXER_BLEND_CSC_COEFF(DE3_BLD_BASE, layer, 0, 0); | |
114 | regmap_bulk_write(map, base_reg, table, 12); | |
115 | } | |
116 | ||
88302939 JS |
117 | static void sun8i_csc_enable(struct regmap *map, u32 base, bool enable) |
118 | { | |
119 | u32 val; | |
120 | ||
121 | if (enable) | |
122 | val = SUN8I_CSC_CTRL_EN; | |
123 | else | |
124 | val = 0; | |
125 | ||
126 | regmap_update_bits(map, SUN8I_CSC_CTRL(base), SUN8I_CSC_CTRL_EN, val); | |
127 | } | |
128 | ||
c50519e6 JS |
129 | static void sun8i_de3_ccsc_enable(struct regmap *map, int layer, bool enable) |
130 | { | |
131 | u32 val, mask; | |
132 | ||
133 | mask = SUN50I_MIXER_BLEND_CSC_CTL_EN(layer); | |
134 | ||
135 | if (enable) | |
136 | val = mask; | |
137 | else | |
138 | val = 0; | |
139 | ||
140 | regmap_update_bits(map, SUN50I_MIXER_BLEND_CSC_CTL(DE3_BLD_BASE), | |
141 | mask, val); | |
142 | } | |
143 | ||
88302939 JS |
144 | void sun8i_csc_set_ccsc_coefficients(struct sun8i_mixer *mixer, int layer, |
145 | enum sun8i_csc_mode mode) | |
146 | { | |
147 | u32 base; | |
148 | ||
c50519e6 JS |
149 | if (mixer->cfg->is_de3) { |
150 | sun8i_de3_ccsc_set_coefficients(mixer->engine.regs, | |
151 | layer, mode); | |
152 | return; | |
153 | } | |
154 | ||
88302939 JS |
155 | base = ccsc_base[mixer->cfg->ccsc][layer]; |
156 | ||
157 | sun8i_csc_set_coefficients(mixer->engine.regs, base, mode); | |
158 | } | |
159 | ||
160 | void sun8i_csc_enable_ccsc(struct sun8i_mixer *mixer, int layer, bool enable) | |
161 | { | |
162 | u32 base; | |
163 | ||
c50519e6 JS |
164 | if (mixer->cfg->is_de3) { |
165 | sun8i_de3_ccsc_enable(mixer->engine.regs, layer, enable); | |
166 | return; | |
167 | } | |
168 | ||
88302939 JS |
169 | base = ccsc_base[mixer->cfg->ccsc][layer]; |
170 | ||
171 | sun8i_csc_enable(mixer->engine.regs, base, enable); | |
172 | } |