18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2016 Linaro Ltd. 48c2ecf20Sopenharmony_ci * Copyright 2016 ZTE Corporation. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/clk.h> 88c2ecf20Sopenharmony_ci#include <linux/component.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/of_address.h> 118c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <video/videomode.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 168c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h> 178c2ecf20Sopenharmony_ci#include <drm/drm_fb_cma_helper.h> 188c2ecf20Sopenharmony_ci#include <drm/drm_fb_helper.h> 198c2ecf20Sopenharmony_ci#include <drm/drm_gem_cma_helper.h> 208c2ecf20Sopenharmony_ci#include <drm/drm_of.h> 218c2ecf20Sopenharmony_ci#include <drm/drm_plane_helper.h> 228c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h> 238c2ecf20Sopenharmony_ci#include <drm/drm_vblank.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "zx_common_regs.h" 268c2ecf20Sopenharmony_ci#include "zx_drm_drv.h" 278c2ecf20Sopenharmony_ci#include "zx_plane.h" 288c2ecf20Sopenharmony_ci#include "zx_vou.h" 298c2ecf20Sopenharmony_ci#include "zx_vou_regs.h" 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define GL_NUM 2 328c2ecf20Sopenharmony_ci#define VL_NUM 3 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cienum vou_chn_type { 358c2ecf20Sopenharmony_ci VOU_CHN_MAIN, 368c2ecf20Sopenharmony_ci VOU_CHN_AUX, 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistruct zx_crtc_regs { 408c2ecf20Sopenharmony_ci u32 fir_active; 418c2ecf20Sopenharmony_ci u32 fir_htiming; 428c2ecf20Sopenharmony_ci u32 fir_vtiming; 438c2ecf20Sopenharmony_ci u32 sec_vtiming; 448c2ecf20Sopenharmony_ci u32 timing_shift; 458c2ecf20Sopenharmony_ci u32 timing_pi_shift; 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic const struct zx_crtc_regs main_crtc_regs = { 498c2ecf20Sopenharmony_ci .fir_active = FIR_MAIN_ACTIVE, 508c2ecf20Sopenharmony_ci .fir_htiming = FIR_MAIN_H_TIMING, 518c2ecf20Sopenharmony_ci .fir_vtiming = FIR_MAIN_V_TIMING, 528c2ecf20Sopenharmony_ci .sec_vtiming = SEC_MAIN_V_TIMING, 538c2ecf20Sopenharmony_ci .timing_shift = TIMING_MAIN_SHIFT, 548c2ecf20Sopenharmony_ci .timing_pi_shift = TIMING_MAIN_PI_SHIFT, 558c2ecf20Sopenharmony_ci}; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistatic const struct zx_crtc_regs aux_crtc_regs = { 588c2ecf20Sopenharmony_ci .fir_active = FIR_AUX_ACTIVE, 598c2ecf20Sopenharmony_ci .fir_htiming = FIR_AUX_H_TIMING, 608c2ecf20Sopenharmony_ci .fir_vtiming = FIR_AUX_V_TIMING, 618c2ecf20Sopenharmony_ci .sec_vtiming = SEC_AUX_V_TIMING, 628c2ecf20Sopenharmony_ci .timing_shift = TIMING_AUX_SHIFT, 638c2ecf20Sopenharmony_ci .timing_pi_shift = TIMING_AUX_PI_SHIFT, 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistruct zx_crtc_bits { 678c2ecf20Sopenharmony_ci u32 polarity_mask; 688c2ecf20Sopenharmony_ci u32 polarity_shift; 698c2ecf20Sopenharmony_ci u32 int_frame_mask; 708c2ecf20Sopenharmony_ci u32 tc_enable; 718c2ecf20Sopenharmony_ci u32 sec_vactive_shift; 728c2ecf20Sopenharmony_ci u32 sec_vactive_mask; 738c2ecf20Sopenharmony_ci u32 interlace_select; 748c2ecf20Sopenharmony_ci u32 pi_enable; 758c2ecf20Sopenharmony_ci u32 div_vga_shift; 768c2ecf20Sopenharmony_ci u32 div_pic_shift; 778c2ecf20Sopenharmony_ci u32 div_tvenc_shift; 788c2ecf20Sopenharmony_ci u32 div_hdmi_pnx_shift; 798c2ecf20Sopenharmony_ci u32 div_hdmi_shift; 808c2ecf20Sopenharmony_ci u32 div_inf_shift; 818c2ecf20Sopenharmony_ci u32 div_layer_shift; 828c2ecf20Sopenharmony_ci}; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic const struct zx_crtc_bits main_crtc_bits = { 858c2ecf20Sopenharmony_ci .polarity_mask = MAIN_POL_MASK, 868c2ecf20Sopenharmony_ci .polarity_shift = MAIN_POL_SHIFT, 878c2ecf20Sopenharmony_ci .int_frame_mask = TIMING_INT_MAIN_FRAME, 888c2ecf20Sopenharmony_ci .tc_enable = MAIN_TC_EN, 898c2ecf20Sopenharmony_ci .sec_vactive_shift = SEC_VACT_MAIN_SHIFT, 908c2ecf20Sopenharmony_ci .sec_vactive_mask = SEC_VACT_MAIN_MASK, 918c2ecf20Sopenharmony_ci .interlace_select = MAIN_INTERLACE_SEL, 928c2ecf20Sopenharmony_ci .pi_enable = MAIN_PI_EN, 938c2ecf20Sopenharmony_ci .div_vga_shift = VGA_MAIN_DIV_SHIFT, 948c2ecf20Sopenharmony_ci .div_pic_shift = PIC_MAIN_DIV_SHIFT, 958c2ecf20Sopenharmony_ci .div_tvenc_shift = TVENC_MAIN_DIV_SHIFT, 968c2ecf20Sopenharmony_ci .div_hdmi_pnx_shift = HDMI_MAIN_PNX_DIV_SHIFT, 978c2ecf20Sopenharmony_ci .div_hdmi_shift = HDMI_MAIN_DIV_SHIFT, 988c2ecf20Sopenharmony_ci .div_inf_shift = INF_MAIN_DIV_SHIFT, 998c2ecf20Sopenharmony_ci .div_layer_shift = LAYER_MAIN_DIV_SHIFT, 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic const struct zx_crtc_bits aux_crtc_bits = { 1038c2ecf20Sopenharmony_ci .polarity_mask = AUX_POL_MASK, 1048c2ecf20Sopenharmony_ci .polarity_shift = AUX_POL_SHIFT, 1058c2ecf20Sopenharmony_ci .int_frame_mask = TIMING_INT_AUX_FRAME, 1068c2ecf20Sopenharmony_ci .tc_enable = AUX_TC_EN, 1078c2ecf20Sopenharmony_ci .sec_vactive_shift = SEC_VACT_AUX_SHIFT, 1088c2ecf20Sopenharmony_ci .sec_vactive_mask = SEC_VACT_AUX_MASK, 1098c2ecf20Sopenharmony_ci .interlace_select = AUX_INTERLACE_SEL, 1108c2ecf20Sopenharmony_ci .pi_enable = AUX_PI_EN, 1118c2ecf20Sopenharmony_ci .div_vga_shift = VGA_AUX_DIV_SHIFT, 1128c2ecf20Sopenharmony_ci .div_pic_shift = PIC_AUX_DIV_SHIFT, 1138c2ecf20Sopenharmony_ci .div_tvenc_shift = TVENC_AUX_DIV_SHIFT, 1148c2ecf20Sopenharmony_ci .div_hdmi_pnx_shift = HDMI_AUX_PNX_DIV_SHIFT, 1158c2ecf20Sopenharmony_ci .div_hdmi_shift = HDMI_AUX_DIV_SHIFT, 1168c2ecf20Sopenharmony_ci .div_inf_shift = INF_AUX_DIV_SHIFT, 1178c2ecf20Sopenharmony_ci .div_layer_shift = LAYER_AUX_DIV_SHIFT, 1188c2ecf20Sopenharmony_ci}; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistruct zx_crtc { 1218c2ecf20Sopenharmony_ci struct drm_crtc crtc; 1228c2ecf20Sopenharmony_ci struct drm_plane *primary; 1238c2ecf20Sopenharmony_ci struct zx_vou_hw *vou; 1248c2ecf20Sopenharmony_ci void __iomem *chnreg; 1258c2ecf20Sopenharmony_ci void __iomem *chncsc; 1268c2ecf20Sopenharmony_ci void __iomem *dither; 1278c2ecf20Sopenharmony_ci const struct zx_crtc_regs *regs; 1288c2ecf20Sopenharmony_ci const struct zx_crtc_bits *bits; 1298c2ecf20Sopenharmony_ci enum vou_chn_type chn_type; 1308c2ecf20Sopenharmony_ci struct clk *pixclk; 1318c2ecf20Sopenharmony_ci}; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci#define to_zx_crtc(x) container_of(x, struct zx_crtc, crtc) 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistruct vou_layer_bits { 1368c2ecf20Sopenharmony_ci u32 enable; 1378c2ecf20Sopenharmony_ci u32 chnsel; 1388c2ecf20Sopenharmony_ci u32 clksel; 1398c2ecf20Sopenharmony_ci}; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic const struct vou_layer_bits zx_gl_bits[GL_NUM] = { 1428c2ecf20Sopenharmony_ci { 1438c2ecf20Sopenharmony_ci .enable = OSD_CTRL0_GL0_EN, 1448c2ecf20Sopenharmony_ci .chnsel = OSD_CTRL0_GL0_SEL, 1458c2ecf20Sopenharmony_ci .clksel = VOU_CLK_GL0_SEL, 1468c2ecf20Sopenharmony_ci }, { 1478c2ecf20Sopenharmony_ci .enable = OSD_CTRL0_GL1_EN, 1488c2ecf20Sopenharmony_ci .chnsel = OSD_CTRL0_GL1_SEL, 1498c2ecf20Sopenharmony_ci .clksel = VOU_CLK_GL1_SEL, 1508c2ecf20Sopenharmony_ci }, 1518c2ecf20Sopenharmony_ci}; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic const struct vou_layer_bits zx_vl_bits[VL_NUM] = { 1548c2ecf20Sopenharmony_ci { 1558c2ecf20Sopenharmony_ci .enable = OSD_CTRL0_VL0_EN, 1568c2ecf20Sopenharmony_ci .chnsel = OSD_CTRL0_VL0_SEL, 1578c2ecf20Sopenharmony_ci .clksel = VOU_CLK_VL0_SEL, 1588c2ecf20Sopenharmony_ci }, { 1598c2ecf20Sopenharmony_ci .enable = OSD_CTRL0_VL1_EN, 1608c2ecf20Sopenharmony_ci .chnsel = OSD_CTRL0_VL1_SEL, 1618c2ecf20Sopenharmony_ci .clksel = VOU_CLK_VL1_SEL, 1628c2ecf20Sopenharmony_ci }, { 1638c2ecf20Sopenharmony_ci .enable = OSD_CTRL0_VL2_EN, 1648c2ecf20Sopenharmony_ci .chnsel = OSD_CTRL0_VL2_SEL, 1658c2ecf20Sopenharmony_ci .clksel = VOU_CLK_VL2_SEL, 1668c2ecf20Sopenharmony_ci }, 1678c2ecf20Sopenharmony_ci}; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistruct zx_vou_hw { 1708c2ecf20Sopenharmony_ci struct device *dev; 1718c2ecf20Sopenharmony_ci void __iomem *osd; 1728c2ecf20Sopenharmony_ci void __iomem *timing; 1738c2ecf20Sopenharmony_ci void __iomem *vouctl; 1748c2ecf20Sopenharmony_ci void __iomem *otfppu; 1758c2ecf20Sopenharmony_ci void __iomem *dtrc; 1768c2ecf20Sopenharmony_ci struct clk *axi_clk; 1778c2ecf20Sopenharmony_ci struct clk *ppu_clk; 1788c2ecf20Sopenharmony_ci struct clk *main_clk; 1798c2ecf20Sopenharmony_ci struct clk *aux_clk; 1808c2ecf20Sopenharmony_ci struct zx_crtc *main_crtc; 1818c2ecf20Sopenharmony_ci struct zx_crtc *aux_crtc; 1828c2ecf20Sopenharmony_ci}; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cienum vou_inf_data_sel { 1858c2ecf20Sopenharmony_ci VOU_YUV444 = 0, 1868c2ecf20Sopenharmony_ci VOU_RGB_101010 = 1, 1878c2ecf20Sopenharmony_ci VOU_RGB_888 = 2, 1888c2ecf20Sopenharmony_ci VOU_RGB_666 = 3, 1898c2ecf20Sopenharmony_ci}; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistruct vou_inf { 1928c2ecf20Sopenharmony_ci enum vou_inf_id id; 1938c2ecf20Sopenharmony_ci enum vou_inf_data_sel data_sel; 1948c2ecf20Sopenharmony_ci u32 clocks_en_bits; 1958c2ecf20Sopenharmony_ci u32 clocks_sel_bits; 1968c2ecf20Sopenharmony_ci}; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic struct vou_inf vou_infs[] = { 1998c2ecf20Sopenharmony_ci [VOU_HDMI] = { 2008c2ecf20Sopenharmony_ci .data_sel = VOU_YUV444, 2018c2ecf20Sopenharmony_ci .clocks_en_bits = BIT(24) | BIT(18) | BIT(6), 2028c2ecf20Sopenharmony_ci .clocks_sel_bits = BIT(13) | BIT(2), 2038c2ecf20Sopenharmony_ci }, 2048c2ecf20Sopenharmony_ci [VOU_TV_ENC] = { 2058c2ecf20Sopenharmony_ci .data_sel = VOU_YUV444, 2068c2ecf20Sopenharmony_ci .clocks_en_bits = BIT(15), 2078c2ecf20Sopenharmony_ci .clocks_sel_bits = BIT(11) | BIT(0), 2088c2ecf20Sopenharmony_ci }, 2098c2ecf20Sopenharmony_ci [VOU_VGA] = { 2108c2ecf20Sopenharmony_ci .data_sel = VOU_RGB_888, 2118c2ecf20Sopenharmony_ci .clocks_en_bits = BIT(1), 2128c2ecf20Sopenharmony_ci .clocks_sel_bits = BIT(10), 2138c2ecf20Sopenharmony_ci }, 2148c2ecf20Sopenharmony_ci}; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic inline struct zx_vou_hw *crtc_to_vou(struct drm_crtc *crtc) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci struct zx_crtc *zcrtc = to_zx_crtc(crtc); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci return zcrtc->vou; 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_civoid vou_inf_hdmi_audio_sel(struct drm_crtc *crtc, 2248c2ecf20Sopenharmony_ci enum vou_inf_hdmi_audio aud) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci struct zx_crtc *zcrtc = to_zx_crtc(crtc); 2278c2ecf20Sopenharmony_ci struct zx_vou_hw *vou = zcrtc->vou; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci zx_writel_mask(vou->vouctl + VOU_INF_HDMI_CTRL, VOU_HDMI_AUD_MASK, aud); 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_civoid vou_inf_enable(enum vou_inf_id id, struct drm_crtc *crtc) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci struct zx_crtc *zcrtc = to_zx_crtc(crtc); 2358c2ecf20Sopenharmony_ci struct zx_vou_hw *vou = zcrtc->vou; 2368c2ecf20Sopenharmony_ci struct vou_inf *inf = &vou_infs[id]; 2378c2ecf20Sopenharmony_ci void __iomem *dither = zcrtc->dither; 2388c2ecf20Sopenharmony_ci void __iomem *csc = zcrtc->chncsc; 2398c2ecf20Sopenharmony_ci bool is_main = zcrtc->chn_type == VOU_CHN_MAIN; 2408c2ecf20Sopenharmony_ci u32 data_sel_shift = id << 1; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (inf->data_sel != VOU_YUV444) { 2438c2ecf20Sopenharmony_ci /* Enable channel CSC for RGB output */ 2448c2ecf20Sopenharmony_ci zx_writel_mask(csc + CSC_CTRL0, CSC_COV_MODE_MASK, 2458c2ecf20Sopenharmony_ci CSC_BT709_IMAGE_YCBCR2RGB << CSC_COV_MODE_SHIFT); 2468c2ecf20Sopenharmony_ci zx_writel_mask(csc + CSC_CTRL0, CSC_WORK_ENABLE, 2478c2ecf20Sopenharmony_ci CSC_WORK_ENABLE); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci /* Bypass Dither block for RGB output */ 2508c2ecf20Sopenharmony_ci zx_writel_mask(dither + OSD_DITHER_CTRL0, DITHER_BYSPASS, 2518c2ecf20Sopenharmony_ci DITHER_BYSPASS); 2528c2ecf20Sopenharmony_ci } else { 2538c2ecf20Sopenharmony_ci zx_writel_mask(csc + CSC_CTRL0, CSC_WORK_ENABLE, 0); 2548c2ecf20Sopenharmony_ci zx_writel_mask(dither + OSD_DITHER_CTRL0, DITHER_BYSPASS, 0); 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* Select data format */ 2588c2ecf20Sopenharmony_ci zx_writel_mask(vou->vouctl + VOU_INF_DATA_SEL, 0x3 << data_sel_shift, 2598c2ecf20Sopenharmony_ci inf->data_sel << data_sel_shift); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* Select channel */ 2628c2ecf20Sopenharmony_ci zx_writel_mask(vou->vouctl + VOU_INF_CH_SEL, 0x1 << id, 2638c2ecf20Sopenharmony_ci zcrtc->chn_type << id); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci /* Select interface clocks */ 2668c2ecf20Sopenharmony_ci zx_writel_mask(vou->vouctl + VOU_CLK_SEL, inf->clocks_sel_bits, 2678c2ecf20Sopenharmony_ci is_main ? 0 : inf->clocks_sel_bits); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* Enable interface clocks */ 2708c2ecf20Sopenharmony_ci zx_writel_mask(vou->vouctl + VOU_CLK_EN, inf->clocks_en_bits, 2718c2ecf20Sopenharmony_ci inf->clocks_en_bits); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* Enable the device */ 2748c2ecf20Sopenharmony_ci zx_writel_mask(vou->vouctl + VOU_INF_EN, 1 << id, 1 << id); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_civoid vou_inf_disable(enum vou_inf_id id, struct drm_crtc *crtc) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct zx_vou_hw *vou = crtc_to_vou(crtc); 2808c2ecf20Sopenharmony_ci struct vou_inf *inf = &vou_infs[id]; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci /* Disable the device */ 2838c2ecf20Sopenharmony_ci zx_writel_mask(vou->vouctl + VOU_INF_EN, 1 << id, 0); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* Disable interface clocks */ 2868c2ecf20Sopenharmony_ci zx_writel_mask(vou->vouctl + VOU_CLK_EN, inf->clocks_en_bits, 0); 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_civoid zx_vou_config_dividers(struct drm_crtc *crtc, 2908c2ecf20Sopenharmony_ci struct vou_div_config *configs, int num) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci struct zx_crtc *zcrtc = to_zx_crtc(crtc); 2938c2ecf20Sopenharmony_ci struct zx_vou_hw *vou = zcrtc->vou; 2948c2ecf20Sopenharmony_ci const struct zx_crtc_bits *bits = zcrtc->bits; 2958c2ecf20Sopenharmony_ci int i; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci /* Clear update flag bit */ 2988c2ecf20Sopenharmony_ci zx_writel_mask(vou->vouctl + VOU_DIV_PARA, DIV_PARA_UPDATE, 0); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci for (i = 0; i < num; i++) { 3018c2ecf20Sopenharmony_ci struct vou_div_config *cfg = configs + i; 3028c2ecf20Sopenharmony_ci u32 reg, shift; 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci switch (cfg->id) { 3058c2ecf20Sopenharmony_ci case VOU_DIV_VGA: 3068c2ecf20Sopenharmony_ci reg = VOU_CLK_SEL; 3078c2ecf20Sopenharmony_ci shift = bits->div_vga_shift; 3088c2ecf20Sopenharmony_ci break; 3098c2ecf20Sopenharmony_ci case VOU_DIV_PIC: 3108c2ecf20Sopenharmony_ci reg = VOU_CLK_SEL; 3118c2ecf20Sopenharmony_ci shift = bits->div_pic_shift; 3128c2ecf20Sopenharmony_ci break; 3138c2ecf20Sopenharmony_ci case VOU_DIV_TVENC: 3148c2ecf20Sopenharmony_ci reg = VOU_DIV_PARA; 3158c2ecf20Sopenharmony_ci shift = bits->div_tvenc_shift; 3168c2ecf20Sopenharmony_ci break; 3178c2ecf20Sopenharmony_ci case VOU_DIV_HDMI_PNX: 3188c2ecf20Sopenharmony_ci reg = VOU_DIV_PARA; 3198c2ecf20Sopenharmony_ci shift = bits->div_hdmi_pnx_shift; 3208c2ecf20Sopenharmony_ci break; 3218c2ecf20Sopenharmony_ci case VOU_DIV_HDMI: 3228c2ecf20Sopenharmony_ci reg = VOU_DIV_PARA; 3238c2ecf20Sopenharmony_ci shift = bits->div_hdmi_shift; 3248c2ecf20Sopenharmony_ci break; 3258c2ecf20Sopenharmony_ci case VOU_DIV_INF: 3268c2ecf20Sopenharmony_ci reg = VOU_DIV_PARA; 3278c2ecf20Sopenharmony_ci shift = bits->div_inf_shift; 3288c2ecf20Sopenharmony_ci break; 3298c2ecf20Sopenharmony_ci case VOU_DIV_LAYER: 3308c2ecf20Sopenharmony_ci reg = VOU_DIV_PARA; 3318c2ecf20Sopenharmony_ci shift = bits->div_layer_shift; 3328c2ecf20Sopenharmony_ci break; 3338c2ecf20Sopenharmony_ci default: 3348c2ecf20Sopenharmony_ci continue; 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci /* Each divider occupies 3 bits */ 3388c2ecf20Sopenharmony_ci zx_writel_mask(vou->vouctl + reg, 0x7 << shift, 3398c2ecf20Sopenharmony_ci cfg->val << shift); 3408c2ecf20Sopenharmony_ci } 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* Set update flag bit to get dividers effected */ 3438c2ecf20Sopenharmony_ci zx_writel_mask(vou->vouctl + VOU_DIV_PARA, DIV_PARA_UPDATE, 3448c2ecf20Sopenharmony_ci DIV_PARA_UPDATE); 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic inline void vou_chn_set_update(struct zx_crtc *zcrtc) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci zx_writel(zcrtc->chnreg + CHN_UPDATE, 1); 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic void zx_crtc_atomic_enable(struct drm_crtc *crtc, 3538c2ecf20Sopenharmony_ci struct drm_crtc_state *old_state) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci struct drm_display_mode *mode = &crtc->state->adjusted_mode; 3568c2ecf20Sopenharmony_ci bool interlaced = mode->flags & DRM_MODE_FLAG_INTERLACE; 3578c2ecf20Sopenharmony_ci struct zx_crtc *zcrtc = to_zx_crtc(crtc); 3588c2ecf20Sopenharmony_ci struct zx_vou_hw *vou = zcrtc->vou; 3598c2ecf20Sopenharmony_ci const struct zx_crtc_regs *regs = zcrtc->regs; 3608c2ecf20Sopenharmony_ci const struct zx_crtc_bits *bits = zcrtc->bits; 3618c2ecf20Sopenharmony_ci struct videomode vm; 3628c2ecf20Sopenharmony_ci u32 scan_mask; 3638c2ecf20Sopenharmony_ci u32 pol = 0; 3648c2ecf20Sopenharmony_ci u32 val; 3658c2ecf20Sopenharmony_ci int ret; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci drm_display_mode_to_videomode(mode, &vm); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci /* Set up timing parameters */ 3708c2ecf20Sopenharmony_ci val = V_ACTIVE((interlaced ? vm.vactive / 2 : vm.vactive) - 1); 3718c2ecf20Sopenharmony_ci val |= H_ACTIVE(vm.hactive - 1); 3728c2ecf20Sopenharmony_ci zx_writel(vou->timing + regs->fir_active, val); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci val = SYNC_WIDE(vm.hsync_len - 1); 3758c2ecf20Sopenharmony_ci val |= BACK_PORCH(vm.hback_porch - 1); 3768c2ecf20Sopenharmony_ci val |= FRONT_PORCH(vm.hfront_porch - 1); 3778c2ecf20Sopenharmony_ci zx_writel(vou->timing + regs->fir_htiming, val); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci val = SYNC_WIDE(vm.vsync_len - 1); 3808c2ecf20Sopenharmony_ci val |= BACK_PORCH(vm.vback_porch - 1); 3818c2ecf20Sopenharmony_ci val |= FRONT_PORCH(vm.vfront_porch - 1); 3828c2ecf20Sopenharmony_ci zx_writel(vou->timing + regs->fir_vtiming, val); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (interlaced) { 3858c2ecf20Sopenharmony_ci u32 shift = bits->sec_vactive_shift; 3868c2ecf20Sopenharmony_ci u32 mask = bits->sec_vactive_mask; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci val = zx_readl(vou->timing + SEC_V_ACTIVE); 3898c2ecf20Sopenharmony_ci val &= ~mask; 3908c2ecf20Sopenharmony_ci val |= ((vm.vactive / 2 - 1) << shift) & mask; 3918c2ecf20Sopenharmony_ci zx_writel(vou->timing + SEC_V_ACTIVE, val); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci val = SYNC_WIDE(vm.vsync_len - 1); 3948c2ecf20Sopenharmony_ci /* 3958c2ecf20Sopenharmony_ci * The vback_porch for the second field needs to shift one on 3968c2ecf20Sopenharmony_ci * the value for the first field. 3978c2ecf20Sopenharmony_ci */ 3988c2ecf20Sopenharmony_ci val |= BACK_PORCH(vm.vback_porch); 3998c2ecf20Sopenharmony_ci val |= FRONT_PORCH(vm.vfront_porch - 1); 4008c2ecf20Sopenharmony_ci zx_writel(vou->timing + regs->sec_vtiming, val); 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci /* Set up polarities */ 4048c2ecf20Sopenharmony_ci if (vm.flags & DISPLAY_FLAGS_VSYNC_LOW) 4058c2ecf20Sopenharmony_ci pol |= 1 << POL_VSYNC_SHIFT; 4068c2ecf20Sopenharmony_ci if (vm.flags & DISPLAY_FLAGS_HSYNC_LOW) 4078c2ecf20Sopenharmony_ci pol |= 1 << POL_HSYNC_SHIFT; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci zx_writel_mask(vou->timing + TIMING_CTRL, bits->polarity_mask, 4108c2ecf20Sopenharmony_ci pol << bits->polarity_shift); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci /* Setup SHIFT register by following what ZTE BSP does */ 4138c2ecf20Sopenharmony_ci val = H_SHIFT_VAL; 4148c2ecf20Sopenharmony_ci if (interlaced) 4158c2ecf20Sopenharmony_ci val |= V_SHIFT_VAL << 16; 4168c2ecf20Sopenharmony_ci zx_writel(vou->timing + regs->timing_shift, val); 4178c2ecf20Sopenharmony_ci zx_writel(vou->timing + regs->timing_pi_shift, H_PI_SHIFT_VAL); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci /* Progressive or interlace scan select */ 4208c2ecf20Sopenharmony_ci scan_mask = bits->interlace_select | bits->pi_enable; 4218c2ecf20Sopenharmony_ci zx_writel_mask(vou->timing + SCAN_CTRL, scan_mask, 4228c2ecf20Sopenharmony_ci interlaced ? scan_mask : 0); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci /* Enable TIMING_CTRL */ 4258c2ecf20Sopenharmony_ci zx_writel_mask(vou->timing + TIMING_TC_ENABLE, bits->tc_enable, 4268c2ecf20Sopenharmony_ci bits->tc_enable); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci /* Configure channel screen size */ 4298c2ecf20Sopenharmony_ci zx_writel_mask(zcrtc->chnreg + CHN_CTRL1, CHN_SCREEN_W_MASK, 4308c2ecf20Sopenharmony_ci vm.hactive << CHN_SCREEN_W_SHIFT); 4318c2ecf20Sopenharmony_ci zx_writel_mask(zcrtc->chnreg + CHN_CTRL1, CHN_SCREEN_H_MASK, 4328c2ecf20Sopenharmony_ci vm.vactive << CHN_SCREEN_H_SHIFT); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* Configure channel interlace buffer control */ 4358c2ecf20Sopenharmony_ci zx_writel_mask(zcrtc->chnreg + CHN_INTERLACE_BUF_CTRL, CHN_INTERLACE_EN, 4368c2ecf20Sopenharmony_ci interlaced ? CHN_INTERLACE_EN : 0); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci /* Update channel */ 4398c2ecf20Sopenharmony_ci vou_chn_set_update(zcrtc); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci /* Enable channel */ 4428c2ecf20Sopenharmony_ci zx_writel_mask(zcrtc->chnreg + CHN_CTRL0, CHN_ENABLE, CHN_ENABLE); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci drm_crtc_vblank_on(crtc); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci ret = clk_set_rate(zcrtc->pixclk, mode->clock * 1000); 4478c2ecf20Sopenharmony_ci if (ret) { 4488c2ecf20Sopenharmony_ci DRM_DEV_ERROR(vou->dev, "failed to set pixclk rate: %d\n", ret); 4498c2ecf20Sopenharmony_ci return; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci ret = clk_prepare_enable(zcrtc->pixclk); 4538c2ecf20Sopenharmony_ci if (ret) 4548c2ecf20Sopenharmony_ci DRM_DEV_ERROR(vou->dev, "failed to enable pixclk: %d\n", ret); 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic void zx_crtc_atomic_disable(struct drm_crtc *crtc, 4588c2ecf20Sopenharmony_ci struct drm_crtc_state *old_state) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci struct zx_crtc *zcrtc = to_zx_crtc(crtc); 4618c2ecf20Sopenharmony_ci const struct zx_crtc_bits *bits = zcrtc->bits; 4628c2ecf20Sopenharmony_ci struct zx_vou_hw *vou = zcrtc->vou; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci clk_disable_unprepare(zcrtc->pixclk); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci drm_crtc_vblank_off(crtc); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci /* Disable channel */ 4698c2ecf20Sopenharmony_ci zx_writel_mask(zcrtc->chnreg + CHN_CTRL0, CHN_ENABLE, 0); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci /* Disable TIMING_CTRL */ 4728c2ecf20Sopenharmony_ci zx_writel_mask(vou->timing + TIMING_TC_ENABLE, bits->tc_enable, 0); 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_cistatic void zx_crtc_atomic_flush(struct drm_crtc *crtc, 4768c2ecf20Sopenharmony_ci struct drm_crtc_state *old_state) 4778c2ecf20Sopenharmony_ci{ 4788c2ecf20Sopenharmony_ci struct drm_pending_vblank_event *event = crtc->state->event; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci if (!event) 4818c2ecf20Sopenharmony_ci return; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci crtc->state->event = NULL; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci spin_lock_irq(&crtc->dev->event_lock); 4868c2ecf20Sopenharmony_ci if (drm_crtc_vblank_get(crtc) == 0) 4878c2ecf20Sopenharmony_ci drm_crtc_arm_vblank_event(crtc, event); 4888c2ecf20Sopenharmony_ci else 4898c2ecf20Sopenharmony_ci drm_crtc_send_vblank_event(crtc, event); 4908c2ecf20Sopenharmony_ci spin_unlock_irq(&crtc->dev->event_lock); 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_cistatic const struct drm_crtc_helper_funcs zx_crtc_helper_funcs = { 4948c2ecf20Sopenharmony_ci .atomic_flush = zx_crtc_atomic_flush, 4958c2ecf20Sopenharmony_ci .atomic_enable = zx_crtc_atomic_enable, 4968c2ecf20Sopenharmony_ci .atomic_disable = zx_crtc_atomic_disable, 4978c2ecf20Sopenharmony_ci}; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_cistatic int zx_vou_enable_vblank(struct drm_crtc *crtc) 5008c2ecf20Sopenharmony_ci{ 5018c2ecf20Sopenharmony_ci struct zx_crtc *zcrtc = to_zx_crtc(crtc); 5028c2ecf20Sopenharmony_ci struct zx_vou_hw *vou = crtc_to_vou(crtc); 5038c2ecf20Sopenharmony_ci u32 int_frame_mask = zcrtc->bits->int_frame_mask; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci zx_writel_mask(vou->timing + TIMING_INT_CTRL, int_frame_mask, 5068c2ecf20Sopenharmony_ci int_frame_mask); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci return 0; 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_cistatic void zx_vou_disable_vblank(struct drm_crtc *crtc) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci struct zx_crtc *zcrtc = to_zx_crtc(crtc); 5148c2ecf20Sopenharmony_ci struct zx_vou_hw *vou = crtc_to_vou(crtc); 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci zx_writel_mask(vou->timing + TIMING_INT_CTRL, 5178c2ecf20Sopenharmony_ci zcrtc->bits->int_frame_mask, 0); 5188c2ecf20Sopenharmony_ci} 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_cistatic const struct drm_crtc_funcs zx_crtc_funcs = { 5218c2ecf20Sopenharmony_ci .destroy = drm_crtc_cleanup, 5228c2ecf20Sopenharmony_ci .set_config = drm_atomic_helper_set_config, 5238c2ecf20Sopenharmony_ci .page_flip = drm_atomic_helper_page_flip, 5248c2ecf20Sopenharmony_ci .reset = drm_atomic_helper_crtc_reset, 5258c2ecf20Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, 5268c2ecf20Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, 5278c2ecf20Sopenharmony_ci .enable_vblank = zx_vou_enable_vblank, 5288c2ecf20Sopenharmony_ci .disable_vblank = zx_vou_disable_vblank, 5298c2ecf20Sopenharmony_ci}; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_cistatic int zx_crtc_init(struct drm_device *drm, struct zx_vou_hw *vou, 5328c2ecf20Sopenharmony_ci enum vou_chn_type chn_type) 5338c2ecf20Sopenharmony_ci{ 5348c2ecf20Sopenharmony_ci struct device *dev = vou->dev; 5358c2ecf20Sopenharmony_ci struct zx_plane *zplane; 5368c2ecf20Sopenharmony_ci struct zx_crtc *zcrtc; 5378c2ecf20Sopenharmony_ci int ret; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci zcrtc = devm_kzalloc(dev, sizeof(*zcrtc), GFP_KERNEL); 5408c2ecf20Sopenharmony_ci if (!zcrtc) 5418c2ecf20Sopenharmony_ci return -ENOMEM; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci zcrtc->vou = vou; 5448c2ecf20Sopenharmony_ci zcrtc->chn_type = chn_type; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci zplane = devm_kzalloc(dev, sizeof(*zplane), GFP_KERNEL); 5478c2ecf20Sopenharmony_ci if (!zplane) 5488c2ecf20Sopenharmony_ci return -ENOMEM; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci zplane->dev = dev; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci if (chn_type == VOU_CHN_MAIN) { 5538c2ecf20Sopenharmony_ci zplane->layer = vou->osd + MAIN_GL_OFFSET; 5548c2ecf20Sopenharmony_ci zplane->csc = vou->osd + MAIN_GL_CSC_OFFSET; 5558c2ecf20Sopenharmony_ci zplane->hbsc = vou->osd + MAIN_HBSC_OFFSET; 5568c2ecf20Sopenharmony_ci zplane->rsz = vou->otfppu + MAIN_RSZ_OFFSET; 5578c2ecf20Sopenharmony_ci zplane->bits = &zx_gl_bits[0]; 5588c2ecf20Sopenharmony_ci zcrtc->chnreg = vou->osd + OSD_MAIN_CHN; 5598c2ecf20Sopenharmony_ci zcrtc->chncsc = vou->osd + MAIN_CHN_CSC_OFFSET; 5608c2ecf20Sopenharmony_ci zcrtc->dither = vou->osd + MAIN_DITHER_OFFSET; 5618c2ecf20Sopenharmony_ci zcrtc->regs = &main_crtc_regs; 5628c2ecf20Sopenharmony_ci zcrtc->bits = &main_crtc_bits; 5638c2ecf20Sopenharmony_ci } else { 5648c2ecf20Sopenharmony_ci zplane->layer = vou->osd + AUX_GL_OFFSET; 5658c2ecf20Sopenharmony_ci zplane->csc = vou->osd + AUX_GL_CSC_OFFSET; 5668c2ecf20Sopenharmony_ci zplane->hbsc = vou->osd + AUX_HBSC_OFFSET; 5678c2ecf20Sopenharmony_ci zplane->rsz = vou->otfppu + AUX_RSZ_OFFSET; 5688c2ecf20Sopenharmony_ci zplane->bits = &zx_gl_bits[1]; 5698c2ecf20Sopenharmony_ci zcrtc->chnreg = vou->osd + OSD_AUX_CHN; 5708c2ecf20Sopenharmony_ci zcrtc->chncsc = vou->osd + AUX_CHN_CSC_OFFSET; 5718c2ecf20Sopenharmony_ci zcrtc->dither = vou->osd + AUX_DITHER_OFFSET; 5728c2ecf20Sopenharmony_ci zcrtc->regs = &aux_crtc_regs; 5738c2ecf20Sopenharmony_ci zcrtc->bits = &aux_crtc_bits; 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci zcrtc->pixclk = devm_clk_get(dev, (chn_type == VOU_CHN_MAIN) ? 5778c2ecf20Sopenharmony_ci "main_wclk" : "aux_wclk"); 5788c2ecf20Sopenharmony_ci if (IS_ERR(zcrtc->pixclk)) { 5798c2ecf20Sopenharmony_ci ret = PTR_ERR(zcrtc->pixclk); 5808c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to get pix clk: %d\n", ret); 5818c2ecf20Sopenharmony_ci return ret; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci ret = zx_plane_init(drm, zplane, DRM_PLANE_TYPE_PRIMARY); 5858c2ecf20Sopenharmony_ci if (ret) { 5868c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to init primary plane: %d\n", ret); 5878c2ecf20Sopenharmony_ci return ret; 5888c2ecf20Sopenharmony_ci } 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci zcrtc->primary = &zplane->plane; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci ret = drm_crtc_init_with_planes(drm, &zcrtc->crtc, zcrtc->primary, NULL, 5938c2ecf20Sopenharmony_ci &zx_crtc_funcs, NULL); 5948c2ecf20Sopenharmony_ci if (ret) { 5958c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to init drm crtc: %d\n", ret); 5968c2ecf20Sopenharmony_ci return ret; 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci drm_crtc_helper_add(&zcrtc->crtc, &zx_crtc_helper_funcs); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci if (chn_type == VOU_CHN_MAIN) 6028c2ecf20Sopenharmony_ci vou->main_crtc = zcrtc; 6038c2ecf20Sopenharmony_ci else 6048c2ecf20Sopenharmony_ci vou->aux_crtc = zcrtc; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci return 0; 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_civoid zx_vou_layer_enable(struct drm_plane *plane) 6108c2ecf20Sopenharmony_ci{ 6118c2ecf20Sopenharmony_ci struct zx_crtc *zcrtc = to_zx_crtc(plane->state->crtc); 6128c2ecf20Sopenharmony_ci struct zx_vou_hw *vou = zcrtc->vou; 6138c2ecf20Sopenharmony_ci struct zx_plane *zplane = to_zx_plane(plane); 6148c2ecf20Sopenharmony_ci const struct vou_layer_bits *bits = zplane->bits; 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci if (zcrtc->chn_type == VOU_CHN_MAIN) { 6178c2ecf20Sopenharmony_ci zx_writel_mask(vou->osd + OSD_CTRL0, bits->chnsel, 0); 6188c2ecf20Sopenharmony_ci zx_writel_mask(vou->vouctl + VOU_CLK_SEL, bits->clksel, 0); 6198c2ecf20Sopenharmony_ci } else { 6208c2ecf20Sopenharmony_ci zx_writel_mask(vou->osd + OSD_CTRL0, bits->chnsel, 6218c2ecf20Sopenharmony_ci bits->chnsel); 6228c2ecf20Sopenharmony_ci zx_writel_mask(vou->vouctl + VOU_CLK_SEL, bits->clksel, 6238c2ecf20Sopenharmony_ci bits->clksel); 6248c2ecf20Sopenharmony_ci } 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci zx_writel_mask(vou->osd + OSD_CTRL0, bits->enable, bits->enable); 6278c2ecf20Sopenharmony_ci} 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_civoid zx_vou_layer_disable(struct drm_plane *plane, 6308c2ecf20Sopenharmony_ci struct drm_plane_state *old_state) 6318c2ecf20Sopenharmony_ci{ 6328c2ecf20Sopenharmony_ci struct zx_crtc *zcrtc = to_zx_crtc(old_state->crtc); 6338c2ecf20Sopenharmony_ci struct zx_vou_hw *vou = zcrtc->vou; 6348c2ecf20Sopenharmony_ci struct zx_plane *zplane = to_zx_plane(plane); 6358c2ecf20Sopenharmony_ci const struct vou_layer_bits *bits = zplane->bits; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci zx_writel_mask(vou->osd + OSD_CTRL0, bits->enable, 0); 6388c2ecf20Sopenharmony_ci} 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_cistatic void zx_overlay_init(struct drm_device *drm, struct zx_vou_hw *vou) 6418c2ecf20Sopenharmony_ci{ 6428c2ecf20Sopenharmony_ci struct device *dev = vou->dev; 6438c2ecf20Sopenharmony_ci struct zx_plane *zplane; 6448c2ecf20Sopenharmony_ci int i; 6458c2ecf20Sopenharmony_ci int ret; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci /* 6488c2ecf20Sopenharmony_ci * VL0 has some quirks on scaling support which need special handling. 6498c2ecf20Sopenharmony_ci * Let's leave it out for now. 6508c2ecf20Sopenharmony_ci */ 6518c2ecf20Sopenharmony_ci for (i = 1; i < VL_NUM; i++) { 6528c2ecf20Sopenharmony_ci zplane = devm_kzalloc(dev, sizeof(*zplane), GFP_KERNEL); 6538c2ecf20Sopenharmony_ci if (!zplane) { 6548c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to allocate zplane %d\n", i); 6558c2ecf20Sopenharmony_ci return; 6568c2ecf20Sopenharmony_ci } 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci zplane->layer = vou->osd + OSD_VL_OFFSET(i); 6598c2ecf20Sopenharmony_ci zplane->hbsc = vou->osd + HBSC_VL_OFFSET(i); 6608c2ecf20Sopenharmony_ci zplane->rsz = vou->otfppu + RSZ_VL_OFFSET(i); 6618c2ecf20Sopenharmony_ci zplane->bits = &zx_vl_bits[i]; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci ret = zx_plane_init(drm, zplane, DRM_PLANE_TYPE_OVERLAY); 6648c2ecf20Sopenharmony_ci if (ret) { 6658c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to init overlay %d\n", i); 6668c2ecf20Sopenharmony_ci continue; 6678c2ecf20Sopenharmony_ci } 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci} 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_cistatic inline void zx_osd_int_update(struct zx_crtc *zcrtc) 6728c2ecf20Sopenharmony_ci{ 6738c2ecf20Sopenharmony_ci struct drm_crtc *crtc = &zcrtc->crtc; 6748c2ecf20Sopenharmony_ci struct drm_plane *plane; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci vou_chn_set_update(zcrtc); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci drm_for_each_plane_mask(plane, crtc->dev, crtc->state->plane_mask) 6798c2ecf20Sopenharmony_ci zx_plane_set_update(plane); 6808c2ecf20Sopenharmony_ci} 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_cistatic irqreturn_t vou_irq_handler(int irq, void *dev_id) 6838c2ecf20Sopenharmony_ci{ 6848c2ecf20Sopenharmony_ci struct zx_vou_hw *vou = dev_id; 6858c2ecf20Sopenharmony_ci u32 state; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci /* Handle TIMING_CTRL frame interrupts */ 6888c2ecf20Sopenharmony_ci state = zx_readl(vou->timing + TIMING_INT_STATE); 6898c2ecf20Sopenharmony_ci zx_writel(vou->timing + TIMING_INT_STATE, state); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci if (state & TIMING_INT_MAIN_FRAME) 6928c2ecf20Sopenharmony_ci drm_crtc_handle_vblank(&vou->main_crtc->crtc); 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci if (state & TIMING_INT_AUX_FRAME) 6958c2ecf20Sopenharmony_ci drm_crtc_handle_vblank(&vou->aux_crtc->crtc); 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci /* Handle OSD interrupts */ 6988c2ecf20Sopenharmony_ci state = zx_readl(vou->osd + OSD_INT_STA); 6998c2ecf20Sopenharmony_ci zx_writel(vou->osd + OSD_INT_CLRSTA, state); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci if (state & OSD_INT_MAIN_UPT) 7028c2ecf20Sopenharmony_ci zx_osd_int_update(vou->main_crtc); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci if (state & OSD_INT_AUX_UPT) 7058c2ecf20Sopenharmony_ci zx_osd_int_update(vou->aux_crtc); 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci if (state & OSD_INT_ERROR) 7088c2ecf20Sopenharmony_ci DRM_DEV_ERROR(vou->dev, "OSD ERROR: 0x%08x!\n", state); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci return IRQ_HANDLED; 7118c2ecf20Sopenharmony_ci} 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_cistatic void vou_dtrc_init(struct zx_vou_hw *vou) 7148c2ecf20Sopenharmony_ci{ 7158c2ecf20Sopenharmony_ci /* Clear bit for bypass by ID */ 7168c2ecf20Sopenharmony_ci zx_writel_mask(vou->dtrc + DTRC_DETILE_CTRL, 7178c2ecf20Sopenharmony_ci TILE2RASTESCAN_BYPASS_MODE, 0); 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci /* Select ARIDR mode */ 7208c2ecf20Sopenharmony_ci zx_writel_mask(vou->dtrc + DTRC_DETILE_CTRL, DETILE_ARIDR_MODE_MASK, 7218c2ecf20Sopenharmony_ci DETILE_ARID_IN_ARIDR); 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci /* Bypass decompression for both frames */ 7248c2ecf20Sopenharmony_ci zx_writel_mask(vou->dtrc + DTRC_F0_CTRL, DTRC_DECOMPRESS_BYPASS, 7258c2ecf20Sopenharmony_ci DTRC_DECOMPRESS_BYPASS); 7268c2ecf20Sopenharmony_ci zx_writel_mask(vou->dtrc + DTRC_F1_CTRL, DTRC_DECOMPRESS_BYPASS, 7278c2ecf20Sopenharmony_ci DTRC_DECOMPRESS_BYPASS); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci /* Set up ARID register */ 7308c2ecf20Sopenharmony_ci zx_writel(vou->dtrc + DTRC_ARID, DTRC_ARID3(0xf) | DTRC_ARID2(0xe) | 7318c2ecf20Sopenharmony_ci DTRC_ARID1(0xf) | DTRC_ARID0(0xe)); 7328c2ecf20Sopenharmony_ci} 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_cistatic void vou_hw_init(struct zx_vou_hw *vou) 7358c2ecf20Sopenharmony_ci{ 7368c2ecf20Sopenharmony_ci /* Release reset for all VOU modules */ 7378c2ecf20Sopenharmony_ci zx_writel(vou->vouctl + VOU_SOFT_RST, ~0); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci /* Enable all VOU module clocks */ 7408c2ecf20Sopenharmony_ci zx_writel(vou->vouctl + VOU_CLK_EN, ~0); 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci /* Clear both OSD and TIMING_CTRL interrupt state */ 7438c2ecf20Sopenharmony_ci zx_writel(vou->osd + OSD_INT_CLRSTA, ~0); 7448c2ecf20Sopenharmony_ci zx_writel(vou->timing + TIMING_INT_STATE, ~0); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci /* Enable OSD and TIMING_CTRL interrrupts */ 7478c2ecf20Sopenharmony_ci zx_writel(vou->osd + OSD_INT_MSK, OSD_INT_ENABLE); 7488c2ecf20Sopenharmony_ci zx_writel(vou->timing + TIMING_INT_CTRL, TIMING_INT_ENABLE); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci /* Select GPC as input to gl/vl scaler as a sane default setting */ 7518c2ecf20Sopenharmony_ci zx_writel(vou->otfppu + OTFPPU_RSZ_DATA_SOURCE, 0x2a); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci /* 7548c2ecf20Sopenharmony_ci * Needs to reset channel and layer logic per frame when frame starts 7558c2ecf20Sopenharmony_ci * to get VOU work properly. 7568c2ecf20Sopenharmony_ci */ 7578c2ecf20Sopenharmony_ci zx_writel_mask(vou->osd + OSD_RST_CLR, RST_PER_FRAME, RST_PER_FRAME); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci vou_dtrc_init(vou); 7608c2ecf20Sopenharmony_ci} 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_cistatic int zx_crtc_bind(struct device *dev, struct device *master, void *data) 7638c2ecf20Sopenharmony_ci{ 7648c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 7658c2ecf20Sopenharmony_ci struct drm_device *drm = data; 7668c2ecf20Sopenharmony_ci struct zx_vou_hw *vou; 7678c2ecf20Sopenharmony_ci struct resource *res; 7688c2ecf20Sopenharmony_ci int irq; 7698c2ecf20Sopenharmony_ci int ret; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci vou = devm_kzalloc(dev, sizeof(*vou), GFP_KERNEL); 7728c2ecf20Sopenharmony_ci if (!vou) 7738c2ecf20Sopenharmony_ci return -ENOMEM; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "osd"); 7768c2ecf20Sopenharmony_ci vou->osd = devm_ioremap_resource(dev, res); 7778c2ecf20Sopenharmony_ci if (IS_ERR(vou->osd)) { 7788c2ecf20Sopenharmony_ci ret = PTR_ERR(vou->osd); 7798c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to remap osd region: %d\n", ret); 7808c2ecf20Sopenharmony_ci return ret; 7818c2ecf20Sopenharmony_ci } 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "timing_ctrl"); 7848c2ecf20Sopenharmony_ci vou->timing = devm_ioremap_resource(dev, res); 7858c2ecf20Sopenharmony_ci if (IS_ERR(vou->timing)) { 7868c2ecf20Sopenharmony_ci ret = PTR_ERR(vou->timing); 7878c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to remap timing_ctrl region: %d\n", 7888c2ecf20Sopenharmony_ci ret); 7898c2ecf20Sopenharmony_ci return ret; 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dtrc"); 7938c2ecf20Sopenharmony_ci vou->dtrc = devm_ioremap_resource(dev, res); 7948c2ecf20Sopenharmony_ci if (IS_ERR(vou->dtrc)) { 7958c2ecf20Sopenharmony_ci ret = PTR_ERR(vou->dtrc); 7968c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to remap dtrc region: %d\n", ret); 7978c2ecf20Sopenharmony_ci return ret; 7988c2ecf20Sopenharmony_ci } 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vou_ctrl"); 8018c2ecf20Sopenharmony_ci vou->vouctl = devm_ioremap_resource(dev, res); 8028c2ecf20Sopenharmony_ci if (IS_ERR(vou->vouctl)) { 8038c2ecf20Sopenharmony_ci ret = PTR_ERR(vou->vouctl); 8048c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to remap vou_ctrl region: %d\n", 8058c2ecf20Sopenharmony_ci ret); 8068c2ecf20Sopenharmony_ci return ret; 8078c2ecf20Sopenharmony_ci } 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otfppu"); 8108c2ecf20Sopenharmony_ci vou->otfppu = devm_ioremap_resource(dev, res); 8118c2ecf20Sopenharmony_ci if (IS_ERR(vou->otfppu)) { 8128c2ecf20Sopenharmony_ci ret = PTR_ERR(vou->otfppu); 8138c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to remap otfppu region: %d\n", ret); 8148c2ecf20Sopenharmony_ci return ret; 8158c2ecf20Sopenharmony_ci } 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 8188c2ecf20Sopenharmony_ci if (irq < 0) 8198c2ecf20Sopenharmony_ci return irq; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci vou->axi_clk = devm_clk_get(dev, "aclk"); 8228c2ecf20Sopenharmony_ci if (IS_ERR(vou->axi_clk)) { 8238c2ecf20Sopenharmony_ci ret = PTR_ERR(vou->axi_clk); 8248c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to get axi_clk: %d\n", ret); 8258c2ecf20Sopenharmony_ci return ret; 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci vou->ppu_clk = devm_clk_get(dev, "ppu_wclk"); 8298c2ecf20Sopenharmony_ci if (IS_ERR(vou->ppu_clk)) { 8308c2ecf20Sopenharmony_ci ret = PTR_ERR(vou->ppu_clk); 8318c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to get ppu_clk: %d\n", ret); 8328c2ecf20Sopenharmony_ci return ret; 8338c2ecf20Sopenharmony_ci } 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci ret = clk_prepare_enable(vou->axi_clk); 8368c2ecf20Sopenharmony_ci if (ret) { 8378c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to enable axi_clk: %d\n", ret); 8388c2ecf20Sopenharmony_ci return ret; 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci clk_prepare_enable(vou->ppu_clk); 8428c2ecf20Sopenharmony_ci if (ret) { 8438c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to enable ppu_clk: %d\n", ret); 8448c2ecf20Sopenharmony_ci goto disable_axi_clk; 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci vou->dev = dev; 8488c2ecf20Sopenharmony_ci dev_set_drvdata(dev, vou); 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci vou_hw_init(vou); 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, irq, vou_irq_handler, 0, "zx_vou", vou); 8538c2ecf20Sopenharmony_ci if (ret < 0) { 8548c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to request vou irq: %d\n", ret); 8558c2ecf20Sopenharmony_ci goto disable_ppu_clk; 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci ret = zx_crtc_init(drm, vou, VOU_CHN_MAIN); 8598c2ecf20Sopenharmony_ci if (ret) { 8608c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to init main channel crtc: %d\n", 8618c2ecf20Sopenharmony_ci ret); 8628c2ecf20Sopenharmony_ci goto disable_ppu_clk; 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci ret = zx_crtc_init(drm, vou, VOU_CHN_AUX); 8668c2ecf20Sopenharmony_ci if (ret) { 8678c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to init aux channel crtc: %d\n", 8688c2ecf20Sopenharmony_ci ret); 8698c2ecf20Sopenharmony_ci goto disable_ppu_clk; 8708c2ecf20Sopenharmony_ci } 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci zx_overlay_init(drm, vou); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci return 0; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_cidisable_ppu_clk: 8778c2ecf20Sopenharmony_ci clk_disable_unprepare(vou->ppu_clk); 8788c2ecf20Sopenharmony_cidisable_axi_clk: 8798c2ecf20Sopenharmony_ci clk_disable_unprepare(vou->axi_clk); 8808c2ecf20Sopenharmony_ci return ret; 8818c2ecf20Sopenharmony_ci} 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_cistatic void zx_crtc_unbind(struct device *dev, struct device *master, 8848c2ecf20Sopenharmony_ci void *data) 8858c2ecf20Sopenharmony_ci{ 8868c2ecf20Sopenharmony_ci struct zx_vou_hw *vou = dev_get_drvdata(dev); 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci clk_disable_unprepare(vou->axi_clk); 8898c2ecf20Sopenharmony_ci clk_disable_unprepare(vou->ppu_clk); 8908c2ecf20Sopenharmony_ci} 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_cistatic const struct component_ops zx_crtc_component_ops = { 8938c2ecf20Sopenharmony_ci .bind = zx_crtc_bind, 8948c2ecf20Sopenharmony_ci .unbind = zx_crtc_unbind, 8958c2ecf20Sopenharmony_ci}; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_cistatic int zx_crtc_probe(struct platform_device *pdev) 8988c2ecf20Sopenharmony_ci{ 8998c2ecf20Sopenharmony_ci return component_add(&pdev->dev, &zx_crtc_component_ops); 9008c2ecf20Sopenharmony_ci} 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_cistatic int zx_crtc_remove(struct platform_device *pdev) 9038c2ecf20Sopenharmony_ci{ 9048c2ecf20Sopenharmony_ci component_del(&pdev->dev, &zx_crtc_component_ops); 9058c2ecf20Sopenharmony_ci return 0; 9068c2ecf20Sopenharmony_ci} 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_cistatic const struct of_device_id zx_crtc_of_match[] = { 9098c2ecf20Sopenharmony_ci { .compatible = "zte,zx296718-dpc", }, 9108c2ecf20Sopenharmony_ci { /* end */ }, 9118c2ecf20Sopenharmony_ci}; 9128c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, zx_crtc_of_match); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_cistruct platform_driver zx_crtc_driver = { 9158c2ecf20Sopenharmony_ci .probe = zx_crtc_probe, 9168c2ecf20Sopenharmony_ci .remove = zx_crtc_remove, 9178c2ecf20Sopenharmony_ci .driver = { 9188c2ecf20Sopenharmony_ci .name = "zx-crtc", 9198c2ecf20Sopenharmony_ci .of_match_table = zx_crtc_of_match, 9208c2ecf20Sopenharmony_ci }, 9218c2ecf20Sopenharmony_ci}; 922