// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (c) 2025 Amlogic, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include "meson_venc.h" #include "meson_drm_main.h" #include "vpu-hw/meson_vpu_reg.h" #define DEVICE_NAME "meson_tx_venc" #define MAX_VENC 4 struct regmap { phys_addr_t phys_addr; size_t size; void __iomem *virt_addr; }; struct meson_tx_venc { struct platform_device *pdev; struct regmap *regmap[MAX_VENC]; u32 venc_offset[MAX_VENC]; int num_regmap; }; static struct regmap *regmap_init_mmio(phys_addr_t phys_addr, void __iomem *virt_addr, size_t size) { struct regmap *reg_map; reg_map = kzalloc(sizeof(*reg_map), GFP_KERNEL); if (!reg_map) return NULL; reg_map->phys_addr = phys_addr; reg_map->size = size; reg_map->virt_addr = virt_addr; return reg_map; } static void regmap_write(struct regmap *regmap, unsigned int reg, unsigned int val) { unsigned int reg_offset = reg << 2; void __iomem *p = regmap->virt_addr + reg_offset; writel(val, p); } static void regmap_update_bits(struct regmap *regmap, unsigned int reg, unsigned int mask, unsigned int val) { unsigned int reg_val; unsigned int new_val; unsigned int reg_offset = reg << 2; void __iomem *p = regmap->virt_addr + reg_offset; reg_val = readl(p); new_val = (reg_val & ~mask) | (val & mask); regmap_write(regmap, reg, new_val); } static void regmap_read(struct regmap *regmap, unsigned int reg, unsigned int *val) { unsigned int reg_offset = reg << 2; void __iomem *p = regmap->virt_addr + reg_offset; *val = readl(p); } int _meson_venc_bist_mode_set(struct regmap *regmap, enum venc_type enc_type, enum venc_bist_type bist_type) { /* Both encp and encl have VIDEO_TST_EN, VIDEO_TST_MDSEL, ..., VIDEO_TST_VDCNT_STSET, * only with the offset 0x128 */ u32 encl_offset; u32 reg_mode_adv; u32 reg_rgbin_ctrl; u32 h_active; u32 v_active; u32 h_start; u32 temp; if (!regmap) return -1; pr_info("%s enc_type[%s] bist_type[%d]\n", __func__, enc_type == VENC_ENCL ? "ENCL" : "ENCP", bist_type); encl_offset = (enc_type == VENC_ENCL) ? 0x0128 : 0; reg_mode_adv = (enc_type == VENC_ENCL) ? ENCL_VIDEO_MODE_ADV : ENCP_VIDEO_MODE_ADV; reg_rgbin_ctrl = (enc_type == VENC_ENCL) ? ENCL_VIDEO_RGBIN_CTRL : ENCP_VIDEO_RGBIN_CTRL; if (bist_type == VENC_BIST_PTTN_OFF) { regmap_update_bits(regmap, reg_mode_adv, BIT(3), BIT(3)); regmap_update_bits(regmap, VENC_VIDEO_TST_EN + encl_offset, BIT(0), 0); return 0; } regmap_update_bits(regmap, VENC_VIDEO_TST_EN + encl_offset, GENMASK(2, 0), GENMASK(2, 0)); regmap_update_bits(regmap, reg_mode_adv, BIT(3), 0); if (enc_type == VENC_ENCL) { /* ENCL pure color pattern with RGB mode */ if (bist_type == VENC_BIST_PTTN_BLACK) { regmap_write(regmap, VENC_VIDEO_TST_Y + encl_offset, 0x0); regmap_write(regmap, VENC_VIDEO_TST_CB + encl_offset, 0x0); regmap_write(regmap, VENC_VIDEO_TST_CR + encl_offset, 0x0); regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 0); return 0; } if (bist_type == VENC_BIST_PTTN_WHITE) { regmap_write(regmap, VENC_VIDEO_TST_Y + encl_offset, 0x3ff); regmap_write(regmap, VENC_VIDEO_TST_CB + encl_offset, 0x3ff); regmap_write(regmap, VENC_VIDEO_TST_CR + encl_offset, 0x3ff); regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 0); return 0; } if (bist_type == VENC_BIST_PTTN_RED) { regmap_write(regmap, VENC_VIDEO_TST_Y + encl_offset, 0x3ff); regmap_write(regmap, VENC_VIDEO_TST_CB + encl_offset, 0x0); regmap_write(regmap, VENC_VIDEO_TST_CR + encl_offset, 0x0); regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 0); return 0; } if (bist_type == VENC_BIST_PTTN_GREEN) { regmap_write(regmap, VENC_VIDEO_TST_Y + encl_offset, 0x0); regmap_write(regmap, VENC_VIDEO_TST_CB + encl_offset, 0x3ff); regmap_write(regmap, VENC_VIDEO_TST_CR + encl_offset, 0x0); regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 0); return 0; } if (bist_type == VENC_BIST_PTTN_BLUE) { regmap_write(regmap, VENC_VIDEO_TST_Y + encl_offset, 0x0); regmap_write(regmap, VENC_VIDEO_TST_CB + encl_offset, 0x0); regmap_write(regmap, VENC_VIDEO_TST_CR + encl_offset, 0x3ff); regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 0); return 0; } /* for other patterns */ regmap_read(regmap, ENCL_VIDEO_HAVON_END, &temp); regmap_read(regmap, ENCL_VIDEO_HAVON_BEGIN, &h_active); /* note that need to use accurate h/vactive(1920/1080) instead * of 1919/1079 in gray/cross pattern */ h_active = temp - h_active + 1; regmap_read(regmap, ENCL_VIDEO_VAVON_ELINE, &temp); regmap_read(regmap, ENCL_VIDEO_VAVON_BLINE, &v_active); v_active = temp - v_active + 1; regmap_read(regmap, ENCL_VIDEO_HAVON_BEGIN, &h_start); } else if (enc_type == VENC_ENCP) { /* ENCP pure color pattern with YCC mode */ if (bist_type == VENC_BIST_PTTN_BLACK) { regmap_write(regmap, VENC_VIDEO_TST_Y + encl_offset, 0x0); regmap_write(regmap, VENC_VIDEO_TST_CB + encl_offset, 0x200); regmap_write(regmap, VENC_VIDEO_TST_CR + encl_offset, 0x200); regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 0); return 0; } if (bist_type == VENC_BIST_PTTN_WHITE) { regmap_write(regmap, VENC_VIDEO_TST_Y + encl_offset, 0x3ff); regmap_write(regmap, VENC_VIDEO_TST_CB + encl_offset, 0x200); regmap_write(regmap, VENC_VIDEO_TST_CR + encl_offset, 0x200); regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 0); return 0; } if (bist_type == VENC_BIST_PTTN_RED) { regmap_write(regmap, VENC_VIDEO_TST_Y + encl_offset, 0x200); regmap_write(regmap, VENC_VIDEO_TST_CB + encl_offset, 0x0); regmap_write(regmap, VENC_VIDEO_TST_CR + encl_offset, 0x3ff); regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 0); return 0; } if (bist_type == VENC_BIST_PTTN_GREEN) { regmap_write(regmap, VENC_VIDEO_TST_Y + encl_offset, 0x200); regmap_write(regmap, VENC_VIDEO_TST_CB + encl_offset, 0x0); regmap_write(regmap, VENC_VIDEO_TST_CR + encl_offset, 0x0); regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 0); return 0; } if (bist_type == VENC_BIST_PTTN_BLUE) { regmap_write(regmap, VENC_VIDEO_TST_Y + encl_offset, 0x200); regmap_write(regmap, VENC_VIDEO_TST_CB + encl_offset, 0x3ff); regmap_write(regmap, VENC_VIDEO_TST_CR + encl_offset, 0x0); regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 0); return 0; } /* for other patterns */ regmap_read(regmap, ENCP_DE_H_END, &temp); regmap_read(regmap, ENCP_DE_H_BEGIN, &h_active); h_active = temp - h_active; regmap_read(regmap, ENCP_DE_V_END_EVEN, &temp); regmap_read(regmap, ENCP_DE_V_BEGIN_EVEN, &v_active); v_active = temp - v_active; regmap_read(regmap, ENCP_VIDEO_HAVON_BEGIN, &h_start); } if (bist_type == VENC_BIST_PTTN_LINE) { regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 2); return 0; } if (bist_type == VENC_BIST_PTTN_DOT) { regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 3); return 0; } if (bist_type == VENC_BIST_PTTN_COLORBAR) { regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 1); regmap_write(regmap, VENC_VIDEO_TST_CLRBAR_STRT + encl_offset, h_start - 2); regmap_write(regmap, VENC_VIDEO_TST_CLRBAR_WIDTH + encl_offset, h_active / 8); return 0; } if (bist_type == VENC_BIST_PTTN_CROSSING) { regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 4); regmap_write(regmap, VENC_VIDEO_TST_Y + encl_offset, 0x3ff); /* aspect ratio: 16:9 */ regmap_update_bits(regmap, VENC_VIDEO_TST_CB + encl_offset, GENMASK(5, 0), 16); regmap_update_bits(regmap, VENC_VIDEO_TST_CR + encl_offset, GENMASK(5, 0), 9); regmap_write(regmap, VENC_VIDEO_TST_CLRBAR_WIDTH + encl_offset, h_active); regmap_write(regmap, VENC_VIDEO_TST_CLRBAR_STRT + encl_offset, v_active); /* cross box width: value 0 means default width = 1 */ regmap_write(regmap, VENC_VIDEO_TST_VDCNT_STSET + encl_offset, 0); regmap_update_bits(regmap, reg_rgbin_ctrl, BIT(1), BIT(1)); /* when TST_MDSEL is 4, need to reset BIT(9) */ regmap_update_bits(regmap, VENC_VIDEO_TST_CB + encl_offset, BIT(9), BIT(9)); regmap_update_bits(regmap, VENC_VIDEO_TST_CB + encl_offset, BIT(9), 0); return 0; } if (bist_type == VENC_BIST_PTTN_GRAY) { u32 steps = 32; regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 5); regmap_write(regmap, VENC_VIDEO_TST_Y + encl_offset, 1024 / steps - 1); regmap_update_bits(regmap, VENC_VIDEO_TST_CB + encl_offset, GENMASK(2, 0), 0); regmap_update_bits(regmap, VENC_VIDEO_TST_CR + encl_offset, GENMASK(2, 0), GENMASK(2, 0)); regmap_update_bits(regmap, VENC_VIDEO_TST_CR + encl_offset, GENMASK(9, 3), (h_active / steps - 1) << 3); regmap_write(regmap, VENC_VIDEO_TST_CLRBAR_WIDTH + encl_offset, 0); regmap_write(regmap, VENC_VIDEO_TST_CLRBAR_STRT + encl_offset, h_start - 2); regmap_write(regmap, VENC_VIDEO_TST_VDCNT_STSET + encl_offset, 0); regmap_update_bits(regmap, reg_rgbin_ctrl, BIT(1), BIT(1)); regmap_update_bits(regmap, VENC_VIDEO_TST_CB + encl_offset, BIT(9), 0); return 0; } if (bist_type == VENC_BIST_PTTN_WINDOW) { u32 startx, starty, endx, endy; starty = v_active * 2 / 10; // 2/10 border startx = h_active * 3 / 10; // 3/10 border endy = v_active * 6 / 10; // 4/10 border endx = h_active * 5 / 10; // 5/10 border regmap_write(regmap, VENC_VIDEO_TST_MDSEL + encl_offset, 6); regmap_update_bits(regmap, reg_rgbin_ctrl, BIT(1), BIT(1)); // we shall set BIT7 to enable axis config latch regmap_update_bits(regmap, VENC_VIDEO_TST_EN + encl_offset, BIT(7), BIT(7)); regmap_write(regmap, VENC_VIDEO_TST_VDCNT_STSET + encl_offset, 2); regmap_write(regmap, VENC_VIDEO_TST_CLRBAR_WIDTH + encl_offset, starty); regmap_write(regmap, VENC_VIDEO_TST_CLRBAR_STRT + encl_offset, startx); regmap_write(regmap, VENC_VIDEO_TST_Y + encl_offset, 0x3ff); regmap_write(regmap, VENC_VIDEO_TST_CB + encl_offset, 0x3ff); regmap_write(regmap, VENC_VIDEO_TST_CR + encl_offset, 0x0); regmap_write(regmap, VENC_VIDEO_TST_VDCNT_STSET + encl_offset, 2); regmap_write(regmap, VENC_VIDEO_TST_VDCNT_STSET + encl_offset, 3); regmap_write(regmap, VENC_VIDEO_TST_CLRBAR_WIDTH + encl_offset, endy); regmap_write(regmap, VENC_VIDEO_TST_CLRBAR_STRT + encl_offset, endx); regmap_write(regmap, VENC_VIDEO_TST_Y + encl_offset, 0); regmap_write(regmap, VENC_VIDEO_TST_CB + encl_offset, 0x3ff); regmap_write(regmap, VENC_VIDEO_TST_CR + encl_offset, 0x3ff); regmap_write(regmap, VENC_VIDEO_TST_VDCNT_STSET + encl_offset, 3); regmap_write(regmap, VENC_VIDEO_TST_VDCNT_STSET + encl_offset, 0); regmap_write(regmap, VENC_VIDEO_TST_CLRBAR_WIDTH + encl_offset, h_active); regmap_write(regmap, VENC_VIDEO_TST_CLRBAR_STRT + encl_offset, v_active); } return 0; } static void config_tv_encp_calc(struct regmap *regmap, struct meson_tx_format_para *para, enum venc_bist_type bist_type) { const struct tx_timing *tp = NULL; struct tx_timing timing = {0}; /* adjust to align upsample and video enable */ u32 hsync_st = 5; // hsync start pixel count u32 vsync_st = 1; // vsync start line count // Latency in pixel clock from ENCP_VFIFO2VD request to data ready to HDMI const u32 vfifo2vd_to_hdmi_latency = 2; u32 de_h_begin = 0; u32 de_h_end = 0; u32 de_v_begin = 0; u32 de_v_end = 0; bool y420_mode = 0; int hpara_div = 1; if (para->cs == HDMI_COLORSPACE_YUV420) y420_mode = 1; tp = ¶->timing; timing = *tp; timing.h_total /= hpara_div; timing.h_blank /= hpara_div; timing.h_front /= hpara_div; timing.h_sync /= hpara_div; timing.h_back /= hpara_div; timing.h_active /= hpara_div; de_h_end = tp->h_total - (tp->h_front - hsync_st); de_h_begin = de_h_end - tp->h_active; de_v_end = tp->v_total - (tp->v_front - vsync_st); de_v_begin = de_v_end - tp->v_active; // VENC timing gen is disabled regmap_write(regmap, ENCP_VIDEO_EN, 0); // Enable viu vsync interrupt regmap_write(regmap, VPU_VENC_CTRL, 1); // set DVI/HDMI transfer timing // generate hsync regmap_write(regmap, ENCP_DVI_HSO_BEGIN, hsync_st); regmap_write(regmap, ENCP_DVI_HSO_END, hsync_st + tp->h_sync); // generate vsync regmap_write(regmap, ENCP_DVI_VSO_BLINE_EVN, vsync_st + y420_mode); regmap_write(regmap, ENCP_DVI_VSO_ELINE_EVN, vsync_st + tp->v_sync + y420_mode); regmap_write(regmap, ENCP_DVI_VSO_BEGIN_EVN, hsync_st); regmap_write(regmap, ENCP_DVI_VSO_END_EVN, hsync_st); // generate data valid regmap_write(regmap, ENCP_DE_H_BEGIN, de_h_begin); regmap_write(regmap, ENCP_DE_H_END, de_h_end); regmap_write(regmap, ENCP_DE_V_BEGIN_EVEN, de_v_begin); /* * The counting range is 0 ~ vtotal-1. When v_front_porch <= vsync start, * the vactive_end needs to be processed according to vactive_end % vtotal. */ if (tp->v_front <= vsync_st) regmap_write(regmap, ENCP_DE_V_END_EVEN, de_v_end % tp->v_total); else regmap_write(regmap, ENCP_DE_V_END_EVEN, de_v_end); // set mode // Enable Hsync and equalization pulse switch in center; bit[14] cfg_de_v = 1 regmap_write(regmap, ENCP_VIDEO_MODE, 0x0040 | (1 << 14)); regmap_write(regmap, ENCP_VIDEO_MODE_ADV, 0x18); // Sampling rate: 1 // set active region regmap_write(regmap, ENCP_VIDEO_HAVON_BEGIN, de_h_begin - vfifo2vd_to_hdmi_latency); regmap_write(regmap, ENCP_VIDEO_HAVON_END, de_h_end - vfifo2vd_to_hdmi_latency - 1); regmap_write(regmap, ENCP_VIDEO_VAVON_BLINE, de_v_begin); regmap_write(regmap, ENCP_VIDEO_VAVON_ELINE, de_v_end - 1); //set hsync regmap_write(regmap, ENCP_VIDEO_HSO_BEGIN, hsync_st - vfifo2vd_to_hdmi_latency); regmap_write(regmap, ENCP_VIDEO_HSO_END, hsync_st + tp->h_sync - vfifo2vd_to_hdmi_latency); //set vsync regmap_write(regmap, ENCP_VIDEO_VSO_BEGIN, 0); regmap_write(regmap, ENCP_VIDEO_VSO_END, 0); regmap_write(regmap, ENCP_VIDEO_VSO_BLINE, vsync_st); regmap_write(regmap, ENCP_VIDEO_VSO_ELINE, vsync_st + tp->v_sync); //set vtotal & htotal regmap_write(regmap, ENCP_VIDEO_MAX_PXCNT, tp->h_total - 1); regmap_write(regmap, ENCP_VIDEO_MAX_LNCNT, tp->v_total - 1); _meson_venc_bist_mode_set(regmap, VENC_ENCP, bist_type); regmap_write(regmap, ENCP_VIDEO_EN, 1); pr_info("%s set done\n", __func__); } static void config_tv_encl_calc(struct regmap *regmap, struct meson_tx_format_para *para, enum venc_bist_type bist_type) { const struct tx_timing *tp = NULL; u32 de_h_begin = 0; u32 de_h_end = 0; u32 de_v_begin = 0; u32 de_v_end = 0; tp = ¶->timing; de_h_begin = tp->h_total - tp->h_front - tp->h_active; de_h_end = tp->h_total - tp->h_front - 1; de_v_begin = tp->v_total - tp->v_front - tp->v_active; de_v_end = tp->v_total - tp->v_front - 1; // VENC timing gen is disabled regmap_write(regmap, ENCL_VIDEO_EN, 0); // bit[15] shadow en regmap_write(regmap, ENCL_VIDEO_MODE, 0x8000); // Sampling rate: 1 regmap_write(regmap, ENCL_VIDEO_MODE_ADV, 0x0418); // bypass filter regmap_write(regmap, ENCL_VIDEO_FILT_CTRL, 0x1000); //set vtotal & htotal regmap_write(regmap, ENCL_VIDEO_MAX_PXCNT, tp->h_total - 1); regmap_write(regmap, ENCL_VIDEO_MAX_LNCNT, tp->v_total - 1); regmap_write(regmap, ENCL_VIDEO_HAVON_BEGIN, de_h_begin); regmap_write(regmap, ENCL_VIDEO_HAVON_END, de_h_end); regmap_write(regmap, ENCL_VIDEO_VAVON_BLINE, de_v_begin); regmap_write(regmap, ENCL_VIDEO_VAVON_ELINE, de_v_end); // set hsync regmap_write(regmap, ENCL_VIDEO_HSO_BEGIN, 0x0); regmap_write(regmap, ENCL_VIDEO_HSO_END, tp->h_sync); // set vsync regmap_write(regmap, ENCL_VIDEO_VSO_BEGIN, 0x0); regmap_write(regmap, ENCL_VIDEO_VSO_END, 0x0); regmap_write(regmap, ENCL_VIDEO_VSO_BLINE, 0x0); regmap_write(regmap, ENCL_VIDEO_VSO_ELINE, tp->v_sync - 1); // set inbuf regmap_write(regmap, ENCL_INBUF_CNTL1, (5 << 13) | (tp->h_sync - 1)); regmap_write(regmap, ENCL_INBUF_CNTL0, 0x200); regmap_write(regmap, LCD_RGB_BASE_ADDR, 0x0); regmap_write(regmap, LCD_RGB_COEFF_ADDR, 0x400); regmap_write(regmap, LCD_DITH_CNTL_ADDR, 0x0); //regmap_write(regmap, LCD_POL_CNTL_ADDR); // DE signal regmap_write(regmap, DE_HS_ADDR, de_h_begin); regmap_write(regmap, DE_HE_ADDR, de_h_end); regmap_write(regmap, DE_VS_ADDR, de_v_begin); regmap_write(regmap, DE_VE_ADDR, de_v_end); // Hsync signal regmap_write(regmap, HSYNC_HS_ADDR, 0x0); regmap_write(regmap, HSYNC_HE_ADDR, tp->h_sync); regmap_write(regmap, HSYNC_VS_ADDR, 0x0); regmap_write(regmap, HSYNC_VE_ADDR, tp->v_total - 1); // Vsync signal regmap_write(regmap, VSYNC_HS_ADDR, 0x0); regmap_write(regmap, VSYNC_HE_ADDR, 0x0); regmap_write(regmap, VSYNC_VS_ADDR, 0x0); regmap_write(regmap, VSYNC_VE_ADDR, tp->v_sync - 1); regmap_write(regmap, ENCL_VIDEO_RGBIN_CTRL, 3); _meson_venc_bist_mode_set(regmap, VENC_ENCL, bist_type); regmap_write(regmap, ENCL_VIDEO_EN, 1); regmap_write(regmap, VPU_VENC_CTRL, 2); } int meson_venc_mode_set(struct meson_tx_venc *venc, u32 enc_index, u32 enc_type, enum venc_bist_type bist_type, void *para) { struct meson_tx_format_para *fmt_para = (struct meson_tx_format_para *)para; struct regmap *regmap = venc->regmap[enc_index]; if (enc_type == VENC_ENCP) config_tv_encp_calc(regmap, fmt_para, bist_type); else if (enc_type == VENC_ENCL) config_tv_encl_calc(regmap, fmt_para, bist_type); return 0; } int meson_venc_bist_mode_set(struct meson_tx_venc *venc, u32 enc_index, u32 enc_type, enum venc_bist_type bist_type, void *para) { struct regmap *regmap = venc->regmap[enc_index]; if (enc_type == VENC_ENCP) _meson_venc_bist_mode_set(regmap, VENC_ENCP, bist_type); else if (enc_type == VENC_ENCL) _meson_venc_bist_mode_set(regmap, VENC_ENCL, bist_type); return 0; } int meson_venc_mode_check(struct meson_tx_venc *venc, u32 enc_index, void *para) { return 0; } int meson_venc_mode_disable(struct meson_tx_venc *venc, u32 enc_index, u32 enc_type) { struct regmap *regmap = venc->regmap[enc_index]; u32 venc_en_addr = (enc_type == VENC_ENCP) ? ENCP_VIDEO_EN : ENCL_VIDEO_EN; // VENC timing gen is disabled regmap_write(regmap, venc_en_addr, 0); return 0; } static int meson_tx_venc_probe(struct platform_device *pdev) { int i, ret, num_venc; struct resource *res; void __iomem *base, *venc_base; struct regmap **regmap; u32 *venc_offset; struct meson_tx_venc *tx_venc; struct device *device = &pdev->dev; tx_venc = devm_kmalloc(device, sizeof(*tx_venc), GFP_KERNEL); if (!tx_venc) return -ENOMEM; num_venc = of_property_count_u32_elems(device->of_node, "venc_offset"); if (num_venc < 0) { pr_err("%s Invalid or missing venc_offset\n", __func__); return -EINVAL; } ret = of_property_read_u32_array(device->of_node, "venc_offset", tx_venc->venc_offset, num_venc); if (ret) { pr_err("%s Invalid or missing venc_offset\n", __func__); return -EINVAL; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -EINVAL; base = devm_ioremap(device, res->start, resource_size(res)); if (!base) return -ENOMEM; regmap = tx_venc->regmap; venc_offset = tx_venc->venc_offset; tx_venc->num_regmap = num_venc; for (i = 0; i < tx_venc->num_regmap; i++) { venc_base = base + (venc_offset[i] << 2); regmap[i] = regmap_init_mmio(res->start, venc_base, resource_size(res)); if (IS_ERR(regmap[i])) { pr_err("%s regmap init mmio fail\n", __func__); return -EINVAL; } } dev_set_drvdata(device, tx_venc); pr_info("%s %px\n", __func__, tx_venc); return 0; } static void meson_tx_venc_remove(struct platform_device *pdev) { struct device *device = &pdev->dev; pr_info("%s %s\n", __func__, dev_name(device)); } static const struct of_device_id meson_tx_venc_of_match[] = { { .compatible = "amlogic, tx-venc-t7c", }, { .compatible = "amlogic, tx-venc-a9", }, {}, }; static struct platform_driver meson_tx_venc_driver = { .probe = meson_tx_venc_probe, .remove = meson_tx_venc_remove, .driver = { .name = DEVICE_NAME, .owner = THIS_MODULE, .of_match_table = of_match_ptr(meson_tx_venc_of_match), } }; int __init meson_tx_venc_init(void) { return platform_driver_register(&meson_tx_venc_driver); } void __exit meson_tx_venc_exit(void) { pr_info("%s\n", __func__); platform_driver_unregister(&meson_tx_venc_driver); }