18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2015 Broadcom 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci/** 78c2ecf20Sopenharmony_ci * DOC: VC4 HVS module. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * The Hardware Video Scaler (HVS) is the piece of hardware that does 108c2ecf20Sopenharmony_ci * translation, scaling, colorspace conversion, and compositing of 118c2ecf20Sopenharmony_ci * pixels stored in framebuffers into a FIFO of pixels going out to 128c2ecf20Sopenharmony_ci * the Pixel Valve (CRTC). It operates at the system clock rate (the 138c2ecf20Sopenharmony_ci * system audio clock gate, specifically), which is much higher than 148c2ecf20Sopenharmony_ci * the pixel clock rate. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * There is a single global HVS, with multiple output FIFOs that can 178c2ecf20Sopenharmony_ci * be consumed by the PVs. This file just manages the resources for 188c2ecf20Sopenharmony_ci * the HVS, while the vc4_crtc.c code actually drives HVS setup for 198c2ecf20Sopenharmony_ci * each CRTC. 208c2ecf20Sopenharmony_ci */ 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 238c2ecf20Sopenharmony_ci#include <linux/clk.h> 248c2ecf20Sopenharmony_ci#include <linux/component.h> 258c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 288c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include "vc4_drv.h" 318c2ecf20Sopenharmony_ci#include "vc4_regs.h" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic const struct debugfs_reg32 hvs_regs[] = { 348c2ecf20Sopenharmony_ci VC4_REG32(SCALER_DISPCTRL), 358c2ecf20Sopenharmony_ci VC4_REG32(SCALER_DISPSTAT), 368c2ecf20Sopenharmony_ci VC4_REG32(SCALER_DISPID), 378c2ecf20Sopenharmony_ci VC4_REG32(SCALER_DISPECTRL), 388c2ecf20Sopenharmony_ci VC4_REG32(SCALER_DISPPROF), 398c2ecf20Sopenharmony_ci VC4_REG32(SCALER_DISPDITHER), 408c2ecf20Sopenharmony_ci VC4_REG32(SCALER_DISPEOLN), 418c2ecf20Sopenharmony_ci VC4_REG32(SCALER_DISPLIST0), 428c2ecf20Sopenharmony_ci VC4_REG32(SCALER_DISPLIST1), 438c2ecf20Sopenharmony_ci VC4_REG32(SCALER_DISPLIST2), 448c2ecf20Sopenharmony_ci VC4_REG32(SCALER_DISPLSTAT), 458c2ecf20Sopenharmony_ci VC4_REG32(SCALER_DISPLACT0), 468c2ecf20Sopenharmony_ci VC4_REG32(SCALER_DISPLACT1), 478c2ecf20Sopenharmony_ci VC4_REG32(SCALER_DISPLACT2), 488c2ecf20Sopenharmony_ci VC4_REG32(SCALER_DISPCTRL0), 498c2ecf20Sopenharmony_ci VC4_REG32(SCALER_DISPBKGND0), 508c2ecf20Sopenharmony_ci VC4_REG32(SCALER_DISPSTAT0), 518c2ecf20Sopenharmony_ci VC4_REG32(SCALER_DISPBASE0), 528c2ecf20Sopenharmony_ci VC4_REG32(SCALER_DISPCTRL1), 538c2ecf20Sopenharmony_ci VC4_REG32(SCALER_DISPBKGND1), 548c2ecf20Sopenharmony_ci VC4_REG32(SCALER_DISPSTAT1), 558c2ecf20Sopenharmony_ci VC4_REG32(SCALER_DISPBASE1), 568c2ecf20Sopenharmony_ci VC4_REG32(SCALER_DISPCTRL2), 578c2ecf20Sopenharmony_ci VC4_REG32(SCALER_DISPBKGND2), 588c2ecf20Sopenharmony_ci VC4_REG32(SCALER_DISPSTAT2), 598c2ecf20Sopenharmony_ci VC4_REG32(SCALER_DISPBASE2), 608c2ecf20Sopenharmony_ci VC4_REG32(SCALER_DISPALPHA2), 618c2ecf20Sopenharmony_ci VC4_REG32(SCALER_OLEDOFFS), 628c2ecf20Sopenharmony_ci VC4_REG32(SCALER_OLEDCOEF0), 638c2ecf20Sopenharmony_ci VC4_REG32(SCALER_OLEDCOEF1), 648c2ecf20Sopenharmony_ci VC4_REG32(SCALER_OLEDCOEF2), 658c2ecf20Sopenharmony_ci}; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_civoid vc4_hvs_dump_state(struct drm_device *dev) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 708c2ecf20Sopenharmony_ci struct drm_printer p = drm_info_printer(&vc4->hvs->pdev->dev); 718c2ecf20Sopenharmony_ci int i; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci drm_print_regset32(&p, &vc4->hvs->regset); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci DRM_INFO("HVS ctx:\n"); 768c2ecf20Sopenharmony_ci for (i = 0; i < 64; i += 4) { 778c2ecf20Sopenharmony_ci DRM_INFO("0x%08x (%s): 0x%08x 0x%08x 0x%08x 0x%08x\n", 788c2ecf20Sopenharmony_ci i * 4, i < HVS_BOOTLOADER_DLIST_END ? "B" : "D", 798c2ecf20Sopenharmony_ci readl((u32 __iomem *)vc4->hvs->dlist + i + 0), 808c2ecf20Sopenharmony_ci readl((u32 __iomem *)vc4->hvs->dlist + i + 1), 818c2ecf20Sopenharmony_ci readl((u32 __iomem *)vc4->hvs->dlist + i + 2), 828c2ecf20Sopenharmony_ci readl((u32 __iomem *)vc4->hvs->dlist + i + 3)); 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic int vc4_hvs_debugfs_underrun(struct seq_file *m, void *data) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci struct drm_info_node *node = m->private; 898c2ecf20Sopenharmony_ci struct drm_device *dev = node->minor->dev; 908c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 918c2ecf20Sopenharmony_ci struct drm_printer p = drm_seq_file_printer(m); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci drm_printf(&p, "%d\n", atomic_read(&vc4->underrun)); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci return 0; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* The filter kernel is composed of dwords each containing 3 9-bit 998c2ecf20Sopenharmony_ci * signed integers packed next to each other. 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_ci#define VC4_INT_TO_COEFF(coeff) (coeff & 0x1ff) 1028c2ecf20Sopenharmony_ci#define VC4_PPF_FILTER_WORD(c0, c1, c2) \ 1038c2ecf20Sopenharmony_ci ((((c0) & 0x1ff) << 0) | \ 1048c2ecf20Sopenharmony_ci (((c1) & 0x1ff) << 9) | \ 1058c2ecf20Sopenharmony_ci (((c2) & 0x1ff) << 18)) 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci/* The whole filter kernel is arranged as the coefficients 0-16 going 1088c2ecf20Sopenharmony_ci * up, then a pad, then 17-31 going down and reversed within the 1098c2ecf20Sopenharmony_ci * dwords. This means that a linear phase kernel (where it's 1108c2ecf20Sopenharmony_ci * symmetrical at the boundary between 15 and 16) has the last 5 1118c2ecf20Sopenharmony_ci * dwords matching the first 5, but reversed. 1128c2ecf20Sopenharmony_ci */ 1138c2ecf20Sopenharmony_ci#define VC4_LINEAR_PHASE_KERNEL(c0, c1, c2, c3, c4, c5, c6, c7, c8, \ 1148c2ecf20Sopenharmony_ci c9, c10, c11, c12, c13, c14, c15) \ 1158c2ecf20Sopenharmony_ci {VC4_PPF_FILTER_WORD(c0, c1, c2), \ 1168c2ecf20Sopenharmony_ci VC4_PPF_FILTER_WORD(c3, c4, c5), \ 1178c2ecf20Sopenharmony_ci VC4_PPF_FILTER_WORD(c6, c7, c8), \ 1188c2ecf20Sopenharmony_ci VC4_PPF_FILTER_WORD(c9, c10, c11), \ 1198c2ecf20Sopenharmony_ci VC4_PPF_FILTER_WORD(c12, c13, c14), \ 1208c2ecf20Sopenharmony_ci VC4_PPF_FILTER_WORD(c15, c15, 0)} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci#define VC4_LINEAR_PHASE_KERNEL_DWORDS 6 1238c2ecf20Sopenharmony_ci#define VC4_KERNEL_DWORDS (VC4_LINEAR_PHASE_KERNEL_DWORDS * 2 - 1) 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/* Recommended B=1/3, C=1/3 filter choice from Mitchell/Netravali. 1268c2ecf20Sopenharmony_ci * http://www.cs.utexas.edu/~fussell/courses/cs384g/lectures/mitchell/Mitchell.pdf 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_cistatic const u32 mitchell_netravali_1_3_1_3_kernel[] = 1298c2ecf20Sopenharmony_ci VC4_LINEAR_PHASE_KERNEL(0, -2, -6, -8, -10, -8, -3, 2, 18, 1308c2ecf20Sopenharmony_ci 50, 82, 119, 155, 187, 213, 227); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic int vc4_hvs_upload_linear_kernel(struct vc4_hvs *hvs, 1338c2ecf20Sopenharmony_ci struct drm_mm_node *space, 1348c2ecf20Sopenharmony_ci const u32 *kernel) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci int ret, i; 1378c2ecf20Sopenharmony_ci u32 __iomem *dst_kernel; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci ret = drm_mm_insert_node(&hvs->dlist_mm, space, VC4_KERNEL_DWORDS); 1408c2ecf20Sopenharmony_ci if (ret) { 1418c2ecf20Sopenharmony_ci DRM_ERROR("Failed to allocate space for filter kernel: %d\n", 1428c2ecf20Sopenharmony_ci ret); 1438c2ecf20Sopenharmony_ci return ret; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci dst_kernel = hvs->dlist + space->start; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci for (i = 0; i < VC4_KERNEL_DWORDS; i++) { 1498c2ecf20Sopenharmony_ci if (i < VC4_LINEAR_PHASE_KERNEL_DWORDS) 1508c2ecf20Sopenharmony_ci writel(kernel[i], &dst_kernel[i]); 1518c2ecf20Sopenharmony_ci else { 1528c2ecf20Sopenharmony_ci writel(kernel[VC4_KERNEL_DWORDS - i - 1], 1538c2ecf20Sopenharmony_ci &dst_kernel[i]); 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return 0; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic void vc4_hvs_lut_load(struct drm_crtc *crtc) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 1638c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 1648c2ecf20Sopenharmony_ci struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); 1658c2ecf20Sopenharmony_ci struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); 1668c2ecf20Sopenharmony_ci u32 i; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* The LUT memory is laid out with each HVS channel in order, 1698c2ecf20Sopenharmony_ci * each of which takes 256 writes for R, 256 for G, then 256 1708c2ecf20Sopenharmony_ci * for B. 1718c2ecf20Sopenharmony_ci */ 1728c2ecf20Sopenharmony_ci HVS_WRITE(SCALER_GAMADDR, 1738c2ecf20Sopenharmony_ci SCALER_GAMADDR_AUTOINC | 1748c2ecf20Sopenharmony_ci (vc4_state->assigned_channel * 3 * crtc->gamma_size)); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci for (i = 0; i < crtc->gamma_size; i++) 1778c2ecf20Sopenharmony_ci HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_r[i]); 1788c2ecf20Sopenharmony_ci for (i = 0; i < crtc->gamma_size; i++) 1798c2ecf20Sopenharmony_ci HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_g[i]); 1808c2ecf20Sopenharmony_ci for (i = 0; i < crtc->gamma_size; i++) 1818c2ecf20Sopenharmony_ci HVS_WRITE(SCALER_GAMDATA, vc4_crtc->lut_b[i]); 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic void vc4_hvs_update_gamma_lut(struct drm_crtc *crtc) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); 1878c2ecf20Sopenharmony_ci struct drm_color_lut *lut = crtc->state->gamma_lut->data; 1888c2ecf20Sopenharmony_ci u32 length = drm_color_lut_size(crtc->state->gamma_lut); 1898c2ecf20Sopenharmony_ci u32 i; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci for (i = 0; i < length; i++) { 1928c2ecf20Sopenharmony_ci vc4_crtc->lut_r[i] = drm_color_lut_extract(lut[i].red, 8); 1938c2ecf20Sopenharmony_ci vc4_crtc->lut_g[i] = drm_color_lut_extract(lut[i].green, 8); 1948c2ecf20Sopenharmony_ci vc4_crtc->lut_b[i] = drm_color_lut_extract(lut[i].blue, 8); 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci vc4_hvs_lut_load(crtc); 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ciint vc4_hvs_get_fifo_from_output(struct drm_device *dev, unsigned int output) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 2038c2ecf20Sopenharmony_ci u32 reg; 2048c2ecf20Sopenharmony_ci int ret; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (!vc4->hvs->hvs5) 2078c2ecf20Sopenharmony_ci return output; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci switch (output) { 2108c2ecf20Sopenharmony_ci case 0: 2118c2ecf20Sopenharmony_ci return 0; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci case 1: 2148c2ecf20Sopenharmony_ci return 1; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci case 2: 2178c2ecf20Sopenharmony_ci reg = HVS_READ(SCALER_DISPECTRL); 2188c2ecf20Sopenharmony_ci ret = FIELD_GET(SCALER_DISPECTRL_DSP2_MUX_MASK, reg); 2198c2ecf20Sopenharmony_ci if (ret == 0) 2208c2ecf20Sopenharmony_ci return 2; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci case 3: 2258c2ecf20Sopenharmony_ci reg = HVS_READ(SCALER_DISPCTRL); 2268c2ecf20Sopenharmony_ci ret = FIELD_GET(SCALER_DISPCTRL_DSP3_MUX_MASK, reg); 2278c2ecf20Sopenharmony_ci if (ret == 3) 2288c2ecf20Sopenharmony_ci return -EPIPE; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci return ret; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci case 4: 2338c2ecf20Sopenharmony_ci reg = HVS_READ(SCALER_DISPEOLN); 2348c2ecf20Sopenharmony_ci ret = FIELD_GET(SCALER_DISPEOLN_DSP4_MUX_MASK, reg); 2358c2ecf20Sopenharmony_ci if (ret == 3) 2368c2ecf20Sopenharmony_ci return -EPIPE; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci return ret; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci case 5: 2418c2ecf20Sopenharmony_ci reg = HVS_READ(SCALER_DISPDITHER); 2428c2ecf20Sopenharmony_ci ret = FIELD_GET(SCALER_DISPDITHER_DSP5_MUX_MASK, reg); 2438c2ecf20Sopenharmony_ci if (ret == 3) 2448c2ecf20Sopenharmony_ci return -EPIPE; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci return ret; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci default: 2498c2ecf20Sopenharmony_ci return -EPIPE; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic int vc4_hvs_init_channel(struct vc4_dev *vc4, struct drm_crtc *crtc, 2548c2ecf20Sopenharmony_ci struct drm_display_mode *mode, bool oneshot) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci struct vc4_crtc_state *vc4_crtc_state = to_vc4_crtc_state(crtc->state); 2578c2ecf20Sopenharmony_ci unsigned int chan = vc4_crtc_state->assigned_channel; 2588c2ecf20Sopenharmony_ci bool interlace = mode->flags & DRM_MODE_FLAG_INTERLACE; 2598c2ecf20Sopenharmony_ci u32 dispbkgndx; 2608c2ecf20Sopenharmony_ci u32 dispctrl; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci HVS_WRITE(SCALER_DISPCTRLX(chan), 0); 2638c2ecf20Sopenharmony_ci HVS_WRITE(SCALER_DISPCTRLX(chan), SCALER_DISPCTRLX_RESET); 2648c2ecf20Sopenharmony_ci HVS_WRITE(SCALER_DISPCTRLX(chan), 0); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* Turn on the scaler, which will wait for vstart to start 2678c2ecf20Sopenharmony_ci * compositing. 2688c2ecf20Sopenharmony_ci * When feeding the transposer, we should operate in oneshot 2698c2ecf20Sopenharmony_ci * mode. 2708c2ecf20Sopenharmony_ci */ 2718c2ecf20Sopenharmony_ci dispctrl = SCALER_DISPCTRLX_ENABLE; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci if (!vc4->hvs->hvs5) 2748c2ecf20Sopenharmony_ci dispctrl |= VC4_SET_FIELD(mode->hdisplay, 2758c2ecf20Sopenharmony_ci SCALER_DISPCTRLX_WIDTH) | 2768c2ecf20Sopenharmony_ci VC4_SET_FIELD(mode->vdisplay, 2778c2ecf20Sopenharmony_ci SCALER_DISPCTRLX_HEIGHT) | 2788c2ecf20Sopenharmony_ci (oneshot ? SCALER_DISPCTRLX_ONESHOT : 0); 2798c2ecf20Sopenharmony_ci else 2808c2ecf20Sopenharmony_ci dispctrl |= VC4_SET_FIELD(mode->hdisplay, 2818c2ecf20Sopenharmony_ci SCALER5_DISPCTRLX_WIDTH) | 2828c2ecf20Sopenharmony_ci VC4_SET_FIELD(mode->vdisplay, 2838c2ecf20Sopenharmony_ci SCALER5_DISPCTRLX_HEIGHT) | 2848c2ecf20Sopenharmony_ci (oneshot ? SCALER5_DISPCTRLX_ONESHOT : 0); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci HVS_WRITE(SCALER_DISPCTRLX(chan), dispctrl); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(chan)); 2898c2ecf20Sopenharmony_ci dispbkgndx &= ~SCALER_DISPBKGND_GAMMA; 2908c2ecf20Sopenharmony_ci dispbkgndx &= ~SCALER_DISPBKGND_INTERLACE; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci HVS_WRITE(SCALER_DISPBKGNDX(chan), dispbkgndx | 2938c2ecf20Sopenharmony_ci SCALER_DISPBKGND_AUTOHS | 2948c2ecf20Sopenharmony_ci ((!vc4->hvs->hvs5) ? SCALER_DISPBKGND_GAMMA : 0) | 2958c2ecf20Sopenharmony_ci (interlace ? SCALER_DISPBKGND_INTERLACE : 0)); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci /* Reload the LUT, since the SRAMs would have been disabled if 2988c2ecf20Sopenharmony_ci * all CRTCs had SCALER_DISPBKGND_GAMMA unset at once. 2998c2ecf20Sopenharmony_ci */ 3008c2ecf20Sopenharmony_ci vc4_hvs_lut_load(crtc); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci return 0; 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_civoid vc4_hvs_stop_channel(struct drm_device *dev, unsigned int chan) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (HVS_READ(SCALER_DISPCTRLX(chan)) & SCALER_DISPCTRLX_ENABLE) 3108c2ecf20Sopenharmony_ci return; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci HVS_WRITE(SCALER_DISPCTRLX(chan), 3138c2ecf20Sopenharmony_ci HVS_READ(SCALER_DISPCTRLX(chan)) | SCALER_DISPCTRLX_RESET); 3148c2ecf20Sopenharmony_ci HVS_WRITE(SCALER_DISPCTRLX(chan), 3158c2ecf20Sopenharmony_ci HVS_READ(SCALER_DISPCTRLX(chan)) & ~SCALER_DISPCTRLX_ENABLE); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* Once we leave, the scaler should be disabled and its fifo empty. */ 3188c2ecf20Sopenharmony_ci WARN_ON_ONCE(HVS_READ(SCALER_DISPCTRLX(chan)) & SCALER_DISPCTRLX_RESET); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci WARN_ON_ONCE(VC4_GET_FIELD(HVS_READ(SCALER_DISPSTATX(chan)), 3218c2ecf20Sopenharmony_ci SCALER_DISPSTATX_MODE) != 3228c2ecf20Sopenharmony_ci SCALER_DISPSTATX_MODE_DISABLED); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci WARN_ON_ONCE((HVS_READ(SCALER_DISPSTATX(chan)) & 3258c2ecf20Sopenharmony_ci (SCALER_DISPSTATX_FULL | SCALER_DISPSTATX_EMPTY)) != 3268c2ecf20Sopenharmony_ci SCALER_DISPSTATX_EMPTY); 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ciint vc4_hvs_atomic_check(struct drm_crtc *crtc, 3308c2ecf20Sopenharmony_ci struct drm_crtc_state *state) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(state); 3338c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 3348c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 3358c2ecf20Sopenharmony_ci struct drm_plane *plane; 3368c2ecf20Sopenharmony_ci unsigned long flags; 3378c2ecf20Sopenharmony_ci const struct drm_plane_state *plane_state; 3388c2ecf20Sopenharmony_ci u32 dlist_count = 0; 3398c2ecf20Sopenharmony_ci int ret; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci /* The pixelvalve can only feed one encoder (and encoders are 3428c2ecf20Sopenharmony_ci * 1:1 with connectors.) 3438c2ecf20Sopenharmony_ci */ 3448c2ecf20Sopenharmony_ci if (hweight32(state->connector_mask) > 1) 3458c2ecf20Sopenharmony_ci return -EINVAL; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, state) 3488c2ecf20Sopenharmony_ci dlist_count += vc4_plane_dlist_size(plane_state); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci dlist_count++; /* Account for SCALER_CTL0_END. */ 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci spin_lock_irqsave(&vc4->hvs->mm_lock, flags); 3538c2ecf20Sopenharmony_ci ret = drm_mm_insert_node(&vc4->hvs->dlist_mm, &vc4_state->mm, 3548c2ecf20Sopenharmony_ci dlist_count); 3558c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vc4->hvs->mm_lock, flags); 3568c2ecf20Sopenharmony_ci if (ret) 3578c2ecf20Sopenharmony_ci return ret; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci return 0; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic void vc4_hvs_update_dlist(struct drm_crtc *crtc) 3638c2ecf20Sopenharmony_ci{ 3648c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 3658c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 3668c2ecf20Sopenharmony_ci struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); 3678c2ecf20Sopenharmony_ci struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci if (crtc->state->event) { 3708c2ecf20Sopenharmony_ci unsigned long flags; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci crtc->state->event->pipe = drm_crtc_index(crtc); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci WARN_ON(drm_crtc_vblank_get(crtc) != 0); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev->event_lock, flags); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (!vc4_state->feed_txp || vc4_state->txp_armed) { 3798c2ecf20Sopenharmony_ci vc4_crtc->event = crtc->state->event; 3808c2ecf20Sopenharmony_ci crtc->state->event = NULL; 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci HVS_WRITE(SCALER_DISPLISTX(vc4_state->assigned_channel), 3848c2ecf20Sopenharmony_ci vc4_state->mm.start); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev->event_lock, flags); 3878c2ecf20Sopenharmony_ci } else { 3888c2ecf20Sopenharmony_ci HVS_WRITE(SCALER_DISPLISTX(vc4_state->assigned_channel), 3898c2ecf20Sopenharmony_ci vc4_state->mm.start); 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci} 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_civoid vc4_hvs_atomic_enable(struct drm_crtc *crtc, 3948c2ecf20Sopenharmony_ci struct drm_crtc_state *old_state) 3958c2ecf20Sopenharmony_ci{ 3968c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 3978c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 3988c2ecf20Sopenharmony_ci struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); 3998c2ecf20Sopenharmony_ci struct drm_display_mode *mode = &crtc->state->adjusted_mode; 4008c2ecf20Sopenharmony_ci bool oneshot = vc4_state->feed_txp; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci vc4_hvs_update_dlist(crtc); 4038c2ecf20Sopenharmony_ci vc4_hvs_init_channel(vc4, crtc, mode, oneshot); 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_civoid vc4_hvs_atomic_disable(struct drm_crtc *crtc, 4078c2ecf20Sopenharmony_ci struct drm_crtc_state *old_state) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 4108c2ecf20Sopenharmony_ci struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(old_state); 4118c2ecf20Sopenharmony_ci unsigned int chan = vc4_state->assigned_channel; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci vc4_hvs_stop_channel(dev, chan); 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_civoid vc4_hvs_atomic_flush(struct drm_crtc *crtc, 4178c2ecf20Sopenharmony_ci struct drm_crtc_state *old_state) 4188c2ecf20Sopenharmony_ci{ 4198c2ecf20Sopenharmony_ci struct drm_device *dev = crtc->dev; 4208c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 4218c2ecf20Sopenharmony_ci struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc->state); 4228c2ecf20Sopenharmony_ci struct drm_plane *plane; 4238c2ecf20Sopenharmony_ci struct vc4_plane_state *vc4_plane_state; 4248c2ecf20Sopenharmony_ci bool debug_dump_regs = false; 4258c2ecf20Sopenharmony_ci bool enable_bg_fill = false; 4268c2ecf20Sopenharmony_ci u32 __iomem *dlist_start = vc4->hvs->dlist + vc4_state->mm.start; 4278c2ecf20Sopenharmony_ci u32 __iomem *dlist_next = dlist_start; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci if (debug_dump_regs) { 4308c2ecf20Sopenharmony_ci DRM_INFO("CRTC %d HVS before:\n", drm_crtc_index(crtc)); 4318c2ecf20Sopenharmony_ci vc4_hvs_dump_state(dev); 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* Copy all the active planes' dlist contents to the hardware dlist. */ 4358c2ecf20Sopenharmony_ci drm_atomic_crtc_for_each_plane(plane, crtc) { 4368c2ecf20Sopenharmony_ci /* Is this the first active plane? */ 4378c2ecf20Sopenharmony_ci if (dlist_next == dlist_start) { 4388c2ecf20Sopenharmony_ci /* We need to enable background fill when a plane 4398c2ecf20Sopenharmony_ci * could be alpha blending from the background, i.e. 4408c2ecf20Sopenharmony_ci * where no other plane is underneath. It suffices to 4418c2ecf20Sopenharmony_ci * consider the first active plane here since we set 4428c2ecf20Sopenharmony_ci * needs_bg_fill such that either the first plane 4438c2ecf20Sopenharmony_ci * already needs it or all planes on top blend from 4448c2ecf20Sopenharmony_ci * the first or a lower plane. 4458c2ecf20Sopenharmony_ci */ 4468c2ecf20Sopenharmony_ci vc4_plane_state = to_vc4_plane_state(plane->state); 4478c2ecf20Sopenharmony_ci enable_bg_fill = vc4_plane_state->needs_bg_fill; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci dlist_next += vc4_plane_write_dlist(plane, dlist_next); 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci writel(SCALER_CTL0_END, dlist_next); 4548c2ecf20Sopenharmony_ci dlist_next++; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci WARN_ON_ONCE(dlist_next - dlist_start != vc4_state->mm.size); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (enable_bg_fill) 4598c2ecf20Sopenharmony_ci /* This sets a black background color fill, as is the case 4608c2ecf20Sopenharmony_ci * with other DRM drivers. 4618c2ecf20Sopenharmony_ci */ 4628c2ecf20Sopenharmony_ci HVS_WRITE(SCALER_DISPBKGNDX(vc4_state->assigned_channel), 4638c2ecf20Sopenharmony_ci HVS_READ(SCALER_DISPBKGNDX(vc4_state->assigned_channel)) | 4648c2ecf20Sopenharmony_ci SCALER_DISPBKGND_FILL); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci /* Only update DISPLIST if the CRTC was already running and is not 4678c2ecf20Sopenharmony_ci * being disabled. 4688c2ecf20Sopenharmony_ci * vc4_crtc_enable() takes care of updating the dlist just after 4698c2ecf20Sopenharmony_ci * re-enabling VBLANK interrupts and before enabling the engine. 4708c2ecf20Sopenharmony_ci * If the CRTC is being disabled, there's no point in updating this 4718c2ecf20Sopenharmony_ci * information. 4728c2ecf20Sopenharmony_ci */ 4738c2ecf20Sopenharmony_ci if (crtc->state->active && old_state->active) 4748c2ecf20Sopenharmony_ci vc4_hvs_update_dlist(crtc); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci if (crtc->state->color_mgmt_changed) { 4778c2ecf20Sopenharmony_ci u32 dispbkgndx = HVS_READ(SCALER_DISPBKGNDX(vc4_state->assigned_channel)); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (crtc->state->gamma_lut) { 4808c2ecf20Sopenharmony_ci vc4_hvs_update_gamma_lut(crtc); 4818c2ecf20Sopenharmony_ci dispbkgndx |= SCALER_DISPBKGND_GAMMA; 4828c2ecf20Sopenharmony_ci } else { 4838c2ecf20Sopenharmony_ci /* Unsetting DISPBKGND_GAMMA skips the gamma lut step 4848c2ecf20Sopenharmony_ci * in hardware, which is the same as a linear lut that 4858c2ecf20Sopenharmony_ci * DRM expects us to use in absence of a user lut. 4868c2ecf20Sopenharmony_ci */ 4878c2ecf20Sopenharmony_ci dispbkgndx &= ~SCALER_DISPBKGND_GAMMA; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci HVS_WRITE(SCALER_DISPBKGNDX(vc4_state->assigned_channel), dispbkgndx); 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci if (debug_dump_regs) { 4938c2ecf20Sopenharmony_ci DRM_INFO("CRTC %d HVS after:\n", drm_crtc_index(crtc)); 4948c2ecf20Sopenharmony_ci vc4_hvs_dump_state(dev); 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_civoid vc4_hvs_mask_underrun(struct drm_device *dev, int channel) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 5018c2ecf20Sopenharmony_ci u32 dispctrl = HVS_READ(SCALER_DISPCTRL); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci dispctrl &= ~SCALER_DISPCTRL_DSPEISLUR(channel); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci HVS_WRITE(SCALER_DISPCTRL, dispctrl); 5068c2ecf20Sopenharmony_ci} 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_civoid vc4_hvs_unmask_underrun(struct drm_device *dev, int channel) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 5118c2ecf20Sopenharmony_ci u32 dispctrl = HVS_READ(SCALER_DISPCTRL); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci dispctrl |= SCALER_DISPCTRL_DSPEISLUR(channel); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci HVS_WRITE(SCALER_DISPSTAT, 5168c2ecf20Sopenharmony_ci SCALER_DISPSTAT_EUFLOW(channel)); 5178c2ecf20Sopenharmony_ci HVS_WRITE(SCALER_DISPCTRL, dispctrl); 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic void vc4_hvs_report_underrun(struct drm_device *dev) 5218c2ecf20Sopenharmony_ci{ 5228c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci atomic_inc(&vc4->underrun); 5258c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev->dev, "HVS underrun\n"); 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cistatic irqreturn_t vc4_hvs_irq_handler(int irq, void *data) 5298c2ecf20Sopenharmony_ci{ 5308c2ecf20Sopenharmony_ci struct drm_device *dev = data; 5318c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 5328c2ecf20Sopenharmony_ci irqreturn_t irqret = IRQ_NONE; 5338c2ecf20Sopenharmony_ci int channel; 5348c2ecf20Sopenharmony_ci u32 control; 5358c2ecf20Sopenharmony_ci u32 status; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci status = HVS_READ(SCALER_DISPSTAT); 5388c2ecf20Sopenharmony_ci control = HVS_READ(SCALER_DISPCTRL); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci for (channel = 0; channel < SCALER_CHANNELS_COUNT; channel++) { 5418c2ecf20Sopenharmony_ci /* Interrupt masking is not always honored, so check it here. */ 5428c2ecf20Sopenharmony_ci if (status & SCALER_DISPSTAT_EUFLOW(channel) && 5438c2ecf20Sopenharmony_ci control & SCALER_DISPCTRL_DSPEISLUR(channel)) { 5448c2ecf20Sopenharmony_ci vc4_hvs_mask_underrun(dev, channel); 5458c2ecf20Sopenharmony_ci vc4_hvs_report_underrun(dev); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci irqret = IRQ_HANDLED; 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci /* Clear every per-channel interrupt flag. */ 5528c2ecf20Sopenharmony_ci HVS_WRITE(SCALER_DISPSTAT, SCALER_DISPSTAT_IRQMASK(0) | 5538c2ecf20Sopenharmony_ci SCALER_DISPSTAT_IRQMASK(1) | 5548c2ecf20Sopenharmony_ci SCALER_DISPSTAT_IRQMASK(2)); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci return irqret; 5578c2ecf20Sopenharmony_ci} 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_cistatic int vc4_hvs_bind(struct device *dev, struct device *master, void *data) 5608c2ecf20Sopenharmony_ci{ 5618c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 5628c2ecf20Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(master); 5638c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(drm); 5648c2ecf20Sopenharmony_ci struct vc4_hvs *hvs = NULL; 5658c2ecf20Sopenharmony_ci int ret; 5668c2ecf20Sopenharmony_ci u32 dispctrl; 5678c2ecf20Sopenharmony_ci u32 reg; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci hvs = devm_kzalloc(&pdev->dev, sizeof(*hvs), GFP_KERNEL); 5708c2ecf20Sopenharmony_ci if (!hvs) 5718c2ecf20Sopenharmony_ci return -ENOMEM; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci hvs->pdev = pdev; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci if (of_device_is_compatible(pdev->dev.of_node, "brcm,bcm2711-hvs")) 5768c2ecf20Sopenharmony_ci hvs->hvs5 = true; 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci hvs->regs = vc4_ioremap_regs(pdev, 0); 5798c2ecf20Sopenharmony_ci if (IS_ERR(hvs->regs)) 5808c2ecf20Sopenharmony_ci return PTR_ERR(hvs->regs); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci hvs->regset.base = hvs->regs; 5838c2ecf20Sopenharmony_ci hvs->regset.regs = hvs_regs; 5848c2ecf20Sopenharmony_ci hvs->regset.nregs = ARRAY_SIZE(hvs_regs); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci if (hvs->hvs5) { 5878c2ecf20Sopenharmony_ci hvs->core_clk = devm_clk_get(&pdev->dev, NULL); 5888c2ecf20Sopenharmony_ci if (IS_ERR(hvs->core_clk)) { 5898c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Couldn't get core clock\n"); 5908c2ecf20Sopenharmony_ci return PTR_ERR(hvs->core_clk); 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci ret = clk_prepare_enable(hvs->core_clk); 5948c2ecf20Sopenharmony_ci if (ret) { 5958c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Couldn't enable the core clock\n"); 5968c2ecf20Sopenharmony_ci return ret; 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci if (!hvs->hvs5) 6018c2ecf20Sopenharmony_ci hvs->dlist = hvs->regs + SCALER_DLIST_START; 6028c2ecf20Sopenharmony_ci else 6038c2ecf20Sopenharmony_ci hvs->dlist = hvs->regs + SCALER5_DLIST_START; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci spin_lock_init(&hvs->mm_lock); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci /* Set up the HVS display list memory manager. We never 6088c2ecf20Sopenharmony_ci * overwrite the setup from the bootloader (just 128b out of 6098c2ecf20Sopenharmony_ci * our 16K), since we don't want to scramble the screen when 6108c2ecf20Sopenharmony_ci * transitioning from the firmware's boot setup to runtime. 6118c2ecf20Sopenharmony_ci */ 6128c2ecf20Sopenharmony_ci drm_mm_init(&hvs->dlist_mm, 6138c2ecf20Sopenharmony_ci HVS_BOOTLOADER_DLIST_END, 6148c2ecf20Sopenharmony_ci (SCALER_DLIST_SIZE >> 2) - HVS_BOOTLOADER_DLIST_END); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci /* Set up the HVS LBM memory manager. We could have some more 6178c2ecf20Sopenharmony_ci * complicated data structure that allowed reuse of LBM areas 6188c2ecf20Sopenharmony_ci * between planes when they don't overlap on the screen, but 6198c2ecf20Sopenharmony_ci * for now we just allocate globally. 6208c2ecf20Sopenharmony_ci */ 6218c2ecf20Sopenharmony_ci if (!hvs->hvs5) 6228c2ecf20Sopenharmony_ci /* 48k words of 2x12-bit pixels */ 6238c2ecf20Sopenharmony_ci drm_mm_init(&hvs->lbm_mm, 0, 48 * 1024); 6248c2ecf20Sopenharmony_ci else 6258c2ecf20Sopenharmony_ci /* 60k words of 4x12-bit pixels */ 6268c2ecf20Sopenharmony_ci drm_mm_init(&hvs->lbm_mm, 0, 60 * 1024); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci /* Upload filter kernels. We only have the one for now, so we 6298c2ecf20Sopenharmony_ci * keep it around for the lifetime of the driver. 6308c2ecf20Sopenharmony_ci */ 6318c2ecf20Sopenharmony_ci ret = vc4_hvs_upload_linear_kernel(hvs, 6328c2ecf20Sopenharmony_ci &hvs->mitchell_netravali_filter, 6338c2ecf20Sopenharmony_ci mitchell_netravali_1_3_1_3_kernel); 6348c2ecf20Sopenharmony_ci if (ret) 6358c2ecf20Sopenharmony_ci return ret; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci vc4->hvs = hvs; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci reg = HVS_READ(SCALER_DISPECTRL); 6408c2ecf20Sopenharmony_ci reg &= ~SCALER_DISPECTRL_DSP2_MUX_MASK; 6418c2ecf20Sopenharmony_ci HVS_WRITE(SCALER_DISPECTRL, 6428c2ecf20Sopenharmony_ci reg | VC4_SET_FIELD(0, SCALER_DISPECTRL_DSP2_MUX)); 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci reg = HVS_READ(SCALER_DISPCTRL); 6458c2ecf20Sopenharmony_ci reg &= ~SCALER_DISPCTRL_DSP3_MUX_MASK; 6468c2ecf20Sopenharmony_ci HVS_WRITE(SCALER_DISPCTRL, 6478c2ecf20Sopenharmony_ci reg | VC4_SET_FIELD(3, SCALER_DISPCTRL_DSP3_MUX)); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci reg = HVS_READ(SCALER_DISPEOLN); 6508c2ecf20Sopenharmony_ci reg &= ~SCALER_DISPEOLN_DSP4_MUX_MASK; 6518c2ecf20Sopenharmony_ci HVS_WRITE(SCALER_DISPEOLN, 6528c2ecf20Sopenharmony_ci reg | VC4_SET_FIELD(3, SCALER_DISPEOLN_DSP4_MUX)); 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci reg = HVS_READ(SCALER_DISPDITHER); 6558c2ecf20Sopenharmony_ci reg &= ~SCALER_DISPDITHER_DSP5_MUX_MASK; 6568c2ecf20Sopenharmony_ci HVS_WRITE(SCALER_DISPDITHER, 6578c2ecf20Sopenharmony_ci reg | VC4_SET_FIELD(3, SCALER_DISPDITHER_DSP5_MUX)); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci dispctrl = HVS_READ(SCALER_DISPCTRL); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci dispctrl |= SCALER_DISPCTRL_ENABLE; 6628c2ecf20Sopenharmony_ci dispctrl |= SCALER_DISPCTRL_DISPEIRQ(0) | 6638c2ecf20Sopenharmony_ci SCALER_DISPCTRL_DISPEIRQ(1) | 6648c2ecf20Sopenharmony_ci SCALER_DISPCTRL_DISPEIRQ(2); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci dispctrl &= ~(SCALER_DISPCTRL_DMAEIRQ | 6678c2ecf20Sopenharmony_ci SCALER_DISPCTRL_SLVWREIRQ | 6688c2ecf20Sopenharmony_ci SCALER_DISPCTRL_SLVRDEIRQ | 6698c2ecf20Sopenharmony_ci SCALER_DISPCTRL_DSPEIEOF(0) | 6708c2ecf20Sopenharmony_ci SCALER_DISPCTRL_DSPEIEOF(1) | 6718c2ecf20Sopenharmony_ci SCALER_DISPCTRL_DSPEIEOF(2) | 6728c2ecf20Sopenharmony_ci SCALER_DISPCTRL_DSPEIEOLN(0) | 6738c2ecf20Sopenharmony_ci SCALER_DISPCTRL_DSPEIEOLN(1) | 6748c2ecf20Sopenharmony_ci SCALER_DISPCTRL_DSPEIEOLN(2) | 6758c2ecf20Sopenharmony_ci SCALER_DISPCTRL_DSPEISLUR(0) | 6768c2ecf20Sopenharmony_ci SCALER_DISPCTRL_DSPEISLUR(1) | 6778c2ecf20Sopenharmony_ci SCALER_DISPCTRL_DSPEISLUR(2) | 6788c2ecf20Sopenharmony_ci SCALER_DISPCTRL_SCLEIRQ); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci /* Set AXI panic mode. 6818c2ecf20Sopenharmony_ci * VC4 panics when < 2 lines in FIFO. 6828c2ecf20Sopenharmony_ci * VC5 panics when less than 1 line in the FIFO. 6838c2ecf20Sopenharmony_ci */ 6848c2ecf20Sopenharmony_ci dispctrl &= ~(SCALER_DISPCTRL_PANIC0_MASK | 6858c2ecf20Sopenharmony_ci SCALER_DISPCTRL_PANIC1_MASK | 6868c2ecf20Sopenharmony_ci SCALER_DISPCTRL_PANIC2_MASK); 6878c2ecf20Sopenharmony_ci dispctrl |= VC4_SET_FIELD(2, SCALER_DISPCTRL_PANIC0); 6888c2ecf20Sopenharmony_ci dispctrl |= VC4_SET_FIELD(2, SCALER_DISPCTRL_PANIC1); 6898c2ecf20Sopenharmony_ci dispctrl |= VC4_SET_FIELD(2, SCALER_DISPCTRL_PANIC2); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci HVS_WRITE(SCALER_DISPCTRL, dispctrl); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, platform_get_irq(pdev, 0), 6948c2ecf20Sopenharmony_ci vc4_hvs_irq_handler, 0, "vc4 hvs", drm); 6958c2ecf20Sopenharmony_ci if (ret) 6968c2ecf20Sopenharmony_ci return ret; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci vc4_debugfs_add_regset32(drm, "hvs_regs", &hvs->regset); 6998c2ecf20Sopenharmony_ci vc4_debugfs_add_file(drm, "hvs_underrun", vc4_hvs_debugfs_underrun, 7008c2ecf20Sopenharmony_ci NULL); 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci return 0; 7038c2ecf20Sopenharmony_ci} 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_cistatic void vc4_hvs_unbind(struct device *dev, struct device *master, 7068c2ecf20Sopenharmony_ci void *data) 7078c2ecf20Sopenharmony_ci{ 7088c2ecf20Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(master); 7098c2ecf20Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(drm); 7108c2ecf20Sopenharmony_ci struct vc4_hvs *hvs = vc4->hvs; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci if (drm_mm_node_allocated(&vc4->hvs->mitchell_netravali_filter)) 7138c2ecf20Sopenharmony_ci drm_mm_remove_node(&vc4->hvs->mitchell_netravali_filter); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci drm_mm_takedown(&vc4->hvs->dlist_mm); 7168c2ecf20Sopenharmony_ci drm_mm_takedown(&vc4->hvs->lbm_mm); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci clk_disable_unprepare(hvs->core_clk); 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci vc4->hvs = NULL; 7218c2ecf20Sopenharmony_ci} 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_cistatic const struct component_ops vc4_hvs_ops = { 7248c2ecf20Sopenharmony_ci .bind = vc4_hvs_bind, 7258c2ecf20Sopenharmony_ci .unbind = vc4_hvs_unbind, 7268c2ecf20Sopenharmony_ci}; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_cistatic int vc4_hvs_dev_probe(struct platform_device *pdev) 7298c2ecf20Sopenharmony_ci{ 7308c2ecf20Sopenharmony_ci return component_add(&pdev->dev, &vc4_hvs_ops); 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_cistatic int vc4_hvs_dev_remove(struct platform_device *pdev) 7348c2ecf20Sopenharmony_ci{ 7358c2ecf20Sopenharmony_ci component_del(&pdev->dev, &vc4_hvs_ops); 7368c2ecf20Sopenharmony_ci return 0; 7378c2ecf20Sopenharmony_ci} 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_cistatic const struct of_device_id vc4_hvs_dt_match[] = { 7408c2ecf20Sopenharmony_ci { .compatible = "brcm,bcm2711-hvs" }, 7418c2ecf20Sopenharmony_ci { .compatible = "brcm,bcm2835-hvs" }, 7428c2ecf20Sopenharmony_ci {} 7438c2ecf20Sopenharmony_ci}; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_cistruct platform_driver vc4_hvs_driver = { 7468c2ecf20Sopenharmony_ci .probe = vc4_hvs_dev_probe, 7478c2ecf20Sopenharmony_ci .remove = vc4_hvs_dev_remove, 7488c2ecf20Sopenharmony_ci .driver = { 7498c2ecf20Sopenharmony_ci .name = "vc4_hvs", 7508c2ecf20Sopenharmony_ci .of_match_table = vc4_hvs_dt_match, 7518c2ecf20Sopenharmony_ci }, 7528c2ecf20Sopenharmony_ci}; 753