162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) STMicroelectronics SA 2014 462306a36Sopenharmony_ci * Authors: Benjamin Gaignard <benjamin.gaignard@st.com> 562306a36Sopenharmony_ci * Fabien Dessenne <fabien.dessenne@st.com> 662306a36Sopenharmony_ci * for STMicroelectronics. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1062306a36Sopenharmony_ci#include <linux/of.h> 1162306a36Sopenharmony_ci#include <linux/seq_file.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <drm/drm_atomic.h> 1462306a36Sopenharmony_ci#include <drm/drm_device.h> 1562306a36Sopenharmony_ci#include <drm/drm_fb_dma_helper.h> 1662306a36Sopenharmony_ci#include <drm/drm_fourcc.h> 1762306a36Sopenharmony_ci#include <drm/drm_framebuffer.h> 1862306a36Sopenharmony_ci#include <drm/drm_gem_dma_helper.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include "sti_compositor.h" 2162306a36Sopenharmony_ci#include "sti_gdp.h" 2262306a36Sopenharmony_ci#include "sti_plane.h" 2362306a36Sopenharmony_ci#include "sti_vtg.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define ALPHASWITCH BIT(6) 2662306a36Sopenharmony_ci#define ENA_COLOR_FILL BIT(8) 2762306a36Sopenharmony_ci#define BIGNOTLITTLE BIT(23) 2862306a36Sopenharmony_ci#define WAIT_NEXT_VSYNC BIT(31) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* GDP color formats */ 3162306a36Sopenharmony_ci#define GDP_RGB565 0x00 3262306a36Sopenharmony_ci#define GDP_RGB888 0x01 3362306a36Sopenharmony_ci#define GDP_RGB888_32 0x02 3462306a36Sopenharmony_ci#define GDP_XBGR8888 (GDP_RGB888_32 | BIGNOTLITTLE | ALPHASWITCH) 3562306a36Sopenharmony_ci#define GDP_ARGB8565 0x04 3662306a36Sopenharmony_ci#define GDP_ARGB8888 0x05 3762306a36Sopenharmony_ci#define GDP_ABGR8888 (GDP_ARGB8888 | BIGNOTLITTLE | ALPHASWITCH) 3862306a36Sopenharmony_ci#define GDP_ARGB1555 0x06 3962306a36Sopenharmony_ci#define GDP_ARGB4444 0x07 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define GDP2STR(fmt) { GDP_ ## fmt, #fmt } 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic struct gdp_format_to_str { 4462306a36Sopenharmony_ci int format; 4562306a36Sopenharmony_ci char name[20]; 4662306a36Sopenharmony_ci} gdp_format_to_str[] = { 4762306a36Sopenharmony_ci GDP2STR(RGB565), 4862306a36Sopenharmony_ci GDP2STR(RGB888), 4962306a36Sopenharmony_ci GDP2STR(RGB888_32), 5062306a36Sopenharmony_ci GDP2STR(XBGR8888), 5162306a36Sopenharmony_ci GDP2STR(ARGB8565), 5262306a36Sopenharmony_ci GDP2STR(ARGB8888), 5362306a36Sopenharmony_ci GDP2STR(ABGR8888), 5462306a36Sopenharmony_ci GDP2STR(ARGB1555), 5562306a36Sopenharmony_ci GDP2STR(ARGB4444) 5662306a36Sopenharmony_ci }; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#define GAM_GDP_CTL_OFFSET 0x00 5962306a36Sopenharmony_ci#define GAM_GDP_AGC_OFFSET 0x04 6062306a36Sopenharmony_ci#define GAM_GDP_VPO_OFFSET 0x0C 6162306a36Sopenharmony_ci#define GAM_GDP_VPS_OFFSET 0x10 6262306a36Sopenharmony_ci#define GAM_GDP_PML_OFFSET 0x14 6362306a36Sopenharmony_ci#define GAM_GDP_PMP_OFFSET 0x18 6462306a36Sopenharmony_ci#define GAM_GDP_SIZE_OFFSET 0x1C 6562306a36Sopenharmony_ci#define GAM_GDP_NVN_OFFSET 0x24 6662306a36Sopenharmony_ci#define GAM_GDP_KEY1_OFFSET 0x28 6762306a36Sopenharmony_ci#define GAM_GDP_KEY2_OFFSET 0x2C 6862306a36Sopenharmony_ci#define GAM_GDP_PPT_OFFSET 0x34 6962306a36Sopenharmony_ci#define GAM_GDP_CML_OFFSET 0x3C 7062306a36Sopenharmony_ci#define GAM_GDP_MST_OFFSET 0x68 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci#define GAM_GDP_ALPHARANGE_255 BIT(5) 7362306a36Sopenharmony_ci#define GAM_GDP_AGC_FULL_RANGE 0x00808080 7462306a36Sopenharmony_ci#define GAM_GDP_PPT_IGNORE (BIT(1) | BIT(0)) 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#define GAM_GDP_SIZE_MAX_WIDTH 3840 7762306a36Sopenharmony_ci#define GAM_GDP_SIZE_MAX_HEIGHT 2160 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#define GDP_NODE_NB_BANK 2 8062306a36Sopenharmony_ci#define GDP_NODE_PER_FIELD 2 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistruct sti_gdp_node { 8362306a36Sopenharmony_ci u32 gam_gdp_ctl; 8462306a36Sopenharmony_ci u32 gam_gdp_agc; 8562306a36Sopenharmony_ci u32 reserved1; 8662306a36Sopenharmony_ci u32 gam_gdp_vpo; 8762306a36Sopenharmony_ci u32 gam_gdp_vps; 8862306a36Sopenharmony_ci u32 gam_gdp_pml; 8962306a36Sopenharmony_ci u32 gam_gdp_pmp; 9062306a36Sopenharmony_ci u32 gam_gdp_size; 9162306a36Sopenharmony_ci u32 reserved2; 9262306a36Sopenharmony_ci u32 gam_gdp_nvn; 9362306a36Sopenharmony_ci u32 gam_gdp_key1; 9462306a36Sopenharmony_ci u32 gam_gdp_key2; 9562306a36Sopenharmony_ci u32 reserved3; 9662306a36Sopenharmony_ci u32 gam_gdp_ppt; 9762306a36Sopenharmony_ci u32 reserved4; 9862306a36Sopenharmony_ci u32 gam_gdp_cml; 9962306a36Sopenharmony_ci}; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistruct sti_gdp_node_list { 10262306a36Sopenharmony_ci struct sti_gdp_node *top_field; 10362306a36Sopenharmony_ci dma_addr_t top_field_paddr; 10462306a36Sopenharmony_ci struct sti_gdp_node *btm_field; 10562306a36Sopenharmony_ci dma_addr_t btm_field_paddr; 10662306a36Sopenharmony_ci}; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/* 10962306a36Sopenharmony_ci * STI GDP structure 11062306a36Sopenharmony_ci * 11162306a36Sopenharmony_ci * @sti_plane: sti_plane structure 11262306a36Sopenharmony_ci * @dev: driver device 11362306a36Sopenharmony_ci * @regs: gdp registers 11462306a36Sopenharmony_ci * @clk_pix: pixel clock for the current gdp 11562306a36Sopenharmony_ci * @clk_main_parent: gdp parent clock if main path used 11662306a36Sopenharmony_ci * @clk_aux_parent: gdp parent clock if aux path used 11762306a36Sopenharmony_ci * @vtg_field_nb: callback for VTG FIELD (top or bottom) notification 11862306a36Sopenharmony_ci * @is_curr_top: true if the current node processed is the top field 11962306a36Sopenharmony_ci * @node_list: array of node list 12062306a36Sopenharmony_ci * @vtg: registered vtg 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_cistruct sti_gdp { 12362306a36Sopenharmony_ci struct sti_plane plane; 12462306a36Sopenharmony_ci struct device *dev; 12562306a36Sopenharmony_ci void __iomem *regs; 12662306a36Sopenharmony_ci struct clk *clk_pix; 12762306a36Sopenharmony_ci struct clk *clk_main_parent; 12862306a36Sopenharmony_ci struct clk *clk_aux_parent; 12962306a36Sopenharmony_ci struct notifier_block vtg_field_nb; 13062306a36Sopenharmony_ci bool is_curr_top; 13162306a36Sopenharmony_ci struct sti_gdp_node_list node_list[GDP_NODE_NB_BANK]; 13262306a36Sopenharmony_ci struct sti_vtg *vtg; 13362306a36Sopenharmony_ci}; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci#define to_sti_gdp(x) container_of(x, struct sti_gdp, plane) 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic const uint32_t gdp_supported_formats[] = { 13862306a36Sopenharmony_ci DRM_FORMAT_XRGB8888, 13962306a36Sopenharmony_ci DRM_FORMAT_XBGR8888, 14062306a36Sopenharmony_ci DRM_FORMAT_ARGB8888, 14162306a36Sopenharmony_ci DRM_FORMAT_ABGR8888, 14262306a36Sopenharmony_ci DRM_FORMAT_ARGB4444, 14362306a36Sopenharmony_ci DRM_FORMAT_ARGB1555, 14462306a36Sopenharmony_ci DRM_FORMAT_RGB565, 14562306a36Sopenharmony_ci DRM_FORMAT_RGB888, 14662306a36Sopenharmony_ci}; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci#define DBGFS_DUMP(reg) seq_printf(s, "\n %-25s 0x%08X", #reg, \ 14962306a36Sopenharmony_ci readl(gdp->regs + reg ## _OFFSET)) 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic void gdp_dbg_ctl(struct seq_file *s, int val) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci int i; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci seq_puts(s, "\tColor:"); 15662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(gdp_format_to_str); i++) { 15762306a36Sopenharmony_ci if (gdp_format_to_str[i].format == (val & 0x1F)) { 15862306a36Sopenharmony_ci seq_puts(s, gdp_format_to_str[i].name); 15962306a36Sopenharmony_ci break; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci if (i == ARRAY_SIZE(gdp_format_to_str)) 16362306a36Sopenharmony_ci seq_puts(s, "<UNKNOWN>"); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci seq_printf(s, "\tWaitNextVsync:%d", val & WAIT_NEXT_VSYNC ? 1 : 0); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic void gdp_dbg_vpo(struct seq_file *s, int val) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci seq_printf(s, "\txdo:%4d\tydo:%4d", val & 0xFFFF, (val >> 16) & 0xFFFF); 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic void gdp_dbg_vps(struct seq_file *s, int val) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci seq_printf(s, "\txds:%4d\tyds:%4d", val & 0xFFFF, (val >> 16) & 0xFFFF); 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic void gdp_dbg_size(struct seq_file *s, int val) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci seq_printf(s, "\t%d x %d", val & 0xFFFF, (val >> 16) & 0xFFFF); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic void gdp_dbg_nvn(struct seq_file *s, struct sti_gdp *gdp, int val) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci void *base = NULL; 18662306a36Sopenharmony_ci unsigned int i; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci for (i = 0; i < GDP_NODE_NB_BANK; i++) { 18962306a36Sopenharmony_ci if (gdp->node_list[i].top_field_paddr == val) { 19062306a36Sopenharmony_ci base = gdp->node_list[i].top_field; 19162306a36Sopenharmony_ci break; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci if (gdp->node_list[i].btm_field_paddr == val) { 19462306a36Sopenharmony_ci base = gdp->node_list[i].btm_field; 19562306a36Sopenharmony_ci break; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (base) 20062306a36Sopenharmony_ci seq_printf(s, "\tVirt @: %p", base); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic void gdp_dbg_ppt(struct seq_file *s, int val) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci if (val & GAM_GDP_PPT_IGNORE) 20662306a36Sopenharmony_ci seq_puts(s, "\tNot displayed on mixer!"); 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic void gdp_dbg_mst(struct seq_file *s, int val) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci if (val & 1) 21262306a36Sopenharmony_ci seq_puts(s, "\tBUFFER UNDERFLOW!"); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic int gdp_dbg_show(struct seq_file *s, void *data) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct drm_info_node *node = s->private; 21862306a36Sopenharmony_ci struct sti_gdp *gdp = (struct sti_gdp *)node->info_ent->data; 21962306a36Sopenharmony_ci struct drm_plane *drm_plane = &gdp->plane.drm_plane; 22062306a36Sopenharmony_ci struct drm_crtc *crtc; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci drm_modeset_lock(&drm_plane->mutex, NULL); 22362306a36Sopenharmony_ci crtc = drm_plane->state->crtc; 22462306a36Sopenharmony_ci drm_modeset_unlock(&drm_plane->mutex); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci seq_printf(s, "%s: (vaddr = 0x%p)", 22762306a36Sopenharmony_ci sti_plane_to_str(&gdp->plane), gdp->regs); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci DBGFS_DUMP(GAM_GDP_CTL); 23062306a36Sopenharmony_ci gdp_dbg_ctl(s, readl(gdp->regs + GAM_GDP_CTL_OFFSET)); 23162306a36Sopenharmony_ci DBGFS_DUMP(GAM_GDP_AGC); 23262306a36Sopenharmony_ci DBGFS_DUMP(GAM_GDP_VPO); 23362306a36Sopenharmony_ci gdp_dbg_vpo(s, readl(gdp->regs + GAM_GDP_VPO_OFFSET)); 23462306a36Sopenharmony_ci DBGFS_DUMP(GAM_GDP_VPS); 23562306a36Sopenharmony_ci gdp_dbg_vps(s, readl(gdp->regs + GAM_GDP_VPS_OFFSET)); 23662306a36Sopenharmony_ci DBGFS_DUMP(GAM_GDP_PML); 23762306a36Sopenharmony_ci DBGFS_DUMP(GAM_GDP_PMP); 23862306a36Sopenharmony_ci DBGFS_DUMP(GAM_GDP_SIZE); 23962306a36Sopenharmony_ci gdp_dbg_size(s, readl(gdp->regs + GAM_GDP_SIZE_OFFSET)); 24062306a36Sopenharmony_ci DBGFS_DUMP(GAM_GDP_NVN); 24162306a36Sopenharmony_ci gdp_dbg_nvn(s, gdp, readl(gdp->regs + GAM_GDP_NVN_OFFSET)); 24262306a36Sopenharmony_ci DBGFS_DUMP(GAM_GDP_KEY1); 24362306a36Sopenharmony_ci DBGFS_DUMP(GAM_GDP_KEY2); 24462306a36Sopenharmony_ci DBGFS_DUMP(GAM_GDP_PPT); 24562306a36Sopenharmony_ci gdp_dbg_ppt(s, readl(gdp->regs + GAM_GDP_PPT_OFFSET)); 24662306a36Sopenharmony_ci DBGFS_DUMP(GAM_GDP_CML); 24762306a36Sopenharmony_ci DBGFS_DUMP(GAM_GDP_MST); 24862306a36Sopenharmony_ci gdp_dbg_mst(s, readl(gdp->regs + GAM_GDP_MST_OFFSET)); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci seq_puts(s, "\n\n"); 25162306a36Sopenharmony_ci if (!crtc) 25262306a36Sopenharmony_ci seq_puts(s, " Not connected to any DRM CRTC\n"); 25362306a36Sopenharmony_ci else 25462306a36Sopenharmony_ci seq_printf(s, " Connected to DRM CRTC #%d (%s)\n", 25562306a36Sopenharmony_ci crtc->base.id, sti_mixer_to_str(to_sti_mixer(crtc))); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return 0; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic void gdp_node_dump_node(struct seq_file *s, struct sti_gdp_node *node) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci seq_printf(s, "\t@:0x%p", node); 26362306a36Sopenharmony_ci seq_printf(s, "\n\tCTL 0x%08X", node->gam_gdp_ctl); 26462306a36Sopenharmony_ci gdp_dbg_ctl(s, node->gam_gdp_ctl); 26562306a36Sopenharmony_ci seq_printf(s, "\n\tAGC 0x%08X", node->gam_gdp_agc); 26662306a36Sopenharmony_ci seq_printf(s, "\n\tVPO 0x%08X", node->gam_gdp_vpo); 26762306a36Sopenharmony_ci gdp_dbg_vpo(s, node->gam_gdp_vpo); 26862306a36Sopenharmony_ci seq_printf(s, "\n\tVPS 0x%08X", node->gam_gdp_vps); 26962306a36Sopenharmony_ci gdp_dbg_vps(s, node->gam_gdp_vps); 27062306a36Sopenharmony_ci seq_printf(s, "\n\tPML 0x%08X", node->gam_gdp_pml); 27162306a36Sopenharmony_ci seq_printf(s, "\n\tPMP 0x%08X", node->gam_gdp_pmp); 27262306a36Sopenharmony_ci seq_printf(s, "\n\tSIZE 0x%08X", node->gam_gdp_size); 27362306a36Sopenharmony_ci gdp_dbg_size(s, node->gam_gdp_size); 27462306a36Sopenharmony_ci seq_printf(s, "\n\tNVN 0x%08X", node->gam_gdp_nvn); 27562306a36Sopenharmony_ci seq_printf(s, "\n\tKEY1 0x%08X", node->gam_gdp_key1); 27662306a36Sopenharmony_ci seq_printf(s, "\n\tKEY2 0x%08X", node->gam_gdp_key2); 27762306a36Sopenharmony_ci seq_printf(s, "\n\tPPT 0x%08X", node->gam_gdp_ppt); 27862306a36Sopenharmony_ci gdp_dbg_ppt(s, node->gam_gdp_ppt); 27962306a36Sopenharmony_ci seq_printf(s, "\n\tCML 0x%08X\n", node->gam_gdp_cml); 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic int gdp_node_dbg_show(struct seq_file *s, void *arg) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci struct drm_info_node *node = s->private; 28562306a36Sopenharmony_ci struct sti_gdp *gdp = (struct sti_gdp *)node->info_ent->data; 28662306a36Sopenharmony_ci unsigned int b; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci for (b = 0; b < GDP_NODE_NB_BANK; b++) { 28962306a36Sopenharmony_ci seq_printf(s, "\n%s[%d].top", sti_plane_to_str(&gdp->plane), b); 29062306a36Sopenharmony_ci gdp_node_dump_node(s, gdp->node_list[b].top_field); 29162306a36Sopenharmony_ci seq_printf(s, "\n%s[%d].btm", sti_plane_to_str(&gdp->plane), b); 29262306a36Sopenharmony_ci gdp_node_dump_node(s, gdp->node_list[b].btm_field); 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci return 0; 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic struct drm_info_list gdp0_debugfs_files[] = { 29962306a36Sopenharmony_ci { "gdp0", gdp_dbg_show, 0, NULL }, 30062306a36Sopenharmony_ci { "gdp0_node", gdp_node_dbg_show, 0, NULL }, 30162306a36Sopenharmony_ci}; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic struct drm_info_list gdp1_debugfs_files[] = { 30462306a36Sopenharmony_ci { "gdp1", gdp_dbg_show, 0, NULL }, 30562306a36Sopenharmony_ci { "gdp1_node", gdp_node_dbg_show, 0, NULL }, 30662306a36Sopenharmony_ci}; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic struct drm_info_list gdp2_debugfs_files[] = { 30962306a36Sopenharmony_ci { "gdp2", gdp_dbg_show, 0, NULL }, 31062306a36Sopenharmony_ci { "gdp2_node", gdp_node_dbg_show, 0, NULL }, 31162306a36Sopenharmony_ci}; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic struct drm_info_list gdp3_debugfs_files[] = { 31462306a36Sopenharmony_ci { "gdp3", gdp_dbg_show, 0, NULL }, 31562306a36Sopenharmony_ci { "gdp3_node", gdp_node_dbg_show, 0, NULL }, 31662306a36Sopenharmony_ci}; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_cistatic int gdp_debugfs_init(struct sti_gdp *gdp, struct drm_minor *minor) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci unsigned int i; 32162306a36Sopenharmony_ci struct drm_info_list *gdp_debugfs_files; 32262306a36Sopenharmony_ci int nb_files; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci switch (gdp->plane.desc) { 32562306a36Sopenharmony_ci case STI_GDP_0: 32662306a36Sopenharmony_ci gdp_debugfs_files = gdp0_debugfs_files; 32762306a36Sopenharmony_ci nb_files = ARRAY_SIZE(gdp0_debugfs_files); 32862306a36Sopenharmony_ci break; 32962306a36Sopenharmony_ci case STI_GDP_1: 33062306a36Sopenharmony_ci gdp_debugfs_files = gdp1_debugfs_files; 33162306a36Sopenharmony_ci nb_files = ARRAY_SIZE(gdp1_debugfs_files); 33262306a36Sopenharmony_ci break; 33362306a36Sopenharmony_ci case STI_GDP_2: 33462306a36Sopenharmony_ci gdp_debugfs_files = gdp2_debugfs_files; 33562306a36Sopenharmony_ci nb_files = ARRAY_SIZE(gdp2_debugfs_files); 33662306a36Sopenharmony_ci break; 33762306a36Sopenharmony_ci case STI_GDP_3: 33862306a36Sopenharmony_ci gdp_debugfs_files = gdp3_debugfs_files; 33962306a36Sopenharmony_ci nb_files = ARRAY_SIZE(gdp3_debugfs_files); 34062306a36Sopenharmony_ci break; 34162306a36Sopenharmony_ci default: 34262306a36Sopenharmony_ci return -EINVAL; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci for (i = 0; i < nb_files; i++) 34662306a36Sopenharmony_ci gdp_debugfs_files[i].data = gdp; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci drm_debugfs_create_files(gdp_debugfs_files, 34962306a36Sopenharmony_ci nb_files, 35062306a36Sopenharmony_ci minor->debugfs_root, minor); 35162306a36Sopenharmony_ci return 0; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic int sti_gdp_fourcc2format(int fourcc) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci switch (fourcc) { 35762306a36Sopenharmony_ci case DRM_FORMAT_XRGB8888: 35862306a36Sopenharmony_ci return GDP_RGB888_32; 35962306a36Sopenharmony_ci case DRM_FORMAT_XBGR8888: 36062306a36Sopenharmony_ci return GDP_XBGR8888; 36162306a36Sopenharmony_ci case DRM_FORMAT_ARGB8888: 36262306a36Sopenharmony_ci return GDP_ARGB8888; 36362306a36Sopenharmony_ci case DRM_FORMAT_ABGR8888: 36462306a36Sopenharmony_ci return GDP_ABGR8888; 36562306a36Sopenharmony_ci case DRM_FORMAT_ARGB4444: 36662306a36Sopenharmony_ci return GDP_ARGB4444; 36762306a36Sopenharmony_ci case DRM_FORMAT_ARGB1555: 36862306a36Sopenharmony_ci return GDP_ARGB1555; 36962306a36Sopenharmony_ci case DRM_FORMAT_RGB565: 37062306a36Sopenharmony_ci return GDP_RGB565; 37162306a36Sopenharmony_ci case DRM_FORMAT_RGB888: 37262306a36Sopenharmony_ci return GDP_RGB888; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci return -1; 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic int sti_gdp_get_alpharange(int format) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci switch (format) { 38062306a36Sopenharmony_ci case GDP_ARGB8565: 38162306a36Sopenharmony_ci case GDP_ARGB8888: 38262306a36Sopenharmony_ci case GDP_ABGR8888: 38362306a36Sopenharmony_ci return GAM_GDP_ALPHARANGE_255; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci return 0; 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci/** 38962306a36Sopenharmony_ci * sti_gdp_get_free_nodes 39062306a36Sopenharmony_ci * @gdp: gdp pointer 39162306a36Sopenharmony_ci * 39262306a36Sopenharmony_ci * Look for a GDP node list that is not currently read by the HW. 39362306a36Sopenharmony_ci * 39462306a36Sopenharmony_ci * RETURNS: 39562306a36Sopenharmony_ci * Pointer to the free GDP node list 39662306a36Sopenharmony_ci */ 39762306a36Sopenharmony_cistatic struct sti_gdp_node_list *sti_gdp_get_free_nodes(struct sti_gdp *gdp) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci int hw_nvn; 40062306a36Sopenharmony_ci unsigned int i; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci hw_nvn = readl(gdp->regs + GAM_GDP_NVN_OFFSET); 40362306a36Sopenharmony_ci if (!hw_nvn) 40462306a36Sopenharmony_ci goto end; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci for (i = 0; i < GDP_NODE_NB_BANK; i++) 40762306a36Sopenharmony_ci if ((hw_nvn != gdp->node_list[i].btm_field_paddr) && 40862306a36Sopenharmony_ci (hw_nvn != gdp->node_list[i].top_field_paddr)) 40962306a36Sopenharmony_ci return &gdp->node_list[i]; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci /* in hazardous cases restart with the first node */ 41262306a36Sopenharmony_ci DRM_ERROR("inconsistent NVN for %s: 0x%08X\n", 41362306a36Sopenharmony_ci sti_plane_to_str(&gdp->plane), hw_nvn); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ciend: 41662306a36Sopenharmony_ci return &gdp->node_list[0]; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci/** 42062306a36Sopenharmony_ci * sti_gdp_get_current_nodes 42162306a36Sopenharmony_ci * @gdp: gdp pointer 42262306a36Sopenharmony_ci * 42362306a36Sopenharmony_ci * Look for GDP nodes that are currently read by the HW. 42462306a36Sopenharmony_ci * 42562306a36Sopenharmony_ci * RETURNS: 42662306a36Sopenharmony_ci * Pointer to the current GDP node list 42762306a36Sopenharmony_ci */ 42862306a36Sopenharmony_cistatic 42962306a36Sopenharmony_cistruct sti_gdp_node_list *sti_gdp_get_current_nodes(struct sti_gdp *gdp) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci int hw_nvn; 43262306a36Sopenharmony_ci unsigned int i; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci hw_nvn = readl(gdp->regs + GAM_GDP_NVN_OFFSET); 43562306a36Sopenharmony_ci if (!hw_nvn) 43662306a36Sopenharmony_ci goto end; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci for (i = 0; i < GDP_NODE_NB_BANK; i++) 43962306a36Sopenharmony_ci if ((hw_nvn == gdp->node_list[i].btm_field_paddr) || 44062306a36Sopenharmony_ci (hw_nvn == gdp->node_list[i].top_field_paddr)) 44162306a36Sopenharmony_ci return &gdp->node_list[i]; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ciend: 44462306a36Sopenharmony_ci DRM_DEBUG_DRIVER("Warning, NVN 0x%08X for %s does not match any node\n", 44562306a36Sopenharmony_ci hw_nvn, sti_plane_to_str(&gdp->plane)); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci return NULL; 44862306a36Sopenharmony_ci} 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci/** 45162306a36Sopenharmony_ci * sti_gdp_disable 45262306a36Sopenharmony_ci * @gdp: gdp pointer 45362306a36Sopenharmony_ci * 45462306a36Sopenharmony_ci * Disable a GDP. 45562306a36Sopenharmony_ci */ 45662306a36Sopenharmony_cistatic void sti_gdp_disable(struct sti_gdp *gdp) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci unsigned int i; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci DRM_DEBUG_DRIVER("%s\n", sti_plane_to_str(&gdp->plane)); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* Set the nodes as 'to be ignored on mixer' */ 46362306a36Sopenharmony_ci for (i = 0; i < GDP_NODE_NB_BANK; i++) { 46462306a36Sopenharmony_ci gdp->node_list[i].top_field->gam_gdp_ppt |= GAM_GDP_PPT_IGNORE; 46562306a36Sopenharmony_ci gdp->node_list[i].btm_field->gam_gdp_ppt |= GAM_GDP_PPT_IGNORE; 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (sti_vtg_unregister_client(gdp->vtg, &gdp->vtg_field_nb)) 46962306a36Sopenharmony_ci DRM_DEBUG_DRIVER("Warning: cannot unregister VTG notifier\n"); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (gdp->clk_pix) 47262306a36Sopenharmony_ci clk_disable_unprepare(gdp->clk_pix); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci gdp->plane.status = STI_PLANE_DISABLED; 47562306a36Sopenharmony_ci gdp->vtg = NULL; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci/** 47962306a36Sopenharmony_ci * sti_gdp_field_cb 48062306a36Sopenharmony_ci * @nb: notifier block 48162306a36Sopenharmony_ci * @event: event message 48262306a36Sopenharmony_ci * @data: private data 48362306a36Sopenharmony_ci * 48462306a36Sopenharmony_ci * Handle VTG top field and bottom field event. 48562306a36Sopenharmony_ci * 48662306a36Sopenharmony_ci * RETURNS: 48762306a36Sopenharmony_ci * 0 on success. 48862306a36Sopenharmony_ci */ 48962306a36Sopenharmony_cistatic int sti_gdp_field_cb(struct notifier_block *nb, 49062306a36Sopenharmony_ci unsigned long event, void *data) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci struct sti_gdp *gdp = container_of(nb, struct sti_gdp, vtg_field_nb); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci if (gdp->plane.status == STI_PLANE_FLUSHING) { 49562306a36Sopenharmony_ci /* disable need to be synchronize on vsync event */ 49662306a36Sopenharmony_ci DRM_DEBUG_DRIVER("Vsync event received => disable %s\n", 49762306a36Sopenharmony_ci sti_plane_to_str(&gdp->plane)); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci sti_gdp_disable(gdp); 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci switch (event) { 50362306a36Sopenharmony_ci case VTG_TOP_FIELD_EVENT: 50462306a36Sopenharmony_ci gdp->is_curr_top = true; 50562306a36Sopenharmony_ci break; 50662306a36Sopenharmony_ci case VTG_BOTTOM_FIELD_EVENT: 50762306a36Sopenharmony_ci gdp->is_curr_top = false; 50862306a36Sopenharmony_ci break; 50962306a36Sopenharmony_ci default: 51062306a36Sopenharmony_ci DRM_ERROR("unsupported event: %lu\n", event); 51162306a36Sopenharmony_ci break; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci return 0; 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic void sti_gdp_init(struct sti_gdp *gdp) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci struct device_node *np = gdp->dev->of_node; 52062306a36Sopenharmony_ci dma_addr_t dma_addr; 52162306a36Sopenharmony_ci void *base; 52262306a36Sopenharmony_ci unsigned int i, size; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci /* Allocate all the nodes within a single memory page */ 52562306a36Sopenharmony_ci size = sizeof(struct sti_gdp_node) * 52662306a36Sopenharmony_ci GDP_NODE_PER_FIELD * GDP_NODE_NB_BANK; 52762306a36Sopenharmony_ci base = dma_alloc_wc(gdp->dev, size, &dma_addr, GFP_KERNEL); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci if (!base) { 53062306a36Sopenharmony_ci DRM_ERROR("Failed to allocate memory for GDP node\n"); 53162306a36Sopenharmony_ci return; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci memset(base, 0, size); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci for (i = 0; i < GDP_NODE_NB_BANK; i++) { 53662306a36Sopenharmony_ci if (dma_addr & 0xF) { 53762306a36Sopenharmony_ci DRM_ERROR("Mem alignment failed\n"); 53862306a36Sopenharmony_ci return; 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci gdp->node_list[i].top_field = base; 54162306a36Sopenharmony_ci gdp->node_list[i].top_field_paddr = dma_addr; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci DRM_DEBUG_DRIVER("node[%d].top_field=%p\n", i, base); 54462306a36Sopenharmony_ci base += sizeof(struct sti_gdp_node); 54562306a36Sopenharmony_ci dma_addr += sizeof(struct sti_gdp_node); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (dma_addr & 0xF) { 54862306a36Sopenharmony_ci DRM_ERROR("Mem alignment failed\n"); 54962306a36Sopenharmony_ci return; 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci gdp->node_list[i].btm_field = base; 55262306a36Sopenharmony_ci gdp->node_list[i].btm_field_paddr = dma_addr; 55362306a36Sopenharmony_ci DRM_DEBUG_DRIVER("node[%d].btm_field=%p\n", i, base); 55462306a36Sopenharmony_ci base += sizeof(struct sti_gdp_node); 55562306a36Sopenharmony_ci dma_addr += sizeof(struct sti_gdp_node); 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (of_device_is_compatible(np, "st,stih407-compositor")) { 55962306a36Sopenharmony_ci /* GDP of STiH407 chip have its own pixel clock */ 56062306a36Sopenharmony_ci char *clk_name; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci switch (gdp->plane.desc) { 56362306a36Sopenharmony_ci case STI_GDP_0: 56462306a36Sopenharmony_ci clk_name = "pix_gdp1"; 56562306a36Sopenharmony_ci break; 56662306a36Sopenharmony_ci case STI_GDP_1: 56762306a36Sopenharmony_ci clk_name = "pix_gdp2"; 56862306a36Sopenharmony_ci break; 56962306a36Sopenharmony_ci case STI_GDP_2: 57062306a36Sopenharmony_ci clk_name = "pix_gdp3"; 57162306a36Sopenharmony_ci break; 57262306a36Sopenharmony_ci case STI_GDP_3: 57362306a36Sopenharmony_ci clk_name = "pix_gdp4"; 57462306a36Sopenharmony_ci break; 57562306a36Sopenharmony_ci default: 57662306a36Sopenharmony_ci DRM_ERROR("GDP id not recognized\n"); 57762306a36Sopenharmony_ci return; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci gdp->clk_pix = devm_clk_get(gdp->dev, clk_name); 58162306a36Sopenharmony_ci if (IS_ERR(gdp->clk_pix)) 58262306a36Sopenharmony_ci DRM_ERROR("Cannot get %s clock\n", clk_name); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci gdp->clk_main_parent = devm_clk_get(gdp->dev, "main_parent"); 58562306a36Sopenharmony_ci if (IS_ERR(gdp->clk_main_parent)) 58662306a36Sopenharmony_ci DRM_ERROR("Cannot get main_parent clock\n"); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci gdp->clk_aux_parent = devm_clk_get(gdp->dev, "aux_parent"); 58962306a36Sopenharmony_ci if (IS_ERR(gdp->clk_aux_parent)) 59062306a36Sopenharmony_ci DRM_ERROR("Cannot get aux_parent clock\n"); 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci/** 59562306a36Sopenharmony_ci * sti_gdp_get_dst 59662306a36Sopenharmony_ci * @dev: device 59762306a36Sopenharmony_ci * @dst: requested destination size 59862306a36Sopenharmony_ci * @src: source size 59962306a36Sopenharmony_ci * 60062306a36Sopenharmony_ci * Return the cropped / clamped destination size 60162306a36Sopenharmony_ci * 60262306a36Sopenharmony_ci * RETURNS: 60362306a36Sopenharmony_ci * cropped / clamped destination size 60462306a36Sopenharmony_ci */ 60562306a36Sopenharmony_cistatic int sti_gdp_get_dst(struct device *dev, int dst, int src) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci if (dst == src) 60862306a36Sopenharmony_ci return dst; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci if (dst < src) { 61162306a36Sopenharmony_ci dev_dbg(dev, "WARNING: GDP scale not supported, will crop\n"); 61262306a36Sopenharmony_ci return dst; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci dev_dbg(dev, "WARNING: GDP scale not supported, will clamp\n"); 61662306a36Sopenharmony_ci return src; 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic int sti_gdp_atomic_check(struct drm_plane *drm_plane, 62062306a36Sopenharmony_ci struct drm_atomic_state *state) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, 62362306a36Sopenharmony_ci drm_plane); 62462306a36Sopenharmony_ci struct sti_plane *plane = to_sti_plane(drm_plane); 62562306a36Sopenharmony_ci struct sti_gdp *gdp = to_sti_gdp(plane); 62662306a36Sopenharmony_ci struct drm_crtc *crtc = new_plane_state->crtc; 62762306a36Sopenharmony_ci struct drm_framebuffer *fb = new_plane_state->fb; 62862306a36Sopenharmony_ci struct drm_crtc_state *crtc_state; 62962306a36Sopenharmony_ci struct sti_mixer *mixer; 63062306a36Sopenharmony_ci struct drm_display_mode *mode; 63162306a36Sopenharmony_ci int dst_x, dst_y, dst_w, dst_h; 63262306a36Sopenharmony_ci int src_x, src_y, src_w, src_h; 63362306a36Sopenharmony_ci int format; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci /* no need for further checks if the plane is being disabled */ 63662306a36Sopenharmony_ci if (!crtc || !fb) 63762306a36Sopenharmony_ci return 0; 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci mixer = to_sti_mixer(crtc); 64062306a36Sopenharmony_ci crtc_state = drm_atomic_get_crtc_state(state, crtc); 64162306a36Sopenharmony_ci mode = &crtc_state->mode; 64262306a36Sopenharmony_ci dst_x = new_plane_state->crtc_x; 64362306a36Sopenharmony_ci dst_y = new_plane_state->crtc_y; 64462306a36Sopenharmony_ci dst_w = clamp_val(new_plane_state->crtc_w, 0, mode->hdisplay - dst_x); 64562306a36Sopenharmony_ci dst_h = clamp_val(new_plane_state->crtc_h, 0, mode->vdisplay - dst_y); 64662306a36Sopenharmony_ci /* src_x are in 16.16 format */ 64762306a36Sopenharmony_ci src_x = new_plane_state->src_x >> 16; 64862306a36Sopenharmony_ci src_y = new_plane_state->src_y >> 16; 64962306a36Sopenharmony_ci src_w = clamp_val(new_plane_state->src_w >> 16, 0, 65062306a36Sopenharmony_ci GAM_GDP_SIZE_MAX_WIDTH); 65162306a36Sopenharmony_ci src_h = clamp_val(new_plane_state->src_h >> 16, 0, 65262306a36Sopenharmony_ci GAM_GDP_SIZE_MAX_HEIGHT); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci format = sti_gdp_fourcc2format(fb->format->format); 65562306a36Sopenharmony_ci if (format == -1) { 65662306a36Sopenharmony_ci DRM_ERROR("Format not supported by GDP %.4s\n", 65762306a36Sopenharmony_ci (char *)&fb->format->format); 65862306a36Sopenharmony_ci return -EINVAL; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci if (!drm_fb_dma_get_gem_obj(fb, 0)) { 66262306a36Sopenharmony_ci DRM_ERROR("Can't get DMA GEM object for fb\n"); 66362306a36Sopenharmony_ci return -EINVAL; 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci /* Set gdp clock */ 66762306a36Sopenharmony_ci if (mode->clock && gdp->clk_pix) { 66862306a36Sopenharmony_ci struct clk *clkp; 66962306a36Sopenharmony_ci int rate = mode->clock * 1000; 67062306a36Sopenharmony_ci int res; 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci /* 67362306a36Sopenharmony_ci * According to the mixer used, the gdp pixel clock 67462306a36Sopenharmony_ci * should have a different parent clock. 67562306a36Sopenharmony_ci */ 67662306a36Sopenharmony_ci if (mixer->id == STI_MIXER_MAIN) 67762306a36Sopenharmony_ci clkp = gdp->clk_main_parent; 67862306a36Sopenharmony_ci else 67962306a36Sopenharmony_ci clkp = gdp->clk_aux_parent; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (clkp) 68262306a36Sopenharmony_ci clk_set_parent(gdp->clk_pix, clkp); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci res = clk_set_rate(gdp->clk_pix, rate); 68562306a36Sopenharmony_ci if (res < 0) { 68662306a36Sopenharmony_ci DRM_ERROR("Cannot set rate (%dHz) for gdp\n", 68762306a36Sopenharmony_ci rate); 68862306a36Sopenharmony_ci return -EINVAL; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci DRM_DEBUG_KMS("CRTC:%d (%s) drm plane:%d (%s)\n", 69362306a36Sopenharmony_ci crtc->base.id, sti_mixer_to_str(mixer), 69462306a36Sopenharmony_ci drm_plane->base.id, sti_plane_to_str(plane)); 69562306a36Sopenharmony_ci DRM_DEBUG_KMS("%s dst=(%dx%d)@(%d,%d) - src=(%dx%d)@(%d,%d)\n", 69662306a36Sopenharmony_ci sti_plane_to_str(plane), 69762306a36Sopenharmony_ci dst_w, dst_h, dst_x, dst_y, 69862306a36Sopenharmony_ci src_w, src_h, src_x, src_y); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci return 0; 70162306a36Sopenharmony_ci} 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_cistatic void sti_gdp_atomic_update(struct drm_plane *drm_plane, 70462306a36Sopenharmony_ci struct drm_atomic_state *state) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci struct drm_plane_state *oldstate = drm_atomic_get_old_plane_state(state, 70762306a36Sopenharmony_ci drm_plane); 70862306a36Sopenharmony_ci struct drm_plane_state *newstate = drm_atomic_get_new_plane_state(state, 70962306a36Sopenharmony_ci drm_plane); 71062306a36Sopenharmony_ci struct sti_plane *plane = to_sti_plane(drm_plane); 71162306a36Sopenharmony_ci struct sti_gdp *gdp = to_sti_gdp(plane); 71262306a36Sopenharmony_ci struct drm_crtc *crtc = newstate->crtc; 71362306a36Sopenharmony_ci struct drm_framebuffer *fb = newstate->fb; 71462306a36Sopenharmony_ci struct drm_display_mode *mode; 71562306a36Sopenharmony_ci int dst_x, dst_y, dst_w, dst_h; 71662306a36Sopenharmony_ci int src_x, src_y, src_w, src_h; 71762306a36Sopenharmony_ci struct drm_gem_dma_object *dma_obj; 71862306a36Sopenharmony_ci struct sti_gdp_node_list *list; 71962306a36Sopenharmony_ci struct sti_gdp_node_list *curr_list; 72062306a36Sopenharmony_ci struct sti_gdp_node *top_field, *btm_field; 72162306a36Sopenharmony_ci u32 dma_updated_top; 72262306a36Sopenharmony_ci u32 dma_updated_btm; 72362306a36Sopenharmony_ci int format; 72462306a36Sopenharmony_ci unsigned int bpp; 72562306a36Sopenharmony_ci u32 ydo, xdo, yds, xds; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci if (!crtc || !fb) 72862306a36Sopenharmony_ci return; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci if ((oldstate->fb == newstate->fb) && 73162306a36Sopenharmony_ci (oldstate->crtc_x == newstate->crtc_x) && 73262306a36Sopenharmony_ci (oldstate->crtc_y == newstate->crtc_y) && 73362306a36Sopenharmony_ci (oldstate->crtc_w == newstate->crtc_w) && 73462306a36Sopenharmony_ci (oldstate->crtc_h == newstate->crtc_h) && 73562306a36Sopenharmony_ci (oldstate->src_x == newstate->src_x) && 73662306a36Sopenharmony_ci (oldstate->src_y == newstate->src_y) && 73762306a36Sopenharmony_ci (oldstate->src_w == newstate->src_w) && 73862306a36Sopenharmony_ci (oldstate->src_h == newstate->src_h)) { 73962306a36Sopenharmony_ci /* No change since last update, do not post cmd */ 74062306a36Sopenharmony_ci DRM_DEBUG_DRIVER("No change, not posting cmd\n"); 74162306a36Sopenharmony_ci plane->status = STI_PLANE_UPDATED; 74262306a36Sopenharmony_ci return; 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci if (!gdp->vtg) { 74662306a36Sopenharmony_ci struct sti_compositor *compo = dev_get_drvdata(gdp->dev); 74762306a36Sopenharmony_ci struct sti_mixer *mixer = to_sti_mixer(crtc); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci /* Register gdp callback */ 75062306a36Sopenharmony_ci gdp->vtg = compo->vtg[mixer->id]; 75162306a36Sopenharmony_ci sti_vtg_register_client(gdp->vtg, &gdp->vtg_field_nb, crtc); 75262306a36Sopenharmony_ci clk_prepare_enable(gdp->clk_pix); 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci mode = &crtc->mode; 75662306a36Sopenharmony_ci dst_x = newstate->crtc_x; 75762306a36Sopenharmony_ci dst_y = newstate->crtc_y; 75862306a36Sopenharmony_ci dst_w = clamp_val(newstate->crtc_w, 0, mode->hdisplay - dst_x); 75962306a36Sopenharmony_ci dst_h = clamp_val(newstate->crtc_h, 0, mode->vdisplay - dst_y); 76062306a36Sopenharmony_ci /* src_x are in 16.16 format */ 76162306a36Sopenharmony_ci src_x = newstate->src_x >> 16; 76262306a36Sopenharmony_ci src_y = newstate->src_y >> 16; 76362306a36Sopenharmony_ci src_w = clamp_val(newstate->src_w >> 16, 0, GAM_GDP_SIZE_MAX_WIDTH); 76462306a36Sopenharmony_ci src_h = clamp_val(newstate->src_h >> 16, 0, GAM_GDP_SIZE_MAX_HEIGHT); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci list = sti_gdp_get_free_nodes(gdp); 76762306a36Sopenharmony_ci top_field = list->top_field; 76862306a36Sopenharmony_ci btm_field = list->btm_field; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci dev_dbg(gdp->dev, "%s %s top_node:0x%p btm_node:0x%p\n", __func__, 77162306a36Sopenharmony_ci sti_plane_to_str(plane), top_field, btm_field); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci /* build the top field */ 77462306a36Sopenharmony_ci top_field->gam_gdp_agc = GAM_GDP_AGC_FULL_RANGE; 77562306a36Sopenharmony_ci top_field->gam_gdp_ctl = WAIT_NEXT_VSYNC; 77662306a36Sopenharmony_ci format = sti_gdp_fourcc2format(fb->format->format); 77762306a36Sopenharmony_ci top_field->gam_gdp_ctl |= format; 77862306a36Sopenharmony_ci top_field->gam_gdp_ctl |= sti_gdp_get_alpharange(format); 77962306a36Sopenharmony_ci top_field->gam_gdp_ppt &= ~GAM_GDP_PPT_IGNORE; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci dma_obj = drm_fb_dma_get_gem_obj(fb, 0); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci DRM_DEBUG_DRIVER("drm FB:%d format:%.4s phys@:0x%lx\n", fb->base.id, 78462306a36Sopenharmony_ci (char *)&fb->format->format, 78562306a36Sopenharmony_ci (unsigned long) dma_obj->dma_addr); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci /* pixel memory location */ 78862306a36Sopenharmony_ci bpp = fb->format->cpp[0]; 78962306a36Sopenharmony_ci top_field->gam_gdp_pml = (u32) dma_obj->dma_addr + fb->offsets[0]; 79062306a36Sopenharmony_ci top_field->gam_gdp_pml += src_x * bpp; 79162306a36Sopenharmony_ci top_field->gam_gdp_pml += src_y * fb->pitches[0]; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci /* output parameters (clamped / cropped) */ 79462306a36Sopenharmony_ci dst_w = sti_gdp_get_dst(gdp->dev, dst_w, src_w); 79562306a36Sopenharmony_ci dst_h = sti_gdp_get_dst(gdp->dev, dst_h, src_h); 79662306a36Sopenharmony_ci ydo = sti_vtg_get_line_number(*mode, dst_y); 79762306a36Sopenharmony_ci yds = sti_vtg_get_line_number(*mode, dst_y + dst_h - 1); 79862306a36Sopenharmony_ci xdo = sti_vtg_get_pixel_number(*mode, dst_x); 79962306a36Sopenharmony_ci xds = sti_vtg_get_pixel_number(*mode, dst_x + dst_w - 1); 80062306a36Sopenharmony_ci top_field->gam_gdp_vpo = (ydo << 16) | xdo; 80162306a36Sopenharmony_ci top_field->gam_gdp_vps = (yds << 16) | xds; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci /* input parameters */ 80462306a36Sopenharmony_ci src_w = dst_w; 80562306a36Sopenharmony_ci top_field->gam_gdp_pmp = fb->pitches[0]; 80662306a36Sopenharmony_ci top_field->gam_gdp_size = src_h << 16 | src_w; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci /* Same content and chained together */ 80962306a36Sopenharmony_ci memcpy(btm_field, top_field, sizeof(*btm_field)); 81062306a36Sopenharmony_ci top_field->gam_gdp_nvn = list->btm_field_paddr; 81162306a36Sopenharmony_ci btm_field->gam_gdp_nvn = list->top_field_paddr; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci /* Interlaced mode */ 81462306a36Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_INTERLACE) 81562306a36Sopenharmony_ci btm_field->gam_gdp_pml = top_field->gam_gdp_pml + 81662306a36Sopenharmony_ci fb->pitches[0]; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci /* Update the NVN field of the 'right' field of the current GDP node 81962306a36Sopenharmony_ci * (being used by the HW) with the address of the updated ('free') top 82062306a36Sopenharmony_ci * field GDP node. 82162306a36Sopenharmony_ci * - In interlaced mode the 'right' field is the bottom field as we 82262306a36Sopenharmony_ci * update frames starting from their top field 82362306a36Sopenharmony_ci * - In progressive mode, we update both bottom and top fields which 82462306a36Sopenharmony_ci * are equal nodes. 82562306a36Sopenharmony_ci * At the next VSYNC, the updated node list will be used by the HW. 82662306a36Sopenharmony_ci */ 82762306a36Sopenharmony_ci curr_list = sti_gdp_get_current_nodes(gdp); 82862306a36Sopenharmony_ci dma_updated_top = list->top_field_paddr; 82962306a36Sopenharmony_ci dma_updated_btm = list->btm_field_paddr; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci dev_dbg(gdp->dev, "Current NVN:0x%X\n", 83262306a36Sopenharmony_ci readl(gdp->regs + GAM_GDP_NVN_OFFSET)); 83362306a36Sopenharmony_ci dev_dbg(gdp->dev, "Posted buff: %lx current buff: %x\n", 83462306a36Sopenharmony_ci (unsigned long) dma_obj->dma_addr, 83562306a36Sopenharmony_ci readl(gdp->regs + GAM_GDP_PML_OFFSET)); 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci if (!curr_list) { 83862306a36Sopenharmony_ci /* First update or invalid node should directly write in the 83962306a36Sopenharmony_ci * hw register */ 84062306a36Sopenharmony_ci DRM_DEBUG_DRIVER("%s first update (or invalid node)\n", 84162306a36Sopenharmony_ci sti_plane_to_str(plane)); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci writel(gdp->is_curr_top ? 84462306a36Sopenharmony_ci dma_updated_btm : dma_updated_top, 84562306a36Sopenharmony_ci gdp->regs + GAM_GDP_NVN_OFFSET); 84662306a36Sopenharmony_ci goto end; 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci if (mode->flags & DRM_MODE_FLAG_INTERLACE) { 85062306a36Sopenharmony_ci if (gdp->is_curr_top) { 85162306a36Sopenharmony_ci /* Do not update in the middle of the frame, but 85262306a36Sopenharmony_ci * postpone the update after the bottom field has 85362306a36Sopenharmony_ci * been displayed */ 85462306a36Sopenharmony_ci curr_list->btm_field->gam_gdp_nvn = dma_updated_top; 85562306a36Sopenharmony_ci } else { 85662306a36Sopenharmony_ci /* Direct update to avoid one frame delay */ 85762306a36Sopenharmony_ci writel(dma_updated_top, 85862306a36Sopenharmony_ci gdp->regs + GAM_GDP_NVN_OFFSET); 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci } else { 86162306a36Sopenharmony_ci /* Direct update for progressive to avoid one frame delay */ 86262306a36Sopenharmony_ci writel(dma_updated_top, gdp->regs + GAM_GDP_NVN_OFFSET); 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ciend: 86662306a36Sopenharmony_ci sti_plane_update_fps(plane, true, false); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci plane->status = STI_PLANE_UPDATED; 86962306a36Sopenharmony_ci} 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_cistatic void sti_gdp_atomic_disable(struct drm_plane *drm_plane, 87262306a36Sopenharmony_ci struct drm_atomic_state *state) 87362306a36Sopenharmony_ci{ 87462306a36Sopenharmony_ci struct drm_plane_state *oldstate = drm_atomic_get_old_plane_state(state, 87562306a36Sopenharmony_ci drm_plane); 87662306a36Sopenharmony_ci struct sti_plane *plane = to_sti_plane(drm_plane); 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci if (!oldstate->crtc) { 87962306a36Sopenharmony_ci DRM_DEBUG_DRIVER("drm plane:%d not enabled\n", 88062306a36Sopenharmony_ci drm_plane->base.id); 88162306a36Sopenharmony_ci return; 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci DRM_DEBUG_DRIVER("CRTC:%d (%s) drm plane:%d (%s)\n", 88562306a36Sopenharmony_ci oldstate->crtc->base.id, 88662306a36Sopenharmony_ci sti_mixer_to_str(to_sti_mixer(oldstate->crtc)), 88762306a36Sopenharmony_ci drm_plane->base.id, sti_plane_to_str(plane)); 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci plane->status = STI_PLANE_DISABLING; 89062306a36Sopenharmony_ci} 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs sti_gdp_helpers_funcs = { 89362306a36Sopenharmony_ci .atomic_check = sti_gdp_atomic_check, 89462306a36Sopenharmony_ci .atomic_update = sti_gdp_atomic_update, 89562306a36Sopenharmony_ci .atomic_disable = sti_gdp_atomic_disable, 89662306a36Sopenharmony_ci}; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_cistatic int sti_gdp_late_register(struct drm_plane *drm_plane) 89962306a36Sopenharmony_ci{ 90062306a36Sopenharmony_ci struct sti_plane *plane = to_sti_plane(drm_plane); 90162306a36Sopenharmony_ci struct sti_gdp *gdp = to_sti_gdp(plane); 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci return gdp_debugfs_init(gdp, drm_plane->dev->primary); 90462306a36Sopenharmony_ci} 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_cistatic const struct drm_plane_funcs sti_gdp_plane_helpers_funcs = { 90762306a36Sopenharmony_ci .update_plane = drm_atomic_helper_update_plane, 90862306a36Sopenharmony_ci .disable_plane = drm_atomic_helper_disable_plane, 90962306a36Sopenharmony_ci .destroy = drm_plane_cleanup, 91062306a36Sopenharmony_ci .reset = drm_atomic_helper_plane_reset, 91162306a36Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, 91262306a36Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, 91362306a36Sopenharmony_ci .late_register = sti_gdp_late_register, 91462306a36Sopenharmony_ci}; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_cistruct drm_plane *sti_gdp_create(struct drm_device *drm_dev, 91762306a36Sopenharmony_ci struct device *dev, int desc, 91862306a36Sopenharmony_ci void __iomem *baseaddr, 91962306a36Sopenharmony_ci unsigned int possible_crtcs, 92062306a36Sopenharmony_ci enum drm_plane_type type) 92162306a36Sopenharmony_ci{ 92262306a36Sopenharmony_ci struct sti_gdp *gdp; 92362306a36Sopenharmony_ci int res; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci gdp = devm_kzalloc(dev, sizeof(*gdp), GFP_KERNEL); 92662306a36Sopenharmony_ci if (!gdp) { 92762306a36Sopenharmony_ci DRM_ERROR("Failed to allocate memory for GDP\n"); 92862306a36Sopenharmony_ci return NULL; 92962306a36Sopenharmony_ci } 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci gdp->dev = dev; 93262306a36Sopenharmony_ci gdp->regs = baseaddr; 93362306a36Sopenharmony_ci gdp->plane.desc = desc; 93462306a36Sopenharmony_ci gdp->plane.status = STI_PLANE_DISABLED; 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci gdp->vtg_field_nb.notifier_call = sti_gdp_field_cb; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci sti_gdp_init(gdp); 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci res = drm_universal_plane_init(drm_dev, &gdp->plane.drm_plane, 94162306a36Sopenharmony_ci possible_crtcs, 94262306a36Sopenharmony_ci &sti_gdp_plane_helpers_funcs, 94362306a36Sopenharmony_ci gdp_supported_formats, 94462306a36Sopenharmony_ci ARRAY_SIZE(gdp_supported_formats), 94562306a36Sopenharmony_ci NULL, type, NULL); 94662306a36Sopenharmony_ci if (res) { 94762306a36Sopenharmony_ci DRM_ERROR("Failed to initialize universal plane\n"); 94862306a36Sopenharmony_ci goto err; 94962306a36Sopenharmony_ci } 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci drm_plane_helper_add(&gdp->plane.drm_plane, &sti_gdp_helpers_funcs); 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci sti_plane_init_property(&gdp->plane, type); 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci return &gdp->plane.drm_plane; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_cierr: 95862306a36Sopenharmony_ci devm_kfree(dev, gdp); 95962306a36Sopenharmony_ci return NULL; 96062306a36Sopenharmony_ci} 961