// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2021 MediaTek Inc. */ #include #include #include #include #include #include #include #include //do_gettimeofday() #include "mtk-interconnect.h" #include "camera_pda.h" // --------- define region -------- // #define FOR_DEBUG #define PDA_MMQOS // -------------------------------- #define PDA_DEV_NAME "camera-pda" #define LOG_INF(format, args...) \ pr_info(PDA_DEV_NAME " [%s] " format, __func__, ##args) //define the write register function #define mt_reg_sync_writel(v, a) \ do { \ *(unsigned int *)(a) = (v); \ mb(); /*make sure register access in order */ \ } while (0) #define PDA_WR32(addr, data) mt_reg_sync_writel(data, addr) #define PDA_RD32(addr) ioread32(addr) static unsigned int g_Frame_Width, g_Frame_Height, g_B_N; /******************************************************************************* * Porting Part ******************************************************************************/ #define CAMSYS_NODE_COMPATIBLE "mediatek,mt6886-camsys_mraw" #define PDA_0_RESET_BITMASK (BIT(12) | BIT(13)) #define PDA_1_RESET_BITMASK (BIT(14) | BIT(15)) // clock relate static const char * const clk_names[] = { "camsys_mraw_pda0", "camsys_mraw_pda1", "mraw_larbx", "cam_main_cam2mm0_gals_cg_con", "cam_main_cam2mm1_gals_cg_con", "cam_main_cam_cg_con", }; #define PDA_CLK_NUM ARRAY_SIZE(clk_names) struct PDA_CLK_STRUCT pda_clk[PDA_CLK_NUM]; #ifdef PDA_MMQOS // mmqos relate static const char * const mmqos_names_rdma[] = { "l25_pdai_a0", "l25_pdai_a1" }; #define PDA_MMQOS_RDMA_NUM ARRAY_SIZE(mmqos_names_rdma) struct icc_path *icc_path_pda_rdma[PDA_MMQOS_RDMA_NUM]; static const char * const mmqos_names_rdma_b[] = { "l25_pdai_a2", "l25_pdai_a3", "l25_pdai_a4" }; #define PDA_MMQOS_RDMA_B_NUM ARRAY_SIZE(mmqos_names_rdma_b) struct icc_path *icc_path_pda_rdma_b[PDA_MMQOS_RDMA_B_NUM]; static const char * const mmqos_names_wdma[] = { "l25_pdao_a" }; #define PDA_MMQOS_WDMA_NUM ARRAY_SIZE(mmqos_names_wdma) struct icc_path *icc_path_pda_wdma[PDA_MMQOS_WDMA_NUM]; #endif /******************************************************************************* * Internal function ******************************************************************************/ struct device *init_larb(struct platform_device *pdev, int idx) { struct device_node *node; struct platform_device *larb_pdev; struct device_link *link; /* get larb node from dts */ node = of_parse_phandle(pdev->dev.of_node, "mediatek,larbs", idx); if (!node) { LOG_INF("fail to parse mediatek,larb\n"); return NULL; } larb_pdev = of_find_device_by_node(node); if (WARN_ON(!larb_pdev)) { of_node_put(node); LOG_INF("no larb for idx %d\n", idx); return NULL; } of_node_put(node); link = device_link_add(&pdev->dev, &larb_pdev->dev, DL_FLAG_PM_RUNTIME | DL_FLAG_STATELESS); if (!link) LOG_INF("unable to link smi larb%d\n", idx); LOG_INF("pdev %p idx %d\n", pdev, idx); return &larb_pdev->dev; } /******************************************************************************* * API ******************************************************************************/ #ifdef PDA_MMQOS void pda_mmqos_init(struct device *pdev) { int i = 0; // get interconnect path for MMQOS for (i = 0; i < PDA_MMQOS_RDMA_NUM; ++i) { LOG_INF("rdma index: %d, mmqos name: %s\n", i, mmqos_names_rdma[i]); icc_path_pda_rdma[i] = of_mtk_icc_get(pdev, mmqos_names_rdma[i]); } // get interconnect path for MMQOS for (i = 0; i < PDA_MMQOS_RDMA_B_NUM; ++i) { LOG_INF("rdma b index: %d, mmqos name: %s\n", i, mmqos_names_rdma_b[i]); icc_path_pda_rdma_b[i] = of_mtk_icc_get(pdev, mmqos_names_rdma_b[i]); } // get interconnect path for MMQOS for (i = 0; i < PDA_MMQOS_WDMA_NUM; ++i) { LOG_INF("wdma index: %d, mmqos name: %s\n", i, mmqos_names_wdma[i]); icc_path_pda_wdma[i] = of_mtk_icc_get(pdev, mmqos_names_wdma[i]); } } void pda_mmqos_bw_set(struct _pda_a_reg_t_ *PDA_FrameSetting) { int i = 0; unsigned int Inter_Frame_Size_Width = 0; unsigned int Inter_Frame_Size_Height = 0; unsigned int B_N = 0; unsigned int Mach_ROI_Max_Width = 2048; unsigned int Mach_ROI_Max_Height = 96; unsigned int Mach_Frame_Size_Width = 4096; unsigned int Mach_Frame_Size_Height = 192; unsigned int Freqency = 360; unsigned int FOV = 200; unsigned int ROI_Number = PDA_MAXROI_PER_ROUND; unsigned int Frame_Rate = 30; unsigned int Search_Range = 40; #define Operation_Margin (12 / 10) unsigned int Inter_Frame_Size = 0, Mach_Frame_Size = 0; unsigned int Inter_Frame_Size_FOV = 0, Mach_Frame_Size_FOV = 0; unsigned int Inter_Input_Total_pixel_Itar = 0; unsigned int Inter_Input_Total_pixel_Iref = 0; unsigned int Mach_Input_Total_pixel_Itar = 0; unsigned int Required_Operation_Cycle = 0; unsigned int WDMA_Data = 0, temp = 0, RDMA_Data = 0; unsigned int OperationTime = 0; unsigned int WDMA_PEAK_BW = 0, WDMA_AVG_BW = 0; unsigned int RDMA_PEAK_BW = 0, RDMA_AVG_BW = 0; unsigned int IMAGE_TABLE_RDMA_PEAK_BW = 0; unsigned int IMAGE_TABLE_RDMA_AVG_BW = 0; unsigned int IMAGE_IMAGE_RDMA_PEAK_BW = 0; unsigned int IMAGE_IMAGE_RDMA_AVG_BW = 0; // -------------------------- parameter estimate ------------------------ Inter_Frame_Size_Width = PDA_FrameSetting->PDA_CFG_0.Bits.PDA_WIDTH; Inter_Frame_Size_Height = PDA_FrameSetting->PDA_CFG_0.Bits.PDA_HEIGHT; B_N = PDA_FrameSetting->PDA_CFG_254.Bits.PDA_B_N; if (g_Frame_Width == Inter_Frame_Size_Width && g_Frame_Height == Inter_Frame_Size_Height && g_B_N == B_N) { #ifdef FOR_DEBUG LOG_INF("Frame WIDTH/HEIGHT/B_N no change, no need to set qos\n"); #endif return; } FOV = (B_N > 0) ? 100 : 200; #ifdef FOR_DEBUG LOG_INF("Frame WIDTH/HEIGHT/B_N: %d/%d/%d\n", Inter_Frame_Size_Width, Inter_Frame_Size_Height, B_N); #endif Inter_Frame_Size = Inter_Frame_Size_Width * Inter_Frame_Size_Height; #ifdef FOR_DEBUG LOG_INF("E16 Inter_Frame_Size: %d\n", Inter_Frame_Size); #endif Mach_Frame_Size = Mach_Frame_Size_Width * Mach_Frame_Size_Height; Inter_Frame_Size_FOV = Inter_Frame_Size * FOV / 100; #ifdef FOR_DEBUG LOG_INF("E19 Inter_Frame_Size_FOV: %d\n", Inter_Frame_Size_FOV); #endif Mach_Frame_Size_FOV = Mach_Frame_Size * FOV / 100; Inter_Input_Total_pixel_Itar = Inter_Frame_Size_FOV; Inter_Input_Total_pixel_Iref = Inter_Frame_Size_FOV; Mach_Input_Total_pixel_Itar = (ROI_Number*Search_Range*Mach_ROI_Max_Height) + Mach_Frame_Size_FOV + (1*Mach_ROI_Max_Width); Required_Operation_Cycle = (unsigned int)(Mach_Input_Total_pixel_Itar * Operation_Margin * (Search_Range+1)) / Search_Range + 1; WDMA_Data = OUT_BYTE_PER_ROI*ROI_Number; temp = Inter_Input_Total_pixel_Itar+Inter_Input_Total_pixel_Iref; RDMA_Data = temp*(16+2)/8; #ifdef FOR_DEBUG LOG_INF("E27 RDMA_Data: %d\n", RDMA_Data); #endif OperationTime = Required_Operation_Cycle / Freqency / 1000; // WDMA BW estimate WDMA_PEAK_BW = WDMA_Data / OperationTime; WDMA_AVG_BW = WDMA_Data * Frame_Rate * 133 / 100 / 1000; // RDMA BW estimate RDMA_PEAK_BW = RDMA_Data / OperationTime; RDMA_AVG_BW = RDMA_Data * Frame_Rate / 1000; // Left/Right RDMA BW IMAGE_TABLE_RDMA_PEAK_BW = RDMA_PEAK_BW / 2; IMAGE_TABLE_RDMA_AVG_BW = RDMA_AVG_BW * 133 / 100 / 2; // IMAGE IMAGE SMI port is equal to IMAGE TABLE SMI port * 16/9 IMAGE_IMAGE_RDMA_AVG_BW = IMAGE_TABLE_RDMA_AVG_BW * 16 / 9; // pda is not HRT engine, no need to set HRT bw IMAGE_TABLE_RDMA_PEAK_BW = 0; IMAGE_IMAGE_RDMA_PEAK_BW = 0; WDMA_PEAK_BW = 0; if (B_N > 0) { LOG_INF("RDMA_BW IT,II AVG/PEAK: %d/%d, %d/%d, WDMA_BW AVG/PEAK: %d/%d\n", IMAGE_TABLE_RDMA_AVG_BW, IMAGE_TABLE_RDMA_PEAK_BW, IMAGE_IMAGE_RDMA_AVG_BW, IMAGE_IMAGE_RDMA_PEAK_BW, WDMA_AVG_BW, WDMA_PEAK_BW); } else { LOG_INF("RDMA_BW IT AVG/PEAK: %d/%d, WDMA_BW AVG/PEAK: %d/%d\n", IMAGE_TABLE_RDMA_AVG_BW, IMAGE_TABLE_RDMA_PEAK_BW, WDMA_AVG_BW, WDMA_PEAK_BW); } // MMQOS set bw for (i = 0; i < PDA_MMQOS_RDMA_NUM; ++i) { if (icc_path_pda_rdma[i]) { mtk_icc_set_bw(icc_path_pda_rdma[i], (int)(IMAGE_TABLE_RDMA_AVG_BW), (int)(IMAGE_TABLE_RDMA_PEAK_BW)); } } if (B_N <= 3) { // MMQOS set bw for (i = 0; i < (B_N * 2); ++i) { if (icc_path_pda_rdma_b[i]) { mtk_icc_set_bw(icc_path_pda_rdma_b[i], (int)(IMAGE_IMAGE_RDMA_AVG_BW), (int)(IMAGE_IMAGE_RDMA_PEAK_BW)); } } } else { LOG_INF("B_N out of range, B_N:%d\n", B_N); } for (i = 0; i < PDA_MMQOS_WDMA_NUM; ++i) { if (icc_path_pda_wdma[i]) { mtk_icc_set_bw(icc_path_pda_wdma[i], (int)(WDMA_AVG_BW), (int)(WDMA_PEAK_BW)); } } g_Frame_Width = Inter_Frame_Size_Width; g_Frame_Height = Inter_Frame_Size_Height; g_B_N = B_N; } void pda_mmqos_bw_reset(void) { int i = 0; #ifdef FOR_DEBUG LOG_INF("mmqos reset\n"); #endif // MMQOS reset bw for (i = 0; i < PDA_MMQOS_RDMA_NUM; ++i) { if (icc_path_pda_rdma[i]) mtk_icc_set_bw(icc_path_pda_rdma[i], 0, 0); } for (i = 0; i < PDA_MMQOS_RDMA_B_NUM; ++i) { if (icc_path_pda_rdma_b[i]) mtk_icc_set_bw(icc_path_pda_rdma_b[i], 0, 0); } for (i = 0; i < PDA_MMQOS_WDMA_NUM; ++i) { if (icc_path_pda_wdma[i]) mtk_icc_set_bw(icc_path_pda_wdma[i], 0, 0); } g_Frame_Width = 0; g_Frame_Height = 0; g_B_N = 0; } #endif void pda_init_larb(struct platform_device *pdev) { int larbs, i; struct device *larb; // must porting in dts larbs = of_count_phandle_with_args( pdev->dev.of_node, "mediatek,larbs", NULL); LOG_INF("larb_num:%d\n", larbs); for (i = 0; i < larbs; i++) larb = init_larb(pdev, i); } int pda_devm_clk_get(struct platform_device *pdev) { int i = 0; for (i = 0; i < PDA_CLK_NUM; ++i) { // CCF: Grab clock pointer (struct clk*) LOG_INF("index: %d, clock name: %s\n", i, clk_names[i]); pda_clk[i].CG_PDA_TOP_MUX = devm_clk_get(&pdev->dev, clk_names[i]); if (IS_ERR(pda_clk[i].CG_PDA_TOP_MUX)) { LOG_INF("cannot get %s clock\n", clk_names[i]); return PTR_ERR(pda_clk[i].CG_PDA_TOP_MUX); } } return 0; } void pda_clk_prepare_enable(void) { int ret, i; for (i = 0; i < PDA_CLK_NUM; i++) { ret = clk_prepare_enable(pda_clk[i].CG_PDA_TOP_MUX); if (ret) LOG_INF("cannot enable clock (%s)\n", clk_names[i]); #ifdef FOR_DEBUG LOG_INF("clk_prepare_enable (%s) done", clk_names[i]); #endif } #ifdef FOR_DEBUG LOG_INF("clk_prepare_enable done"); #endif } void pda_clk_disable_unprepare(void) { int i; for (i = 0; i < PDA_CLK_NUM; i++) { clk_disable_unprepare(pda_clk[i].CG_PDA_TOP_MUX); #ifdef FOR_DEBUG LOG_INF("clk_disable_unprepare (%s) done\n", clk_names[i]); #endif } } void __iomem *pda_get_camsys_address(void) { struct device_node *camsys_node; // camsys node camsys_node = of_find_compatible_node(NULL, NULL, CAMSYS_NODE_COMPATIBLE); return of_iomap(camsys_node, 0); } unsigned int GetResetBitMask(int PDA_Index) { unsigned int ret = 0; if (PDA_Index == 0) ret = PDA_0_RESET_BITMASK; else if (PDA_Index == 1) ret = PDA_1_RESET_BITMASK; return ret; }