brintos

brintos / linux-shallow public Read only

0
0
Text · 39.7 KiB · acbf70b Raw
1426 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) STMicroelectronics SA 20144 * Authors: Fabien Dessenne <fabien.dessenne@st.com> for STMicroelectronics.5 */6 7#include <linux/component.h>8#include <linux/delay.h>9#include <linux/dma-mapping.h>10#include <linux/firmware.h>11#include <linux/io.h>12#include <linux/module.h>13#include <linux/of.h>14#include <linux/reset.h>15#include <linux/seq_file.h>16 17#include <drm/drm_atomic.h>18#include <drm/drm_device.h>19#include <drm/drm_fb_dma_helper.h>20#include <drm/drm_fourcc.h>21#include <drm/drm_framebuffer.h>22#include <drm/drm_gem_dma_helper.h>23 24#include "sti_compositor.h"25#include "sti_drv.h"26#include "sti_hqvdp_lut.h"27#include "sti_plane.h"28#include "sti_vtg.h"29 30/* Firmware name */31#define HQVDP_FMW_NAME          "hqvdp-stih407.bin"32 33/* Regs address */34#define HQVDP_DMEM              0x00000000               /* 0x00000000 */35#define HQVDP_PMEM              0x00040000               /* 0x00040000 */36#define HQVDP_RD_PLUG           0x000E0000               /* 0x000E0000 */37#define HQVDP_RD_PLUG_CONTROL   (HQVDP_RD_PLUG + 0x1000) /* 0x000E1000 */38#define HQVDP_RD_PLUG_PAGE_SIZE (HQVDP_RD_PLUG + 0x1004) /* 0x000E1004 */39#define HQVDP_RD_PLUG_MIN_OPC   (HQVDP_RD_PLUG + 0x1008) /* 0x000E1008 */40#define HQVDP_RD_PLUG_MAX_OPC   (HQVDP_RD_PLUG + 0x100C) /* 0x000E100C */41#define HQVDP_RD_PLUG_MAX_CHK   (HQVDP_RD_PLUG + 0x1010) /* 0x000E1010 */42#define HQVDP_RD_PLUG_MAX_MSG   (HQVDP_RD_PLUG + 0x1014) /* 0x000E1014 */43#define HQVDP_RD_PLUG_MIN_SPACE (HQVDP_RD_PLUG + 0x1018) /* 0x000E1018 */44#define HQVDP_WR_PLUG           0x000E2000               /* 0x000E2000 */45#define HQVDP_WR_PLUG_CONTROL   (HQVDP_WR_PLUG + 0x1000) /* 0x000E3000 */46#define HQVDP_WR_PLUG_PAGE_SIZE (HQVDP_WR_PLUG + 0x1004) /* 0x000E3004 */47#define HQVDP_WR_PLUG_MIN_OPC   (HQVDP_WR_PLUG + 0x1008) /* 0x000E3008 */48#define HQVDP_WR_PLUG_MAX_OPC   (HQVDP_WR_PLUG + 0x100C) /* 0x000E300C */49#define HQVDP_WR_PLUG_MAX_CHK   (HQVDP_WR_PLUG + 0x1010) /* 0x000E3010 */50#define HQVDP_WR_PLUG_MAX_MSG   (HQVDP_WR_PLUG + 0x1014) /* 0x000E3014 */51#define HQVDP_WR_PLUG_MIN_SPACE (HQVDP_WR_PLUG + 0x1018) /* 0x000E3018 */52#define HQVDP_MBX               0x000E4000               /* 0x000E4000 */53#define HQVDP_MBX_IRQ_TO_XP70   (HQVDP_MBX + 0x0000)     /* 0x000E4000 */54#define HQVDP_MBX_INFO_HOST     (HQVDP_MBX + 0x0004)     /* 0x000E4004 */55#define HQVDP_MBX_IRQ_TO_HOST   (HQVDP_MBX + 0x0008)     /* 0x000E4008 */56#define HQVDP_MBX_INFO_XP70     (HQVDP_MBX + 0x000C)     /* 0x000E400C */57#define HQVDP_MBX_SW_RESET_CTRL (HQVDP_MBX + 0x0010)     /* 0x000E4010 */58#define HQVDP_MBX_STARTUP_CTRL1 (HQVDP_MBX + 0x0014)     /* 0x000E4014 */59#define HQVDP_MBX_STARTUP_CTRL2 (HQVDP_MBX + 0x0018)     /* 0x000E4018 */60#define HQVDP_MBX_GP_STATUS     (HQVDP_MBX + 0x001C)     /* 0x000E401C */61#define HQVDP_MBX_NEXT_CMD      (HQVDP_MBX + 0x0020)     /* 0x000E4020 */62#define HQVDP_MBX_CURRENT_CMD   (HQVDP_MBX + 0x0024)     /* 0x000E4024 */63#define HQVDP_MBX_SOFT_VSYNC    (HQVDP_MBX + 0x0028)     /* 0x000E4028 */64 65/* Plugs config */66#define PLUG_CONTROL_ENABLE     0x0000000167#define PLUG_PAGE_SIZE_256      0x0000000268#define PLUG_MIN_OPC_8          0x0000000369#define PLUG_MAX_OPC_64         0x0000000670#define PLUG_MAX_CHK_2X         0x0000000171#define PLUG_MAX_MSG_1X         0x0000000072#define PLUG_MIN_SPACE_1        0x0000000073 74/* SW reset CTRL */75#define SW_RESET_CTRL_FULL      BIT(0)76#define SW_RESET_CTRL_CORE      BIT(1)77 78/* Startup ctrl 1 */79#define STARTUP_CTRL1_RST_DONE  BIT(0)80#define STARTUP_CTRL1_AUTH_IDLE BIT(2)81 82/* Startup ctrl 2 */83#define STARTUP_CTRL2_FETCH_EN  BIT(1)84 85/* Info xP70 */86#define INFO_XP70_FW_READY      BIT(15)87#define INFO_XP70_FW_PROCESSING BIT(14)88#define INFO_XP70_FW_INITQUEUES BIT(13)89 90/* SOFT_VSYNC */91#define SOFT_VSYNC_HW           0x0000000092#define SOFT_VSYNC_SW_CMD       0x0000000193#define SOFT_VSYNC_SW_CTRL_IRQ  0x0000000394 95/* Reset & boot poll config */96#define POLL_MAX_ATTEMPT        5097#define POLL_DELAY_MS           2098 99#define SCALE_FACTOR            8192100#define SCALE_MAX_FOR_LEG_LUT_F 4096101#define SCALE_MAX_FOR_LEG_LUT_E 4915102#define SCALE_MAX_FOR_LEG_LUT_D 6654103#define SCALE_MAX_FOR_LEG_LUT_C 8192104 105enum sti_hvsrc_orient {106	HVSRC_HORI,107	HVSRC_VERT108};109 110/* Command structures */111struct sti_hqvdp_top {112	u32 config;113	u32 mem_format;114	u32 current_luma;115	u32 current_enh_luma;116	u32 current_right_luma;117	u32 current_enh_right_luma;118	u32 current_chroma;119	u32 current_enh_chroma;120	u32 current_right_chroma;121	u32 current_enh_right_chroma;122	u32 output_luma;123	u32 output_chroma;124	u32 luma_src_pitch;125	u32 luma_enh_src_pitch;126	u32 luma_right_src_pitch;127	u32 luma_enh_right_src_pitch;128	u32 chroma_src_pitch;129	u32 chroma_enh_src_pitch;130	u32 chroma_right_src_pitch;131	u32 chroma_enh_right_src_pitch;132	u32 luma_processed_pitch;133	u32 chroma_processed_pitch;134	u32 input_frame_size;135	u32 input_viewport_ori;136	u32 input_viewport_ori_right;137	u32 input_viewport_size;138	u32 left_view_border_width;139	u32 right_view_border_width;140	u32 left_view_3d_offset_width;141	u32 right_view_3d_offset_width;142	u32 side_stripe_color;143	u32 crc_reset_ctrl;144};145 146/* Configs for interlaced : no IT, no pass thru, 3 fields */147#define TOP_CONFIG_INTER_BTM            0x00000000148#define TOP_CONFIG_INTER_TOP            0x00000002149 150/* Config for progressive : no IT, no pass thru, 3 fields */151#define TOP_CONFIG_PROGRESSIVE          0x00000001152 153/* Default MemFormat: in=420_raster_dual out=444_raster;opaque Mem2Tv mode */154#define TOP_MEM_FORMAT_DFLT             0x00018060155 156/* Min/Max size */157#define MAX_WIDTH                       0x1FFF158#define MAX_HEIGHT                      0x0FFF159#define MIN_WIDTH                       0x0030160#define MIN_HEIGHT                      0x0010161 162struct sti_hqvdp_vc1re {163	u32 ctrl_prv_csdi;164	u32 ctrl_cur_csdi;165	u32 ctrl_nxt_csdi;166	u32 ctrl_cur_fmd;167	u32 ctrl_nxt_fmd;168};169 170struct sti_hqvdp_fmd {171	u32 config;172	u32 viewport_ori;173	u32 viewport_size;174	u32 next_next_luma;175	u32 next_next_right_luma;176	u32 next_next_next_luma;177	u32 next_next_next_right_luma;178	u32 threshold_scd;179	u32 threshold_rfd;180	u32 threshold_move;181	u32 threshold_cfd;182};183 184struct sti_hqvdp_csdi {185	u32 config;186	u32 config2;187	u32 dcdi_config;188	u32 prev_luma;189	u32 prev_enh_luma;190	u32 prev_right_luma;191	u32 prev_enh_right_luma;192	u32 next_luma;193	u32 next_enh_luma;194	u32 next_right_luma;195	u32 next_enh_right_luma;196	u32 prev_chroma;197	u32 prev_enh_chroma;198	u32 prev_right_chroma;199	u32 prev_enh_right_chroma;200	u32 next_chroma;201	u32 next_enh_chroma;202	u32 next_right_chroma;203	u32 next_enh_right_chroma;204	u32 prev_motion;205	u32 prev_right_motion;206	u32 cur_motion;207	u32 cur_right_motion;208	u32 next_motion;209	u32 next_right_motion;210};211 212/* Config for progressive: by pass */213#define CSDI_CONFIG_PROG                0x00000000214/* Config for directional deinterlacing without motion */215#define CSDI_CONFIG_INTER_DIR           0x00000016216/* Additional configs for fader, blender, motion,... deinterlace algorithms */217#define CSDI_CONFIG2_DFLT               0x000001B3218#define CSDI_DCDI_CONFIG_DFLT           0x00203803219 220struct sti_hqvdp_hvsrc {221	u32 hor_panoramic_ctrl;222	u32 output_picture_size;223	u32 init_horizontal;224	u32 init_vertical;225	u32 param_ctrl;226	u32 yh_coef[NB_COEF];227	u32 ch_coef[NB_COEF];228	u32 yv_coef[NB_COEF];229	u32 cv_coef[NB_COEF];230	u32 hori_shift;231	u32 vert_shift;232};233 234/* Default ParamCtrl: all controls enabled */235#define HVSRC_PARAM_CTRL_DFLT           0xFFFFFFFF236 237struct sti_hqvdp_iqi {238	u32 config;239	u32 demo_wind_size;240	u32 pk_config;241	u32 coeff0_coeff1;242	u32 coeff2_coeff3;243	u32 coeff4;244	u32 pk_lut;245	u32 pk_gain;246	u32 pk_coring_level;247	u32 cti_config;248	u32 le_config;249	u32 le_lut[64];250	u32 con_bri;251	u32 sat_gain;252	u32 pxf_conf;253	u32 default_color;254};255 256/* Default Config : IQI bypassed */257#define IQI_CONFIG_DFLT                 0x00000001258/* Default Contrast & Brightness gain = 256 */259#define IQI_CON_BRI_DFLT                0x00000100260/* Default Saturation gain = 256 */261#define IQI_SAT_GAIN_DFLT               0x00000100262/* Default PxfConf : P2I bypassed */263#define IQI_PXF_CONF_DFLT               0x00000001264 265struct sti_hqvdp_top_status {266	u32 processing_time;267	u32 input_y_crc;268	u32 input_uv_crc;269};270 271struct sti_hqvdp_fmd_status {272	u32 fmd_repeat_move_status;273	u32 fmd_scene_count_status;274	u32 cfd_sum;275	u32 field_sum;276	u32 next_y_fmd_crc;277	u32 next_next_y_fmd_crc;278	u32 next_next_next_y_fmd_crc;279};280 281struct sti_hqvdp_csdi_status {282	u32 prev_y_csdi_crc;283	u32 cur_y_csdi_crc;284	u32 next_y_csdi_crc;285	u32 prev_uv_csdi_crc;286	u32 cur_uv_csdi_crc;287	u32 next_uv_csdi_crc;288	u32 y_csdi_crc;289	u32 uv_csdi_crc;290	u32 uv_cup_crc;291	u32 mot_csdi_crc;292	u32 mot_cur_csdi_crc;293	u32 mot_prev_csdi_crc;294};295 296struct sti_hqvdp_hvsrc_status {297	u32 y_hvsrc_crc;298	u32 u_hvsrc_crc;299	u32 v_hvsrc_crc;300};301 302struct sti_hqvdp_iqi_status {303	u32 pxf_it_status;304	u32 y_iqi_crc;305	u32 u_iqi_crc;306	u32 v_iqi_crc;307};308 309/* Main commands. We use 2 commands one being processed by the firmware, one310 * ready to be fetched upon next Vsync*/311#define NB_VDP_CMD	2312 313struct sti_hqvdp_cmd {314	struct sti_hqvdp_top top;315	struct sti_hqvdp_vc1re vc1re;316	struct sti_hqvdp_fmd fmd;317	struct sti_hqvdp_csdi csdi;318	struct sti_hqvdp_hvsrc hvsrc;319	struct sti_hqvdp_iqi iqi;320	struct sti_hqvdp_top_status top_status;321	struct sti_hqvdp_fmd_status fmd_status;322	struct sti_hqvdp_csdi_status csdi_status;323	struct sti_hqvdp_hvsrc_status hvsrc_status;324	struct sti_hqvdp_iqi_status iqi_status;325};326 327/*328 * STI HQVDP structure329 *330 * @dev:               driver device331 * @drm_dev:           the drm device332 * @regs:              registers333 * @plane:             plane structure for hqvdp it self334 * @clk:               IP clock335 * @clk_pix_main:      pix main clock336 * @reset:             reset control337 * @vtg_nb:            notifier to handle VTG Vsync338 * @btm_field_pending: is there any bottom field (interlaced frame) to display339 * @hqvdp_cmd:         buffer of commands340 * @hqvdp_cmd_paddr:   physical address of hqvdp_cmd341 * @vtg:               vtg for main data path342 * @xp70_initialized:  true if xp70 is already initialized343 * @vtg_registered:    true if registered to VTG344 */345struct sti_hqvdp {346	struct device *dev;347	struct drm_device *drm_dev;348	void __iomem *regs;349	struct sti_plane plane;350	struct clk *clk;351	struct clk *clk_pix_main;352	struct reset_control *reset;353	struct notifier_block vtg_nb;354	bool btm_field_pending;355	void *hqvdp_cmd;356	u32 hqvdp_cmd_paddr;357	struct sti_vtg *vtg;358	bool xp70_initialized;359	bool vtg_registered;360};361 362#define to_sti_hqvdp(x) container_of(x, struct sti_hqvdp, plane)363 364static const uint32_t hqvdp_supported_formats[] = {365	DRM_FORMAT_NV12,366};367 368/**369 * sti_hqvdp_get_free_cmd370 * @hqvdp: hqvdp structure371 *372 * Look for a hqvdp_cmd that is not being used (or about to be used) by the FW.373 *374 * RETURNS:375 * the offset of the command to be used.376 * -1 in error cases377 */378static int sti_hqvdp_get_free_cmd(struct sti_hqvdp *hqvdp)379{380	u32 curr_cmd, next_cmd;381	u32 cmd = hqvdp->hqvdp_cmd_paddr;382	int i;383 384	curr_cmd = readl(hqvdp->regs + HQVDP_MBX_CURRENT_CMD);385	next_cmd = readl(hqvdp->regs + HQVDP_MBX_NEXT_CMD);386 387	for (i = 0; i < NB_VDP_CMD; i++) {388		if ((cmd != curr_cmd) && (cmd != next_cmd))389			return i * sizeof(struct sti_hqvdp_cmd);390		cmd += sizeof(struct sti_hqvdp_cmd);391	}392 393	return -1;394}395 396/**397 * sti_hqvdp_get_curr_cmd398 * @hqvdp: hqvdp structure399 *400 * Look for the hqvdp_cmd that is being used by the FW.401 *402 * RETURNS:403 *  the offset of the command to be used.404 * -1 in error cases405 */406static int sti_hqvdp_get_curr_cmd(struct sti_hqvdp *hqvdp)407{408	u32 curr_cmd;409	u32 cmd = hqvdp->hqvdp_cmd_paddr;410	unsigned int i;411 412	curr_cmd = readl(hqvdp->regs + HQVDP_MBX_CURRENT_CMD);413 414	for (i = 0; i < NB_VDP_CMD; i++) {415		if (cmd == curr_cmd)416			return i * sizeof(struct sti_hqvdp_cmd);417 418		cmd += sizeof(struct sti_hqvdp_cmd);419	}420 421	return -1;422}423 424/**425 * sti_hqvdp_get_next_cmd426 * @hqvdp: hqvdp structure427 *428 * Look for the next hqvdp_cmd that will be used by the FW.429 *430 * RETURNS:431 *  the offset of the next command that will be used.432 * -1 in error cases433 */434static int sti_hqvdp_get_next_cmd(struct sti_hqvdp *hqvdp)435{436	int next_cmd;437	dma_addr_t cmd = hqvdp->hqvdp_cmd_paddr;438	unsigned int i;439 440	next_cmd = readl(hqvdp->regs + HQVDP_MBX_NEXT_CMD);441 442	for (i = 0; i < NB_VDP_CMD; i++) {443		if (cmd == next_cmd)444			return i * sizeof(struct sti_hqvdp_cmd);445 446		cmd += sizeof(struct sti_hqvdp_cmd);447	}448 449	return -1;450}451 452#define DBGFS_DUMP(reg) seq_printf(s, "\n  %-25s 0x%08X", #reg, \453				   readl(hqvdp->regs + reg))454 455static const char *hqvdp_dbg_get_lut(u32 *coef)456{457	if (!memcmp(coef, coef_lut_a_legacy, 16))458		return "LUT A";459	if (!memcmp(coef, coef_lut_b, 16))460		return "LUT B";461	if (!memcmp(coef, coef_lut_c_y_legacy, 16))462		return "LUT C Y";463	if (!memcmp(coef, coef_lut_c_c_legacy, 16))464		return "LUT C C";465	if (!memcmp(coef, coef_lut_d_y_legacy, 16))466		return "LUT D Y";467	if (!memcmp(coef, coef_lut_d_c_legacy, 16))468		return "LUT D C";469	if (!memcmp(coef, coef_lut_e_y_legacy, 16))470		return "LUT E Y";471	if (!memcmp(coef, coef_lut_e_c_legacy, 16))472		return "LUT E C";473	if (!memcmp(coef, coef_lut_f_y_legacy, 16))474		return "LUT F Y";475	if (!memcmp(coef, coef_lut_f_c_legacy, 16))476		return "LUT F C";477	return "<UNKNOWN>";478}479 480static void hqvdp_dbg_dump_cmd(struct seq_file *s, struct sti_hqvdp_cmd *c)481{482	int src_w, src_h, dst_w, dst_h;483 484	seq_puts(s, "\n\tTOP:");485	seq_printf(s, "\n\t %-20s 0x%08X", "Config", c->top.config);486	switch (c->top.config) {487	case TOP_CONFIG_PROGRESSIVE:488		seq_puts(s, "\tProgressive");489		break;490	case TOP_CONFIG_INTER_TOP:491		seq_puts(s, "\tInterlaced, top field");492		break;493	case TOP_CONFIG_INTER_BTM:494		seq_puts(s, "\tInterlaced, bottom field");495		break;496	default:497		seq_puts(s, "\t<UNKNOWN>");498		break;499	}500 501	seq_printf(s, "\n\t %-20s 0x%08X", "MemFormat", c->top.mem_format);502	seq_printf(s, "\n\t %-20s 0x%08X", "CurrentY", c->top.current_luma);503	seq_printf(s, "\n\t %-20s 0x%08X", "CurrentC", c->top.current_chroma);504	seq_printf(s, "\n\t %-20s 0x%08X", "YSrcPitch", c->top.luma_src_pitch);505	seq_printf(s, "\n\t %-20s 0x%08X", "CSrcPitch",506		   c->top.chroma_src_pitch);507	seq_printf(s, "\n\t %-20s 0x%08X", "InputFrameSize",508		   c->top.input_frame_size);509	seq_printf(s, "\t%dx%d",510		   c->top.input_frame_size & 0x0000FFFF,511		   c->top.input_frame_size >> 16);512	seq_printf(s, "\n\t %-20s 0x%08X", "InputViewportSize",513		   c->top.input_viewport_size);514	src_w = c->top.input_viewport_size & 0x0000FFFF;515	src_h = c->top.input_viewport_size >> 16;516	seq_printf(s, "\t%dx%d", src_w, src_h);517 518	seq_puts(s, "\n\tHVSRC:");519	seq_printf(s, "\n\t %-20s 0x%08X", "OutputPictureSize",520		   c->hvsrc.output_picture_size);521	dst_w = c->hvsrc.output_picture_size & 0x0000FFFF;522	dst_h = c->hvsrc.output_picture_size >> 16;523	seq_printf(s, "\t%dx%d", dst_w, dst_h);524	seq_printf(s, "\n\t %-20s 0x%08X", "ParamCtrl", c->hvsrc.param_ctrl);525 526	seq_printf(s, "\n\t %-20s %s", "yh_coef",527		   hqvdp_dbg_get_lut(c->hvsrc.yh_coef));528	seq_printf(s, "\n\t %-20s %s", "ch_coef",529		   hqvdp_dbg_get_lut(c->hvsrc.ch_coef));530	seq_printf(s, "\n\t %-20s %s", "yv_coef",531		   hqvdp_dbg_get_lut(c->hvsrc.yv_coef));532	seq_printf(s, "\n\t %-20s %s", "cv_coef",533		   hqvdp_dbg_get_lut(c->hvsrc.cv_coef));534 535	seq_printf(s, "\n\t %-20s", "ScaleH");536	if (dst_w > src_w)537		seq_printf(s, " %d/1", dst_w / src_w);538	else539		seq_printf(s, " 1/%d", src_w / dst_w);540 541	seq_printf(s, "\n\t %-20s", "tScaleV");542	if (dst_h > src_h)543		seq_printf(s, " %d/1", dst_h / src_h);544	else545		seq_printf(s, " 1/%d", src_h / dst_h);546 547	seq_puts(s, "\n\tCSDI:");548	seq_printf(s, "\n\t %-20s 0x%08X\t", "Config", c->csdi.config);549	switch (c->csdi.config) {550	case CSDI_CONFIG_PROG:551		seq_puts(s, "Bypass");552		break;553	case CSDI_CONFIG_INTER_DIR:554		seq_puts(s, "Deinterlace, directional");555		break;556	default:557		seq_puts(s, "<UNKNOWN>");558		break;559	}560 561	seq_printf(s, "\n\t %-20s 0x%08X", "Config2", c->csdi.config2);562	seq_printf(s, "\n\t %-20s 0x%08X", "DcdiConfig", c->csdi.dcdi_config);563}564 565static int hqvdp_dbg_show(struct seq_file *s, void *data)566{567	struct drm_info_node *node = s->private;568	struct sti_hqvdp *hqvdp = (struct sti_hqvdp *)node->info_ent->data;569	int cmd, cmd_offset, infoxp70;570	void *virt;571 572	seq_printf(s, "%s: (vaddr = 0x%p)",573		   sti_plane_to_str(&hqvdp->plane), hqvdp->regs);574 575	DBGFS_DUMP(HQVDP_MBX_IRQ_TO_XP70);576	DBGFS_DUMP(HQVDP_MBX_INFO_HOST);577	DBGFS_DUMP(HQVDP_MBX_IRQ_TO_HOST);578	DBGFS_DUMP(HQVDP_MBX_INFO_XP70);579	infoxp70 = readl(hqvdp->regs + HQVDP_MBX_INFO_XP70);580	seq_puts(s, "\tFirmware state: ");581	if (infoxp70 & INFO_XP70_FW_READY)582		seq_puts(s, "idle and ready");583	else if (infoxp70 & INFO_XP70_FW_PROCESSING)584		seq_puts(s, "processing a picture");585	else if (infoxp70 & INFO_XP70_FW_INITQUEUES)586		seq_puts(s, "programming queues");587	else588		seq_puts(s, "NOT READY");589 590	DBGFS_DUMP(HQVDP_MBX_SW_RESET_CTRL);591	DBGFS_DUMP(HQVDP_MBX_STARTUP_CTRL1);592	if (readl(hqvdp->regs + HQVDP_MBX_STARTUP_CTRL1)593					& STARTUP_CTRL1_RST_DONE)594		seq_puts(s, "\tReset is done");595	else596		seq_puts(s, "\tReset is NOT done");597	DBGFS_DUMP(HQVDP_MBX_STARTUP_CTRL2);598	if (readl(hqvdp->regs + HQVDP_MBX_STARTUP_CTRL2)599					& STARTUP_CTRL2_FETCH_EN)600		seq_puts(s, "\tFetch is enabled");601	else602		seq_puts(s, "\tFetch is NOT enabled");603	DBGFS_DUMP(HQVDP_MBX_GP_STATUS);604	DBGFS_DUMP(HQVDP_MBX_NEXT_CMD);605	DBGFS_DUMP(HQVDP_MBX_CURRENT_CMD);606	DBGFS_DUMP(HQVDP_MBX_SOFT_VSYNC);607	if (!(readl(hqvdp->regs + HQVDP_MBX_SOFT_VSYNC) & 3))608		seq_puts(s, "\tHW Vsync");609	else610		seq_puts(s, "\tSW Vsync ?!?!");611 612	/* Last command */613	cmd = readl(hqvdp->regs + HQVDP_MBX_CURRENT_CMD);614	cmd_offset = sti_hqvdp_get_curr_cmd(hqvdp);615	if (cmd_offset == -1) {616		seq_puts(s, "\n\n  Last command: unknown");617	} else {618		virt = hqvdp->hqvdp_cmd + cmd_offset;619		seq_printf(s, "\n\n  Last command: address @ 0x%x (0x%p)",620			   cmd, virt);621		hqvdp_dbg_dump_cmd(s, (struct sti_hqvdp_cmd *)virt);622	}623 624	/* Next command */625	cmd = readl(hqvdp->regs + HQVDP_MBX_NEXT_CMD);626	cmd_offset = sti_hqvdp_get_next_cmd(hqvdp);627	if (cmd_offset == -1) {628		seq_puts(s, "\n\n  Next command: unknown");629	} else {630		virt = hqvdp->hqvdp_cmd + cmd_offset;631		seq_printf(s, "\n\n  Next command address: @ 0x%x (0x%p)",632			   cmd, virt);633		hqvdp_dbg_dump_cmd(s, (struct sti_hqvdp_cmd *)virt);634	}635 636	seq_putc(s, '\n');637	return 0;638}639 640static struct drm_info_list hqvdp_debugfs_files[] = {641	{ "hqvdp", hqvdp_dbg_show, 0, NULL },642};643 644static void hqvdp_debugfs_init(struct sti_hqvdp *hqvdp, struct drm_minor *minor)645{646	unsigned int i;647 648	for (i = 0; i < ARRAY_SIZE(hqvdp_debugfs_files); i++)649		hqvdp_debugfs_files[i].data = hqvdp;650 651	drm_debugfs_create_files(hqvdp_debugfs_files,652				 ARRAY_SIZE(hqvdp_debugfs_files),653				 minor->debugfs_root, minor);654}655 656/**657 * sti_hqvdp_update_hvsrc658 * @orient: horizontal or vertical659 * @scale:  scaling/zoom factor660 * @hvsrc:  the structure containing the LUT coef661 *662 * Update the Y and C Lut coef, as well as the shift param663 *664 * RETURNS:665 * None.666 */667static void sti_hqvdp_update_hvsrc(enum sti_hvsrc_orient orient, int scale,668		struct sti_hqvdp_hvsrc *hvsrc)669{670	const int *coef_c, *coef_y;671	int shift_c, shift_y;672 673	/* Get the appropriate coef tables */674	if (scale < SCALE_MAX_FOR_LEG_LUT_F) {675		coef_y = coef_lut_f_y_legacy;676		coef_c = coef_lut_f_c_legacy;677		shift_y = SHIFT_LUT_F_Y_LEGACY;678		shift_c = SHIFT_LUT_F_C_LEGACY;679	} else if (scale < SCALE_MAX_FOR_LEG_LUT_E) {680		coef_y = coef_lut_e_y_legacy;681		coef_c = coef_lut_e_c_legacy;682		shift_y = SHIFT_LUT_E_Y_LEGACY;683		shift_c = SHIFT_LUT_E_C_LEGACY;684	} else if (scale < SCALE_MAX_FOR_LEG_LUT_D) {685		coef_y = coef_lut_d_y_legacy;686		coef_c = coef_lut_d_c_legacy;687		shift_y = SHIFT_LUT_D_Y_LEGACY;688		shift_c = SHIFT_LUT_D_C_LEGACY;689	} else if (scale < SCALE_MAX_FOR_LEG_LUT_C) {690		coef_y = coef_lut_c_y_legacy;691		coef_c = coef_lut_c_c_legacy;692		shift_y = SHIFT_LUT_C_Y_LEGACY;693		shift_c = SHIFT_LUT_C_C_LEGACY;694	} else if (scale == SCALE_MAX_FOR_LEG_LUT_C) {695		coef_y = coef_c = coef_lut_b;696		shift_y = shift_c = SHIFT_LUT_B;697	} else {698		coef_y = coef_c = coef_lut_a_legacy;699		shift_y = shift_c = SHIFT_LUT_A_LEGACY;700	}701 702	if (orient == HVSRC_HORI) {703		hvsrc->hori_shift = (shift_c << 16) | shift_y;704		memcpy(hvsrc->yh_coef, coef_y, sizeof(hvsrc->yh_coef));705		memcpy(hvsrc->ch_coef, coef_c, sizeof(hvsrc->ch_coef));706	} else {707		hvsrc->vert_shift = (shift_c << 16) | shift_y;708		memcpy(hvsrc->yv_coef, coef_y, sizeof(hvsrc->yv_coef));709		memcpy(hvsrc->cv_coef, coef_c, sizeof(hvsrc->cv_coef));710	}711}712 713/**714 * sti_hqvdp_check_hw_scaling715 * @hqvdp: hqvdp pointer716 * @mode: display mode with timing constraints717 * @src_w: source width718 * @src_h: source height719 * @dst_w: destination width720 * @dst_h: destination height721 *722 * Check if the HW is able to perform the scaling request723 * The firmware scaling limitation is "CEIL(1/Zy) <= FLOOR(LFW)" where:724 *   Zy = OutputHeight / InputHeight725 *   LFW = (Tx * IPClock) / (MaxNbCycles * Cp)726 *     Tx : Total video mode horizontal resolution727 *     IPClock : HQVDP IP clock (Mhz)728 *     MaxNbCycles: max(InputWidth, OutputWidth)729 *     Cp: Video mode pixel clock (Mhz)730 *731 * RETURNS:732 * True if the HW can scale.733 */734static bool sti_hqvdp_check_hw_scaling(struct sti_hqvdp *hqvdp,735				       struct drm_display_mode *mode,736				       int src_w, int src_h,737				       int dst_w, int dst_h)738{739	unsigned long lfw;740	unsigned int inv_zy;741 742	lfw = mode->htotal * (clk_get_rate(hqvdp->clk) / 1000000);743	lfw /= max(src_w, dst_w) * mode->clock / 1000;744 745	inv_zy = DIV_ROUND_UP(src_h, dst_h);746 747	return (inv_zy <= lfw) ? true : false;748}749 750/**751 * sti_hqvdp_disable752 * @hqvdp: hqvdp pointer753 *754 * Disables the HQVDP plane755 */756static void sti_hqvdp_disable(struct sti_hqvdp *hqvdp)757{758	int i;759 760	DRM_DEBUG_DRIVER("%s\n", sti_plane_to_str(&hqvdp->plane));761 762	/* Unregister VTG Vsync callback */763	if (sti_vtg_unregister_client(hqvdp->vtg, &hqvdp->vtg_nb))764		DRM_DEBUG_DRIVER("Warning: cannot unregister VTG notifier\n");765 766	/* Set next cmd to NULL */767	writel(0, hqvdp->regs + HQVDP_MBX_NEXT_CMD);768 769	for (i = 0; i < POLL_MAX_ATTEMPT; i++) {770		if (readl(hqvdp->regs + HQVDP_MBX_INFO_XP70)771				& INFO_XP70_FW_READY)772			break;773		msleep(POLL_DELAY_MS);774	}775 776	/* VTG can stop now */777	clk_disable_unprepare(hqvdp->clk_pix_main);778 779	if (i == POLL_MAX_ATTEMPT)780		DRM_ERROR("XP70 could not revert to idle\n");781 782	hqvdp->plane.status = STI_PLANE_DISABLED;783	hqvdp->vtg_registered = false;784}785 786/**787 * sti_hqvdp_vtg_cb788 * @nb: notifier block789 * @evt: event message790 * @data: private data791 *792 * Handle VTG Vsync event, display pending bottom field793 *794 * RETURNS:795 * 0 on success.796 */797static int sti_hqvdp_vtg_cb(struct notifier_block *nb, unsigned long evt, void *data)798{799	struct sti_hqvdp *hqvdp = container_of(nb, struct sti_hqvdp, vtg_nb);800	int btm_cmd_offset, top_cmd_offest;801	struct sti_hqvdp_cmd *btm_cmd, *top_cmd;802 803	if ((evt != VTG_TOP_FIELD_EVENT) && (evt != VTG_BOTTOM_FIELD_EVENT)) {804		DRM_DEBUG_DRIVER("Unknown event\n");805		return 0;806	}807 808	if (hqvdp->plane.status == STI_PLANE_FLUSHING) {809		/* disable need to be synchronize on vsync event */810		DRM_DEBUG_DRIVER("Vsync event received => disable %s\n",811				 sti_plane_to_str(&hqvdp->plane));812 813		sti_hqvdp_disable(hqvdp);814	}815 816	if (hqvdp->btm_field_pending) {817		/* Create the btm field command from the current one */818		btm_cmd_offset = sti_hqvdp_get_free_cmd(hqvdp);819		top_cmd_offest = sti_hqvdp_get_curr_cmd(hqvdp);820		if ((btm_cmd_offset == -1) || (top_cmd_offest == -1)) {821			DRM_DEBUG_DRIVER("Warning: no cmd, will skip field\n");822			return -EBUSY;823		}824 825		btm_cmd = hqvdp->hqvdp_cmd + btm_cmd_offset;826		top_cmd = hqvdp->hqvdp_cmd + top_cmd_offest;827 828		memcpy(btm_cmd, top_cmd, sizeof(*btm_cmd));829 830		btm_cmd->top.config = TOP_CONFIG_INTER_BTM;831		btm_cmd->top.current_luma +=832				btm_cmd->top.luma_src_pitch / 2;833		btm_cmd->top.current_chroma +=834				btm_cmd->top.chroma_src_pitch / 2;835 836		/* Post the command to mailbox */837		writel(hqvdp->hqvdp_cmd_paddr + btm_cmd_offset,838				hqvdp->regs + HQVDP_MBX_NEXT_CMD);839 840		hqvdp->btm_field_pending = false;841 842		dev_dbg(hqvdp->dev, "%s Posted command:0x%x\n",843				__func__, hqvdp->hqvdp_cmd_paddr);844 845		sti_plane_update_fps(&hqvdp->plane, false, true);846	}847 848	return 0;849}850 851static void sti_hqvdp_init(struct sti_hqvdp *hqvdp)852{853	int size;854	dma_addr_t dma_addr;855 856	hqvdp->vtg_nb.notifier_call = sti_hqvdp_vtg_cb;857 858	/* Allocate memory for the VDP commands */859	size = NB_VDP_CMD * sizeof(struct sti_hqvdp_cmd);860	hqvdp->hqvdp_cmd = dma_alloc_wc(hqvdp->dev, size,861					&dma_addr,862					GFP_KERNEL | GFP_DMA);863	if (!hqvdp->hqvdp_cmd) {864		DRM_ERROR("Failed to allocate memory for VDP cmd\n");865		return;866	}867 868	hqvdp->hqvdp_cmd_paddr = (u32)dma_addr;869	memset(hqvdp->hqvdp_cmd, 0, size);870}871 872static void sti_hqvdp_init_plugs(struct sti_hqvdp *hqvdp)873{874	/* Configure Plugs (same for RD & WR) */875	writel(PLUG_PAGE_SIZE_256, hqvdp->regs + HQVDP_RD_PLUG_PAGE_SIZE);876	writel(PLUG_MIN_OPC_8, hqvdp->regs + HQVDP_RD_PLUG_MIN_OPC);877	writel(PLUG_MAX_OPC_64, hqvdp->regs + HQVDP_RD_PLUG_MAX_OPC);878	writel(PLUG_MAX_CHK_2X, hqvdp->regs + HQVDP_RD_PLUG_MAX_CHK);879	writel(PLUG_MAX_MSG_1X, hqvdp->regs + HQVDP_RD_PLUG_MAX_MSG);880	writel(PLUG_MIN_SPACE_1, hqvdp->regs + HQVDP_RD_PLUG_MIN_SPACE);881	writel(PLUG_CONTROL_ENABLE, hqvdp->regs + HQVDP_RD_PLUG_CONTROL);882 883	writel(PLUG_PAGE_SIZE_256, hqvdp->regs + HQVDP_WR_PLUG_PAGE_SIZE);884	writel(PLUG_MIN_OPC_8, hqvdp->regs + HQVDP_WR_PLUG_MIN_OPC);885	writel(PLUG_MAX_OPC_64, hqvdp->regs + HQVDP_WR_PLUG_MAX_OPC);886	writel(PLUG_MAX_CHK_2X, hqvdp->regs + HQVDP_WR_PLUG_MAX_CHK);887	writel(PLUG_MAX_MSG_1X, hqvdp->regs + HQVDP_WR_PLUG_MAX_MSG);888	writel(PLUG_MIN_SPACE_1, hqvdp->regs + HQVDP_WR_PLUG_MIN_SPACE);889	writel(PLUG_CONTROL_ENABLE, hqvdp->regs + HQVDP_WR_PLUG_CONTROL);890}891 892/**893 * sti_hqvdp_start_xp70894 * @hqvdp: hqvdp pointer895 *896 * Run the xP70 initialization sequence897 */898static void sti_hqvdp_start_xp70(struct sti_hqvdp *hqvdp)899{900	const struct firmware *firmware;901	u32 *fw_rd_plug, *fw_wr_plug, *fw_pmem, *fw_dmem;902	u8 *data;903	int i;904	struct fw_header {905		int rd_size;906		int wr_size;907		int pmem_size;908		int dmem_size;909	} *header;910 911	DRM_DEBUG_DRIVER("\n");912 913	if (hqvdp->xp70_initialized) {914		DRM_DEBUG_DRIVER("HQVDP XP70 already initialized\n");915		return;916	}917 918	/* Request firmware */919	if (request_firmware(&firmware, HQVDP_FMW_NAME, hqvdp->dev)) {920		DRM_ERROR("Can't get HQVDP firmware\n");921		return;922	}923 924	/* Check firmware parts */925	if (!firmware) {926		DRM_ERROR("Firmware not available\n");927		return;928	}929 930	header = (struct fw_header *)firmware->data;931	if (firmware->size < sizeof(*header)) {932		DRM_ERROR("Invalid firmware size (%zu)\n", firmware->size);933		goto out;934	}935	if ((sizeof(*header) + header->rd_size + header->wr_size +936		header->pmem_size + header->dmem_size) != firmware->size) {937		DRM_ERROR("Invalid fmw structure (%zu+%d+%d+%d+%d != %zu)\n",938			  sizeof(*header), header->rd_size, header->wr_size,939			  header->pmem_size, header->dmem_size,940			  firmware->size);941		goto out;942	}943 944	data = (u8 *)firmware->data;945	data += sizeof(*header);946	fw_rd_plug = (void *)data;947	data += header->rd_size;948	fw_wr_plug = (void *)data;949	data += header->wr_size;950	fw_pmem = (void *)data;951	data += header->pmem_size;952	fw_dmem = (void *)data;953 954	/* Enable clock */955	if (clk_prepare_enable(hqvdp->clk))956		DRM_ERROR("Failed to prepare/enable HQVDP clk\n");957 958	/* Reset */959	writel(SW_RESET_CTRL_FULL, hqvdp->regs + HQVDP_MBX_SW_RESET_CTRL);960 961	for (i = 0; i < POLL_MAX_ATTEMPT; i++) {962		if (readl(hqvdp->regs + HQVDP_MBX_STARTUP_CTRL1)963				& STARTUP_CTRL1_RST_DONE)964			break;965		msleep(POLL_DELAY_MS);966	}967	if (i == POLL_MAX_ATTEMPT) {968		DRM_ERROR("Could not reset\n");969		clk_disable_unprepare(hqvdp->clk);970		goto out;971	}972 973	/* Init Read & Write plugs */974	for (i = 0; i < header->rd_size / 4; i++)975		writel(fw_rd_plug[i], hqvdp->regs + HQVDP_RD_PLUG + i * 4);976	for (i = 0; i < header->wr_size / 4; i++)977		writel(fw_wr_plug[i], hqvdp->regs + HQVDP_WR_PLUG + i * 4);978 979	sti_hqvdp_init_plugs(hqvdp);980 981	/* Authorize Idle Mode */982	writel(STARTUP_CTRL1_AUTH_IDLE, hqvdp->regs + HQVDP_MBX_STARTUP_CTRL1);983 984	/* Prevent VTG interruption during the boot */985	writel(SOFT_VSYNC_SW_CTRL_IRQ, hqvdp->regs + HQVDP_MBX_SOFT_VSYNC);986	writel(0, hqvdp->regs + HQVDP_MBX_NEXT_CMD);987 988	/* Download PMEM & DMEM */989	for (i = 0; i < header->pmem_size / 4; i++)990		writel(fw_pmem[i], hqvdp->regs + HQVDP_PMEM + i * 4);991	for (i = 0; i < header->dmem_size / 4; i++)992		writel(fw_dmem[i], hqvdp->regs + HQVDP_DMEM + i * 4);993 994	/* Enable fetch */995	writel(STARTUP_CTRL2_FETCH_EN, hqvdp->regs + HQVDP_MBX_STARTUP_CTRL2);996 997	/* Wait end of boot */998	for (i = 0; i < POLL_MAX_ATTEMPT; i++) {999		if (readl(hqvdp->regs + HQVDP_MBX_INFO_XP70)1000				& INFO_XP70_FW_READY)1001			break;1002		msleep(POLL_DELAY_MS);1003	}1004	if (i == POLL_MAX_ATTEMPT) {1005		DRM_ERROR("Could not boot\n");1006		clk_disable_unprepare(hqvdp->clk);1007		goto out;1008	}1009 1010	/* Launch Vsync */1011	writel(SOFT_VSYNC_HW, hqvdp->regs + HQVDP_MBX_SOFT_VSYNC);1012 1013	DRM_INFO("HQVDP XP70 initialized\n");1014 1015	hqvdp->xp70_initialized = true;1016 1017out:1018	release_firmware(firmware);1019}1020 1021static int sti_hqvdp_atomic_check(struct drm_plane *drm_plane,1022				  struct drm_atomic_state *state)1023{1024	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,1025										 drm_plane);1026	struct sti_plane *plane = to_sti_plane(drm_plane);1027	struct sti_hqvdp *hqvdp = to_sti_hqvdp(plane);1028	struct drm_crtc *crtc = new_plane_state->crtc;1029	struct drm_framebuffer *fb = new_plane_state->fb;1030	struct drm_crtc_state *crtc_state;1031	struct drm_display_mode *mode;1032	int dst_x, dst_y, dst_w, dst_h;1033	int src_x, src_y, src_w, src_h;1034 1035	/* no need for further checks if the plane is being disabled */1036	if (!crtc || !fb)1037		return 0;1038 1039	crtc_state = drm_atomic_get_crtc_state(state, crtc);1040	mode = &crtc_state->mode;1041	dst_x = new_plane_state->crtc_x;1042	dst_y = new_plane_state->crtc_y;1043	dst_w = clamp_val(new_plane_state->crtc_w, 0, mode->hdisplay - dst_x);1044	dst_h = clamp_val(new_plane_state->crtc_h, 0, mode->vdisplay - dst_y);1045	/* src_x are in 16.16 format */1046	src_x = new_plane_state->src_x >> 16;1047	src_y = new_plane_state->src_y >> 16;1048	src_w = new_plane_state->src_w >> 16;1049	src_h = new_plane_state->src_h >> 16;1050 1051	if (mode->clock && !sti_hqvdp_check_hw_scaling(hqvdp, mode,1052						       src_w, src_h,1053						       dst_w, dst_h)) {1054		DRM_ERROR("Scaling beyond HW capabilities\n");1055		return -EINVAL;1056	}1057 1058	if (!drm_fb_dma_get_gem_obj(fb, 0)) {1059		DRM_ERROR("Can't get DMA GEM object for fb\n");1060		return -EINVAL;1061	}1062 1063	/*1064	 * Input / output size1065	 * Align to upper even value1066	 */1067	dst_w = ALIGN(dst_w, 2);1068	dst_h = ALIGN(dst_h, 2);1069 1070	if ((src_w > MAX_WIDTH) || (src_w < MIN_WIDTH) ||1071	    (src_h > MAX_HEIGHT) || (src_h < MIN_HEIGHT) ||1072	    (dst_w > MAX_WIDTH) || (dst_w < MIN_WIDTH) ||1073	    (dst_h > MAX_HEIGHT) || (dst_h < MIN_HEIGHT)) {1074		DRM_ERROR("Invalid in/out size %dx%d -> %dx%d\n",1075			  src_w, src_h,1076			  dst_w, dst_h);1077		return -EINVAL;1078	}1079 1080	if (!hqvdp->xp70_initialized)1081		/* Start HQVDP XP70 coprocessor */1082		sti_hqvdp_start_xp70(hqvdp);1083 1084	if (!hqvdp->vtg_registered) {1085		/* Prevent VTG shutdown */1086		if (clk_prepare_enable(hqvdp->clk_pix_main)) {1087			DRM_ERROR("Failed to prepare/enable pix main clk\n");1088			return -EINVAL;1089		}1090 1091		/* Register VTG Vsync callback to handle bottom fields */1092		if (sti_vtg_register_client(hqvdp->vtg,1093					    &hqvdp->vtg_nb,1094					    crtc)) {1095			DRM_ERROR("Cannot register VTG notifier\n");1096			clk_disable_unprepare(hqvdp->clk_pix_main);1097			return -EINVAL;1098		}1099		hqvdp->vtg_registered = true;1100	}1101 1102	DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n",1103		      crtc->base.id, sti_mixer_to_str(to_sti_mixer(crtc)),1104		      drm_plane->base.id, sti_plane_to_str(plane));1105	DRM_DEBUG_KMS("%s dst=(%dx%d)@(%d,%d) - src=(%dx%d)@(%d,%d)\n",1106		      sti_plane_to_str(plane),1107		      dst_w, dst_h, dst_x, dst_y,1108		      src_w, src_h, src_x, src_y);1109 1110	return 0;1111}1112 1113static void sti_hqvdp_atomic_update(struct drm_plane *drm_plane,1114				    struct drm_atomic_state *state)1115{1116	struct drm_plane_state *oldstate = drm_atomic_get_old_plane_state(state,1117									  drm_plane);1118	struct drm_plane_state *newstate = drm_atomic_get_new_plane_state(state,1119									  drm_plane);1120	struct sti_plane *plane = to_sti_plane(drm_plane);1121	struct sti_hqvdp *hqvdp = to_sti_hqvdp(plane);1122	struct drm_crtc *crtc = newstate->crtc;1123	struct drm_framebuffer *fb = newstate->fb;1124	struct drm_display_mode *mode;1125	int dst_x, dst_y, dst_w, dst_h;1126	int src_x, src_y, src_w, src_h;1127	struct drm_gem_dma_object *dma_obj;1128	struct sti_hqvdp_cmd *cmd;1129	int scale_h, scale_v;1130	int cmd_offset;1131 1132	if (!crtc || !fb)1133		return;1134 1135	if ((oldstate->fb == newstate->fb) &&1136	    (oldstate->crtc_x == newstate->crtc_x) &&1137	    (oldstate->crtc_y == newstate->crtc_y) &&1138	    (oldstate->crtc_w == newstate->crtc_w) &&1139	    (oldstate->crtc_h == newstate->crtc_h) &&1140	    (oldstate->src_x == newstate->src_x) &&1141	    (oldstate->src_y == newstate->src_y) &&1142	    (oldstate->src_w == newstate->src_w) &&1143	    (oldstate->src_h == newstate->src_h)) {1144		/* No change since last update, do not post cmd */1145		DRM_DEBUG_DRIVER("No change, not posting cmd\n");1146		plane->status = STI_PLANE_UPDATED;1147		return;1148	}1149 1150	mode = &crtc->mode;1151	dst_x = newstate->crtc_x;1152	dst_y = newstate->crtc_y;1153	dst_w = clamp_val(newstate->crtc_w, 0, mode->hdisplay - dst_x);1154	dst_h = clamp_val(newstate->crtc_h, 0, mode->vdisplay - dst_y);1155	/* src_x are in 16.16 format */1156	src_x = newstate->src_x >> 16;1157	src_y = newstate->src_y >> 16;1158	src_w = newstate->src_w >> 16;1159	src_h = newstate->src_h >> 16;1160 1161	cmd_offset = sti_hqvdp_get_free_cmd(hqvdp);1162	if (cmd_offset == -1) {1163		DRM_DEBUG_DRIVER("Warning: no cmd, will skip frame\n");1164		return;1165	}1166	cmd = hqvdp->hqvdp_cmd + cmd_offset;1167 1168	/* Static parameters, defaulting to progressive mode */1169	cmd->top.config = TOP_CONFIG_PROGRESSIVE;1170	cmd->top.mem_format = TOP_MEM_FORMAT_DFLT;1171	cmd->hvsrc.param_ctrl = HVSRC_PARAM_CTRL_DFLT;1172	cmd->csdi.config = CSDI_CONFIG_PROG;1173 1174	/* VC1RE, FMD bypassed : keep everything set to 01175	 * IQI/P2I bypassed */1176	cmd->iqi.config = IQI_CONFIG_DFLT;1177	cmd->iqi.con_bri = IQI_CON_BRI_DFLT;1178	cmd->iqi.sat_gain = IQI_SAT_GAIN_DFLT;1179	cmd->iqi.pxf_conf = IQI_PXF_CONF_DFLT;1180 1181	dma_obj = drm_fb_dma_get_gem_obj(fb, 0);1182 1183	DRM_DEBUG_DRIVER("drm FB:%d format:%.4s phys@:0x%lx\n", fb->base.id,1184			 (char *)&fb->format->format,1185			 (unsigned long) dma_obj->dma_addr);1186 1187	/* Buffer planes address */1188	cmd->top.current_luma = (u32) dma_obj->dma_addr + fb->offsets[0];1189	cmd->top.current_chroma = (u32) dma_obj->dma_addr + fb->offsets[1];1190 1191	/* Pitches */1192	cmd->top.luma_processed_pitch = fb->pitches[0];1193	cmd->top.luma_src_pitch = fb->pitches[0];1194	cmd->top.chroma_processed_pitch = fb->pitches[1];1195	cmd->top.chroma_src_pitch = fb->pitches[1];1196 1197	/* Input / output size1198	 * Align to upper even value */1199	dst_w = ALIGN(dst_w, 2);1200	dst_h = ALIGN(dst_h, 2);1201 1202	cmd->top.input_viewport_size = src_h << 16 | src_w;1203	cmd->top.input_frame_size = src_h << 16 | src_w;1204	cmd->hvsrc.output_picture_size = dst_h << 16 | dst_w;1205	cmd->top.input_viewport_ori = src_y << 16 | src_x;1206 1207	/* Handle interlaced */1208	if (fb->flags & DRM_MODE_FB_INTERLACED) {1209		/* Top field to display */1210		cmd->top.config = TOP_CONFIG_INTER_TOP;1211 1212		/* Update pitches and vert size */1213		cmd->top.input_frame_size = (src_h / 2) << 16 | src_w;1214		cmd->top.luma_processed_pitch *= 2;1215		cmd->top.luma_src_pitch *= 2;1216		cmd->top.chroma_processed_pitch *= 2;1217		cmd->top.chroma_src_pitch *= 2;1218 1219		/* Enable directional deinterlacing processing */1220		cmd->csdi.config = CSDI_CONFIG_INTER_DIR;1221		cmd->csdi.config2 = CSDI_CONFIG2_DFLT;1222		cmd->csdi.dcdi_config = CSDI_DCDI_CONFIG_DFLT;1223	}1224 1225	/* Update hvsrc lut coef */1226	scale_h = SCALE_FACTOR * dst_w / src_w;1227	sti_hqvdp_update_hvsrc(HVSRC_HORI, scale_h, &cmd->hvsrc);1228 1229	scale_v = SCALE_FACTOR * dst_h / src_h;1230	sti_hqvdp_update_hvsrc(HVSRC_VERT, scale_v, &cmd->hvsrc);1231 1232	writel(hqvdp->hqvdp_cmd_paddr + cmd_offset,1233	       hqvdp->regs + HQVDP_MBX_NEXT_CMD);1234 1235	/* Interlaced : get ready to display the bottom field at next Vsync */1236	if (fb->flags & DRM_MODE_FB_INTERLACED)1237		hqvdp->btm_field_pending = true;1238 1239	dev_dbg(hqvdp->dev, "%s Posted command:0x%x\n",1240		__func__, hqvdp->hqvdp_cmd_paddr + cmd_offset);1241 1242	sti_plane_update_fps(plane, true, true);1243 1244	plane->status = STI_PLANE_UPDATED;1245}1246 1247static void sti_hqvdp_atomic_disable(struct drm_plane *drm_plane,1248				     struct drm_atomic_state *state)1249{1250	struct drm_plane_state *oldstate = drm_atomic_get_old_plane_state(state,1251									  drm_plane);1252	struct sti_plane *plane = to_sti_plane(drm_plane);1253 1254	if (!oldstate->crtc) {1255		DRM_DEBUG_DRIVER("drm plane:%d not enabled\n",1256				 drm_plane->base.id);1257		return;1258	}1259 1260	DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n",1261			 oldstate->crtc->base.id,1262			 sti_mixer_to_str(to_sti_mixer(oldstate->crtc)),1263			 drm_plane->base.id, sti_plane_to_str(plane));1264 1265	plane->status = STI_PLANE_DISABLING;1266}1267 1268static const struct drm_plane_helper_funcs sti_hqvdp_helpers_funcs = {1269	.atomic_check = sti_hqvdp_atomic_check,1270	.atomic_update = sti_hqvdp_atomic_update,1271	.atomic_disable = sti_hqvdp_atomic_disable,1272};1273 1274static int sti_hqvdp_late_register(struct drm_plane *drm_plane)1275{1276	struct sti_plane *plane = to_sti_plane(drm_plane);1277	struct sti_hqvdp *hqvdp = to_sti_hqvdp(plane);1278 1279	hqvdp_debugfs_init(hqvdp, drm_plane->dev->primary);1280 1281	return 0;1282}1283 1284static const struct drm_plane_funcs sti_hqvdp_plane_helpers_funcs = {1285	.update_plane = drm_atomic_helper_update_plane,1286	.disable_plane = drm_atomic_helper_disable_plane,1287	.destroy = drm_plane_cleanup,1288	.reset = drm_atomic_helper_plane_reset,1289	.atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,1290	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,1291	.late_register = sti_hqvdp_late_register,1292};1293 1294static struct drm_plane *sti_hqvdp_create(struct drm_device *drm_dev,1295					  struct device *dev, int desc)1296{1297	struct sti_hqvdp *hqvdp = dev_get_drvdata(dev);1298	int res;1299 1300	hqvdp->plane.desc = desc;1301	hqvdp->plane.status = STI_PLANE_DISABLED;1302 1303	sti_hqvdp_init(hqvdp);1304 1305	res = drm_universal_plane_init(drm_dev, &hqvdp->plane.drm_plane, 1,1306				       &sti_hqvdp_plane_helpers_funcs,1307				       hqvdp_supported_formats,1308				       ARRAY_SIZE(hqvdp_supported_formats),1309				       NULL, DRM_PLANE_TYPE_OVERLAY, NULL);1310	if (res) {1311		DRM_ERROR("Failed to initialize universal plane\n");1312		return NULL;1313	}1314 1315	drm_plane_helper_add(&hqvdp->plane.drm_plane, &sti_hqvdp_helpers_funcs);1316 1317	sti_plane_init_property(&hqvdp->plane, DRM_PLANE_TYPE_OVERLAY);1318 1319	return &hqvdp->plane.drm_plane;1320}1321 1322static int sti_hqvdp_bind(struct device *dev, struct device *master, void *data)1323{1324	struct sti_hqvdp *hqvdp = dev_get_drvdata(dev);1325	struct drm_device *drm_dev = data;1326	struct drm_plane *plane;1327 1328	DRM_DEBUG_DRIVER("\n");1329 1330	hqvdp->drm_dev = drm_dev;1331 1332	/* Create HQVDP plane once xp70 is initialized */1333	plane = sti_hqvdp_create(drm_dev, hqvdp->dev, STI_HQVDP_0);1334	if (!plane)1335		DRM_ERROR("Can't create HQVDP plane\n");1336 1337	return 0;1338}1339 1340static void sti_hqvdp_unbind(struct device *dev,1341		struct device *master, void *data)1342{1343	/* do nothing */1344}1345 1346static const struct component_ops sti_hqvdp_ops = {1347	.bind = sti_hqvdp_bind,1348	.unbind = sti_hqvdp_unbind,1349};1350 1351static int sti_hqvdp_probe(struct platform_device *pdev)1352{1353	struct device *dev = &pdev->dev;1354	struct device_node *vtg_np;1355	struct sti_hqvdp *hqvdp;1356	struct resource *res;1357 1358	DRM_DEBUG_DRIVER("\n");1359 1360	hqvdp = devm_kzalloc(dev, sizeof(*hqvdp), GFP_KERNEL);1361	if (!hqvdp) {1362		DRM_ERROR("Failed to allocate HQVDP context\n");1363		return -ENOMEM;1364	}1365 1366	hqvdp->dev = dev;1367 1368	/* Get Memory resources */1369	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);1370	if (!res) {1371		DRM_ERROR("Get memory resource failed\n");1372		return -ENXIO;1373	}1374	hqvdp->regs = devm_ioremap(dev, res->start, resource_size(res));1375	if (!hqvdp->regs) {1376		DRM_ERROR("Register mapping failed\n");1377		return -ENXIO;1378	}1379 1380	/* Get clock resources */1381	hqvdp->clk = devm_clk_get(dev, "hqvdp");1382	hqvdp->clk_pix_main = devm_clk_get(dev, "pix_main");1383	if (IS_ERR(hqvdp->clk) || IS_ERR(hqvdp->clk_pix_main)) {1384		DRM_ERROR("Cannot get clocks\n");1385		return -ENXIO;1386	}1387 1388	/* Get reset resources */1389	hqvdp->reset = devm_reset_control_get(dev, "hqvdp");1390	if (!IS_ERR(hqvdp->reset))1391		reset_control_deassert(hqvdp->reset);1392 1393	vtg_np = of_parse_phandle(pdev->dev.of_node, "st,vtg", 0);1394	if (vtg_np)1395		hqvdp->vtg = of_vtg_find(vtg_np);1396	of_node_put(vtg_np);1397 1398	platform_set_drvdata(pdev, hqvdp);1399 1400	return component_add(&pdev->dev, &sti_hqvdp_ops);1401}1402 1403static void sti_hqvdp_remove(struct platform_device *pdev)1404{1405	component_del(&pdev->dev, &sti_hqvdp_ops);1406}1407 1408static const struct of_device_id hqvdp_of_match[] = {1409	{ .compatible = "st,stih407-hqvdp", },1410	{ /* end node */ }1411};1412MODULE_DEVICE_TABLE(of, hqvdp_of_match);1413 1414struct platform_driver sti_hqvdp_driver = {1415	.driver = {1416		.name = "sti-hqvdp",1417		.of_match_table = hqvdp_of_match,1418	},1419	.probe = sti_hqvdp_probe,1420	.remove_new = sti_hqvdp_remove,1421};1422 1423MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");1424MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver");1425MODULE_LICENSE("GPL");1426