2 * Copyright (C) STMicroelectronics SA 2014
3 * Authors: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics.
4 * License terms: GNU General Public License (GPL), version 2
8 #include <linux/component.h>
9 #include <linux/firmware.h>
10 #include <linux/module.h>
11 #include <linux/platform_device.h>
12 #include <linux/reset.h>
15 #include <drm/drm_fb_cma_helper.h>
16 #include <drm/drm_gem_cma_helper.h>
18 #include "sti_compositor.h"
19 #include "sti_hqvdp_lut.h"
20 #include "sti_plane.h"
24 #define HQVDP_FMW_NAME "hqvdp-stih407.bin"
27 #define HQVDP_DMEM 0x00000000 /* 0x00000000 */
28 #define HQVDP_PMEM 0x00040000 /* 0x00040000 */
29 #define HQVDP_RD_PLUG 0x000E0000 /* 0x000E0000 */
30 #define HQVDP_RD_PLUG_CONTROL (HQVDP_RD_PLUG + 0x1000) /* 0x000E1000 */
31 #define HQVDP_RD_PLUG_PAGE_SIZE (HQVDP_RD_PLUG + 0x1004) /* 0x000E1004 */
32 #define HQVDP_RD_PLUG_MIN_OPC (HQVDP_RD_PLUG + 0x1008) /* 0x000E1008 */
33 #define HQVDP_RD_PLUG_MAX_OPC (HQVDP_RD_PLUG + 0x100C) /* 0x000E100C */
34 #define HQVDP_RD_PLUG_MAX_CHK (HQVDP_RD_PLUG + 0x1010) /* 0x000E1010 */
35 #define HQVDP_RD_PLUG_MAX_MSG (HQVDP_RD_PLUG + 0x1014) /* 0x000E1014 */
36 #define HQVDP_RD_PLUG_MIN_SPACE (HQVDP_RD_PLUG + 0x1018) /* 0x000E1018 */
37 #define HQVDP_WR_PLUG 0x000E2000 /* 0x000E2000 */
38 #define HQVDP_WR_PLUG_CONTROL (HQVDP_WR_PLUG + 0x1000) /* 0x000E3000 */
39 #define HQVDP_WR_PLUG_PAGE_SIZE (HQVDP_WR_PLUG + 0x1004) /* 0x000E3004 */
40 #define HQVDP_WR_PLUG_MIN_OPC (HQVDP_WR_PLUG + 0x1008) /* 0x000E3008 */
41 #define HQVDP_WR_PLUG_MAX_OPC (HQVDP_WR_PLUG + 0x100C) /* 0x000E300C */
42 #define HQVDP_WR_PLUG_MAX_CHK (HQVDP_WR_PLUG + 0x1010) /* 0x000E3010 */
43 #define HQVDP_WR_PLUG_MAX_MSG (HQVDP_WR_PLUG + 0x1014) /* 0x000E3014 */
44 #define HQVDP_WR_PLUG_MIN_SPACE (HQVDP_WR_PLUG + 0x1018) /* 0x000E3018 */
45 #define HQVDP_MBX 0x000E4000 /* 0x000E4000 */
46 #define HQVDP_MBX_IRQ_TO_XP70 (HQVDP_MBX + 0x0000) /* 0x000E4000 */
47 #define HQVDP_MBX_INFO_HOST (HQVDP_MBX + 0x0004) /* 0x000E4004 */
48 #define HQVDP_MBX_IRQ_TO_HOST (HQVDP_MBX + 0x0008) /* 0x000E4008 */
49 #define HQVDP_MBX_INFO_XP70 (HQVDP_MBX + 0x000C) /* 0x000E400C */
50 #define HQVDP_MBX_SW_RESET_CTRL (HQVDP_MBX + 0x0010) /* 0x000E4010 */
51 #define HQVDP_MBX_STARTUP_CTRL1 (HQVDP_MBX + 0x0014) /* 0x000E4014 */
52 #define HQVDP_MBX_STARTUP_CTRL2 (HQVDP_MBX + 0x0018) /* 0x000E4018 */
53 #define HQVDP_MBX_GP_STATUS (HQVDP_MBX + 0x001C) /* 0x000E401C */
54 #define HQVDP_MBX_NEXT_CMD (HQVDP_MBX + 0x0020) /* 0x000E4020 */
55 #define HQVDP_MBX_CURRENT_CMD (HQVDP_MBX + 0x0024) /* 0x000E4024 */
56 #define HQVDP_MBX_SOFT_VSYNC (HQVDP_MBX + 0x0028) /* 0x000E4028 */
59 #define PLUG_CONTROL_ENABLE 0x00000001
60 #define PLUG_PAGE_SIZE_256 0x00000002
61 #define PLUG_MIN_OPC_8 0x00000003
62 #define PLUG_MAX_OPC_64 0x00000006
63 #define PLUG_MAX_CHK_2X 0x00000001
64 #define PLUG_MAX_MSG_1X 0x00000000
65 #define PLUG_MIN_SPACE_1 0x00000000
68 #define SW_RESET_CTRL_FULL BIT(0)
69 #define SW_RESET_CTRL_CORE BIT(1)
72 #define STARTUP_CTRL1_RST_DONE BIT(0)
73 #define STARTUP_CTRL1_AUTH_IDLE BIT(2)
76 #define STARTUP_CTRL2_FETCH_EN BIT(1)
79 #define INFO_XP70_FW_READY BIT(15)
80 #define INFO_XP70_FW_PROCESSING BIT(14)
81 #define INFO_XP70_FW_INITQUEUES BIT(13)
84 #define SOFT_VSYNC_HW 0x00000000
85 #define SOFT_VSYNC_SW_CMD 0x00000001
86 #define SOFT_VSYNC_SW_CTRL_IRQ 0x00000003
88 /* Reset & boot poll config */
89 #define POLL_MAX_ATTEMPT 50
90 #define POLL_DELAY_MS 20
92 #define SCALE_FACTOR 8192
93 #define SCALE_MAX_FOR_LEG_LUT_F 4096
94 #define SCALE_MAX_FOR_LEG_LUT_E 4915
95 #define SCALE_MAX_FOR_LEG_LUT_D 6654
96 #define SCALE_MAX_FOR_LEG_LUT_C 8192
98 enum sti_hvsrc_orient
{
103 /* Command structures */
104 struct sti_hqvdp_top
{
108 u32 current_enh_luma
;
109 u32 current_right_luma
;
110 u32 current_enh_right_luma
;
112 u32 current_enh_chroma
;
113 u32 current_right_chroma
;
114 u32 current_enh_right_chroma
;
118 u32 luma_enh_src_pitch
;
119 u32 luma_right_src_pitch
;
120 u32 luma_enh_right_src_pitch
;
121 u32 chroma_src_pitch
;
122 u32 chroma_enh_src_pitch
;
123 u32 chroma_right_src_pitch
;
124 u32 chroma_enh_right_src_pitch
;
125 u32 luma_processed_pitch
;
126 u32 chroma_processed_pitch
;
127 u32 input_frame_size
;
128 u32 input_viewport_ori
;
129 u32 input_viewport_ori_right
;
130 u32 input_viewport_size
;
131 u32 left_view_border_width
;
132 u32 right_view_border_width
;
133 u32 left_view_3d_offset_width
;
134 u32 right_view_3d_offset_width
;
135 u32 side_stripe_color
;
139 /* Configs for interlaced : no IT, no pass thru, 3 fields */
140 #define TOP_CONFIG_INTER_BTM 0x00000000
141 #define TOP_CONFIG_INTER_TOP 0x00000002
143 /* Config for progressive : no IT, no pass thru, 3 fields */
144 #define TOP_CONFIG_PROGRESSIVE 0x00000001
146 /* Default MemFormat: in=420_raster_dual out=444_raster;opaque Mem2Tv mode */
147 #define TOP_MEM_FORMAT_DFLT 0x00018060
150 #define MAX_WIDTH 0x1FFF
151 #define MAX_HEIGHT 0x0FFF
152 #define MIN_WIDTH 0x0030
153 #define MIN_HEIGHT 0x0010
155 struct sti_hqvdp_vc1re
{
163 struct sti_hqvdp_fmd
{
168 u32 next_next_right_luma
;
169 u32 next_next_next_luma
;
170 u32 next_next_next_right_luma
;
177 struct sti_hqvdp_csdi
{
184 u32 prev_enh_right_luma
;
188 u32 next_enh_right_luma
;
191 u32 prev_right_chroma
;
192 u32 prev_enh_right_chroma
;
195 u32 next_right_chroma
;
196 u32 next_enh_right_chroma
;
198 u32 prev_right_motion
;
200 u32 cur_right_motion
;
202 u32 next_right_motion
;
205 /* Config for progressive: by pass */
206 #define CSDI_CONFIG_PROG 0x00000000
207 /* Config for directional deinterlacing without motion */
208 #define CSDI_CONFIG_INTER_DIR 0x00000016
209 /* Additional configs for fader, blender, motion,... deinterlace algorithms */
210 #define CSDI_CONFIG2_DFLT 0x000001B3
211 #define CSDI_DCDI_CONFIG_DFLT 0x00203803
213 struct sti_hqvdp_hvsrc
{
214 u32 hor_panoramic_ctrl
;
215 u32 output_picture_size
;
219 u32 yh_coef
[NB_COEF
];
220 u32 ch_coef
[NB_COEF
];
221 u32 yv_coef
[NB_COEF
];
222 u32 cv_coef
[NB_COEF
];
227 /* Default ParamCtrl: all controls enabled */
228 #define HVSRC_PARAM_CTRL_DFLT 0xFFFFFFFF
230 struct sti_hqvdp_iqi
{
249 /* Default Config : IQI bypassed */
250 #define IQI_CONFIG_DFLT 0x00000001
251 /* Default Contrast & Brightness gain = 256 */
252 #define IQI_CON_BRI_DFLT 0x00000100
253 /* Default Saturation gain = 256 */
254 #define IQI_SAT_GAIN_DFLT 0x00000100
255 /* Default PxfConf : P2I bypassed */
256 #define IQI_PXF_CONF_DFLT 0x00000001
258 struct sti_hqvdp_top_status
{
264 struct sti_hqvdp_fmd_status
{
265 u32 fmd_repeat_move_status
;
266 u32 fmd_scene_count_status
;
270 u32 next_next_y_fmd_crc
;
271 u32 next_next_next_y_fmd_crc
;
274 struct sti_hqvdp_csdi_status
{
278 u32 prev_uv_csdi_crc
;
280 u32 next_uv_csdi_crc
;
285 u32 mot_cur_csdi_crc
;
286 u32 mot_prev_csdi_crc
;
289 struct sti_hqvdp_hvsrc_status
{
295 struct sti_hqvdp_iqi_status
{
302 /* Main commands. We use 2 commands one being processed by the firmware, one
303 * ready to be fetched upon next Vsync*/
306 struct sti_hqvdp_cmd
{
307 struct sti_hqvdp_top top
;
308 struct sti_hqvdp_vc1re vc1re
;
309 struct sti_hqvdp_fmd fmd
;
310 struct sti_hqvdp_csdi csdi
;
311 struct sti_hqvdp_hvsrc hvsrc
;
312 struct sti_hqvdp_iqi iqi
;
313 struct sti_hqvdp_top_status top_status
;
314 struct sti_hqvdp_fmd_status fmd_status
;
315 struct sti_hqvdp_csdi_status csdi_status
;
316 struct sti_hqvdp_hvsrc_status hvsrc_status
;
317 struct sti_hqvdp_iqi_status iqi_status
;
321 * STI HQVDP structure
323 * @dev: driver device
324 * @drm_dev: the drm device
326 * @plane: plane structure for hqvdp it self
328 * @clk_pix_main: pix main clock
329 * @reset: reset control
330 * @vtg_nb: notifier to handle VTG Vsync
331 * @btm_field_pending: is there any bottom field (interlaced frame) to display
332 * @curr_field_count: number of field updates
333 * @last_field_count: number of field updates since last fps measure
334 * @hqvdp_cmd: buffer of commands
335 * @hqvdp_cmd_paddr: physical address of hqvdp_cmd
336 * @vtg: vtg for main data path
337 * @xp70_initialized: true if xp70 is already initialized
341 struct drm_device
*drm_dev
;
343 struct sti_plane plane
;
345 struct clk
*clk_pix_main
;
346 struct reset_control
*reset
;
347 struct notifier_block vtg_nb
;
348 bool btm_field_pending
;
349 unsigned int curr_field_count
;
350 unsigned int last_field_count
;
352 dma_addr_t hqvdp_cmd_paddr
;
354 bool xp70_initialized
;
357 #define to_sti_hqvdp(x) container_of(x, struct sti_hqvdp, plane)
359 static const uint32_t hqvdp_supported_formats
[] = {
364 * sti_hqvdp_get_free_cmd
365 * @hqvdp: hqvdp structure
367 * Look for a hqvdp_cmd that is not being used (or about to be used) by the FW.
370 * the offset of the command to be used.
373 static int sti_hqvdp_get_free_cmd(struct sti_hqvdp
*hqvdp
)
375 int curr_cmd
, next_cmd
;
376 dma_addr_t cmd
= hqvdp
->hqvdp_cmd_paddr
;
379 curr_cmd
= readl(hqvdp
->regs
+ HQVDP_MBX_CURRENT_CMD
);
380 next_cmd
= readl(hqvdp
->regs
+ HQVDP_MBX_NEXT_CMD
);
382 for (i
= 0; i
< NB_VDP_CMD
; i
++) {
383 if ((cmd
!= curr_cmd
) && (cmd
!= next_cmd
))
384 return i
* sizeof(struct sti_hqvdp_cmd
);
385 cmd
+= sizeof(struct sti_hqvdp_cmd
);
392 * sti_hqvdp_get_curr_cmd
393 * @hqvdp: hqvdp structure
395 * Look for the hqvdp_cmd that is being used by the FW.
398 * the offset of the command to be used.
401 static int sti_hqvdp_get_curr_cmd(struct sti_hqvdp
*hqvdp
)
404 dma_addr_t cmd
= hqvdp
->hqvdp_cmd_paddr
;
407 curr_cmd
= readl(hqvdp
->regs
+ HQVDP_MBX_CURRENT_CMD
);
409 for (i
= 0; i
< NB_VDP_CMD
; i
++) {
411 return i
* sizeof(struct sti_hqvdp_cmd
);
413 cmd
+= sizeof(struct sti_hqvdp_cmd
);
420 * sti_hqvdp_update_hvsrc
421 * @orient: horizontal or vertical
422 * @scale: scaling/zoom factor
423 * @hvsrc: the structure containing the LUT coef
425 * Update the Y and C Lut coef, as well as the shift param
430 static void sti_hqvdp_update_hvsrc(enum sti_hvsrc_orient orient
, int scale
,
431 struct sti_hqvdp_hvsrc
*hvsrc
)
433 const int *coef_c
, *coef_y
;
434 int shift_c
, shift_y
;
436 /* Get the appropriate coef tables */
437 if (scale
< SCALE_MAX_FOR_LEG_LUT_F
) {
438 coef_y
= coef_lut_f_y_legacy
;
439 coef_c
= coef_lut_f_c_legacy
;
440 shift_y
= SHIFT_LUT_F_Y_LEGACY
;
441 shift_c
= SHIFT_LUT_F_C_LEGACY
;
442 } else if (scale
< SCALE_MAX_FOR_LEG_LUT_E
) {
443 coef_y
= coef_lut_e_y_legacy
;
444 coef_c
= coef_lut_e_c_legacy
;
445 shift_y
= SHIFT_LUT_E_Y_LEGACY
;
446 shift_c
= SHIFT_LUT_E_C_LEGACY
;
447 } else if (scale
< SCALE_MAX_FOR_LEG_LUT_D
) {
448 coef_y
= coef_lut_d_y_legacy
;
449 coef_c
= coef_lut_d_c_legacy
;
450 shift_y
= SHIFT_LUT_D_Y_LEGACY
;
451 shift_c
= SHIFT_LUT_D_C_LEGACY
;
452 } else if (scale
< SCALE_MAX_FOR_LEG_LUT_C
) {
453 coef_y
= coef_lut_c_y_legacy
;
454 coef_c
= coef_lut_c_c_legacy
;
455 shift_y
= SHIFT_LUT_C_Y_LEGACY
;
456 shift_c
= SHIFT_LUT_C_C_LEGACY
;
457 } else if (scale
== SCALE_MAX_FOR_LEG_LUT_C
) {
458 coef_y
= coef_c
= coef_lut_b
;
459 shift_y
= shift_c
= SHIFT_LUT_B
;
461 coef_y
= coef_c
= coef_lut_a_legacy
;
462 shift_y
= shift_c
= SHIFT_LUT_A_LEGACY
;
465 if (orient
== HVSRC_HORI
) {
466 hvsrc
->hori_shift
= (shift_c
<< 16) | shift_y
;
467 memcpy(hvsrc
->yh_coef
, coef_y
, sizeof(hvsrc
->yh_coef
));
468 memcpy(hvsrc
->ch_coef
, coef_c
, sizeof(hvsrc
->ch_coef
));
470 hvsrc
->vert_shift
= (shift_c
<< 16) | shift_y
;
471 memcpy(hvsrc
->yv_coef
, coef_y
, sizeof(hvsrc
->yv_coef
));
472 memcpy(hvsrc
->cv_coef
, coef_c
, sizeof(hvsrc
->cv_coef
));
477 * sti_hqvdp_check_hw_scaling
478 * @hqvdp: hqvdp pointer
479 * @mode: display mode with timing constraints
480 * @src_w: source width
481 * @src_h: source height
482 * @dst_w: destination width
483 * @dst_h: destination height
485 * Check if the HW is able to perform the scaling request
486 * The firmware scaling limitation is "CEIL(1/Zy) <= FLOOR(LFW)" where:
487 * Zy = OutputHeight / InputHeight
488 * LFW = (Tx * IPClock) / (MaxNbCycles * Cp)
489 * Tx : Total video mode horizontal resolution
490 * IPClock : HQVDP IP clock (Mhz)
491 * MaxNbCycles: max(InputWidth, OutputWidth)
492 * Cp: Video mode pixel clock (Mhz)
495 * True if the HW can scale.
497 static bool sti_hqvdp_check_hw_scaling(struct sti_hqvdp
*hqvdp
,
498 struct drm_display_mode
*mode
,
499 int src_w
, int src_h
,
500 int dst_w
, int dst_h
)
505 lfw
= mode
->htotal
* (clk_get_rate(hqvdp
->clk
) / 1000000);
506 lfw
/= max(src_w
, dst_w
) * mode
->clock
/ 1000;
508 inv_zy
= DIV_ROUND_UP(src_h
, dst_h
);
510 return (inv_zy
<= lfw
) ? true : false;
515 * @hqvdp: hqvdp pointer
517 * Disables the HQVDP plane
519 static void sti_hqvdp_disable(struct sti_hqvdp
*hqvdp
)
523 DRM_DEBUG_DRIVER("%s\n", sti_plane_to_str(&hqvdp
->plane
));
525 /* Unregister VTG Vsync callback */
526 if (sti_vtg_unregister_client(hqvdp
->vtg
, &hqvdp
->vtg_nb
))
527 DRM_DEBUG_DRIVER("Warning: cannot unregister VTG notifier\n");
529 /* Set next cmd to NULL */
530 writel(0, hqvdp
->regs
+ HQVDP_MBX_NEXT_CMD
);
532 for (i
= 0; i
< POLL_MAX_ATTEMPT
; i
++) {
533 if (readl(hqvdp
->regs
+ HQVDP_MBX_INFO_XP70
)
534 & INFO_XP70_FW_READY
)
536 msleep(POLL_DELAY_MS
);
539 /* VTG can stop now */
540 clk_disable_unprepare(hqvdp
->clk_pix_main
);
542 if (i
== POLL_MAX_ATTEMPT
)
543 DRM_ERROR("XP70 could not revert to idle\n");
545 hqvdp
->plane
.status
= STI_PLANE_DISABLED
;
550 * @nb: notifier block
551 * @evt: event message
552 * @data: private data
554 * Handle VTG Vsync event, display pending bottom field
559 int sti_hqvdp_vtg_cb(struct notifier_block
*nb
, unsigned long evt
, void *data
)
561 struct sti_hqvdp
*hqvdp
= container_of(nb
, struct sti_hqvdp
, vtg_nb
);
562 int btm_cmd_offset
, top_cmd_offest
;
563 struct sti_hqvdp_cmd
*btm_cmd
, *top_cmd
;
565 if ((evt
!= VTG_TOP_FIELD_EVENT
) && (evt
!= VTG_BOTTOM_FIELD_EVENT
)) {
566 DRM_DEBUG_DRIVER("Unknown event\n");
570 if (hqvdp
->plane
.status
== STI_PLANE_FLUSHING
) {
571 /* disable need to be synchronize on vsync event */
572 DRM_DEBUG_DRIVER("Vsync event received => disable %s\n",
573 sti_plane_to_str(&hqvdp
->plane
));
575 sti_hqvdp_disable(hqvdp
);
578 if (hqvdp
->btm_field_pending
) {
579 /* Create the btm field command from the current one */
580 btm_cmd_offset
= sti_hqvdp_get_free_cmd(hqvdp
);
581 top_cmd_offest
= sti_hqvdp_get_curr_cmd(hqvdp
);
582 if ((btm_cmd_offset
== -1) || (top_cmd_offest
== -1)) {
583 DRM_ERROR("Cannot get cmds, skip btm field\n");
587 btm_cmd
= hqvdp
->hqvdp_cmd
+ btm_cmd_offset
;
588 top_cmd
= hqvdp
->hqvdp_cmd
+ top_cmd_offest
;
590 memcpy(btm_cmd
, top_cmd
, sizeof(*btm_cmd
));
592 btm_cmd
->top
.config
= TOP_CONFIG_INTER_BTM
;
593 btm_cmd
->top
.current_luma
+=
594 btm_cmd
->top
.luma_src_pitch
/ 2;
595 btm_cmd
->top
.current_chroma
+=
596 btm_cmd
->top
.chroma_src_pitch
/ 2;
598 /* Post the command to mailbox */
599 writel(hqvdp
->hqvdp_cmd_paddr
+ btm_cmd_offset
,
600 hqvdp
->regs
+ HQVDP_MBX_NEXT_CMD
);
602 hqvdp
->curr_field_count
++;
603 hqvdp
->btm_field_pending
= false;
605 dev_dbg(hqvdp
->dev
, "%s Posted command:0x%x\n",
606 __func__
, hqvdp
->hqvdp_cmd_paddr
);
612 static void sti_hqvdp_init(struct sti_hqvdp
*hqvdp
)
616 hqvdp
->vtg_nb
.notifier_call
= sti_hqvdp_vtg_cb
;
618 /* Allocate memory for the VDP commands */
619 size
= NB_VDP_CMD
* sizeof(struct sti_hqvdp_cmd
);
620 hqvdp
->hqvdp_cmd
= dma_alloc_writecombine(hqvdp
->dev
, size
,
621 &hqvdp
->hqvdp_cmd_paddr
,
622 GFP_KERNEL
| GFP_DMA
);
623 if (!hqvdp
->hqvdp_cmd
) {
624 DRM_ERROR("Failed to allocate memory for VDP cmd\n");
628 memset(hqvdp
->hqvdp_cmd
, 0, size
);
631 static void sti_hqvdp_init_plugs(struct sti_hqvdp
*hqvdp
)
633 /* Configure Plugs (same for RD & WR) */
634 writel(PLUG_PAGE_SIZE_256
, hqvdp
->regs
+ HQVDP_RD_PLUG_PAGE_SIZE
);
635 writel(PLUG_MIN_OPC_8
, hqvdp
->regs
+ HQVDP_RD_PLUG_MIN_OPC
);
636 writel(PLUG_MAX_OPC_64
, hqvdp
->regs
+ HQVDP_RD_PLUG_MAX_OPC
);
637 writel(PLUG_MAX_CHK_2X
, hqvdp
->regs
+ HQVDP_RD_PLUG_MAX_CHK
);
638 writel(PLUG_MAX_MSG_1X
, hqvdp
->regs
+ HQVDP_RD_PLUG_MAX_MSG
);
639 writel(PLUG_MIN_SPACE_1
, hqvdp
->regs
+ HQVDP_RD_PLUG_MIN_SPACE
);
640 writel(PLUG_CONTROL_ENABLE
, hqvdp
->regs
+ HQVDP_RD_PLUG_CONTROL
);
642 writel(PLUG_PAGE_SIZE_256
, hqvdp
->regs
+ HQVDP_WR_PLUG_PAGE_SIZE
);
643 writel(PLUG_MIN_OPC_8
, hqvdp
->regs
+ HQVDP_WR_PLUG_MIN_OPC
);
644 writel(PLUG_MAX_OPC_64
, hqvdp
->regs
+ HQVDP_WR_PLUG_MAX_OPC
);
645 writel(PLUG_MAX_CHK_2X
, hqvdp
->regs
+ HQVDP_WR_PLUG_MAX_CHK
);
646 writel(PLUG_MAX_MSG_1X
, hqvdp
->regs
+ HQVDP_WR_PLUG_MAX_MSG
);
647 writel(PLUG_MIN_SPACE_1
, hqvdp
->regs
+ HQVDP_WR_PLUG_MIN_SPACE
);
648 writel(PLUG_CONTROL_ENABLE
, hqvdp
->regs
+ HQVDP_WR_PLUG_CONTROL
);
652 * sti_hqvdp_start_xp70
653 * @hqvdp: hqvdp pointer
655 * Run the xP70 initialization sequence
657 static void sti_hqvdp_start_xp70(struct sti_hqvdp
*hqvdp
)
659 const struct firmware
*firmware
;
660 u32
*fw_rd_plug
, *fw_wr_plug
, *fw_pmem
, *fw_dmem
;
670 DRM_DEBUG_DRIVER("\n");
672 if (hqvdp
->xp70_initialized
) {
673 DRM_INFO("HQVDP XP70 already initialized\n");
677 /* Request firmware */
678 if (request_firmware(&firmware
, HQVDP_FMW_NAME
, hqvdp
->dev
)) {
679 DRM_ERROR("Can't get HQVDP firmware\n");
683 /* Check firmware parts */
685 DRM_ERROR("Firmware not available\n");
689 header
= (struct fw_header
*)firmware
->data
;
690 if (firmware
->size
< sizeof(*header
)) {
691 DRM_ERROR("Invalid firmware size (%d)\n", firmware
->size
);
694 if ((sizeof(*header
) + header
->rd_size
+ header
->wr_size
+
695 header
->pmem_size
+ header
->dmem_size
) != firmware
->size
) {
696 DRM_ERROR("Invalid fmw structure (%d+%d+%d+%d+%d != %d)\n",
697 sizeof(*header
), header
->rd_size
, header
->wr_size
,
698 header
->pmem_size
, header
->dmem_size
,
703 data
= (u8
*)firmware
->data
;
704 data
+= sizeof(*header
);
705 fw_rd_plug
= (void *)data
;
706 data
+= header
->rd_size
;
707 fw_wr_plug
= (void *)data
;
708 data
+= header
->wr_size
;
709 fw_pmem
= (void *)data
;
710 data
+= header
->pmem_size
;
711 fw_dmem
= (void *)data
;
714 if (clk_prepare_enable(hqvdp
->clk
))
715 DRM_ERROR("Failed to prepare/enable HQVDP clk\n");
718 writel(SW_RESET_CTRL_FULL
, hqvdp
->regs
+ HQVDP_MBX_SW_RESET_CTRL
);
720 for (i
= 0; i
< POLL_MAX_ATTEMPT
; i
++) {
721 if (readl(hqvdp
->regs
+ HQVDP_MBX_STARTUP_CTRL1
)
722 & STARTUP_CTRL1_RST_DONE
)
724 msleep(POLL_DELAY_MS
);
726 if (i
== POLL_MAX_ATTEMPT
) {
727 DRM_ERROR("Could not reset\n");
731 /* Init Read & Write plugs */
732 for (i
= 0; i
< header
->rd_size
/ 4; i
++)
733 writel(fw_rd_plug
[i
], hqvdp
->regs
+ HQVDP_RD_PLUG
+ i
* 4);
734 for (i
= 0; i
< header
->wr_size
/ 4; i
++)
735 writel(fw_wr_plug
[i
], hqvdp
->regs
+ HQVDP_WR_PLUG
+ i
* 4);
737 sti_hqvdp_init_plugs(hqvdp
);
739 /* Authorize Idle Mode */
740 writel(STARTUP_CTRL1_AUTH_IDLE
, hqvdp
->regs
+ HQVDP_MBX_STARTUP_CTRL1
);
742 /* Prevent VTG interruption during the boot */
743 writel(SOFT_VSYNC_SW_CTRL_IRQ
, hqvdp
->regs
+ HQVDP_MBX_SOFT_VSYNC
);
744 writel(0, hqvdp
->regs
+ HQVDP_MBX_NEXT_CMD
);
746 /* Download PMEM & DMEM */
747 for (i
= 0; i
< header
->pmem_size
/ 4; i
++)
748 writel(fw_pmem
[i
], hqvdp
->regs
+ HQVDP_PMEM
+ i
* 4);
749 for (i
= 0; i
< header
->dmem_size
/ 4; i
++)
750 writel(fw_dmem
[i
], hqvdp
->regs
+ HQVDP_DMEM
+ i
* 4);
753 writel(STARTUP_CTRL2_FETCH_EN
, hqvdp
->regs
+ HQVDP_MBX_STARTUP_CTRL2
);
755 /* Wait end of boot */
756 for (i
= 0; i
< POLL_MAX_ATTEMPT
; i
++) {
757 if (readl(hqvdp
->regs
+ HQVDP_MBX_INFO_XP70
)
758 & INFO_XP70_FW_READY
)
760 msleep(POLL_DELAY_MS
);
762 if (i
== POLL_MAX_ATTEMPT
) {
763 DRM_ERROR("Could not boot\n");
768 writel(SOFT_VSYNC_HW
, hqvdp
->regs
+ HQVDP_MBX_SOFT_VSYNC
);
770 DRM_INFO("HQVDP XP70 initialized\n");
772 hqvdp
->xp70_initialized
= true;
775 release_firmware(firmware
);
778 static void sti_hqvdp_atomic_update(struct drm_plane
*drm_plane
,
779 struct drm_plane_state
*oldstate
)
781 struct drm_plane_state
*state
= drm_plane
->state
;
782 struct sti_plane
*plane
= to_sti_plane(drm_plane
);
783 struct sti_hqvdp
*hqvdp
= to_sti_hqvdp(plane
);
784 struct drm_crtc
*crtc
= state
->crtc
;
785 struct sti_mixer
*mixer
= to_sti_mixer(crtc
);
786 struct drm_framebuffer
*fb
= state
->fb
;
787 struct drm_display_mode
*mode
= &crtc
->mode
;
788 int dst_x
= state
->crtc_x
;
789 int dst_y
= state
->crtc_y
;
790 int dst_w
= clamp_val(state
->crtc_w
, 0, mode
->crtc_hdisplay
- dst_x
);
791 int dst_h
= clamp_val(state
->crtc_h
, 0, mode
->crtc_vdisplay
- dst_y
);
792 /* src_x are in 16.16 format */
793 int src_x
= state
->src_x
>> 16;
794 int src_y
= state
->src_y
>> 16;
795 int src_w
= state
->src_w
>> 16;
796 int src_h
= state
->src_h
>> 16;
797 bool first_prepare
= plane
->status
== STI_PLANE_DISABLED
? true : false;
798 struct drm_gem_cma_object
*cma_obj
;
799 struct sti_hqvdp_cmd
*cmd
;
800 int scale_h
, scale_v
;
803 DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n",
804 crtc
->base
.id
, sti_mixer_to_str(mixer
),
805 drm_plane
->base
.id
, sti_plane_to_str(plane
));
806 DRM_DEBUG_KMS("%s dst=(%dx%d)@(%d,%d) - src=(%dx%d)@(%d,%d)\n",
807 sti_plane_to_str(plane
),
808 dst_w
, dst_h
, dst_x
, dst_y
,
809 src_w
, src_h
, src_x
, src_y
);
811 cmd_offset
= sti_hqvdp_get_free_cmd(hqvdp
);
812 if (cmd_offset
== -1) {
813 DRM_ERROR("No available hqvdp_cmd now\n");
816 cmd
= hqvdp
->hqvdp_cmd
+ cmd_offset
;
818 if (!sti_hqvdp_check_hw_scaling(hqvdp
, mode
,
821 DRM_ERROR("Scaling beyond HW capabilities\n");
825 /* Static parameters, defaulting to progressive mode */
826 cmd
->top
.config
= TOP_CONFIG_PROGRESSIVE
;
827 cmd
->top
.mem_format
= TOP_MEM_FORMAT_DFLT
;
828 cmd
->hvsrc
.param_ctrl
= HVSRC_PARAM_CTRL_DFLT
;
829 cmd
->csdi
.config
= CSDI_CONFIG_PROG
;
831 /* VC1RE, FMD bypassed : keep everything set to 0
832 * IQI/P2I bypassed */
833 cmd
->iqi
.config
= IQI_CONFIG_DFLT
;
834 cmd
->iqi
.con_bri
= IQI_CON_BRI_DFLT
;
835 cmd
->iqi
.sat_gain
= IQI_SAT_GAIN_DFLT
;
836 cmd
->iqi
.pxf_conf
= IQI_PXF_CONF_DFLT
;
838 cma_obj
= drm_fb_cma_get_gem_obj(fb
, 0);
840 DRM_ERROR("Can't get CMA GEM object for fb\n");
844 DRM_DEBUG_DRIVER("drm FB:%d format:%.4s phys@:0x%lx\n", fb
->base
.id
,
845 (char *)&fb
->pixel_format
,
846 (unsigned long)cma_obj
->paddr
);
848 /* Buffer planes address */
849 cmd
->top
.current_luma
= (u32
)cma_obj
->paddr
+ fb
->offsets
[0];
850 cmd
->top
.current_chroma
= (u32
)cma_obj
->paddr
+ fb
->offsets
[1];
853 cmd
->top
.luma_processed_pitch
= fb
->pitches
[0];
854 cmd
->top
.luma_src_pitch
= fb
->pitches
[0];
855 cmd
->top
.chroma_processed_pitch
= fb
->pitches
[1];
856 cmd
->top
.chroma_src_pitch
= fb
->pitches
[1];
858 /* Input / output size
859 * Align to upper even value */
860 dst_w
= ALIGN(dst_w
, 2);
861 dst_h
= ALIGN(dst_h
, 2);
863 if ((src_w
> MAX_WIDTH
) || (src_w
< MIN_WIDTH
) ||
864 (src_h
> MAX_HEIGHT
) || (src_h
< MIN_HEIGHT
) ||
865 (dst_w
> MAX_WIDTH
) || (dst_w
< MIN_WIDTH
) ||
866 (dst_h
> MAX_HEIGHT
) || (dst_h
< MIN_HEIGHT
)) {
867 DRM_ERROR("Invalid in/out size %dx%d -> %dx%d\n",
873 cmd
->top
.input_viewport_size
= src_h
<< 16 | src_w
;
874 cmd
->top
.input_frame_size
= src_h
<< 16 | src_w
;
875 cmd
->hvsrc
.output_picture_size
= dst_h
<< 16 | dst_w
;
876 cmd
->top
.input_viewport_ori
= src_y
<< 16 | src_x
;
878 /* Handle interlaced */
879 if (fb
->flags
& DRM_MODE_FB_INTERLACED
) {
880 /* Top field to display */
881 cmd
->top
.config
= TOP_CONFIG_INTER_TOP
;
883 /* Update pitches and vert size */
884 cmd
->top
.input_frame_size
= (src_h
/ 2) << 16 | src_w
;
885 cmd
->top
.luma_processed_pitch
*= 2;
886 cmd
->top
.luma_src_pitch
*= 2;
887 cmd
->top
.chroma_processed_pitch
*= 2;
888 cmd
->top
.chroma_src_pitch
*= 2;
890 /* Enable directional deinterlacing processing */
891 cmd
->csdi
.config
= CSDI_CONFIG_INTER_DIR
;
892 cmd
->csdi
.config2
= CSDI_CONFIG2_DFLT
;
893 cmd
->csdi
.dcdi_config
= CSDI_DCDI_CONFIG_DFLT
;
896 /* Update hvsrc lut coef */
897 scale_h
= SCALE_FACTOR
* dst_w
/ src_w
;
898 sti_hqvdp_update_hvsrc(HVSRC_HORI
, scale_h
, &cmd
->hvsrc
);
900 scale_v
= SCALE_FACTOR
* dst_h
/ src_h
;
901 sti_hqvdp_update_hvsrc(HVSRC_VERT
, scale_v
, &cmd
->hvsrc
);
904 /* Start HQVDP XP70 coprocessor */
905 sti_hqvdp_start_xp70(hqvdp
);
907 /* Prevent VTG shutdown */
908 if (clk_prepare_enable(hqvdp
->clk_pix_main
)) {
909 DRM_ERROR("Failed to prepare/enable pix main clk\n");
913 /* Register VTG Vsync callback to handle bottom fields */
914 if (sti_vtg_register_client(hqvdp
->vtg
,
917 DRM_ERROR("Cannot register VTG notifier\n");
922 writel(hqvdp
->hqvdp_cmd_paddr
+ cmd_offset
,
923 hqvdp
->regs
+ HQVDP_MBX_NEXT_CMD
);
925 hqvdp
->curr_field_count
++;
927 /* Interlaced : get ready to display the bottom field at next Vsync */
928 if (fb
->flags
& DRM_MODE_FB_INTERLACED
)
929 hqvdp
->btm_field_pending
= true;
931 dev_dbg(hqvdp
->dev
, "%s Posted command:0x%x\n",
932 __func__
, hqvdp
->hqvdp_cmd_paddr
+ cmd_offset
);
934 plane
->status
= STI_PLANE_UPDATED
;
937 static void sti_hqvdp_atomic_disable(struct drm_plane
*drm_plane
,
938 struct drm_plane_state
*oldstate
)
940 struct sti_plane
*plane
= to_sti_plane(drm_plane
);
941 struct sti_mixer
*mixer
= to_sti_mixer(drm_plane
->crtc
);
943 if (!drm_plane
->crtc
) {
944 DRM_DEBUG_DRIVER("drm plane:%d not enabled\n",
949 DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n",
950 drm_plane
->crtc
->base
.id
, sti_mixer_to_str(mixer
),
951 drm_plane
->base
.id
, sti_plane_to_str(plane
));
953 plane
->status
= STI_PLANE_DISABLING
;
956 static const struct drm_plane_helper_funcs sti_hqvdp_helpers_funcs
= {
957 .atomic_update
= sti_hqvdp_atomic_update
,
958 .atomic_disable
= sti_hqvdp_atomic_disable
,
961 static struct drm_plane
*sti_hqvdp_create(struct drm_device
*drm_dev
,
962 struct device
*dev
, int desc
)
964 struct sti_hqvdp
*hqvdp
= dev_get_drvdata(dev
);
967 hqvdp
->plane
.desc
= desc
;
968 hqvdp
->plane
.status
= STI_PLANE_DISABLED
;
970 sti_hqvdp_init(hqvdp
);
972 res
= drm_universal_plane_init(drm_dev
, &hqvdp
->plane
.drm_plane
, 1,
973 &sti_plane_helpers_funcs
,
974 hqvdp_supported_formats
,
975 ARRAY_SIZE(hqvdp_supported_formats
),
976 DRM_PLANE_TYPE_OVERLAY
);
978 DRM_ERROR("Failed to initialize universal plane\n");
982 drm_plane_helper_add(&hqvdp
->plane
.drm_plane
, &sti_hqvdp_helpers_funcs
);
984 sti_plane_init_property(&hqvdp
->plane
, DRM_PLANE_TYPE_OVERLAY
);
986 return &hqvdp
->plane
.drm_plane
;
989 int sti_hqvdp_bind(struct device
*dev
, struct device
*master
, void *data
)
991 struct sti_hqvdp
*hqvdp
= dev_get_drvdata(dev
);
992 struct drm_device
*drm_dev
= data
;
993 struct drm_plane
*plane
;
995 DRM_DEBUG_DRIVER("\n");
997 hqvdp
->drm_dev
= drm_dev
;
999 /* Create HQVDP plane once xp70 is initialized */
1000 plane
= sti_hqvdp_create(drm_dev
, hqvdp
->dev
, STI_HQVDP_0
);
1002 DRM_ERROR("Can't create HQVDP plane\n");
1007 static void sti_hqvdp_unbind(struct device
*dev
,
1008 struct device
*master
, void *data
)
1013 static const struct component_ops sti_hqvdp_ops
= {
1014 .bind
= sti_hqvdp_bind
,
1015 .unbind
= sti_hqvdp_unbind
,
1018 static int sti_hqvdp_probe(struct platform_device
*pdev
)
1020 struct device
*dev
= &pdev
->dev
;
1021 struct device_node
*vtg_np
;
1022 struct sti_hqvdp
*hqvdp
;
1023 struct resource
*res
;
1025 DRM_DEBUG_DRIVER("\n");
1027 hqvdp
= devm_kzalloc(dev
, sizeof(*hqvdp
), GFP_KERNEL
);
1029 DRM_ERROR("Failed to allocate HQVDP context\n");
1035 /* Get Memory resources */
1036 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
1038 DRM_ERROR("Get memory resource failed\n");
1041 hqvdp
->regs
= devm_ioremap(dev
, res
->start
, resource_size(res
));
1042 if (hqvdp
->regs
== NULL
) {
1043 DRM_ERROR("Register mapping failed\n");
1047 /* Get clock resources */
1048 hqvdp
->clk
= devm_clk_get(dev
, "hqvdp");
1049 hqvdp
->clk_pix_main
= devm_clk_get(dev
, "pix_main");
1050 if (IS_ERR(hqvdp
->clk
) || IS_ERR(hqvdp
->clk_pix_main
)) {
1051 DRM_ERROR("Cannot get clocks\n");
1055 /* Get reset resources */
1056 hqvdp
->reset
= devm_reset_control_get(dev
, "hqvdp");
1057 if (!IS_ERR(hqvdp
->reset
))
1058 reset_control_deassert(hqvdp
->reset
);
1060 vtg_np
= of_parse_phandle(pdev
->dev
.of_node
, "st,vtg", 0);
1062 hqvdp
->vtg
= of_vtg_find(vtg_np
);
1064 platform_set_drvdata(pdev
, hqvdp
);
1066 return component_add(&pdev
->dev
, &sti_hqvdp_ops
);
1069 static int sti_hqvdp_remove(struct platform_device
*pdev
)
1071 component_del(&pdev
->dev
, &sti_hqvdp_ops
);
1075 static struct of_device_id hqvdp_of_match
[] = {
1076 { .compatible
= "st,stih407-hqvdp", },
1079 MODULE_DEVICE_TABLE(of
, hqvdp_of_match
);
1081 struct platform_driver sti_hqvdp_driver
= {
1083 .name
= "sti-hqvdp",
1084 .owner
= THIS_MODULE
,
1085 .of_match_table
= hqvdp_of_match
,
1087 .probe
= sti_hqvdp_probe
,
1088 .remove
= sti_hqvdp_remove
,
1091 MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
1092 MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver");
1093 MODULE_LICENSE("GPL");