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