]>
Commit | Line | Data |
---|---|---|
32a1795f JS |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright (C) 2016-2018 Texas Instruments Incorporated - http://www.ti.com/ | |
4 | * Author: Jyri Sarha <jsarha@ti.com> | |
5 | */ | |
6 | ||
7 | #include <linux/clk.h> | |
8 | #include <linux/delay.h> | |
9 | #include <linux/dma-mapping.h> | |
10 | #include <linux/err.h> | |
11 | #include <linux/interrupt.h> | |
12 | #include <linux/io.h> | |
13 | #include <linux/kernel.h> | |
14 | #include <linux/module.h> | |
15 | #include <linux/mfd/syscon.h> | |
16 | #include <linux/of.h> | |
17 | #include <linux/of_graph.h> | |
18 | #include <linux/of_device.h> | |
19 | #include <linux/platform_device.h> | |
20 | #include <linux/pm_runtime.h> | |
21 | #include <linux/regmap.h> | |
22 | ||
23 | #include <drm/drm_fourcc.h> | |
24 | #include <drm/drm_fb_cma_helper.h> | |
25 | #include <drm/drm_gem_cma_helper.h> | |
26 | #include <drm/drm_panel.h> | |
27 | ||
28 | #include "tidss_crtc.h" | |
29 | #include "tidss_dispc.h" | |
30 | #include "tidss_drv.h" | |
31 | #include "tidss_irq.h" | |
32 | #include "tidss_plane.h" | |
33 | ||
34 | #include "tidss_dispc_regs.h" | |
35 | #include "tidss_scale_coefs.h" | |
36 | ||
37 | static const u16 tidss_k2g_common_regs[DISPC_COMMON_REG_TABLE_LEN] = { | |
38 | [DSS_REVISION_OFF] = 0x00, | |
39 | [DSS_SYSCONFIG_OFF] = 0x04, | |
40 | [DSS_SYSSTATUS_OFF] = 0x08, | |
41 | [DISPC_IRQ_EOI_OFF] = 0x20, | |
42 | [DISPC_IRQSTATUS_RAW_OFF] = 0x24, | |
43 | [DISPC_IRQSTATUS_OFF] = 0x28, | |
44 | [DISPC_IRQENABLE_SET_OFF] = 0x2c, | |
45 | [DISPC_IRQENABLE_CLR_OFF] = 0x30, | |
46 | ||
47 | [DISPC_GLOBAL_MFLAG_ATTRIBUTE_OFF] = 0x40, | |
48 | [DISPC_GLOBAL_BUFFER_OFF] = 0x44, | |
49 | ||
50 | [DISPC_DBG_CONTROL_OFF] = 0x4c, | |
51 | [DISPC_DBG_STATUS_OFF] = 0x50, | |
52 | ||
53 | [DISPC_CLKGATING_DISABLE_OFF] = 0x54, | |
54 | }; | |
55 | ||
56 | const struct dispc_features dispc_k2g_feats = { | |
57 | .min_pclk_khz = 4375, | |
58 | ||
59 | .max_pclk_khz = { | |
60 | [DISPC_VP_DPI] = 150000, | |
61 | }, | |
62 | ||
63 | /* | |
64 | * XXX According TRM the RGB input buffer width up to 2560 should | |
65 | * work on 3 taps, but in practice it only works up to 1280. | |
66 | */ | |
67 | .scaling = { | |
68 | .in_width_max_5tap_rgb = 1280, | |
69 | .in_width_max_3tap_rgb = 1280, | |
70 | .in_width_max_5tap_yuv = 2560, | |
71 | .in_width_max_3tap_yuv = 2560, | |
72 | .upscale_limit = 16, | |
73 | .downscale_limit_5tap = 4, | |
74 | .downscale_limit_3tap = 2, | |
75 | /* | |
76 | * The max supported pixel inc value is 255. The value | |
77 | * of pixel inc is calculated like this: 1+(xinc-1)*bpp. | |
78 | * The maximum bpp of all formats supported by the HW | |
79 | * is 8. So the maximum supported xinc value is 32, | |
80 | * because 1+(32-1)*8 < 255 < 1+(33-1)*4. | |
81 | */ | |
82 | .xinc_max = 32, | |
83 | }, | |
84 | ||
85 | .subrev = DISPC_K2G, | |
86 | ||
87 | .common = "common", | |
88 | ||
89 | .common_regs = tidss_k2g_common_regs, | |
90 | ||
91 | .num_vps = 1, | |
92 | .vp_name = { "vp1" }, | |
93 | .ovr_name = { "ovr1" }, | |
94 | .vpclk_name = { "vp1" }, | |
95 | .vp_bus_type = { DISPC_VP_DPI }, | |
96 | ||
97 | .vp_feat = { .color = { | |
98 | .has_ctm = true, | |
99 | .gamma_size = 256, | |
100 | .gamma_type = TIDSS_GAMMA_8BIT, | |
101 | }, | |
102 | }, | |
103 | ||
104 | .num_planes = 1, | |
105 | .vid_name = { "vid1" }, | |
106 | .vid_lite = { false }, | |
107 | .vid_order = { 0 }, | |
108 | }; | |
109 | ||
110 | static const u16 tidss_am65x_common_regs[DISPC_COMMON_REG_TABLE_LEN] = { | |
111 | [DSS_REVISION_OFF] = 0x4, | |
112 | [DSS_SYSCONFIG_OFF] = 0x8, | |
113 | [DSS_SYSSTATUS_OFF] = 0x20, | |
114 | [DISPC_IRQ_EOI_OFF] = 0x24, | |
115 | [DISPC_IRQSTATUS_RAW_OFF] = 0x28, | |
116 | [DISPC_IRQSTATUS_OFF] = 0x2c, | |
117 | [DISPC_IRQENABLE_SET_OFF] = 0x30, | |
118 | [DISPC_IRQENABLE_CLR_OFF] = 0x40, | |
119 | [DISPC_VID_IRQENABLE_OFF] = 0x44, | |
120 | [DISPC_VID_IRQSTATUS_OFF] = 0x58, | |
121 | [DISPC_VP_IRQENABLE_OFF] = 0x70, | |
122 | [DISPC_VP_IRQSTATUS_OFF] = 0x7c, | |
123 | ||
124 | [WB_IRQENABLE_OFF] = 0x88, | |
125 | [WB_IRQSTATUS_OFF] = 0x8c, | |
126 | ||
127 | [DISPC_GLOBAL_MFLAG_ATTRIBUTE_OFF] = 0x90, | |
128 | [DISPC_GLOBAL_OUTPUT_ENABLE_OFF] = 0x94, | |
129 | [DISPC_GLOBAL_BUFFER_OFF] = 0x98, | |
130 | [DSS_CBA_CFG_OFF] = 0x9c, | |
131 | [DISPC_DBG_CONTROL_OFF] = 0xa0, | |
132 | [DISPC_DBG_STATUS_OFF] = 0xa4, | |
133 | [DISPC_CLKGATING_DISABLE_OFF] = 0xa8, | |
134 | [DISPC_SECURE_DISABLE_OFF] = 0xac, | |
135 | }; | |
136 | ||
137 | const struct dispc_features dispc_am65x_feats = { | |
138 | .max_pclk_khz = { | |
139 | [DISPC_VP_DPI] = 165000, | |
140 | [DISPC_VP_OLDI] = 165000, | |
141 | }, | |
142 | ||
143 | .scaling = { | |
144 | .in_width_max_5tap_rgb = 1280, | |
145 | .in_width_max_3tap_rgb = 2560, | |
146 | .in_width_max_5tap_yuv = 2560, | |
147 | .in_width_max_3tap_yuv = 4096, | |
148 | .upscale_limit = 16, | |
149 | .downscale_limit_5tap = 4, | |
150 | .downscale_limit_3tap = 2, | |
151 | /* | |
152 | * The max supported pixel inc value is 255. The value | |
153 | * of pixel inc is calculated like this: 1+(xinc-1)*bpp. | |
154 | * The maximum bpp of all formats supported by the HW | |
155 | * is 8. So the maximum supported xinc value is 32, | |
156 | * because 1+(32-1)*8 < 255 < 1+(33-1)*4. | |
157 | */ | |
158 | .xinc_max = 32, | |
159 | }, | |
160 | ||
161 | .subrev = DISPC_AM65X, | |
162 | ||
163 | .common = "common", | |
164 | .common_regs = tidss_am65x_common_regs, | |
165 | ||
166 | .num_vps = 2, | |
167 | .vp_name = { "vp1", "vp2" }, | |
168 | .ovr_name = { "ovr1", "ovr2" }, | |
169 | .vpclk_name = { "vp1", "vp2" }, | |
170 | .vp_bus_type = { DISPC_VP_OLDI, DISPC_VP_DPI }, | |
171 | ||
172 | .vp_feat = { .color = { | |
173 | .has_ctm = true, | |
174 | .gamma_size = 256, | |
175 | .gamma_type = TIDSS_GAMMA_8BIT, | |
176 | }, | |
177 | }, | |
178 | ||
179 | .num_planes = 2, | |
180 | /* note: vid is plane_id 0 and vidl1 is plane_id 1 */ | |
181 | .vid_name = { "vid", "vidl1" }, | |
182 | .vid_lite = { false, true, }, | |
183 | .vid_order = { 1, 0 }, | |
32a1795f JS |
184 | }; |
185 | ||
186 | static const u16 tidss_j721e_common_regs[DISPC_COMMON_REG_TABLE_LEN] = { | |
187 | [DSS_REVISION_OFF] = 0x4, | |
188 | [DSS_SYSCONFIG_OFF] = 0x8, | |
189 | [DSS_SYSSTATUS_OFF] = 0x20, | |
190 | [DISPC_IRQ_EOI_OFF] = 0x80, | |
191 | [DISPC_IRQSTATUS_RAW_OFF] = 0x28, | |
192 | [DISPC_IRQSTATUS_OFF] = 0x2c, | |
193 | [DISPC_IRQENABLE_SET_OFF] = 0x30, | |
194 | [DISPC_IRQENABLE_CLR_OFF] = 0x34, | |
195 | [DISPC_VID_IRQENABLE_OFF] = 0x38, | |
196 | [DISPC_VID_IRQSTATUS_OFF] = 0x48, | |
197 | [DISPC_VP_IRQENABLE_OFF] = 0x58, | |
198 | [DISPC_VP_IRQSTATUS_OFF] = 0x68, | |
199 | ||
200 | [WB_IRQENABLE_OFF] = 0x78, | |
201 | [WB_IRQSTATUS_OFF] = 0x7c, | |
202 | ||
203 | [DISPC_GLOBAL_MFLAG_ATTRIBUTE_OFF] = 0x98, | |
204 | [DISPC_GLOBAL_OUTPUT_ENABLE_OFF] = 0x9c, | |
205 | [DISPC_GLOBAL_BUFFER_OFF] = 0xa0, | |
206 | [DSS_CBA_CFG_OFF] = 0xa4, | |
207 | [DISPC_DBG_CONTROL_OFF] = 0xa8, | |
208 | [DISPC_DBG_STATUS_OFF] = 0xac, | |
209 | [DISPC_CLKGATING_DISABLE_OFF] = 0xb0, | |
210 | [DISPC_SECURE_DISABLE_OFF] = 0x90, | |
211 | ||
212 | [FBDC_REVISION_1_OFF] = 0xb8, | |
213 | [FBDC_REVISION_2_OFF] = 0xbc, | |
214 | [FBDC_REVISION_3_OFF] = 0xc0, | |
215 | [FBDC_REVISION_4_OFF] = 0xc4, | |
216 | [FBDC_REVISION_5_OFF] = 0xc8, | |
217 | [FBDC_REVISION_6_OFF] = 0xcc, | |
218 | [FBDC_COMMON_CONTROL_OFF] = 0xd0, | |
219 | [FBDC_CONSTANT_COLOR_0_OFF] = 0xd4, | |
220 | [FBDC_CONSTANT_COLOR_1_OFF] = 0xd8, | |
221 | [DISPC_CONNECTIONS_OFF] = 0xe4, | |
222 | [DISPC_MSS_VP1_OFF] = 0xe8, | |
223 | [DISPC_MSS_VP3_OFF] = 0xec, | |
224 | }; | |
225 | ||
226 | const struct dispc_features dispc_j721e_feats = { | |
227 | .max_pclk_khz = { | |
228 | [DISPC_VP_DPI] = 170000, | |
229 | [DISPC_VP_INTERNAL] = 600000, | |
230 | }, | |
231 | ||
232 | .scaling = { | |
233 | .in_width_max_5tap_rgb = 2048, | |
234 | .in_width_max_3tap_rgb = 4096, | |
235 | .in_width_max_5tap_yuv = 4096, | |
236 | .in_width_max_3tap_yuv = 4096, | |
237 | .upscale_limit = 16, | |
238 | .downscale_limit_5tap = 4, | |
239 | .downscale_limit_3tap = 2, | |
240 | /* | |
241 | * The max supported pixel inc value is 255. The value | |
242 | * of pixel inc is calculated like this: 1+(xinc-1)*bpp. | |
243 | * The maximum bpp of all formats supported by the HW | |
244 | * is 8. So the maximum supported xinc value is 32, | |
245 | * because 1+(32-1)*8 < 255 < 1+(33-1)*4. | |
246 | */ | |
247 | .xinc_max = 32, | |
248 | }, | |
249 | ||
250 | .subrev = DISPC_J721E, | |
251 | ||
252 | .common = "common_m", | |
253 | .common_regs = tidss_j721e_common_regs, | |
254 | ||
255 | .num_vps = 4, | |
256 | .vp_name = { "vp1", "vp2", "vp3", "vp4" }, | |
257 | .ovr_name = { "ovr1", "ovr2", "ovr3", "ovr4" }, | |
258 | .vpclk_name = { "vp1", "vp2", "vp3", "vp4" }, | |
259 | /* Currently hard coded VP routing (see dispc_initial_config()) */ | |
260 | .vp_bus_type = { DISPC_VP_INTERNAL, DISPC_VP_DPI, | |
261 | DISPC_VP_INTERNAL, DISPC_VP_DPI, }, | |
262 | .vp_feat = { .color = { | |
263 | .has_ctm = true, | |
264 | .gamma_size = 1024, | |
265 | .gamma_type = TIDSS_GAMMA_10BIT, | |
266 | }, | |
267 | }, | |
268 | .num_planes = 4, | |
269 | .vid_name = { "vid1", "vidl1", "vid2", "vidl2" }, | |
270 | .vid_lite = { 0, 1, 0, 1, }, | |
271 | .vid_order = { 1, 3, 0, 2 }, | |
272 | }; | |
273 | ||
274 | static const u16 *dispc_common_regmap; | |
275 | ||
276 | struct dss_vp_data { | |
277 | u32 *gamma_table; | |
278 | }; | |
279 | ||
32a1795f JS |
280 | struct dispc_device { |
281 | struct tidss_device *tidss; | |
282 | struct device *dev; | |
283 | ||
284 | void __iomem *base_common; | |
285 | void __iomem *base_vid[TIDSS_MAX_PLANES]; | |
286 | void __iomem *base_ovr[TIDSS_MAX_PORTS]; | |
287 | void __iomem *base_vp[TIDSS_MAX_PORTS]; | |
288 | ||
289 | struct regmap *oldi_io_ctrl; | |
290 | ||
291 | struct clk *vp_clk[TIDSS_MAX_PORTS]; | |
292 | ||
293 | const struct dispc_features *feat; | |
294 | ||
295 | struct clk *fclk; | |
296 | ||
297 | bool is_enabled; | |
298 | ||
299 | struct dss_vp_data vp_data[TIDSS_MAX_PORTS]; | |
300 | ||
32a1795f JS |
301 | u32 *fourccs; |
302 | u32 num_fourccs; | |
303 | ||
304 | u32 memory_bandwidth_limit; | |
305 | }; | |
306 | ||
307 | static void dispc_write(struct dispc_device *dispc, u16 reg, u32 val) | |
308 | { | |
309 | iowrite32(val, dispc->base_common + reg); | |
310 | } | |
311 | ||
312 | static u32 dispc_read(struct dispc_device *dispc, u16 reg) | |
313 | { | |
314 | return ioread32(dispc->base_common + reg); | |
315 | } | |
316 | ||
317 | static | |
318 | void dispc_vid_write(struct dispc_device *dispc, u32 hw_plane, u16 reg, u32 val) | |
319 | { | |
320 | void __iomem *base = dispc->base_vid[hw_plane]; | |
321 | ||
322 | iowrite32(val, base + reg); | |
323 | } | |
324 | ||
325 | static u32 dispc_vid_read(struct dispc_device *dispc, u32 hw_plane, u16 reg) | |
326 | { | |
327 | void __iomem *base = dispc->base_vid[hw_plane]; | |
328 | ||
329 | return ioread32(base + reg); | |
330 | } | |
331 | ||
332 | static void dispc_ovr_write(struct dispc_device *dispc, u32 hw_videoport, | |
333 | u16 reg, u32 val) | |
334 | { | |
335 | void __iomem *base = dispc->base_ovr[hw_videoport]; | |
336 | ||
337 | iowrite32(val, base + reg); | |
338 | } | |
339 | ||
340 | static u32 dispc_ovr_read(struct dispc_device *dispc, u32 hw_videoport, u16 reg) | |
341 | { | |
342 | void __iomem *base = dispc->base_ovr[hw_videoport]; | |
343 | ||
344 | return ioread32(base + reg); | |
345 | } | |
346 | ||
347 | static void dispc_vp_write(struct dispc_device *dispc, u32 hw_videoport, | |
348 | u16 reg, u32 val) | |
349 | { | |
350 | void __iomem *base = dispc->base_vp[hw_videoport]; | |
351 | ||
352 | iowrite32(val, base + reg); | |
353 | } | |
354 | ||
355 | static u32 dispc_vp_read(struct dispc_device *dispc, u32 hw_videoport, u16 reg) | |
356 | { | |
357 | void __iomem *base = dispc->base_vp[hw_videoport]; | |
358 | ||
359 | return ioread32(base + reg); | |
360 | } | |
361 | ||
362 | /* | |
363 | * TRM gives bitfields as start:end, where start is the higher bit | |
364 | * number. For example 7:0 | |
365 | */ | |
366 | ||
367 | static u32 FLD_MASK(u32 start, u32 end) | |
368 | { | |
369 | return ((1 << (start - end + 1)) - 1) << end; | |
370 | } | |
371 | ||
372 | static u32 FLD_VAL(u32 val, u32 start, u32 end) | |
373 | { | |
374 | return (val << end) & FLD_MASK(start, end); | |
375 | } | |
376 | ||
377 | static u32 FLD_GET(u32 val, u32 start, u32 end) | |
378 | { | |
379 | return (val & FLD_MASK(start, end)) >> end; | |
380 | } | |
381 | ||
382 | static u32 FLD_MOD(u32 orig, u32 val, u32 start, u32 end) | |
383 | { | |
384 | return (orig & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end); | |
385 | } | |
386 | ||
387 | static u32 REG_GET(struct dispc_device *dispc, u32 idx, u32 start, u32 end) | |
388 | { | |
389 | return FLD_GET(dispc_read(dispc, idx), start, end); | |
390 | } | |
391 | ||
392 | static void REG_FLD_MOD(struct dispc_device *dispc, u32 idx, u32 val, | |
393 | u32 start, u32 end) | |
394 | { | |
395 | dispc_write(dispc, idx, FLD_MOD(dispc_read(dispc, idx), val, | |
396 | start, end)); | |
397 | } | |
398 | ||
399 | static u32 VID_REG_GET(struct dispc_device *dispc, u32 hw_plane, u32 idx, | |
400 | u32 start, u32 end) | |
401 | { | |
402 | return FLD_GET(dispc_vid_read(dispc, hw_plane, idx), start, end); | |
403 | } | |
404 | ||
405 | static void VID_REG_FLD_MOD(struct dispc_device *dispc, u32 hw_plane, u32 idx, | |
406 | u32 val, u32 start, u32 end) | |
407 | { | |
408 | dispc_vid_write(dispc, hw_plane, idx, | |
409 | FLD_MOD(dispc_vid_read(dispc, hw_plane, idx), | |
410 | val, start, end)); | |
411 | } | |
412 | ||
413 | static u32 VP_REG_GET(struct dispc_device *dispc, u32 vp, u32 idx, | |
414 | u32 start, u32 end) | |
415 | { | |
416 | return FLD_GET(dispc_vp_read(dispc, vp, idx), start, end); | |
417 | } | |
418 | ||
419 | static void VP_REG_FLD_MOD(struct dispc_device *dispc, u32 vp, u32 idx, u32 val, | |
420 | u32 start, u32 end) | |
421 | { | |
422 | dispc_vp_write(dispc, vp, idx, FLD_MOD(dispc_vp_read(dispc, vp, idx), | |
423 | val, start, end)); | |
424 | } | |
425 | ||
426 | __maybe_unused | |
427 | static u32 OVR_REG_GET(struct dispc_device *dispc, u32 ovr, u32 idx, | |
428 | u32 start, u32 end) | |
429 | { | |
430 | return FLD_GET(dispc_ovr_read(dispc, ovr, idx), start, end); | |
431 | } | |
432 | ||
433 | static void OVR_REG_FLD_MOD(struct dispc_device *dispc, u32 ovr, u32 idx, | |
434 | u32 val, u32 start, u32 end) | |
435 | { | |
436 | dispc_ovr_write(dispc, ovr, idx, | |
437 | FLD_MOD(dispc_ovr_read(dispc, ovr, idx), | |
438 | val, start, end)); | |
439 | } | |
440 | ||
441 | static dispc_irq_t dispc_vp_irq_from_raw(u32 stat, u32 hw_videoport) | |
442 | { | |
443 | dispc_irq_t vp_stat = 0; | |
444 | ||
445 | if (stat & BIT(0)) | |
446 | vp_stat |= DSS_IRQ_VP_FRAME_DONE(hw_videoport); | |
447 | if (stat & BIT(1)) | |
448 | vp_stat |= DSS_IRQ_VP_VSYNC_EVEN(hw_videoport); | |
449 | if (stat & BIT(2)) | |
450 | vp_stat |= DSS_IRQ_VP_VSYNC_ODD(hw_videoport); | |
451 | if (stat & BIT(4)) | |
452 | vp_stat |= DSS_IRQ_VP_SYNC_LOST(hw_videoport); | |
453 | ||
454 | return vp_stat; | |
455 | } | |
456 | ||
457 | static u32 dispc_vp_irq_to_raw(dispc_irq_t vpstat, u32 hw_videoport) | |
458 | { | |
459 | u32 stat = 0; | |
460 | ||
461 | if (vpstat & DSS_IRQ_VP_FRAME_DONE(hw_videoport)) | |
462 | stat |= BIT(0); | |
463 | if (vpstat & DSS_IRQ_VP_VSYNC_EVEN(hw_videoport)) | |
464 | stat |= BIT(1); | |
465 | if (vpstat & DSS_IRQ_VP_VSYNC_ODD(hw_videoport)) | |
466 | stat |= BIT(2); | |
467 | if (vpstat & DSS_IRQ_VP_SYNC_LOST(hw_videoport)) | |
468 | stat |= BIT(4); | |
469 | ||
470 | return stat; | |
471 | } | |
472 | ||
473 | static dispc_irq_t dispc_vid_irq_from_raw(u32 stat, u32 hw_plane) | |
474 | { | |
475 | dispc_irq_t vid_stat = 0; | |
476 | ||
477 | if (stat & BIT(0)) | |
478 | vid_stat |= DSS_IRQ_PLANE_FIFO_UNDERFLOW(hw_plane); | |
479 | ||
480 | return vid_stat; | |
481 | } | |
482 | ||
483 | static u32 dispc_vid_irq_to_raw(dispc_irq_t vidstat, u32 hw_plane) | |
484 | { | |
485 | u32 stat = 0; | |
486 | ||
487 | if (vidstat & DSS_IRQ_PLANE_FIFO_UNDERFLOW(hw_plane)) | |
488 | stat |= BIT(0); | |
489 | ||
490 | return stat; | |
491 | } | |
492 | ||
493 | static dispc_irq_t dispc_k2g_vp_read_irqstatus(struct dispc_device *dispc, | |
494 | u32 hw_videoport) | |
495 | { | |
496 | u32 stat = dispc_vp_read(dispc, hw_videoport, DISPC_VP_K2G_IRQSTATUS); | |
497 | ||
498 | return dispc_vp_irq_from_raw(stat, hw_videoport); | |
499 | } | |
500 | ||
501 | static void dispc_k2g_vp_write_irqstatus(struct dispc_device *dispc, | |
502 | u32 hw_videoport, dispc_irq_t vpstat) | |
503 | { | |
504 | u32 stat = dispc_vp_irq_to_raw(vpstat, hw_videoport); | |
505 | ||
506 | dispc_vp_write(dispc, hw_videoport, DISPC_VP_K2G_IRQSTATUS, stat); | |
507 | } | |
508 | ||
509 | static dispc_irq_t dispc_k2g_vid_read_irqstatus(struct dispc_device *dispc, | |
510 | u32 hw_plane) | |
511 | { | |
512 | u32 stat = dispc_vid_read(dispc, hw_plane, DISPC_VID_K2G_IRQSTATUS); | |
513 | ||
514 | return dispc_vid_irq_from_raw(stat, hw_plane); | |
515 | } | |
516 | ||
517 | static void dispc_k2g_vid_write_irqstatus(struct dispc_device *dispc, | |
518 | u32 hw_plane, dispc_irq_t vidstat) | |
519 | { | |
520 | u32 stat = dispc_vid_irq_to_raw(vidstat, hw_plane); | |
521 | ||
522 | dispc_vid_write(dispc, hw_plane, DISPC_VID_K2G_IRQSTATUS, stat); | |
523 | } | |
524 | ||
525 | static dispc_irq_t dispc_k2g_vp_read_irqenable(struct dispc_device *dispc, | |
526 | u32 hw_videoport) | |
527 | { | |
528 | u32 stat = dispc_vp_read(dispc, hw_videoport, DISPC_VP_K2G_IRQENABLE); | |
529 | ||
530 | return dispc_vp_irq_from_raw(stat, hw_videoport); | |
531 | } | |
532 | ||
533 | static void dispc_k2g_vp_set_irqenable(struct dispc_device *dispc, | |
534 | u32 hw_videoport, dispc_irq_t vpstat) | |
535 | { | |
536 | u32 stat = dispc_vp_irq_to_raw(vpstat, hw_videoport); | |
537 | ||
538 | dispc_vp_write(dispc, hw_videoport, DISPC_VP_K2G_IRQENABLE, stat); | |
539 | } | |
540 | ||
541 | static dispc_irq_t dispc_k2g_vid_read_irqenable(struct dispc_device *dispc, | |
542 | u32 hw_plane) | |
543 | { | |
544 | u32 stat = dispc_vid_read(dispc, hw_plane, DISPC_VID_K2G_IRQENABLE); | |
545 | ||
546 | return dispc_vid_irq_from_raw(stat, hw_plane); | |
547 | } | |
548 | ||
549 | static void dispc_k2g_vid_set_irqenable(struct dispc_device *dispc, | |
550 | u32 hw_plane, dispc_irq_t vidstat) | |
551 | { | |
552 | u32 stat = dispc_vid_irq_to_raw(vidstat, hw_plane); | |
553 | ||
554 | dispc_vid_write(dispc, hw_plane, DISPC_VID_K2G_IRQENABLE, stat); | |
555 | } | |
556 | ||
557 | static void dispc_k2g_clear_irqstatus(struct dispc_device *dispc, | |
558 | dispc_irq_t mask) | |
559 | { | |
560 | dispc_k2g_vp_write_irqstatus(dispc, 0, mask); | |
561 | dispc_k2g_vid_write_irqstatus(dispc, 0, mask); | |
562 | } | |
563 | ||
564 | static | |
565 | dispc_irq_t dispc_k2g_read_and_clear_irqstatus(struct dispc_device *dispc) | |
566 | { | |
567 | dispc_irq_t stat = 0; | |
568 | ||
569 | /* always clear the top level irqstatus */ | |
570 | dispc_write(dispc, DISPC_IRQSTATUS, | |
571 | dispc_read(dispc, DISPC_IRQSTATUS)); | |
572 | ||
573 | stat |= dispc_k2g_vp_read_irqstatus(dispc, 0); | |
574 | stat |= dispc_k2g_vid_read_irqstatus(dispc, 0); | |
575 | ||
576 | dispc_k2g_clear_irqstatus(dispc, stat); | |
577 | ||
578 | return stat; | |
579 | } | |
580 | ||
581 | static dispc_irq_t dispc_k2g_read_irqenable(struct dispc_device *dispc) | |
582 | { | |
583 | dispc_irq_t stat = 0; | |
584 | ||
585 | stat |= dispc_k2g_vp_read_irqenable(dispc, 0); | |
586 | stat |= dispc_k2g_vid_read_irqenable(dispc, 0); | |
587 | ||
588 | return stat; | |
589 | } | |
590 | ||
591 | static | |
592 | void dispc_k2g_set_irqenable(struct dispc_device *dispc, dispc_irq_t mask) | |
593 | { | |
594 | dispc_irq_t old_mask = dispc_k2g_read_irqenable(dispc); | |
595 | ||
596 | /* clear the irqstatus for newly enabled irqs */ | |
597 | dispc_k2g_clear_irqstatus(dispc, (mask ^ old_mask) & mask); | |
598 | ||
599 | dispc_k2g_vp_set_irqenable(dispc, 0, mask); | |
600 | dispc_k2g_vid_set_irqenable(dispc, 0, mask); | |
601 | ||
602 | dispc_write(dispc, DISPC_IRQENABLE_SET, (1 << 0) | (1 << 7)); | |
603 | ||
604 | /* flush posted write */ | |
605 | dispc_k2g_read_irqenable(dispc); | |
606 | } | |
607 | ||
608 | static dispc_irq_t dispc_k3_vp_read_irqstatus(struct dispc_device *dispc, | |
609 | u32 hw_videoport) | |
610 | { | |
611 | u32 stat = dispc_read(dispc, DISPC_VP_IRQSTATUS(hw_videoport)); | |
612 | ||
613 | return dispc_vp_irq_from_raw(stat, hw_videoport); | |
614 | } | |
615 | ||
616 | static void dispc_k3_vp_write_irqstatus(struct dispc_device *dispc, | |
617 | u32 hw_videoport, dispc_irq_t vpstat) | |
618 | { | |
619 | u32 stat = dispc_vp_irq_to_raw(vpstat, hw_videoport); | |
620 | ||
621 | dispc_write(dispc, DISPC_VP_IRQSTATUS(hw_videoport), stat); | |
622 | } | |
623 | ||
624 | static dispc_irq_t dispc_k3_vid_read_irqstatus(struct dispc_device *dispc, | |
625 | u32 hw_plane) | |
626 | { | |
627 | u32 stat = dispc_read(dispc, DISPC_VID_IRQSTATUS(hw_plane)); | |
628 | ||
629 | return dispc_vid_irq_from_raw(stat, hw_plane); | |
630 | } | |
631 | ||
632 | static void dispc_k3_vid_write_irqstatus(struct dispc_device *dispc, | |
633 | u32 hw_plane, dispc_irq_t vidstat) | |
634 | { | |
635 | u32 stat = dispc_vid_irq_to_raw(vidstat, hw_plane); | |
636 | ||
637 | dispc_write(dispc, DISPC_VID_IRQSTATUS(hw_plane), stat); | |
638 | } | |
639 | ||
640 | static dispc_irq_t dispc_k3_vp_read_irqenable(struct dispc_device *dispc, | |
641 | u32 hw_videoport) | |
642 | { | |
643 | u32 stat = dispc_read(dispc, DISPC_VP_IRQENABLE(hw_videoport)); | |
644 | ||
645 | return dispc_vp_irq_from_raw(stat, hw_videoport); | |
646 | } | |
647 | ||
648 | static void dispc_k3_vp_set_irqenable(struct dispc_device *dispc, | |
649 | u32 hw_videoport, dispc_irq_t vpstat) | |
650 | { | |
651 | u32 stat = dispc_vp_irq_to_raw(vpstat, hw_videoport); | |
652 | ||
653 | dispc_write(dispc, DISPC_VP_IRQENABLE(hw_videoport), stat); | |
654 | } | |
655 | ||
656 | static dispc_irq_t dispc_k3_vid_read_irqenable(struct dispc_device *dispc, | |
657 | u32 hw_plane) | |
658 | { | |
659 | u32 stat = dispc_read(dispc, DISPC_VID_IRQENABLE(hw_plane)); | |
660 | ||
661 | return dispc_vid_irq_from_raw(stat, hw_plane); | |
662 | } | |
663 | ||
664 | static void dispc_k3_vid_set_irqenable(struct dispc_device *dispc, | |
665 | u32 hw_plane, dispc_irq_t vidstat) | |
666 | { | |
667 | u32 stat = dispc_vid_irq_to_raw(vidstat, hw_plane); | |
668 | ||
669 | dispc_write(dispc, DISPC_VID_IRQENABLE(hw_plane), stat); | |
670 | } | |
671 | ||
672 | static | |
673 | void dispc_k3_clear_irqstatus(struct dispc_device *dispc, dispc_irq_t clearmask) | |
674 | { | |
675 | unsigned int i; | |
676 | u32 top_clear = 0; | |
677 | ||
678 | for (i = 0; i < dispc->feat->num_vps; ++i) { | |
679 | if (clearmask & DSS_IRQ_VP_MASK(i)) { | |
680 | dispc_k3_vp_write_irqstatus(dispc, i, clearmask); | |
681 | top_clear |= BIT(i); | |
682 | } | |
683 | } | |
684 | for (i = 0; i < dispc->feat->num_planes; ++i) { | |
685 | if (clearmask & DSS_IRQ_PLANE_MASK(i)) { | |
686 | dispc_k3_vid_write_irqstatus(dispc, i, clearmask); | |
687 | top_clear |= BIT(4 + i); | |
688 | } | |
689 | } | |
690 | if (dispc->feat->subrev == DISPC_K2G) | |
691 | return; | |
692 | ||
693 | dispc_write(dispc, DISPC_IRQSTATUS, top_clear); | |
694 | ||
695 | /* Flush posted writes */ | |
696 | dispc_read(dispc, DISPC_IRQSTATUS); | |
697 | } | |
698 | ||
699 | static | |
700 | dispc_irq_t dispc_k3_read_and_clear_irqstatus(struct dispc_device *dispc) | |
701 | { | |
702 | dispc_irq_t status = 0; | |
703 | unsigned int i; | |
704 | ||
705 | for (i = 0; i < dispc->feat->num_vps; ++i) | |
706 | status |= dispc_k3_vp_read_irqstatus(dispc, i); | |
707 | ||
708 | for (i = 0; i < dispc->feat->num_planes; ++i) | |
709 | status |= dispc_k3_vid_read_irqstatus(dispc, i); | |
710 | ||
711 | dispc_k3_clear_irqstatus(dispc, status); | |
712 | ||
713 | return status; | |
714 | } | |
715 | ||
716 | static dispc_irq_t dispc_k3_read_irqenable(struct dispc_device *dispc) | |
717 | { | |
718 | dispc_irq_t enable = 0; | |
719 | unsigned int i; | |
720 | ||
721 | for (i = 0; i < dispc->feat->num_vps; ++i) | |
722 | enable |= dispc_k3_vp_read_irqenable(dispc, i); | |
723 | ||
724 | for (i = 0; i < dispc->feat->num_planes; ++i) | |
725 | enable |= dispc_k3_vid_read_irqenable(dispc, i); | |
726 | ||
727 | return enable; | |
728 | } | |
729 | ||
730 | static void dispc_k3_set_irqenable(struct dispc_device *dispc, | |
731 | dispc_irq_t mask) | |
732 | { | |
733 | unsigned int i; | |
734 | u32 main_enable = 0, main_disable = 0; | |
735 | dispc_irq_t old_mask; | |
736 | ||
737 | old_mask = dispc_k3_read_irqenable(dispc); | |
738 | ||
739 | /* clear the irqstatus for newly enabled irqs */ | |
740 | dispc_k3_clear_irqstatus(dispc, (old_mask ^ mask) & mask); | |
741 | ||
742 | for (i = 0; i < dispc->feat->num_vps; ++i) { | |
743 | dispc_k3_vp_set_irqenable(dispc, i, mask); | |
744 | if (mask & DSS_IRQ_VP_MASK(i)) | |
745 | main_enable |= BIT(i); /* VP IRQ */ | |
746 | else | |
747 | main_disable |= BIT(i); /* VP IRQ */ | |
748 | } | |
749 | ||
750 | for (i = 0; i < dispc->feat->num_planes; ++i) { | |
751 | dispc_k3_vid_set_irqenable(dispc, i, mask); | |
752 | if (mask & DSS_IRQ_PLANE_MASK(i)) | |
753 | main_enable |= BIT(i + 4); /* VID IRQ */ | |
754 | else | |
755 | main_disable |= BIT(i + 4); /* VID IRQ */ | |
756 | } | |
757 | ||
758 | if (main_enable) | |
759 | dispc_write(dispc, DISPC_IRQENABLE_SET, main_enable); | |
760 | ||
761 | if (main_disable) | |
762 | dispc_write(dispc, DISPC_IRQENABLE_CLR, main_disable); | |
763 | ||
764 | /* Flush posted writes */ | |
765 | dispc_read(dispc, DISPC_IRQENABLE_SET); | |
766 | } | |
767 | ||
768 | dispc_irq_t dispc_read_and_clear_irqstatus(struct dispc_device *dispc) | |
769 | { | |
770 | switch (dispc->feat->subrev) { | |
771 | case DISPC_K2G: | |
772 | return dispc_k2g_read_and_clear_irqstatus(dispc); | |
773 | case DISPC_AM65X: | |
774 | case DISPC_J721E: | |
775 | return dispc_k3_read_and_clear_irqstatus(dispc); | |
776 | default: | |
777 | WARN_ON(1); | |
778 | return 0; | |
779 | } | |
780 | } | |
781 | ||
782 | void dispc_set_irqenable(struct dispc_device *dispc, dispc_irq_t mask) | |
783 | { | |
784 | switch (dispc->feat->subrev) { | |
785 | case DISPC_K2G: | |
786 | dispc_k2g_set_irqenable(dispc, mask); | |
787 | break; | |
788 | case DISPC_AM65X: | |
789 | case DISPC_J721E: | |
790 | dispc_k3_set_irqenable(dispc, mask); | |
791 | break; | |
792 | default: | |
793 | WARN_ON(1); | |
794 | break; | |
795 | } | |
796 | } | |
797 | ||
798 | enum dispc_oldi_mode_reg_val { SPWG_18 = 0, JEIDA_24 = 1, SPWG_24 = 2 }; | |
799 | ||
800 | struct dispc_bus_format { | |
801 | u32 bus_fmt; | |
802 | u32 data_width; | |
803 | bool is_oldi_fmt; | |
804 | enum dispc_oldi_mode_reg_val oldi_mode_reg_val; | |
805 | }; | |
806 | ||
807 | static const struct dispc_bus_format dispc_bus_formats[] = { | |
808 | { MEDIA_BUS_FMT_RGB444_1X12, 12, false, 0 }, | |
809 | { MEDIA_BUS_FMT_RGB565_1X16, 16, false, 0 }, | |
810 | { MEDIA_BUS_FMT_RGB666_1X18, 18, false, 0 }, | |
811 | { MEDIA_BUS_FMT_RGB888_1X24, 24, false, 0 }, | |
812 | { MEDIA_BUS_FMT_RGB101010_1X30, 30, false, 0 }, | |
813 | { MEDIA_BUS_FMT_RGB121212_1X36, 36, false, 0 }, | |
814 | { MEDIA_BUS_FMT_RGB666_1X7X3_SPWG, 18, true, SPWG_18 }, | |
815 | { MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, 24, true, SPWG_24 }, | |
816 | { MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, 24, true, JEIDA_24 }, | |
817 | }; | |
818 | ||
819 | static const | |
820 | struct dispc_bus_format *dispc_vp_find_bus_fmt(struct dispc_device *dispc, | |
821 | u32 hw_videoport, | |
822 | u32 bus_fmt, u32 bus_flags) | |
823 | { | |
824 | unsigned int i; | |
825 | ||
826 | for (i = 0; i < ARRAY_SIZE(dispc_bus_formats); ++i) { | |
827 | if (dispc_bus_formats[i].bus_fmt == bus_fmt) | |
828 | return &dispc_bus_formats[i]; | |
829 | } | |
830 | ||
831 | return NULL; | |
832 | } | |
833 | ||
834 | int dispc_vp_bus_check(struct dispc_device *dispc, u32 hw_videoport, | |
835 | const struct drm_crtc_state *state) | |
836 | { | |
837 | const struct tidss_crtc_state *tstate = to_tidss_crtc_state(state); | |
838 | const struct dispc_bus_format *fmt; | |
839 | ||
840 | fmt = dispc_vp_find_bus_fmt(dispc, hw_videoport, tstate->bus_format, | |
841 | tstate->bus_flags); | |
842 | if (!fmt) { | |
843 | dev_dbg(dispc->dev, "%s: Unsupported bus format: %u\n", | |
844 | __func__, tstate->bus_format); | |
845 | return -EINVAL; | |
846 | } | |
847 | ||
848 | if (dispc->feat->vp_bus_type[hw_videoport] != DISPC_VP_OLDI && | |
849 | fmt->is_oldi_fmt) { | |
850 | dev_dbg(dispc->dev, "%s: %s is not OLDI-port\n", | |
851 | __func__, dispc->feat->vp_name[hw_videoport]); | |
852 | return -EINVAL; | |
853 | } | |
854 | ||
855 | return 0; | |
856 | } | |
857 | ||
858 | static void dispc_oldi_tx_power(struct dispc_device *dispc, bool power) | |
859 | { | |
860 | u32 val = power ? 0 : OLDI_PWRDN_TX; | |
861 | ||
862 | if (WARN_ON(!dispc->oldi_io_ctrl)) | |
863 | return; | |
864 | ||
865 | regmap_update_bits(dispc->oldi_io_ctrl, OLDI_DAT0_IO_CTRL, | |
866 | OLDI_PWRDN_TX, val); | |
867 | regmap_update_bits(dispc->oldi_io_ctrl, OLDI_DAT1_IO_CTRL, | |
868 | OLDI_PWRDN_TX, val); | |
869 | regmap_update_bits(dispc->oldi_io_ctrl, OLDI_DAT2_IO_CTRL, | |
870 | OLDI_PWRDN_TX, val); | |
871 | regmap_update_bits(dispc->oldi_io_ctrl, OLDI_DAT3_IO_CTRL, | |
872 | OLDI_PWRDN_TX, val); | |
873 | regmap_update_bits(dispc->oldi_io_ctrl, OLDI_CLK_IO_CTRL, | |
874 | OLDI_PWRDN_TX, val); | |
875 | } | |
876 | ||
877 | static void dispc_set_num_datalines(struct dispc_device *dispc, | |
878 | u32 hw_videoport, int num_lines) | |
879 | { | |
880 | int v; | |
881 | ||
882 | switch (num_lines) { | |
883 | case 12: | |
884 | v = 0; break; | |
885 | case 16: | |
886 | v = 1; break; | |
887 | case 18: | |
888 | v = 2; break; | |
889 | case 24: | |
890 | v = 3; break; | |
891 | case 30: | |
892 | v = 4; break; | |
893 | case 36: | |
894 | v = 5; break; | |
895 | default: | |
896 | WARN_ON(1); | |
897 | v = 3; | |
898 | } | |
899 | ||
900 | VP_REG_FLD_MOD(dispc, hw_videoport, DISPC_VP_CONTROL, v, 10, 8); | |
901 | } | |
902 | ||
903 | static void dispc_enable_oldi(struct dispc_device *dispc, u32 hw_videoport, | |
904 | const struct dispc_bus_format *fmt) | |
905 | { | |
906 | u32 oldi_cfg = 0; | |
907 | u32 oldi_reset_bit = BIT(5 + hw_videoport); | |
908 | int count = 0; | |
909 | ||
910 | /* | |
911 | * For the moment DUALMODESYNC, MASTERSLAVE, MODE, and SRC | |
912 | * bits of DISPC_VP_DSS_OLDI_CFG are set statically to 0. | |
913 | */ | |
914 | ||
915 | if (fmt->data_width == 24) | |
916 | oldi_cfg |= BIT(8); /* MSB */ | |
917 | else if (fmt->data_width != 18) | |
918 | dev_warn(dispc->dev, "%s: %d port width not supported\n", | |
919 | __func__, fmt->data_width); | |
920 | ||
921 | oldi_cfg |= BIT(7); /* DEPOL */ | |
922 | ||
923 | oldi_cfg = FLD_MOD(oldi_cfg, fmt->oldi_mode_reg_val, 3, 1); | |
924 | ||
925 | oldi_cfg |= BIT(12); /* SOFTRST */ | |
926 | ||
927 | oldi_cfg |= BIT(0); /* ENABLE */ | |
928 | ||
929 | dispc_vp_write(dispc, hw_videoport, DISPC_VP_DSS_OLDI_CFG, oldi_cfg); | |
930 | ||
931 | while (!(oldi_reset_bit & dispc_read(dispc, DSS_SYSSTATUS)) && | |
932 | count < 10000) | |
933 | count++; | |
934 | ||
935 | if (!(oldi_reset_bit & dispc_read(dispc, DSS_SYSSTATUS))) | |
936 | dev_warn(dispc->dev, "%s: timeout waiting OLDI reset done\n", | |
937 | __func__); | |
938 | } | |
939 | ||
940 | void dispc_vp_prepare(struct dispc_device *dispc, u32 hw_videoport, | |
941 | const struct drm_crtc_state *state) | |
942 | { | |
943 | const struct tidss_crtc_state *tstate = to_tidss_crtc_state(state); | |
944 | const struct dispc_bus_format *fmt; | |
945 | ||
946 | fmt = dispc_vp_find_bus_fmt(dispc, hw_videoport, tstate->bus_format, | |
947 | tstate->bus_flags); | |
948 | ||
949 | if (WARN_ON(!fmt)) | |
950 | return; | |
951 | ||
952 | if (dispc->feat->vp_bus_type[hw_videoport] == DISPC_VP_OLDI) { | |
953 | dispc_oldi_tx_power(dispc, true); | |
954 | ||
955 | dispc_enable_oldi(dispc, hw_videoport, fmt); | |
956 | } | |
957 | } | |
958 | ||
959 | void dispc_vp_enable(struct dispc_device *dispc, u32 hw_videoport, | |
960 | const struct drm_crtc_state *state) | |
961 | { | |
962 | const struct drm_display_mode *mode = &state->adjusted_mode; | |
963 | const struct tidss_crtc_state *tstate = to_tidss_crtc_state(state); | |
964 | bool align, onoff, rf, ieo, ipc, ihs, ivs; | |
965 | const struct dispc_bus_format *fmt; | |
966 | u32 hsw, hfp, hbp, vsw, vfp, vbp; | |
967 | ||
968 | fmt = dispc_vp_find_bus_fmt(dispc, hw_videoport, tstate->bus_format, | |
969 | tstate->bus_flags); | |
970 | ||
971 | if (WARN_ON(!fmt)) | |
972 | return; | |
973 | ||
974 | dispc_set_num_datalines(dispc, hw_videoport, fmt->data_width); | |
975 | ||
976 | hfp = mode->hsync_start - mode->hdisplay; | |
977 | hsw = mode->hsync_end - mode->hsync_start; | |
978 | hbp = mode->htotal - mode->hsync_end; | |
979 | ||
980 | vfp = mode->vsync_start - mode->vdisplay; | |
981 | vsw = mode->vsync_end - mode->vsync_start; | |
982 | vbp = mode->vtotal - mode->vsync_end; | |
983 | ||
984 | dispc_vp_write(dispc, hw_videoport, DISPC_VP_TIMING_H, | |
985 | FLD_VAL(hsw - 1, 7, 0) | | |
986 | FLD_VAL(hfp - 1, 19, 8) | | |
987 | FLD_VAL(hbp - 1, 31, 20)); | |
988 | ||
989 | dispc_vp_write(dispc, hw_videoport, DISPC_VP_TIMING_V, | |
990 | FLD_VAL(vsw - 1, 7, 0) | | |
991 | FLD_VAL(vfp, 19, 8) | | |
992 | FLD_VAL(vbp, 31, 20)); | |
993 | ||
994 | ivs = !!(mode->flags & DRM_MODE_FLAG_NVSYNC); | |
995 | ||
996 | ihs = !!(mode->flags & DRM_MODE_FLAG_NHSYNC); | |
997 | ||
998 | ieo = !!(tstate->bus_flags & DRM_BUS_FLAG_DE_LOW); | |
999 | ||
1000 | ipc = !!(tstate->bus_flags & DRM_BUS_FLAG_PIXDATA_NEGEDGE); | |
1001 | ||
1002 | /* always use the 'rf' setting */ | |
1003 | onoff = true; | |
1004 | ||
1005 | rf = !!(tstate->bus_flags & DRM_BUS_FLAG_SYNC_POSEDGE); | |
1006 | ||
1007 | /* always use aligned syncs */ | |
1008 | align = true; | |
1009 | ||
1010 | /* always use DE_HIGH for OLDI */ | |
1011 | if (dispc->feat->vp_bus_type[hw_videoport] == DISPC_VP_OLDI) | |
1012 | ieo = false; | |
1013 | ||
1014 | dispc_vp_write(dispc, hw_videoport, DISPC_VP_POL_FREQ, | |
1015 | FLD_VAL(align, 18, 18) | | |
1016 | FLD_VAL(onoff, 17, 17) | | |
1017 | FLD_VAL(rf, 16, 16) | | |
1018 | FLD_VAL(ieo, 15, 15) | | |
1019 | FLD_VAL(ipc, 14, 14) | | |
1020 | FLD_VAL(ihs, 13, 13) | | |
1021 | FLD_VAL(ivs, 12, 12)); | |
1022 | ||
1023 | dispc_vp_write(dispc, hw_videoport, DISPC_VP_SIZE_SCREEN, | |
1024 | FLD_VAL(mode->hdisplay - 1, 11, 0) | | |
1025 | FLD_VAL(mode->vdisplay - 1, 27, 16)); | |
1026 | ||
1027 | VP_REG_FLD_MOD(dispc, hw_videoport, DISPC_VP_CONTROL, 1, 0, 0); | |
1028 | } | |
1029 | ||
1030 | void dispc_vp_disable(struct dispc_device *dispc, u32 hw_videoport) | |
1031 | { | |
1032 | VP_REG_FLD_MOD(dispc, hw_videoport, DISPC_VP_CONTROL, 0, 0, 0); | |
1033 | } | |
1034 | ||
1035 | void dispc_vp_unprepare(struct dispc_device *dispc, u32 hw_videoport) | |
1036 | { | |
1037 | if (dispc->feat->vp_bus_type[hw_videoport] == DISPC_VP_OLDI) { | |
1038 | dispc_vp_write(dispc, hw_videoport, DISPC_VP_DSS_OLDI_CFG, 0); | |
1039 | ||
1040 | dispc_oldi_tx_power(dispc, false); | |
1041 | } | |
1042 | } | |
1043 | ||
1044 | bool dispc_vp_go_busy(struct dispc_device *dispc, u32 hw_videoport) | |
1045 | { | |
1046 | return VP_REG_GET(dispc, hw_videoport, DISPC_VP_CONTROL, 5, 5); | |
1047 | } | |
1048 | ||
1049 | void dispc_vp_go(struct dispc_device *dispc, u32 hw_videoport) | |
1050 | { | |
1051 | WARN_ON(VP_REG_GET(dispc, hw_videoport, DISPC_VP_CONTROL, 5, 5)); | |
1052 | VP_REG_FLD_MOD(dispc, hw_videoport, DISPC_VP_CONTROL, 1, 5, 5); | |
1053 | } | |
1054 | ||
1055 | enum c8_to_c12_mode { C8_TO_C12_REPLICATE, C8_TO_C12_MAX, C8_TO_C12_MIN }; | |
1056 | ||
1057 | static u16 c8_to_c12(u8 c8, enum c8_to_c12_mode mode) | |
1058 | { | |
1059 | u16 c12; | |
1060 | ||
1061 | c12 = c8 << 4; | |
1062 | ||
1063 | switch (mode) { | |
1064 | case C8_TO_C12_REPLICATE: | |
1065 | /* Copy c8 4 MSB to 4 LSB for full scale c12 */ | |
1066 | c12 |= c8 >> 4; | |
1067 | break; | |
1068 | case C8_TO_C12_MAX: | |
1069 | c12 |= 0xF; | |
1070 | break; | |
1071 | default: | |
1072 | case C8_TO_C12_MIN: | |
1073 | break; | |
1074 | } | |
1075 | ||
1076 | return c12; | |
1077 | } | |
1078 | ||
1079 | static u64 argb8888_to_argb12121212(u32 argb8888, enum c8_to_c12_mode m) | |
1080 | { | |
1081 | u8 a, r, g, b; | |
1082 | u64 v; | |
1083 | ||
1084 | a = (argb8888 >> 24) & 0xff; | |
1085 | r = (argb8888 >> 16) & 0xff; | |
1086 | g = (argb8888 >> 8) & 0xff; | |
1087 | b = (argb8888 >> 0) & 0xff; | |
1088 | ||
1089 | v = ((u64)c8_to_c12(a, m) << 36) | ((u64)c8_to_c12(r, m) << 24) | | |
1090 | ((u64)c8_to_c12(g, m) << 12) | (u64)c8_to_c12(b, m); | |
1091 | ||
1092 | return v; | |
1093 | } | |
1094 | ||
1095 | static void dispc_vp_set_default_color(struct dispc_device *dispc, | |
1096 | u32 hw_videoport, u32 default_color) | |
1097 | { | |
1098 | u64 v; | |
1099 | ||
1100 | v = argb8888_to_argb12121212(default_color, C8_TO_C12_REPLICATE); | |
1101 | ||
1102 | dispc_ovr_write(dispc, hw_videoport, | |
1103 | DISPC_OVR_DEFAULT_COLOR, v & 0xffffffff); | |
1104 | dispc_ovr_write(dispc, hw_videoport, | |
1105 | DISPC_OVR_DEFAULT_COLOR2, (v >> 32) & 0xffff); | |
1106 | } | |
1107 | ||
1108 | enum drm_mode_status dispc_vp_mode_valid(struct dispc_device *dispc, | |
1109 | u32 hw_videoport, | |
1110 | const struct drm_display_mode *mode) | |
1111 | { | |
1112 | u32 hsw, hfp, hbp, vsw, vfp, vbp; | |
1113 | enum dispc_vp_bus_type bus_type; | |
1114 | int max_pclk; | |
1115 | ||
1116 | bus_type = dispc->feat->vp_bus_type[hw_videoport]; | |
1117 | ||
1118 | max_pclk = dispc->feat->max_pclk_khz[bus_type]; | |
1119 | ||
1120 | if (WARN_ON(max_pclk == 0)) | |
1121 | return MODE_BAD; | |
1122 | ||
1123 | if (mode->clock < dispc->feat->min_pclk_khz) | |
1124 | return MODE_CLOCK_LOW; | |
1125 | ||
1126 | if (mode->clock > max_pclk) | |
1127 | return MODE_CLOCK_HIGH; | |
1128 | ||
1129 | if (mode->hdisplay > 4096) | |
1130 | return MODE_BAD; | |
1131 | ||
1132 | if (mode->vdisplay > 4096) | |
1133 | return MODE_BAD; | |
1134 | ||
1135 | /* TODO: add interlace support */ | |
1136 | if (mode->flags & DRM_MODE_FLAG_INTERLACE) | |
1137 | return MODE_NO_INTERLACE; | |
1138 | ||
1139 | /* | |
1140 | * Enforce the output width is divisible by 2. Actually this | |
1141 | * is only needed in following cases: | |
1142 | * - YUV output selected (BT656, BT1120) | |
1143 | * - Dithering enabled | |
1144 | * - TDM with TDMCycleFormat == 3 | |
1145 | * But for simplicity we enforce that always. | |
1146 | */ | |
1147 | if ((mode->hdisplay % 2) != 0) | |
1148 | return MODE_BAD_HVALUE; | |
1149 | ||
1150 | hfp = mode->hsync_start - mode->hdisplay; | |
1151 | hsw = mode->hsync_end - mode->hsync_start; | |
1152 | hbp = mode->htotal - mode->hsync_end; | |
1153 | ||
1154 | vfp = mode->vsync_start - mode->vdisplay; | |
1155 | vsw = mode->vsync_end - mode->vsync_start; | |
1156 | vbp = mode->vtotal - mode->vsync_end; | |
1157 | ||
1158 | if (hsw < 1 || hsw > 256 || | |
1159 | hfp < 1 || hfp > 4096 || | |
1160 | hbp < 1 || hbp > 4096) | |
1161 | return MODE_BAD_HVALUE; | |
1162 | ||
1163 | if (vsw < 1 || vsw > 256 || | |
1164 | vfp > 4095 || vbp > 4095) | |
1165 | return MODE_BAD_VVALUE; | |
1166 | ||
1167 | if (dispc->memory_bandwidth_limit) { | |
1168 | const unsigned int bpp = 4; | |
1169 | u64 bandwidth; | |
1170 | ||
1171 | bandwidth = 1000 * mode->clock; | |
1172 | bandwidth = bandwidth * mode->hdisplay * mode->vdisplay * bpp; | |
1173 | bandwidth = div_u64(bandwidth, mode->htotal * mode->vtotal); | |
1174 | ||
1175 | if (dispc->memory_bandwidth_limit < bandwidth) | |
1176 | return MODE_BAD; | |
1177 | } | |
1178 | ||
1179 | return MODE_OK; | |
1180 | } | |
1181 | ||
1182 | int dispc_vp_enable_clk(struct dispc_device *dispc, u32 hw_videoport) | |
1183 | { | |
1184 | int ret = clk_prepare_enable(dispc->vp_clk[hw_videoport]); | |
1185 | ||
1186 | if (ret) | |
1187 | dev_err(dispc->dev, "%s: enabling clk failed: %d\n", __func__, | |
1188 | ret); | |
1189 | ||
1190 | return ret; | |
1191 | } | |
1192 | ||
1193 | void dispc_vp_disable_clk(struct dispc_device *dispc, u32 hw_videoport) | |
1194 | { | |
1195 | clk_disable_unprepare(dispc->vp_clk[hw_videoport]); | |
1196 | } | |
1197 | ||
1198 | /* | |
1199 | * Calculate the percentage difference between the requested pixel clock rate | |
1200 | * and the effective rate resulting from calculating the clock divider value. | |
1201 | */ | |
1202 | static | |
1203 | unsigned int dispc_pclk_diff(unsigned long rate, unsigned long real_rate) | |
1204 | { | |
1205 | int r = rate / 100, rr = real_rate / 100; | |
1206 | ||
1207 | return (unsigned int)(abs(((rr - r) * 100) / r)); | |
1208 | } | |
1209 | ||
1210 | int dispc_vp_set_clk_rate(struct dispc_device *dispc, u32 hw_videoport, | |
1211 | unsigned long rate) | |
1212 | { | |
1213 | int r; | |
1214 | unsigned long new_rate; | |
1215 | ||
1216 | r = clk_set_rate(dispc->vp_clk[hw_videoport], rate); | |
1217 | if (r) { | |
1218 | dev_err(dispc->dev, "vp%d: failed to set clk rate to %lu\n", | |
1219 | hw_videoport, rate); | |
1220 | return r; | |
1221 | } | |
1222 | ||
1223 | new_rate = clk_get_rate(dispc->vp_clk[hw_videoport]); | |
1224 | ||
1225 | if (dispc_pclk_diff(rate, new_rate) > 5) | |
1226 | dev_warn(dispc->dev, | |
652f67fd | 1227 | "vp%d: Clock rate %lu differs over 5%% from requested %lu\n", |
32a1795f JS |
1228 | hw_videoport, new_rate, rate); |
1229 | ||
1230 | dev_dbg(dispc->dev, "vp%d: new rate %lu Hz (requested %lu Hz)\n", | |
1231 | hw_videoport, clk_get_rate(dispc->vp_clk[hw_videoport]), rate); | |
1232 | ||
1233 | return 0; | |
1234 | } | |
1235 | ||
1236 | /* OVR */ | |
1237 | static void dispc_k2g_ovr_set_plane(struct dispc_device *dispc, | |
1238 | u32 hw_plane, u32 hw_videoport, | |
b33b5474 | 1239 | u32 x, u32 y, u32 layer) |
32a1795f JS |
1240 | { |
1241 | /* On k2g there is only one plane and no need for ovr */ | |
1242 | dispc_vid_write(dispc, hw_plane, DISPC_VID_K2G_POSITION, | |
1243 | x | (y << 16)); | |
1244 | } | |
1245 | ||
1246 | static void dispc_am65x_ovr_set_plane(struct dispc_device *dispc, | |
1247 | u32 hw_plane, u32 hw_videoport, | |
b33b5474 | 1248 | u32 x, u32 y, u32 layer) |
32a1795f | 1249 | { |
b33b5474 | 1250 | OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES(layer), |
32a1795f | 1251 | hw_plane, 4, 1); |
b33b5474 | 1252 | OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES(layer), |
32a1795f | 1253 | x, 17, 6); |
b33b5474 | 1254 | OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES(layer), |
32a1795f JS |
1255 | y, 30, 19); |
1256 | } | |
1257 | ||
1258 | static void dispc_j721e_ovr_set_plane(struct dispc_device *dispc, | |
1259 | u32 hw_plane, u32 hw_videoport, | |
b33b5474 | 1260 | u32 x, u32 y, u32 layer) |
32a1795f | 1261 | { |
b33b5474 | 1262 | OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES(layer), |
32a1795f | 1263 | hw_plane, 4, 1); |
b33b5474 | 1264 | OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES2(layer), |
32a1795f | 1265 | x, 13, 0); |
b33b5474 | 1266 | OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES2(layer), |
32a1795f JS |
1267 | y, 29, 16); |
1268 | } | |
1269 | ||
b33b5474 JS |
1270 | void dispc_ovr_set_plane(struct dispc_device *dispc, u32 hw_plane, |
1271 | u32 hw_videoport, u32 x, u32 y, u32 layer) | |
32a1795f JS |
1272 | { |
1273 | switch (dispc->feat->subrev) { | |
1274 | case DISPC_K2G: | |
1275 | dispc_k2g_ovr_set_plane(dispc, hw_plane, hw_videoport, | |
b33b5474 | 1276 | x, y, layer); |
32a1795f JS |
1277 | break; |
1278 | case DISPC_AM65X: | |
1279 | dispc_am65x_ovr_set_plane(dispc, hw_plane, hw_videoport, | |
b33b5474 | 1280 | x, y, layer); |
32a1795f JS |
1281 | break; |
1282 | case DISPC_J721E: | |
1283 | dispc_j721e_ovr_set_plane(dispc, hw_plane, hw_videoport, | |
b33b5474 | 1284 | x, y, layer); |
32a1795f JS |
1285 | break; |
1286 | default: | |
1287 | WARN_ON(1); | |
1288 | break; | |
1289 | } | |
1290 | } | |
1291 | ||
b33b5474 JS |
1292 | void dispc_ovr_enable_layer(struct dispc_device *dispc, |
1293 | u32 hw_videoport, u32 layer, bool enable) | |
32a1795f | 1294 | { |
b33b5474 JS |
1295 | if (dispc->feat->subrev == DISPC_K2G) |
1296 | return; | |
1297 | ||
1298 | OVR_REG_FLD_MOD(dispc, hw_videoport, DISPC_OVR_ATTRIBUTES(layer), | |
32a1795f JS |
1299 | !!enable, 0, 0); |
1300 | } | |
1301 | ||
1302 | /* CSC */ | |
1303 | enum csc_ctm { | |
1304 | CSC_RR, CSC_RG, CSC_RB, | |
1305 | CSC_GR, CSC_GG, CSC_GB, | |
1306 | CSC_BR, CSC_BG, CSC_BB, | |
1307 | }; | |
1308 | ||
1309 | enum csc_yuv2rgb { | |
1310 | CSC_RY, CSC_RCB, CSC_RCR, | |
1311 | CSC_GY, CSC_GCB, CSC_GCR, | |
1312 | CSC_BY, CSC_BCB, CSC_BCR, | |
1313 | }; | |
1314 | ||
1315 | enum csc_rgb2yuv { | |
1316 | CSC_YR, CSC_YG, CSC_YB, | |
1317 | CSC_CBR, CSC_CBG, CSC_CBB, | |
1318 | CSC_CRR, CSC_CRG, CSC_CRB, | |
1319 | }; | |
1320 | ||
1321 | struct dispc_csc_coef { | |
1322 | void (*to_regval)(const struct dispc_csc_coef *csc, u32 *regval); | |
1323 | int m[9]; | |
1324 | int preoffset[3]; | |
1325 | int postoffset[3]; | |
1326 | enum { CLIP_LIMITED_RANGE = 0, CLIP_FULL_RANGE = 1, } cliping; | |
1327 | const char *name; | |
1328 | }; | |
1329 | ||
1330 | #define DISPC_CSC_REGVAL_LEN 8 | |
1331 | ||
1332 | static | |
1333 | void dispc_csc_offset_regval(const struct dispc_csc_coef *csc, u32 *regval) | |
1334 | { | |
1335 | #define OVAL(x, y) (FLD_VAL(x, 15, 3) | FLD_VAL(y, 31, 19)) | |
1336 | regval[5] = OVAL(csc->preoffset[0], csc->preoffset[1]); | |
1337 | regval[6] = OVAL(csc->preoffset[2], csc->postoffset[0]); | |
1338 | regval[7] = OVAL(csc->postoffset[1], csc->postoffset[2]); | |
1339 | #undef OVAL | |
1340 | } | |
1341 | ||
1342 | #define CVAL(x, y) (FLD_VAL(x, 10, 0) | FLD_VAL(y, 26, 16)) | |
1343 | static | |
1344 | void dispc_csc_yuv2rgb_regval(const struct dispc_csc_coef *csc, u32 *regval) | |
1345 | { | |
1346 | regval[0] = CVAL(csc->m[CSC_RY], csc->m[CSC_RCR]); | |
1347 | regval[1] = CVAL(csc->m[CSC_RCB], csc->m[CSC_GY]); | |
1348 | regval[2] = CVAL(csc->m[CSC_GCR], csc->m[CSC_GCB]); | |
1349 | regval[3] = CVAL(csc->m[CSC_BY], csc->m[CSC_BCR]); | |
1350 | regval[4] = CVAL(csc->m[CSC_BCB], 0); | |
1351 | ||
1352 | dispc_csc_offset_regval(csc, regval); | |
1353 | } | |
1354 | ||
1355 | __maybe_unused static | |
1356 | void dispc_csc_rgb2yuv_regval(const struct dispc_csc_coef *csc, u32 *regval) | |
1357 | { | |
1358 | regval[0] = CVAL(csc->m[CSC_YR], csc->m[CSC_YG]); | |
1359 | regval[1] = CVAL(csc->m[CSC_YB], csc->m[CSC_CRR]); | |
1360 | regval[2] = CVAL(csc->m[CSC_CRG], csc->m[CSC_CRB]); | |
1361 | regval[3] = CVAL(csc->m[CSC_CBR], csc->m[CSC_CBG]); | |
1362 | regval[4] = CVAL(csc->m[CSC_CBB], 0); | |
1363 | ||
1364 | dispc_csc_offset_regval(csc, regval); | |
1365 | } | |
1366 | ||
1367 | static void dispc_csc_cpr_regval(const struct dispc_csc_coef *csc, | |
1368 | u32 *regval) | |
1369 | { | |
1370 | regval[0] = CVAL(csc->m[CSC_RR], csc->m[CSC_RG]); | |
1371 | regval[1] = CVAL(csc->m[CSC_RB], csc->m[CSC_GR]); | |
1372 | regval[2] = CVAL(csc->m[CSC_GG], csc->m[CSC_GB]); | |
1373 | regval[3] = CVAL(csc->m[CSC_BR], csc->m[CSC_BG]); | |
1374 | regval[4] = CVAL(csc->m[CSC_BB], 0); | |
1375 | ||
1376 | dispc_csc_offset_regval(csc, regval); | |
1377 | } | |
1378 | ||
1379 | #undef CVAL | |
1380 | ||
1381 | static void dispc_k2g_vid_write_csc(struct dispc_device *dispc, u32 hw_plane, | |
1382 | const struct dispc_csc_coef *csc) | |
1383 | { | |
1384 | static const u16 dispc_vid_csc_coef_reg[] = { | |
1385 | DISPC_VID_CSC_COEF(0), DISPC_VID_CSC_COEF(1), | |
1386 | DISPC_VID_CSC_COEF(2), DISPC_VID_CSC_COEF(3), | |
1387 | DISPC_VID_CSC_COEF(4), DISPC_VID_CSC_COEF(5), | |
1388 | DISPC_VID_CSC_COEF(6), /* K2G has no post offset support */ | |
1389 | }; | |
1390 | u32 regval[DISPC_CSC_REGVAL_LEN]; | |
1391 | unsigned int i; | |
1392 | ||
1393 | csc->to_regval(csc, regval); | |
1394 | ||
1395 | if (regval[7] != 0) | |
1396 | dev_warn(dispc->dev, "%s: No post offset support for %s\n", | |
1397 | __func__, csc->name); | |
1398 | ||
1399 | for (i = 0; i < ARRAY_SIZE(dispc_vid_csc_coef_reg); i++) | |
1400 | dispc_vid_write(dispc, hw_plane, dispc_vid_csc_coef_reg[i], | |
1401 | regval[i]); | |
1402 | } | |
1403 | ||
1404 | static void dispc_k3_vid_write_csc(struct dispc_device *dispc, u32 hw_plane, | |
1405 | const struct dispc_csc_coef *csc) | |
1406 | { | |
1407 | static const u16 dispc_vid_csc_coef_reg[DISPC_CSC_REGVAL_LEN] = { | |
1408 | DISPC_VID_CSC_COEF(0), DISPC_VID_CSC_COEF(1), | |
1409 | DISPC_VID_CSC_COEF(2), DISPC_VID_CSC_COEF(3), | |
1410 | DISPC_VID_CSC_COEF(4), DISPC_VID_CSC_COEF(5), | |
1411 | DISPC_VID_CSC_COEF(6), DISPC_VID_CSC_COEF7, | |
1412 | }; | |
1413 | u32 regval[DISPC_CSC_REGVAL_LEN]; | |
1414 | unsigned int i; | |
1415 | ||
1416 | csc->to_regval(csc, regval); | |
1417 | ||
1418 | for (i = 0; i < ARRAY_SIZE(dispc_vid_csc_coef_reg); i++) | |
1419 | dispc_vid_write(dispc, hw_plane, dispc_vid_csc_coef_reg[i], | |
1420 | regval[i]); | |
1421 | } | |
1422 | ||
1423 | /* YUV -> RGB, ITU-R BT.601, full range */ | |
1424 | static const struct dispc_csc_coef csc_yuv2rgb_bt601_full = { | |
1425 | dispc_csc_yuv2rgb_regval, | |
1426 | { 256, 0, 358, /* ry, rcb, rcr |1.000 0.000 1.402|*/ | |
1427 | 256, -88, -182, /* gy, gcb, gcr |1.000 -0.344 -0.714|*/ | |
1428 | 256, 452, 0, }, /* by, bcb, bcr |1.000 1.772 0.000|*/ | |
1429 | { 0, -2048, -2048, }, /* full range */ | |
1430 | { 0, 0, 0, }, | |
1431 | CLIP_FULL_RANGE, | |
1432 | "BT.601 Full", | |
1433 | }; | |
1434 | ||
1435 | /* YUV -> RGB, ITU-R BT.601, limited range */ | |
1436 | static const struct dispc_csc_coef csc_yuv2rgb_bt601_lim = { | |
1437 | dispc_csc_yuv2rgb_regval, | |
1438 | { 298, 0, 409, /* ry, rcb, rcr |1.164 0.000 1.596|*/ | |
1439 | 298, -100, -208, /* gy, gcb, gcr |1.164 -0.392 -0.813|*/ | |
1440 | 298, 516, 0, }, /* by, bcb, bcr |1.164 2.017 0.000|*/ | |
1441 | { -256, -2048, -2048, }, /* limited range */ | |
1442 | { 0, 0, 0, }, | |
1443 | CLIP_FULL_RANGE, | |
1444 | "BT.601 Limited", | |
1445 | }; | |
1446 | ||
1447 | /* YUV -> RGB, ITU-R BT.709, full range */ | |
1448 | static const struct dispc_csc_coef csc_yuv2rgb_bt709_full = { | |
1449 | dispc_csc_yuv2rgb_regval, | |
1450 | { 256, 0, 402, /* ry, rcb, rcr |1.000 0.000 1.570|*/ | |
1451 | 256, -48, -120, /* gy, gcb, gcr |1.000 -0.187 -0.467|*/ | |
1452 | 256, 475, 0, }, /* by, bcb, bcr |1.000 1.856 0.000|*/ | |
1453 | { 0, -2048, -2048, }, /* full range */ | |
1454 | { 0, 0, 0, }, | |
1455 | CLIP_FULL_RANGE, | |
1456 | "BT.709 Full", | |
1457 | }; | |
1458 | ||
1459 | /* YUV -> RGB, ITU-R BT.709, limited range */ | |
1460 | static const struct dispc_csc_coef csc_yuv2rgb_bt709_lim = { | |
1461 | dispc_csc_yuv2rgb_regval, | |
1462 | { 298, 0, 459, /* ry, rcb, rcr |1.164 0.000 1.793|*/ | |
1463 | 298, -55, -136, /* gy, gcb, gcr |1.164 -0.213 -0.533|*/ | |
1464 | 298, 541, 0, }, /* by, bcb, bcr |1.164 2.112 0.000|*/ | |
1465 | { -256, -2048, -2048, }, /* limited range */ | |
1466 | { 0, 0, 0, }, | |
1467 | CLIP_FULL_RANGE, | |
1468 | "BT.709 Limited", | |
1469 | }; | |
1470 | ||
1471 | static const struct { | |
1472 | enum drm_color_encoding encoding; | |
1473 | enum drm_color_range range; | |
1474 | const struct dispc_csc_coef *csc; | |
1475 | } dispc_csc_table[] = { | |
1476 | { DRM_COLOR_YCBCR_BT601, DRM_COLOR_YCBCR_FULL_RANGE, | |
1477 | &csc_yuv2rgb_bt601_full, }, | |
1478 | { DRM_COLOR_YCBCR_BT601, DRM_COLOR_YCBCR_LIMITED_RANGE, | |
1479 | &csc_yuv2rgb_bt601_lim, }, | |
1480 | { DRM_COLOR_YCBCR_BT709, DRM_COLOR_YCBCR_FULL_RANGE, | |
1481 | &csc_yuv2rgb_bt709_full, }, | |
1482 | { DRM_COLOR_YCBCR_BT709, DRM_COLOR_YCBCR_LIMITED_RANGE, | |
1483 | &csc_yuv2rgb_bt709_lim, }, | |
1484 | }; | |
1485 | ||
1486 | static const | |
1487 | struct dispc_csc_coef *dispc_find_csc(enum drm_color_encoding encoding, | |
1488 | enum drm_color_range range) | |
1489 | { | |
1490 | unsigned int i; | |
1491 | ||
1492 | for (i = 0; i < ARRAY_SIZE(dispc_csc_table); i++) { | |
1493 | if (dispc_csc_table[i].encoding == encoding && | |
1494 | dispc_csc_table[i].range == range) { | |
1495 | return dispc_csc_table[i].csc; | |
1496 | } | |
1497 | } | |
1498 | return NULL; | |
1499 | } | |
1500 | ||
1501 | static void dispc_vid_csc_setup(struct dispc_device *dispc, u32 hw_plane, | |
1502 | const struct drm_plane_state *state) | |
1503 | { | |
46cf7d98 | 1504 | const struct dispc_csc_coef *coef; |
32a1795f JS |
1505 | |
1506 | coef = dispc_find_csc(state->color_encoding, state->color_range); | |
1507 | if (!coef) { | |
1508 | dev_err(dispc->dev, "%s: CSC (%u,%u) not found\n", | |
1509 | __func__, state->color_encoding, state->color_range); | |
1510 | return; | |
1511 | } | |
1512 | ||
1513 | if (dispc->feat->subrev == DISPC_K2G) | |
1514 | dispc_k2g_vid_write_csc(dispc, hw_plane, coef); | |
1515 | else | |
1516 | dispc_k3_vid_write_csc(dispc, hw_plane, coef); | |
1517 | } | |
1518 | ||
1519 | static void dispc_vid_csc_enable(struct dispc_device *dispc, u32 hw_plane, | |
1520 | bool enable) | |
1521 | { | |
1522 | VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, !!enable, 9, 9); | |
1523 | } | |
1524 | ||
1525 | /* SCALER */ | |
1526 | ||
1527 | static u32 dispc_calc_fir_inc(u32 in, u32 out) | |
1528 | { | |
1529 | return (u32)div_u64(0x200000ull * in, out); | |
1530 | } | |
1531 | ||
1532 | enum dispc_vid_fir_coef_set { | |
1533 | DISPC_VID_FIR_COEF_HORIZ, | |
1534 | DISPC_VID_FIR_COEF_HORIZ_UV, | |
1535 | DISPC_VID_FIR_COEF_VERT, | |
1536 | DISPC_VID_FIR_COEF_VERT_UV, | |
1537 | }; | |
1538 | ||
1539 | static void dispc_vid_write_fir_coefs(struct dispc_device *dispc, | |
1540 | u32 hw_plane, | |
1541 | enum dispc_vid_fir_coef_set coef_set, | |
1542 | const struct tidss_scale_coefs *coefs) | |
1543 | { | |
1544 | static const u16 c0_regs[] = { | |
1545 | [DISPC_VID_FIR_COEF_HORIZ] = DISPC_VID_FIR_COEFS_H0, | |
1546 | [DISPC_VID_FIR_COEF_HORIZ_UV] = DISPC_VID_FIR_COEFS_H0_C, | |
1547 | [DISPC_VID_FIR_COEF_VERT] = DISPC_VID_FIR_COEFS_V0, | |
1548 | [DISPC_VID_FIR_COEF_VERT_UV] = DISPC_VID_FIR_COEFS_V0_C, | |
1549 | }; | |
1550 | ||
1551 | static const u16 c12_regs[] = { | |
1552 | [DISPC_VID_FIR_COEF_HORIZ] = DISPC_VID_FIR_COEFS_H12, | |
1553 | [DISPC_VID_FIR_COEF_HORIZ_UV] = DISPC_VID_FIR_COEFS_H12_C, | |
1554 | [DISPC_VID_FIR_COEF_VERT] = DISPC_VID_FIR_COEFS_V12, | |
1555 | [DISPC_VID_FIR_COEF_VERT_UV] = DISPC_VID_FIR_COEFS_V12_C, | |
1556 | }; | |
1557 | ||
1558 | const u16 c0_base = c0_regs[coef_set]; | |
1559 | const u16 c12_base = c12_regs[coef_set]; | |
1560 | int phase; | |
1561 | ||
1562 | if (!coefs) { | |
1563 | dev_err(dispc->dev, "%s: No coefficients given.\n", __func__); | |
1564 | return; | |
1565 | } | |
1566 | ||
1567 | for (phase = 0; phase <= 8; ++phase) { | |
1568 | u16 reg = c0_base + phase * 4; | |
1569 | u16 c0 = coefs->c0[phase]; | |
1570 | ||
1571 | dispc_vid_write(dispc, hw_plane, reg, c0); | |
1572 | } | |
1573 | ||
1574 | for (phase = 0; phase <= 15; ++phase) { | |
1575 | u16 reg = c12_base + phase * 4; | |
1576 | s16 c1, c2; | |
1577 | u32 c12; | |
1578 | ||
1579 | c1 = coefs->c1[phase]; | |
1580 | c2 = coefs->c2[phase]; | |
1581 | c12 = FLD_VAL(c1, 19, 10) | FLD_VAL(c2, 29, 20); | |
1582 | ||
1583 | dispc_vid_write(dispc, hw_plane, reg, c12); | |
1584 | } | |
1585 | } | |
1586 | ||
1587 | static bool dispc_fourcc_is_yuv(u32 fourcc) | |
1588 | { | |
1589 | switch (fourcc) { | |
1590 | case DRM_FORMAT_YUYV: | |
1591 | case DRM_FORMAT_UYVY: | |
1592 | case DRM_FORMAT_NV12: | |
1593 | return true; | |
1594 | default: | |
1595 | return false; | |
1596 | } | |
1597 | } | |
1598 | ||
1599 | struct dispc_scaling_params { | |
1600 | int xinc, yinc; | |
1601 | u32 in_w, in_h, in_w_uv, in_h_uv; | |
1602 | u32 fir_xinc, fir_yinc, fir_xinc_uv, fir_yinc_uv; | |
1603 | bool scale_x, scale_y; | |
1604 | const struct tidss_scale_coefs *xcoef, *ycoef, *xcoef_uv, *ycoef_uv; | |
1605 | bool five_taps; | |
1606 | }; | |
1607 | ||
1608 | static int dispc_vid_calc_scaling(struct dispc_device *dispc, | |
1609 | const struct drm_plane_state *state, | |
1610 | struct dispc_scaling_params *sp, | |
1611 | bool lite_plane) | |
1612 | { | |
1613 | const struct dispc_features_scaling *f = &dispc->feat->scaling; | |
1614 | u32 fourcc = state->fb->format->format; | |
1615 | u32 in_width_max_5tap = f->in_width_max_5tap_rgb; | |
1616 | u32 in_width_max_3tap = f->in_width_max_3tap_rgb; | |
1617 | u32 downscale_limit; | |
1618 | u32 in_width_max; | |
1619 | ||
1620 | memset(sp, 0, sizeof(*sp)); | |
1621 | sp->xinc = 1; | |
1622 | sp->yinc = 1; | |
1623 | sp->in_w = state->src_w >> 16; | |
1624 | sp->in_w_uv = sp->in_w; | |
1625 | sp->in_h = state->src_h >> 16; | |
1626 | sp->in_h_uv = sp->in_h; | |
1627 | ||
1628 | sp->scale_x = sp->in_w != state->crtc_w; | |
1629 | sp->scale_y = sp->in_h != state->crtc_h; | |
1630 | ||
1631 | if (dispc_fourcc_is_yuv(fourcc)) { | |
1632 | in_width_max_5tap = f->in_width_max_5tap_yuv; | |
1633 | in_width_max_3tap = f->in_width_max_3tap_yuv; | |
1634 | ||
1635 | sp->in_w_uv >>= 1; | |
1636 | sp->scale_x = true; | |
1637 | ||
1638 | if (fourcc == DRM_FORMAT_NV12) { | |
1639 | sp->in_h_uv >>= 1; | |
1640 | sp->scale_y = true; | |
1641 | } | |
1642 | } | |
1643 | ||
1644 | /* Skip the rest if no scaling is used */ | |
1645 | if ((!sp->scale_x && !sp->scale_y) || lite_plane) | |
1646 | return 0; | |
1647 | ||
1648 | if (sp->in_w > in_width_max_5tap) { | |
1649 | sp->five_taps = false; | |
1650 | in_width_max = in_width_max_3tap; | |
1651 | downscale_limit = f->downscale_limit_3tap; | |
1652 | } else { | |
1653 | sp->five_taps = true; | |
1654 | in_width_max = in_width_max_5tap; | |
1655 | downscale_limit = f->downscale_limit_5tap; | |
1656 | } | |
1657 | ||
1658 | if (sp->scale_x) { | |
1659 | sp->fir_xinc = dispc_calc_fir_inc(sp->in_w, state->crtc_w); | |
1660 | ||
1661 | if (sp->fir_xinc < dispc_calc_fir_inc(1, f->upscale_limit)) { | |
1662 | dev_dbg(dispc->dev, | |
1663 | "%s: X-scaling factor %u/%u > %u\n", | |
1664 | __func__, state->crtc_w, state->src_w >> 16, | |
1665 | f->upscale_limit); | |
1666 | return -EINVAL; | |
1667 | } | |
1668 | ||
1669 | if (sp->fir_xinc >= dispc_calc_fir_inc(downscale_limit, 1)) { | |
1670 | sp->xinc = DIV_ROUND_UP(DIV_ROUND_UP(sp->in_w, | |
1671 | state->crtc_w), | |
1672 | downscale_limit); | |
1673 | ||
1674 | if (sp->xinc > f->xinc_max) { | |
1675 | dev_dbg(dispc->dev, | |
1676 | "%s: X-scaling factor %u/%u < 1/%u\n", | |
1677 | __func__, state->crtc_w, | |
1678 | state->src_w >> 16, | |
1679 | downscale_limit * f->xinc_max); | |
1680 | return -EINVAL; | |
1681 | } | |
1682 | ||
1683 | sp->in_w = (state->src_w >> 16) / sp->xinc; | |
1684 | } | |
1685 | ||
1686 | while (sp->in_w > in_width_max) { | |
1687 | sp->xinc++; | |
1688 | sp->in_w = (state->src_w >> 16) / sp->xinc; | |
1689 | } | |
1690 | ||
1691 | if (sp->xinc > f->xinc_max) { | |
1692 | dev_dbg(dispc->dev, | |
652f67fd | 1693 | "%s: Too wide input buffer %u > %u\n", __func__, |
32a1795f JS |
1694 | state->src_w >> 16, in_width_max * f->xinc_max); |
1695 | return -EINVAL; | |
1696 | } | |
1697 | ||
1698 | /* | |
1699 | * We need even line length for YUV formats. Decimation | |
1700 | * can lead to odd length, so we need to make it even | |
1701 | * again. | |
1702 | */ | |
1703 | if (dispc_fourcc_is_yuv(fourcc)) | |
1704 | sp->in_w &= ~1; | |
1705 | ||
1706 | sp->fir_xinc = dispc_calc_fir_inc(sp->in_w, state->crtc_w); | |
1707 | } | |
1708 | ||
1709 | if (sp->scale_y) { | |
1710 | sp->fir_yinc = dispc_calc_fir_inc(sp->in_h, state->crtc_h); | |
1711 | ||
1712 | if (sp->fir_yinc < dispc_calc_fir_inc(1, f->upscale_limit)) { | |
1713 | dev_dbg(dispc->dev, | |
1714 | "%s: Y-scaling factor %u/%u > %u\n", | |
1715 | __func__, state->crtc_h, state->src_h >> 16, | |
1716 | f->upscale_limit); | |
1717 | return -EINVAL; | |
1718 | } | |
1719 | ||
1720 | if (sp->fir_yinc >= dispc_calc_fir_inc(downscale_limit, 1)) { | |
1721 | sp->yinc = DIV_ROUND_UP(DIV_ROUND_UP(sp->in_h, | |
1722 | state->crtc_h), | |
1723 | downscale_limit); | |
1724 | ||
1725 | sp->in_h /= sp->yinc; | |
1726 | sp->fir_yinc = dispc_calc_fir_inc(sp->in_h, | |
1727 | state->crtc_h); | |
1728 | } | |
1729 | } | |
1730 | ||
1731 | dev_dbg(dispc->dev, | |
1732 | "%s: %ux%u decim %ux%u -> %ux%u firinc %u.%03ux%u.%03u taps %u -> %ux%u\n", | |
1733 | __func__, state->src_w >> 16, state->src_h >> 16, | |
1734 | sp->xinc, sp->yinc, sp->in_w, sp->in_h, | |
1735 | sp->fir_xinc / 0x200000u, | |
1736 | ((sp->fir_xinc & 0x1FFFFFu) * 999u) / 0x1FFFFFu, | |
1737 | sp->fir_yinc / 0x200000u, | |
1738 | ((sp->fir_yinc & 0x1FFFFFu) * 999u) / 0x1FFFFFu, | |
1739 | sp->five_taps ? 5 : 3, | |
1740 | state->crtc_w, state->crtc_h); | |
1741 | ||
1742 | if (dispc_fourcc_is_yuv(fourcc)) { | |
1743 | if (sp->scale_x) { | |
1744 | sp->in_w_uv /= sp->xinc; | |
1745 | sp->fir_xinc_uv = dispc_calc_fir_inc(sp->in_w_uv, | |
1746 | state->crtc_w); | |
1747 | sp->xcoef_uv = tidss_get_scale_coefs(dispc->dev, | |
1748 | sp->fir_xinc_uv, | |
1749 | true); | |
1750 | } | |
1751 | if (sp->scale_y) { | |
1752 | sp->in_h_uv /= sp->yinc; | |
1753 | sp->fir_yinc_uv = dispc_calc_fir_inc(sp->in_h_uv, | |
1754 | state->crtc_h); | |
1755 | sp->ycoef_uv = tidss_get_scale_coefs(dispc->dev, | |
1756 | sp->fir_yinc_uv, | |
1757 | sp->five_taps); | |
1758 | } | |
1759 | } | |
1760 | ||
1761 | if (sp->scale_x) | |
1762 | sp->xcoef = tidss_get_scale_coefs(dispc->dev, sp->fir_xinc, | |
1763 | true); | |
1764 | ||
1765 | if (sp->scale_y) | |
1766 | sp->ycoef = tidss_get_scale_coefs(dispc->dev, sp->fir_yinc, | |
1767 | sp->five_taps); | |
1768 | ||
1769 | return 0; | |
1770 | } | |
1771 | ||
1772 | static void dispc_vid_set_scaling(struct dispc_device *dispc, | |
1773 | u32 hw_plane, | |
1774 | struct dispc_scaling_params *sp, | |
1775 | u32 fourcc) | |
1776 | { | |
1777 | /* HORIZONTAL RESIZE ENABLE */ | |
1778 | VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, | |
1779 | sp->scale_x, 7, 7); | |
1780 | ||
1781 | /* VERTICAL RESIZE ENABLE */ | |
1782 | VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, | |
1783 | sp->scale_y, 8, 8); | |
1784 | ||
1785 | /* Skip the rest if no scaling is used */ | |
1786 | if (!sp->scale_x && !sp->scale_y) | |
1787 | return; | |
1788 | ||
1789 | /* VERTICAL 5-TAPS */ | |
1790 | VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, | |
1791 | sp->five_taps, 21, 21); | |
1792 | ||
1793 | if (dispc_fourcc_is_yuv(fourcc)) { | |
1794 | if (sp->scale_x) { | |
1795 | dispc_vid_write(dispc, hw_plane, DISPC_VID_FIRH2, | |
1796 | sp->fir_xinc_uv); | |
1797 | dispc_vid_write_fir_coefs(dispc, hw_plane, | |
1798 | DISPC_VID_FIR_COEF_HORIZ_UV, | |
1799 | sp->xcoef_uv); | |
1800 | } | |
1801 | if (sp->scale_y) { | |
1802 | dispc_vid_write(dispc, hw_plane, DISPC_VID_FIRV2, | |
1803 | sp->fir_yinc_uv); | |
1804 | dispc_vid_write_fir_coefs(dispc, hw_plane, | |
1805 | DISPC_VID_FIR_COEF_VERT_UV, | |
1806 | sp->ycoef_uv); | |
1807 | } | |
1808 | } | |
1809 | ||
1810 | if (sp->scale_x) { | |
1811 | dispc_vid_write(dispc, hw_plane, DISPC_VID_FIRH, sp->fir_xinc); | |
1812 | dispc_vid_write_fir_coefs(dispc, hw_plane, | |
1813 | DISPC_VID_FIR_COEF_HORIZ, | |
1814 | sp->xcoef); | |
1815 | } | |
1816 | ||
1817 | if (sp->scale_y) { | |
1818 | dispc_vid_write(dispc, hw_plane, DISPC_VID_FIRV, sp->fir_yinc); | |
1819 | dispc_vid_write_fir_coefs(dispc, hw_plane, | |
1820 | DISPC_VID_FIR_COEF_VERT, sp->ycoef); | |
1821 | } | |
1822 | } | |
1823 | ||
1824 | /* OTHER */ | |
1825 | ||
1826 | static const struct { | |
1827 | u32 fourcc; | |
1828 | u8 dss_code; | |
1829 | } dispc_color_formats[] = { | |
1830 | { DRM_FORMAT_ARGB4444, 0x0, }, | |
1831 | { DRM_FORMAT_ABGR4444, 0x1, }, | |
1832 | { DRM_FORMAT_RGBA4444, 0x2, }, | |
1833 | ||
1834 | { DRM_FORMAT_RGB565, 0x3, }, | |
1835 | { DRM_FORMAT_BGR565, 0x4, }, | |
1836 | ||
1837 | { DRM_FORMAT_ARGB1555, 0x5, }, | |
1838 | { DRM_FORMAT_ABGR1555, 0x6, }, | |
1839 | ||
1840 | { DRM_FORMAT_ARGB8888, 0x7, }, | |
1841 | { DRM_FORMAT_ABGR8888, 0x8, }, | |
1842 | { DRM_FORMAT_RGBA8888, 0x9, }, | |
1843 | { DRM_FORMAT_BGRA8888, 0xa, }, | |
1844 | ||
1845 | { DRM_FORMAT_RGB888, 0xb, }, | |
1846 | { DRM_FORMAT_BGR888, 0xc, }, | |
1847 | ||
1848 | { DRM_FORMAT_ARGB2101010, 0xe, }, | |
1849 | { DRM_FORMAT_ABGR2101010, 0xf, }, | |
1850 | ||
1851 | { DRM_FORMAT_XRGB4444, 0x20, }, | |
1852 | { DRM_FORMAT_XBGR4444, 0x21, }, | |
1853 | { DRM_FORMAT_RGBX4444, 0x22, }, | |
1854 | ||
1855 | { DRM_FORMAT_ARGB1555, 0x25, }, | |
1856 | { DRM_FORMAT_ABGR1555, 0x26, }, | |
1857 | ||
1858 | { DRM_FORMAT_XRGB8888, 0x27, }, | |
1859 | { DRM_FORMAT_XBGR8888, 0x28, }, | |
1860 | { DRM_FORMAT_RGBX8888, 0x29, }, | |
1861 | { DRM_FORMAT_BGRX8888, 0x2a, }, | |
1862 | ||
1863 | { DRM_FORMAT_XRGB2101010, 0x2e, }, | |
1864 | { DRM_FORMAT_XBGR2101010, 0x2f, }, | |
1865 | ||
1866 | { DRM_FORMAT_YUYV, 0x3e, }, | |
1867 | { DRM_FORMAT_UYVY, 0x3f, }, | |
1868 | ||
1869 | { DRM_FORMAT_NV12, 0x3d, }, | |
1870 | }; | |
1871 | ||
1872 | static void dispc_plane_set_pixel_format(struct dispc_device *dispc, | |
1873 | u32 hw_plane, u32 fourcc) | |
1874 | { | |
1875 | unsigned int i; | |
1876 | ||
1877 | for (i = 0; i < ARRAY_SIZE(dispc_color_formats); ++i) { | |
1878 | if (dispc_color_formats[i].fourcc == fourcc) { | |
1879 | VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, | |
1880 | dispc_color_formats[i].dss_code, | |
1881 | 6, 1); | |
1882 | return; | |
1883 | } | |
1884 | } | |
1885 | ||
1886 | WARN_ON(1); | |
1887 | } | |
1888 | ||
1889 | const u32 *dispc_plane_formats(struct dispc_device *dispc, unsigned int *len) | |
1890 | { | |
1891 | WARN_ON(!dispc->fourccs); | |
1892 | ||
1893 | *len = dispc->num_fourccs; | |
1894 | ||
1895 | return dispc->fourccs; | |
1896 | } | |
1897 | ||
1898 | static s32 pixinc(int pixels, u8 ps) | |
1899 | { | |
1900 | if (pixels == 1) | |
1901 | return 1; | |
1902 | else if (pixels > 1) | |
1903 | return 1 + (pixels - 1) * ps; | |
1904 | else if (pixels < 0) | |
1905 | return 1 - (-pixels + 1) * ps; | |
1906 | ||
1907 | WARN_ON(1); | |
1908 | return 0; | |
1909 | } | |
1910 | ||
1911 | int dispc_plane_check(struct dispc_device *dispc, u32 hw_plane, | |
1912 | const struct drm_plane_state *state, | |
1913 | u32 hw_videoport) | |
1914 | { | |
1915 | bool lite = dispc->feat->vid_lite[hw_plane]; | |
1916 | u32 fourcc = state->fb->format->format; | |
1917 | bool need_scaling = state->src_w >> 16 != state->crtc_w || | |
1918 | state->src_h >> 16 != state->crtc_h; | |
1919 | struct dispc_scaling_params scaling; | |
1920 | int ret; | |
1921 | ||
1922 | if (dispc_fourcc_is_yuv(fourcc)) { | |
1923 | if (!dispc_find_csc(state->color_encoding, | |
1924 | state->color_range)) { | |
1925 | dev_dbg(dispc->dev, | |
1926 | "%s: Unsupported CSC (%u,%u) for HW plane %u\n", | |
1927 | __func__, state->color_encoding, | |
1928 | state->color_range, hw_plane); | |
1929 | return -EINVAL; | |
1930 | } | |
1931 | } | |
1932 | ||
1933 | if (need_scaling) { | |
1934 | if (lite) { | |
1935 | dev_dbg(dispc->dev, | |
1936 | "%s: Lite plane %u can't scale %ux%u!=%ux%u\n", | |
1937 | __func__, hw_plane, | |
1938 | state->src_w >> 16, state->src_h >> 16, | |
1939 | state->crtc_w, state->crtc_h); | |
1940 | return -EINVAL; | |
1941 | } | |
1942 | ret = dispc_vid_calc_scaling(dispc, state, &scaling, false); | |
1943 | if (ret) | |
1944 | return ret; | |
1945 | } | |
1946 | ||
1947 | return 0; | |
1948 | } | |
1949 | ||
1950 | static | |
1951 | dma_addr_t dispc_plane_state_paddr(const struct drm_plane_state *state) | |
1952 | { | |
1953 | struct drm_framebuffer *fb = state->fb; | |
1954 | struct drm_gem_cma_object *gem; | |
1955 | u32 x = state->src_x >> 16; | |
1956 | u32 y = state->src_y >> 16; | |
1957 | ||
1958 | gem = drm_fb_cma_get_gem_obj(state->fb, 0); | |
1959 | ||
1960 | return gem->paddr + fb->offsets[0] + x * fb->format->cpp[0] + | |
1961 | y * fb->pitches[0]; | |
1962 | } | |
1963 | ||
1964 | static | |
1965 | dma_addr_t dispc_plane_state_p_uv_addr(const struct drm_plane_state *state) | |
1966 | { | |
1967 | struct drm_framebuffer *fb = state->fb; | |
1968 | struct drm_gem_cma_object *gem; | |
1969 | u32 x = state->src_x >> 16; | |
1970 | u32 y = state->src_y >> 16; | |
1971 | ||
1972 | if (WARN_ON(state->fb->format->num_planes != 2)) | |
1973 | return 0; | |
1974 | ||
1975 | gem = drm_fb_cma_get_gem_obj(fb, 1); | |
1976 | ||
1977 | return gem->paddr + fb->offsets[1] + | |
1978 | (x * fb->format->cpp[1] / fb->format->hsub) + | |
1979 | (y * fb->pitches[1] / fb->format->vsub); | |
1980 | } | |
1981 | ||
1982 | int dispc_plane_setup(struct dispc_device *dispc, u32 hw_plane, | |
1983 | const struct drm_plane_state *state, | |
1984 | u32 hw_videoport) | |
1985 | { | |
1986 | bool lite = dispc->feat->vid_lite[hw_plane]; | |
1987 | u32 fourcc = state->fb->format->format; | |
1988 | u16 cpp = state->fb->format->cpp[0]; | |
1989 | u32 fb_width = state->fb->pitches[0] / cpp; | |
1990 | dma_addr_t paddr = dispc_plane_state_paddr(state); | |
1991 | struct dispc_scaling_params scale; | |
1992 | ||
1993 | dispc_vid_calc_scaling(dispc, state, &scale, lite); | |
1994 | ||
1995 | dispc_plane_set_pixel_format(dispc, hw_plane, fourcc); | |
1996 | ||
1997 | dispc_vid_write(dispc, hw_plane, DISPC_VID_BA_0, paddr & 0xffffffff); | |
1998 | dispc_vid_write(dispc, hw_plane, DISPC_VID_BA_EXT_0, (u64)paddr >> 32); | |
1999 | dispc_vid_write(dispc, hw_plane, DISPC_VID_BA_1, paddr & 0xffffffff); | |
2000 | dispc_vid_write(dispc, hw_plane, DISPC_VID_BA_EXT_1, (u64)paddr >> 32); | |
2001 | ||
2002 | dispc_vid_write(dispc, hw_plane, DISPC_VID_PICTURE_SIZE, | |
2003 | (scale.in_w - 1) | ((scale.in_h - 1) << 16)); | |
2004 | ||
2005 | /* For YUV422 format we use the macropixel size for pixel inc */ | |
2006 | if (fourcc == DRM_FORMAT_YUYV || fourcc == DRM_FORMAT_UYVY) | |
2007 | dispc_vid_write(dispc, hw_plane, DISPC_VID_PIXEL_INC, | |
2008 | pixinc(scale.xinc, cpp * 2)); | |
2009 | else | |
2010 | dispc_vid_write(dispc, hw_plane, DISPC_VID_PIXEL_INC, | |
2011 | pixinc(scale.xinc, cpp)); | |
2012 | ||
2013 | dispc_vid_write(dispc, hw_plane, DISPC_VID_ROW_INC, | |
2014 | pixinc(1 + (scale.yinc * fb_width - | |
2015 | scale.xinc * scale.in_w), | |
2016 | cpp)); | |
2017 | ||
2018 | if (state->fb->format->num_planes == 2) { | |
2019 | u16 cpp_uv = state->fb->format->cpp[1]; | |
2020 | u32 fb_width_uv = state->fb->pitches[1] / cpp_uv; | |
2021 | dma_addr_t p_uv_addr = dispc_plane_state_p_uv_addr(state); | |
2022 | ||
2023 | dispc_vid_write(dispc, hw_plane, | |
2024 | DISPC_VID_BA_UV_0, p_uv_addr & 0xffffffff); | |
2025 | dispc_vid_write(dispc, hw_plane, | |
2026 | DISPC_VID_BA_UV_EXT_0, (u64)p_uv_addr >> 32); | |
2027 | dispc_vid_write(dispc, hw_plane, | |
2028 | DISPC_VID_BA_UV_1, p_uv_addr & 0xffffffff); | |
2029 | dispc_vid_write(dispc, hw_plane, | |
2030 | DISPC_VID_BA_UV_EXT_1, (u64)p_uv_addr >> 32); | |
2031 | ||
2032 | dispc_vid_write(dispc, hw_plane, DISPC_VID_ROW_INC_UV, | |
2033 | pixinc(1 + (scale.yinc * fb_width_uv - | |
2034 | scale.xinc * scale.in_w_uv), | |
2035 | cpp_uv)); | |
2036 | } | |
2037 | ||
2038 | if (!lite) { | |
2039 | dispc_vid_write(dispc, hw_plane, DISPC_VID_SIZE, | |
2040 | (state->crtc_w - 1) | | |
2041 | ((state->crtc_h - 1) << 16)); | |
2042 | ||
2043 | dispc_vid_set_scaling(dispc, hw_plane, &scale, fourcc); | |
2044 | } | |
2045 | ||
2046 | /* enable YUV->RGB color conversion */ | |
2047 | if (dispc_fourcc_is_yuv(fourcc)) { | |
2048 | dispc_vid_csc_setup(dispc, hw_plane, state); | |
2049 | dispc_vid_csc_enable(dispc, hw_plane, true); | |
2050 | } else { | |
2051 | dispc_vid_csc_enable(dispc, hw_plane, false); | |
2052 | } | |
2053 | ||
2054 | dispc_vid_write(dispc, hw_plane, DISPC_VID_GLOBAL_ALPHA, | |
2055 | 0xFF & (state->alpha >> 8)); | |
2056 | ||
2057 | if (state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI) | |
2058 | VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, 1, | |
2059 | 28, 28); | |
2060 | else | |
2061 | VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, 0, | |
2062 | 28, 28); | |
2063 | ||
32a1795f JS |
2064 | return 0; |
2065 | } | |
2066 | ||
2067 | int dispc_plane_enable(struct dispc_device *dispc, u32 hw_plane, bool enable) | |
2068 | { | |
32a1795f JS |
2069 | VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, !!enable, 0, 0); |
2070 | ||
2071 | return 0; | |
2072 | } | |
2073 | ||
2074 | static u32 dispc_vid_get_fifo_size(struct dispc_device *dispc, u32 hw_plane) | |
2075 | { | |
2076 | return VID_REG_GET(dispc, hw_plane, DISPC_VID_BUF_SIZE_STATUS, 15, 0); | |
2077 | } | |
2078 | ||
2079 | static void dispc_vid_set_mflag_threshold(struct dispc_device *dispc, | |
2080 | u32 hw_plane, u32 low, u32 high) | |
2081 | { | |
2082 | dispc_vid_write(dispc, hw_plane, DISPC_VID_MFLAG_THRESHOLD, | |
2083 | FLD_VAL(high, 31, 16) | FLD_VAL(low, 15, 0)); | |
2084 | } | |
2085 | ||
2086 | static void dispc_vid_set_buf_threshold(struct dispc_device *dispc, | |
2087 | u32 hw_plane, u32 low, u32 high) | |
2088 | { | |
2089 | dispc_vid_write(dispc, hw_plane, DISPC_VID_BUF_THRESHOLD, | |
2090 | FLD_VAL(high, 31, 16) | FLD_VAL(low, 15, 0)); | |
2091 | } | |
2092 | ||
2093 | static void dispc_k2g_plane_init(struct dispc_device *dispc) | |
2094 | { | |
2095 | unsigned int hw_plane; | |
2096 | ||
2097 | dev_dbg(dispc->dev, "%s()\n", __func__); | |
2098 | ||
2099 | /* MFLAG_CTRL = ENABLED */ | |
2100 | REG_FLD_MOD(dispc, DISPC_GLOBAL_MFLAG_ATTRIBUTE, 2, 1, 0); | |
2101 | /* MFLAG_START = MFLAGNORMALSTARTMODE */ | |
2102 | REG_FLD_MOD(dispc, DISPC_GLOBAL_MFLAG_ATTRIBUTE, 0, 6, 6); | |
2103 | ||
2104 | for (hw_plane = 0; hw_plane < dispc->feat->num_planes; hw_plane++) { | |
2105 | u32 size = dispc_vid_get_fifo_size(dispc, hw_plane); | |
2106 | u32 thr_low, thr_high; | |
2107 | u32 mflag_low, mflag_high; | |
2108 | u32 preload; | |
2109 | ||
2110 | thr_high = size - 1; | |
2111 | thr_low = size / 2; | |
2112 | ||
2113 | mflag_high = size * 2 / 3; | |
2114 | mflag_low = size / 3; | |
2115 | ||
2116 | preload = thr_low; | |
2117 | ||
2118 | dev_dbg(dispc->dev, | |
2119 | "%s: bufsize %u, buf_threshold %u/%u, mflag threshold %u/%u preload %u\n", | |
2120 | dispc->feat->vid_name[hw_plane], | |
2121 | size, | |
2122 | thr_high, thr_low, | |
2123 | mflag_high, mflag_low, | |
2124 | preload); | |
2125 | ||
2126 | dispc_vid_set_buf_threshold(dispc, hw_plane, | |
2127 | thr_low, thr_high); | |
2128 | dispc_vid_set_mflag_threshold(dispc, hw_plane, | |
2129 | mflag_low, mflag_high); | |
2130 | ||
2131 | dispc_vid_write(dispc, hw_plane, DISPC_VID_PRELOAD, preload); | |
2132 | ||
2133 | /* | |
2134 | * Prefetch up to fifo high-threshold value to minimize the | |
2135 | * possibility of underflows. Note that this means the PRELOAD | |
2136 | * register is ignored. | |
2137 | */ | |
2138 | VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, 1, | |
2139 | 19, 19); | |
2140 | } | |
2141 | } | |
2142 | ||
2143 | static void dispc_k3_plane_init(struct dispc_device *dispc) | |
2144 | { | |
2145 | unsigned int hw_plane; | |
2146 | u32 cba_lo_pri = 1; | |
2147 | u32 cba_hi_pri = 0; | |
2148 | ||
2149 | dev_dbg(dispc->dev, "%s()\n", __func__); | |
2150 | ||
2151 | REG_FLD_MOD(dispc, DSS_CBA_CFG, cba_lo_pri, 2, 0); | |
2152 | REG_FLD_MOD(dispc, DSS_CBA_CFG, cba_hi_pri, 5, 3); | |
2153 | ||
2154 | /* MFLAG_CTRL = ENABLED */ | |
2155 | REG_FLD_MOD(dispc, DISPC_GLOBAL_MFLAG_ATTRIBUTE, 2, 1, 0); | |
2156 | /* MFLAG_START = MFLAGNORMALSTARTMODE */ | |
2157 | REG_FLD_MOD(dispc, DISPC_GLOBAL_MFLAG_ATTRIBUTE, 0, 6, 6); | |
2158 | ||
2159 | for (hw_plane = 0; hw_plane < dispc->feat->num_planes; hw_plane++) { | |
2160 | u32 size = dispc_vid_get_fifo_size(dispc, hw_plane); | |
2161 | u32 thr_low, thr_high; | |
2162 | u32 mflag_low, mflag_high; | |
2163 | u32 preload; | |
2164 | ||
2165 | thr_high = size - 1; | |
2166 | thr_low = size / 2; | |
2167 | ||
2168 | mflag_high = size * 2 / 3; | |
2169 | mflag_low = size / 3; | |
2170 | ||
2171 | preload = thr_low; | |
2172 | ||
2173 | dev_dbg(dispc->dev, | |
2174 | "%s: bufsize %u, buf_threshold %u/%u, mflag threshold %u/%u preload %u\n", | |
2175 | dispc->feat->vid_name[hw_plane], | |
2176 | size, | |
2177 | thr_high, thr_low, | |
2178 | mflag_high, mflag_low, | |
2179 | preload); | |
2180 | ||
2181 | dispc_vid_set_buf_threshold(dispc, hw_plane, | |
2182 | thr_low, thr_high); | |
2183 | dispc_vid_set_mflag_threshold(dispc, hw_plane, | |
2184 | mflag_low, mflag_high); | |
2185 | ||
2186 | dispc_vid_write(dispc, hw_plane, DISPC_VID_PRELOAD, preload); | |
2187 | ||
2188 | /* Prefech up to PRELOAD value */ | |
2189 | VID_REG_FLD_MOD(dispc, hw_plane, DISPC_VID_ATTRIBUTES, 0, | |
2190 | 19, 19); | |
2191 | } | |
2192 | } | |
2193 | ||
2194 | static void dispc_plane_init(struct dispc_device *dispc) | |
2195 | { | |
2196 | switch (dispc->feat->subrev) { | |
2197 | case DISPC_K2G: | |
2198 | dispc_k2g_plane_init(dispc); | |
2199 | break; | |
2200 | case DISPC_AM65X: | |
2201 | case DISPC_J721E: | |
2202 | dispc_k3_plane_init(dispc); | |
2203 | break; | |
2204 | default: | |
2205 | WARN_ON(1); | |
2206 | } | |
2207 | } | |
2208 | ||
2209 | static void dispc_vp_init(struct dispc_device *dispc) | |
2210 | { | |
2211 | unsigned int i; | |
2212 | ||
2213 | dev_dbg(dispc->dev, "%s()\n", __func__); | |
2214 | ||
2215 | /* Enable the gamma Shadow bit-field for all VPs*/ | |
2216 | for (i = 0; i < dispc->feat->num_vps; i++) | |
2217 | VP_REG_FLD_MOD(dispc, i, DISPC_VP_CONFIG, 1, 2, 2); | |
2218 | } | |
2219 | ||
2220 | static void dispc_initial_config(struct dispc_device *dispc) | |
2221 | { | |
2222 | dispc_plane_init(dispc); | |
2223 | dispc_vp_init(dispc); | |
2224 | ||
2225 | /* Note: Hardcoded DPI routing on J721E for now */ | |
2226 | if (dispc->feat->subrev == DISPC_J721E) { | |
2227 | dispc_write(dispc, DISPC_CONNECTIONS, | |
2228 | FLD_VAL(2, 3, 0) | /* VP1 to DPI0 */ | |
2229 | FLD_VAL(8, 7, 4) /* VP3 to DPI1 */ | |
2230 | ); | |
2231 | } | |
2232 | } | |
2233 | ||
2234 | static void dispc_k2g_vp_write_gamma_table(struct dispc_device *dispc, | |
2235 | u32 hw_videoport) | |
2236 | { | |
2237 | u32 *table = dispc->vp_data[hw_videoport].gamma_table; | |
2238 | u32 hwlen = dispc->feat->vp_feat.color.gamma_size; | |
2239 | unsigned int i; | |
2240 | ||
2241 | dev_dbg(dispc->dev, "%s: hw_videoport %d\n", __func__, hw_videoport); | |
2242 | ||
2243 | if (WARN_ON(dispc->feat->vp_feat.color.gamma_type != TIDSS_GAMMA_8BIT)) | |
2244 | return; | |
2245 | ||
2246 | for (i = 0; i < hwlen; ++i) { | |
2247 | u32 v = table[i]; | |
2248 | ||
2249 | v |= i << 24; | |
2250 | ||
2251 | dispc_vp_write(dispc, hw_videoport, DISPC_VP_K2G_GAMMA_TABLE, | |
2252 | v); | |
2253 | } | |
2254 | } | |
2255 | ||
2256 | static void dispc_am65x_vp_write_gamma_table(struct dispc_device *dispc, | |
2257 | u32 hw_videoport) | |
2258 | { | |
2259 | u32 *table = dispc->vp_data[hw_videoport].gamma_table; | |
2260 | u32 hwlen = dispc->feat->vp_feat.color.gamma_size; | |
2261 | unsigned int i; | |
2262 | ||
2263 | dev_dbg(dispc->dev, "%s: hw_videoport %d\n", __func__, hw_videoport); | |
2264 | ||
2265 | if (WARN_ON(dispc->feat->vp_feat.color.gamma_type != TIDSS_GAMMA_8BIT)) | |
2266 | return; | |
2267 | ||
2268 | for (i = 0; i < hwlen; ++i) { | |
2269 | u32 v = table[i]; | |
2270 | ||
2271 | v |= i << 24; | |
2272 | ||
2273 | dispc_vp_write(dispc, hw_videoport, DISPC_VP_GAMMA_TABLE, v); | |
2274 | } | |
2275 | } | |
2276 | ||
2277 | static void dispc_j721e_vp_write_gamma_table(struct dispc_device *dispc, | |
2278 | u32 hw_videoport) | |
2279 | { | |
2280 | u32 *table = dispc->vp_data[hw_videoport].gamma_table; | |
2281 | u32 hwlen = dispc->feat->vp_feat.color.gamma_size; | |
2282 | unsigned int i; | |
2283 | ||
2284 | dev_dbg(dispc->dev, "%s: hw_videoport %d\n", __func__, hw_videoport); | |
2285 | ||
2286 | if (WARN_ON(dispc->feat->vp_feat.color.gamma_type != TIDSS_GAMMA_10BIT)) | |
2287 | return; | |
2288 | ||
2289 | for (i = 0; i < hwlen; ++i) { | |
2290 | u32 v = table[i]; | |
2291 | ||
2292 | if (i == 0) | |
2293 | v |= 1 << 31; | |
2294 | ||
2295 | dispc_vp_write(dispc, hw_videoport, DISPC_VP_GAMMA_TABLE, v); | |
2296 | } | |
2297 | } | |
2298 | ||
2299 | static void dispc_vp_write_gamma_table(struct dispc_device *dispc, | |
2300 | u32 hw_videoport) | |
2301 | { | |
2302 | switch (dispc->feat->subrev) { | |
2303 | case DISPC_K2G: | |
2304 | dispc_k2g_vp_write_gamma_table(dispc, hw_videoport); | |
2305 | break; | |
2306 | case DISPC_AM65X: | |
2307 | dispc_am65x_vp_write_gamma_table(dispc, hw_videoport); | |
2308 | break; | |
2309 | case DISPC_J721E: | |
2310 | dispc_j721e_vp_write_gamma_table(dispc, hw_videoport); | |
2311 | break; | |
2312 | default: | |
2313 | WARN_ON(1); | |
2314 | break; | |
2315 | } | |
2316 | } | |
2317 | ||
2318 | static const struct drm_color_lut dispc_vp_gamma_default_lut[] = { | |
2319 | { .red = 0, .green = 0, .blue = 0, }, | |
2320 | { .red = U16_MAX, .green = U16_MAX, .blue = U16_MAX, }, | |
2321 | }; | |
2322 | ||
2323 | static void dispc_vp_set_gamma(struct dispc_device *dispc, | |
2324 | u32 hw_videoport, | |
2325 | const struct drm_color_lut *lut, | |
2326 | unsigned int length) | |
2327 | { | |
2328 | u32 *table = dispc->vp_data[hw_videoport].gamma_table; | |
2329 | u32 hwlen = dispc->feat->vp_feat.color.gamma_size; | |
2330 | u32 hwbits; | |
2331 | unsigned int i; | |
2332 | ||
2333 | dev_dbg(dispc->dev, "%s: hw_videoport %d, lut len %u, hw len %u\n", | |
2334 | __func__, hw_videoport, length, hwlen); | |
2335 | ||
2336 | if (dispc->feat->vp_feat.color.gamma_type == TIDSS_GAMMA_10BIT) | |
2337 | hwbits = 10; | |
2338 | else | |
2339 | hwbits = 8; | |
2340 | ||
2341 | if (!lut || length < 2) { | |
2342 | lut = dispc_vp_gamma_default_lut; | |
2343 | length = ARRAY_SIZE(dispc_vp_gamma_default_lut); | |
2344 | } | |
2345 | ||
2346 | for (i = 0; i < length - 1; ++i) { | |
2347 | unsigned int first = i * (hwlen - 1) / (length - 1); | |
2348 | unsigned int last = (i + 1) * (hwlen - 1) / (length - 1); | |
2349 | unsigned int w = last - first; | |
2350 | u16 r, g, b; | |
2351 | unsigned int j; | |
2352 | ||
2353 | if (w == 0) | |
2354 | continue; | |
2355 | ||
2356 | for (j = 0; j <= w; j++) { | |
2357 | r = (lut[i].red * (w - j) + lut[i + 1].red * j) / w; | |
2358 | g = (lut[i].green * (w - j) + lut[i + 1].green * j) / w; | |
2359 | b = (lut[i].blue * (w - j) + lut[i + 1].blue * j) / w; | |
2360 | ||
2361 | r >>= 16 - hwbits; | |
2362 | g >>= 16 - hwbits; | |
2363 | b >>= 16 - hwbits; | |
2364 | ||
2365 | table[first + j] = (r << (hwbits * 2)) | | |
2366 | (g << hwbits) | b; | |
2367 | } | |
2368 | } | |
2369 | ||
2370 | dispc_vp_write_gamma_table(dispc, hw_videoport); | |
2371 | } | |
2372 | ||
2373 | static s16 dispc_S31_32_to_s2_8(s64 coef) | |
2374 | { | |
2375 | u64 sign_bit = 1ULL << 63; | |
2376 | u64 cbits = (u64)coef; | |
2377 | s16 ret; | |
2378 | ||
2379 | if (cbits & sign_bit) | |
2380 | ret = -clamp_val(((cbits & ~sign_bit) >> 24), 0, 0x200); | |
2381 | else | |
2382 | ret = clamp_val(((cbits & ~sign_bit) >> 24), 0, 0x1FF); | |
2383 | ||
2384 | return ret; | |
2385 | } | |
2386 | ||
2387 | static void dispc_k2g_cpr_from_ctm(const struct drm_color_ctm *ctm, | |
2388 | struct dispc_csc_coef *cpr) | |
2389 | { | |
2390 | memset(cpr, 0, sizeof(*cpr)); | |
2391 | ||
2392 | cpr->to_regval = dispc_csc_cpr_regval; | |
2393 | cpr->m[CSC_RR] = dispc_S31_32_to_s2_8(ctm->matrix[0]); | |
2394 | cpr->m[CSC_RG] = dispc_S31_32_to_s2_8(ctm->matrix[1]); | |
2395 | cpr->m[CSC_RB] = dispc_S31_32_to_s2_8(ctm->matrix[2]); | |
2396 | cpr->m[CSC_GR] = dispc_S31_32_to_s2_8(ctm->matrix[3]); | |
2397 | cpr->m[CSC_GG] = dispc_S31_32_to_s2_8(ctm->matrix[4]); | |
2398 | cpr->m[CSC_GB] = dispc_S31_32_to_s2_8(ctm->matrix[5]); | |
2399 | cpr->m[CSC_BR] = dispc_S31_32_to_s2_8(ctm->matrix[6]); | |
2400 | cpr->m[CSC_BG] = dispc_S31_32_to_s2_8(ctm->matrix[7]); | |
2401 | cpr->m[CSC_BB] = dispc_S31_32_to_s2_8(ctm->matrix[8]); | |
2402 | } | |
2403 | ||
2404 | #define CVAL(xR, xG, xB) (FLD_VAL(xR, 9, 0) | FLD_VAL(xG, 20, 11) | \ | |
2405 | FLD_VAL(xB, 31, 22)) | |
2406 | ||
2407 | static void dispc_k2g_vp_csc_cpr_regval(const struct dispc_csc_coef *csc, | |
2408 | u32 *regval) | |
2409 | { | |
2410 | regval[0] = CVAL(csc->m[CSC_BB], csc->m[CSC_BG], csc->m[CSC_BR]); | |
2411 | regval[1] = CVAL(csc->m[CSC_GB], csc->m[CSC_GG], csc->m[CSC_GR]); | |
2412 | regval[2] = CVAL(csc->m[CSC_RB], csc->m[CSC_RG], csc->m[CSC_RR]); | |
2413 | } | |
2414 | ||
2415 | #undef CVAL | |
2416 | ||
2417 | static void dispc_k2g_vp_write_csc(struct dispc_device *dispc, u32 hw_videoport, | |
2418 | const struct dispc_csc_coef *csc) | |
2419 | { | |
2420 | static const u16 dispc_vp_cpr_coef_reg[] = { | |
2421 | DISPC_VP_CSC_COEF0, DISPC_VP_CSC_COEF1, DISPC_VP_CSC_COEF2, | |
2422 | /* K2G CPR is packed to three registers. */ | |
2423 | }; | |
2424 | u32 regval[DISPC_CSC_REGVAL_LEN]; | |
2425 | unsigned int i; | |
2426 | ||
2427 | dispc_k2g_vp_csc_cpr_regval(csc, regval); | |
2428 | ||
2429 | for (i = 0; i < ARRAY_SIZE(dispc_vp_cpr_coef_reg); i++) | |
2430 | dispc_vp_write(dispc, hw_videoport, dispc_vp_cpr_coef_reg[i], | |
2431 | regval[i]); | |
2432 | } | |
2433 | ||
2434 | static void dispc_k2g_vp_set_ctm(struct dispc_device *dispc, u32 hw_videoport, | |
2435 | struct drm_color_ctm *ctm) | |
2436 | { | |
2437 | u32 cprenable = 0; | |
2438 | ||
2439 | if (ctm) { | |
2440 | struct dispc_csc_coef cpr; | |
2441 | ||
2442 | dispc_k2g_cpr_from_ctm(ctm, &cpr); | |
2443 | dispc_k2g_vp_write_csc(dispc, hw_videoport, &cpr); | |
2444 | cprenable = 1; | |
2445 | } | |
2446 | ||
2447 | VP_REG_FLD_MOD(dispc, hw_videoport, DISPC_VP_CONFIG, | |
2448 | cprenable, 15, 15); | |
2449 | } | |
2450 | ||
2451 | static s16 dispc_S31_32_to_s3_8(s64 coef) | |
2452 | { | |
2453 | u64 sign_bit = 1ULL << 63; | |
2454 | u64 cbits = (u64)coef; | |
2455 | s16 ret; | |
2456 | ||
2457 | if (cbits & sign_bit) | |
2458 | ret = -clamp_val(((cbits & ~sign_bit) >> 24), 0, 0x400); | |
2459 | else | |
2460 | ret = clamp_val(((cbits & ~sign_bit) >> 24), 0, 0x3FF); | |
2461 | ||
2462 | return ret; | |
2463 | } | |
2464 | ||
2465 | static void dispc_csc_from_ctm(const struct drm_color_ctm *ctm, | |
2466 | struct dispc_csc_coef *cpr) | |
2467 | { | |
2468 | memset(cpr, 0, sizeof(*cpr)); | |
2469 | ||
2470 | cpr->to_regval = dispc_csc_cpr_regval; | |
2471 | cpr->m[CSC_RR] = dispc_S31_32_to_s3_8(ctm->matrix[0]); | |
2472 | cpr->m[CSC_RG] = dispc_S31_32_to_s3_8(ctm->matrix[1]); | |
2473 | cpr->m[CSC_RB] = dispc_S31_32_to_s3_8(ctm->matrix[2]); | |
2474 | cpr->m[CSC_GR] = dispc_S31_32_to_s3_8(ctm->matrix[3]); | |
2475 | cpr->m[CSC_GG] = dispc_S31_32_to_s3_8(ctm->matrix[4]); | |
2476 | cpr->m[CSC_GB] = dispc_S31_32_to_s3_8(ctm->matrix[5]); | |
2477 | cpr->m[CSC_BR] = dispc_S31_32_to_s3_8(ctm->matrix[6]); | |
2478 | cpr->m[CSC_BG] = dispc_S31_32_to_s3_8(ctm->matrix[7]); | |
2479 | cpr->m[CSC_BB] = dispc_S31_32_to_s3_8(ctm->matrix[8]); | |
2480 | } | |
2481 | ||
2482 | static void dispc_k3_vp_write_csc(struct dispc_device *dispc, u32 hw_videoport, | |
2483 | const struct dispc_csc_coef *csc) | |
2484 | { | |
2485 | static const u16 dispc_vp_csc_coef_reg[DISPC_CSC_REGVAL_LEN] = { | |
2486 | DISPC_VP_CSC_COEF0, DISPC_VP_CSC_COEF1, DISPC_VP_CSC_COEF2, | |
2487 | DISPC_VP_CSC_COEF3, DISPC_VP_CSC_COEF4, DISPC_VP_CSC_COEF5, | |
2488 | DISPC_VP_CSC_COEF6, DISPC_VP_CSC_COEF7, | |
2489 | }; | |
2490 | u32 regval[DISPC_CSC_REGVAL_LEN]; | |
2491 | unsigned int i; | |
2492 | ||
2493 | csc->to_regval(csc, regval); | |
2494 | ||
2495 | for (i = 0; i < ARRAY_SIZE(regval); i++) | |
2496 | dispc_vp_write(dispc, hw_videoport, dispc_vp_csc_coef_reg[i], | |
2497 | regval[i]); | |
2498 | } | |
2499 | ||
2500 | static void dispc_k3_vp_set_ctm(struct dispc_device *dispc, u32 hw_videoport, | |
2501 | struct drm_color_ctm *ctm) | |
2502 | { | |
2503 | u32 colorconvenable = 0; | |
2504 | ||
2505 | if (ctm) { | |
2506 | struct dispc_csc_coef csc; | |
2507 | ||
2508 | dispc_csc_from_ctm(ctm, &csc); | |
2509 | dispc_k3_vp_write_csc(dispc, hw_videoport, &csc); | |
2510 | colorconvenable = 1; | |
2511 | } | |
2512 | ||
2513 | VP_REG_FLD_MOD(dispc, hw_videoport, DISPC_VP_CONFIG, | |
2514 | colorconvenable, 24, 24); | |
2515 | } | |
2516 | ||
2517 | static void dispc_vp_set_color_mgmt(struct dispc_device *dispc, | |
2518 | u32 hw_videoport, | |
2519 | const struct drm_crtc_state *state, | |
2520 | bool newmodeset) | |
2521 | { | |
2522 | struct drm_color_lut *lut = NULL; | |
2523 | struct drm_color_ctm *ctm = NULL; | |
2524 | unsigned int length = 0; | |
2525 | ||
2526 | if (!(state->color_mgmt_changed || newmodeset)) | |
2527 | return; | |
2528 | ||
2529 | if (state->gamma_lut) { | |
2530 | lut = (struct drm_color_lut *)state->gamma_lut->data; | |
2531 | length = state->gamma_lut->length / sizeof(*lut); | |
2532 | } | |
2533 | ||
2534 | dispc_vp_set_gamma(dispc, hw_videoport, lut, length); | |
2535 | ||
2536 | if (state->ctm) | |
2537 | ctm = (struct drm_color_ctm *)state->ctm->data; | |
2538 | ||
2539 | if (dispc->feat->subrev == DISPC_K2G) | |
2540 | dispc_k2g_vp_set_ctm(dispc, hw_videoport, ctm); | |
2541 | else | |
2542 | dispc_k3_vp_set_ctm(dispc, hw_videoport, ctm); | |
2543 | } | |
2544 | ||
2545 | void dispc_vp_setup(struct dispc_device *dispc, u32 hw_videoport, | |
2546 | const struct drm_crtc_state *state, bool newmodeset) | |
2547 | { | |
2548 | dispc_vp_set_default_color(dispc, hw_videoport, 0); | |
2549 | dispc_vp_set_color_mgmt(dispc, hw_videoport, state, newmodeset); | |
2550 | } | |
2551 | ||
2552 | int dispc_runtime_suspend(struct dispc_device *dispc) | |
2553 | { | |
2554 | dev_dbg(dispc->dev, "suspend\n"); | |
2555 | ||
2556 | dispc->is_enabled = false; | |
2557 | ||
2558 | clk_disable_unprepare(dispc->fclk); | |
2559 | ||
2560 | return 0; | |
2561 | } | |
2562 | ||
2563 | int dispc_runtime_resume(struct dispc_device *dispc) | |
2564 | { | |
2565 | dev_dbg(dispc->dev, "resume\n"); | |
2566 | ||
2567 | clk_prepare_enable(dispc->fclk); | |
2568 | ||
2569 | if (REG_GET(dispc, DSS_SYSSTATUS, 0, 0) == 0) | |
2570 | dev_warn(dispc->dev, "DSS FUNC RESET not done!\n"); | |
2571 | ||
2572 | dev_dbg(dispc->dev, "OMAP DSS7 rev 0x%x\n", | |
2573 | dispc_read(dispc, DSS_REVISION)); | |
2574 | ||
2575 | dev_dbg(dispc->dev, "VP RESETDONE %d,%d,%d\n", | |
2576 | REG_GET(dispc, DSS_SYSSTATUS, 1, 1), | |
2577 | REG_GET(dispc, DSS_SYSSTATUS, 2, 2), | |
2578 | REG_GET(dispc, DSS_SYSSTATUS, 3, 3)); | |
2579 | ||
2580 | if (dispc->feat->subrev == DISPC_AM65X) | |
2581 | dev_dbg(dispc->dev, "OLDI RESETDONE %d,%d,%d\n", | |
2582 | REG_GET(dispc, DSS_SYSSTATUS, 5, 5), | |
2583 | REG_GET(dispc, DSS_SYSSTATUS, 6, 6), | |
2584 | REG_GET(dispc, DSS_SYSSTATUS, 7, 7)); | |
2585 | ||
2586 | dev_dbg(dispc->dev, "DISPC IDLE %d\n", | |
2587 | REG_GET(dispc, DSS_SYSSTATUS, 9, 9)); | |
2588 | ||
2589 | dispc_initial_config(dispc); | |
2590 | ||
2591 | dispc->is_enabled = true; | |
2592 | ||
2593 | tidss_irq_resume(dispc->tidss); | |
2594 | ||
2595 | return 0; | |
2596 | } | |
2597 | ||
2598 | void dispc_remove(struct tidss_device *tidss) | |
2599 | { | |
2600 | dev_dbg(tidss->dev, "%s\n", __func__); | |
2601 | ||
2602 | tidss->dispc = NULL; | |
2603 | } | |
2604 | ||
2605 | static int dispc_iomap_resource(struct platform_device *pdev, const char *name, | |
2606 | void __iomem **base) | |
2607 | { | |
2608 | struct resource *res; | |
2609 | void __iomem *b; | |
2610 | ||
2611 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); | |
2612 | if (!res) { | |
2613 | dev_err(&pdev->dev, "cannot get mem resource '%s'\n", name); | |
2614 | return -EINVAL; | |
2615 | } | |
2616 | ||
2617 | b = devm_ioremap_resource(&pdev->dev, res); | |
2618 | if (IS_ERR(b)) { | |
2619 | dev_err(&pdev->dev, "cannot ioremap resource '%s'\n", name); | |
2620 | return PTR_ERR(b); | |
2621 | } | |
2622 | ||
2623 | *base = b; | |
2624 | ||
2625 | return 0; | |
2626 | } | |
2627 | ||
2628 | static int dispc_init_am65x_oldi_io_ctrl(struct device *dev, | |
2629 | struct dispc_device *dispc) | |
2630 | { | |
2631 | dispc->oldi_io_ctrl = | |
2632 | syscon_regmap_lookup_by_phandle(dev->of_node, | |
2633 | "ti,am65x-oldi-io-ctrl"); | |
2634 | if (PTR_ERR(dispc->oldi_io_ctrl) == -ENODEV) { | |
2635 | dispc->oldi_io_ctrl = NULL; | |
2636 | } else if (IS_ERR(dispc->oldi_io_ctrl)) { | |
2637 | dev_err(dev, "%s: syscon_regmap_lookup_by_phandle failed %ld\n", | |
2638 | __func__, PTR_ERR(dispc->oldi_io_ctrl)); | |
2639 | return PTR_ERR(dispc->oldi_io_ctrl); | |
2640 | } | |
2641 | return 0; | |
2642 | } | |
2643 | ||
2644 | int dispc_init(struct tidss_device *tidss) | |
2645 | { | |
2646 | struct device *dev = tidss->dev; | |
2647 | struct platform_device *pdev = to_platform_device(dev); | |
2648 | struct dispc_device *dispc; | |
2649 | const struct dispc_features *feat; | |
2650 | unsigned int i, num_fourccs; | |
2651 | int r = 0; | |
2652 | ||
2653 | dev_dbg(dev, "%s\n", __func__); | |
2654 | ||
2655 | feat = tidss->feat; | |
2656 | ||
2657 | if (feat->subrev != DISPC_K2G) { | |
2658 | r = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48)); | |
2659 | if (r) | |
2660 | dev_warn(dev, "cannot set DMA masks to 48-bit\n"); | |
2661 | } | |
2662 | ||
2663 | dispc = devm_kzalloc(dev, sizeof(*dispc), GFP_KERNEL); | |
2664 | if (!dispc) | |
2665 | return -ENOMEM; | |
2666 | ||
2667 | dispc->fourccs = devm_kcalloc(dev, ARRAY_SIZE(dispc_color_formats), | |
2668 | sizeof(*dispc->fourccs), GFP_KERNEL); | |
2669 | if (!dispc->fourccs) | |
2670 | return -ENOMEM; | |
2671 | ||
2672 | num_fourccs = 0; | |
a8d9d7da | 2673 | for (i = 0; i < ARRAY_SIZE(dispc_color_formats); ++i) |
32a1795f | 2674 | dispc->fourccs[num_fourccs++] = dispc_color_formats[i].fourcc; |
a8d9d7da | 2675 | |
32a1795f JS |
2676 | dispc->num_fourccs = num_fourccs; |
2677 | dispc->tidss = tidss; | |
2678 | dispc->dev = dev; | |
2679 | dispc->feat = feat; | |
2680 | ||
2681 | dispc_common_regmap = dispc->feat->common_regs; | |
2682 | ||
2683 | r = dispc_iomap_resource(pdev, dispc->feat->common, | |
2684 | &dispc->base_common); | |
2685 | if (r) | |
2686 | return r; | |
2687 | ||
2688 | for (i = 0; i < dispc->feat->num_planes; i++) { | |
2689 | r = dispc_iomap_resource(pdev, dispc->feat->vid_name[i], | |
2690 | &dispc->base_vid[i]); | |
2691 | if (r) | |
2692 | return r; | |
2693 | } | |
2694 | ||
2695 | for (i = 0; i < dispc->feat->num_vps; i++) { | |
2696 | u32 gamma_size = dispc->feat->vp_feat.color.gamma_size; | |
2697 | u32 *gamma_table; | |
2698 | struct clk *clk; | |
2699 | ||
2700 | r = dispc_iomap_resource(pdev, dispc->feat->ovr_name[i], | |
2701 | &dispc->base_ovr[i]); | |
2702 | if (r) | |
2703 | return r; | |
2704 | ||
2705 | r = dispc_iomap_resource(pdev, dispc->feat->vp_name[i], | |
2706 | &dispc->base_vp[i]); | |
2707 | if (r) | |
2708 | return r; | |
2709 | ||
2710 | clk = devm_clk_get(dev, dispc->feat->vpclk_name[i]); | |
2711 | if (IS_ERR(clk)) { | |
2712 | dev_err(dev, "%s: Failed to get clk %s:%ld\n", __func__, | |
2713 | dispc->feat->vpclk_name[i], PTR_ERR(clk)); | |
2714 | return PTR_ERR(clk); | |
2715 | } | |
2716 | dispc->vp_clk[i] = clk; | |
2717 | ||
2718 | gamma_table = devm_kmalloc_array(dev, gamma_size, | |
2719 | sizeof(*gamma_table), | |
2720 | GFP_KERNEL); | |
2721 | if (!gamma_table) | |
2722 | return -ENOMEM; | |
2723 | dispc->vp_data[i].gamma_table = gamma_table; | |
2724 | } | |
2725 | ||
2726 | if (feat->subrev == DISPC_AM65X) { | |
2727 | r = dispc_init_am65x_oldi_io_ctrl(dev, dispc); | |
2728 | if (r) | |
2729 | return r; | |
2730 | } | |
2731 | ||
2732 | dispc->fclk = devm_clk_get(dev, "fck"); | |
2733 | if (IS_ERR(dispc->fclk)) { | |
2734 | dev_err(dev, "%s: Failed to get fclk: %ld\n", | |
2735 | __func__, PTR_ERR(dispc->fclk)); | |
2736 | return PTR_ERR(dispc->fclk); | |
2737 | } | |
2738 | dev_dbg(dev, "DSS fclk %lu Hz\n", clk_get_rate(dispc->fclk)); | |
2739 | ||
2740 | of_property_read_u32(dispc->dev->of_node, "max-memory-bandwidth", | |
2741 | &dispc->memory_bandwidth_limit); | |
2742 | ||
2743 | tidss->dispc = dispc; | |
2744 | ||
2745 | return 0; | |
2746 | } |