18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * camss-vfe.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. 88c2ecf20Sopenharmony_ci * Copyright (C) 2015-2018 Linaro Ltd. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci#include <linux/clk.h> 118c2ecf20Sopenharmony_ci#include <linux/completion.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/iommu.h> 148c2ecf20Sopenharmony_ci#include <linux/mutex.h> 158c2ecf20Sopenharmony_ci#include <linux/of.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 188c2ecf20Sopenharmony_ci#include <linux/spinlock_types.h> 198c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 208c2ecf20Sopenharmony_ci#include <media/media-entity.h> 218c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 228c2ecf20Sopenharmony_ci#include <media/v4l2-subdev.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "camss-vfe.h" 258c2ecf20Sopenharmony_ci#include "camss.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define MSM_VFE_NAME "msm_vfe" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define vfe_line_array(ptr_line) \ 308c2ecf20Sopenharmony_ci ((const struct vfe_line (*)[]) &(ptr_line[-(ptr_line->id)])) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define to_vfe(ptr_line) \ 338c2ecf20Sopenharmony_ci container_of(vfe_line_array(ptr_line), struct vfe_device, line) 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* VFE reset timeout */ 368c2ecf20Sopenharmony_ci#define VFE_RESET_TIMEOUT_MS 50 378c2ecf20Sopenharmony_ci/* VFE halt timeout */ 388c2ecf20Sopenharmony_ci#define VFE_HALT_TIMEOUT_MS 100 398c2ecf20Sopenharmony_ci/* Max number of frame drop updates per frame */ 408c2ecf20Sopenharmony_ci#define VFE_FRAME_DROP_UPDATES 2 418c2ecf20Sopenharmony_ci/* Frame drop value. VAL + UPDATES - 1 should not exceed 31 */ 428c2ecf20Sopenharmony_ci#define VFE_FRAME_DROP_VAL 30 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define VFE_NEXT_SOF_MS 500 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define SCALER_RATIO_MAX 16 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistruct vfe_format { 498c2ecf20Sopenharmony_ci u32 code; 508c2ecf20Sopenharmony_ci u8 bpp; 518c2ecf20Sopenharmony_ci}; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic const struct vfe_format formats_rdi_8x16[] = { 548c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_UYVY8_2X8, 8 }, 558c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_VYUY8_2X8, 8 }, 568c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YUYV8_2X8, 8 }, 578c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YVYU8_2X8, 8 }, 588c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, 598c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, 608c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, 618c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, 628c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, 638c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, 648c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, 658c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, 668c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, 678c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, 688c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, 698c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, 708c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_Y10_1X10, 10 }, 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic const struct vfe_format formats_pix_8x16[] = { 748c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_UYVY8_2X8, 8 }, 758c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_VYUY8_2X8, 8 }, 768c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YUYV8_2X8, 8 }, 778c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YVYU8_2X8, 8 }, 788c2ecf20Sopenharmony_ci}; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic const struct vfe_format formats_rdi_8x96[] = { 818c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_UYVY8_2X8, 8 }, 828c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_VYUY8_2X8, 8 }, 838c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YUYV8_2X8, 8 }, 848c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YVYU8_2X8, 8 }, 858c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, 868c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, 878c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, 888c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, 898c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, 908c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, 918c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, 928c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, 938c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16 }, 948c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, 958c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, 968c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, 978c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, 988c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR14_1X14, 14 }, 998c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG14_1X14, 14 }, 1008c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG14_1X14, 14 }, 1018c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB14_1X14, 14 }, 1028c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_Y10_1X10, 10 }, 1038c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16 }, 1048c2ecf20Sopenharmony_ci}; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic const struct vfe_format formats_pix_8x96[] = { 1078c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_UYVY8_2X8, 8 }, 1088c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_VYUY8_2X8, 8 }, 1098c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YUYV8_2X8, 8 }, 1108c2ecf20Sopenharmony_ci { MEDIA_BUS_FMT_YVYU8_2X8, 8 }, 1118c2ecf20Sopenharmony_ci}; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/* 1148c2ecf20Sopenharmony_ci * vfe_get_bpp - map media bus format to bits per pixel 1158c2ecf20Sopenharmony_ci * @formats: supported media bus formats array 1168c2ecf20Sopenharmony_ci * @nformats: size of @formats array 1178c2ecf20Sopenharmony_ci * @code: media bus format code 1188c2ecf20Sopenharmony_ci * 1198c2ecf20Sopenharmony_ci * Return number of bits per pixel 1208c2ecf20Sopenharmony_ci */ 1218c2ecf20Sopenharmony_cistatic u8 vfe_get_bpp(const struct vfe_format *formats, 1228c2ecf20Sopenharmony_ci unsigned int nformats, u32 code) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci unsigned int i; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci for (i = 0; i < nformats; i++) 1278c2ecf20Sopenharmony_ci if (code == formats[i].code) 1288c2ecf20Sopenharmony_ci return formats[i].bpp; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci WARN(1, "Unknown format\n"); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci return formats[0].bpp; 1338c2ecf20Sopenharmony_ci} 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_cistatic u32 vfe_find_code(u32 *code, unsigned int n_code, 1368c2ecf20Sopenharmony_ci unsigned int index, u32 req_code) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci int i; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (!req_code && (index >= n_code)) 1418c2ecf20Sopenharmony_ci return 0; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci for (i = 0; i < n_code; i++) 1448c2ecf20Sopenharmony_ci if (req_code) { 1458c2ecf20Sopenharmony_ci if (req_code == code[i]) 1468c2ecf20Sopenharmony_ci return req_code; 1478c2ecf20Sopenharmony_ci } else { 1488c2ecf20Sopenharmony_ci if (i == index) 1498c2ecf20Sopenharmony_ci return code[i]; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return code[0]; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, 1568c2ecf20Sopenharmony_ci unsigned int index, u32 src_req_code) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct vfe_device *vfe = to_vfe(line); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (vfe->camss->version == CAMSS_8x16) 1618c2ecf20Sopenharmony_ci switch (sink_code) { 1628c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_YUYV8_2X8: 1638c2ecf20Sopenharmony_ci { 1648c2ecf20Sopenharmony_ci u32 src_code[] = { 1658c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_YUYV8_2X8, 1668c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_YUYV8_1_5X8, 1678c2ecf20Sopenharmony_ci }; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return vfe_find_code(src_code, ARRAY_SIZE(src_code), 1708c2ecf20Sopenharmony_ci index, src_req_code); 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_YVYU8_2X8: 1738c2ecf20Sopenharmony_ci { 1748c2ecf20Sopenharmony_ci u32 src_code[] = { 1758c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_YVYU8_2X8, 1768c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_YVYU8_1_5X8, 1778c2ecf20Sopenharmony_ci }; 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return vfe_find_code(src_code, ARRAY_SIZE(src_code), 1808c2ecf20Sopenharmony_ci index, src_req_code); 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_UYVY8_2X8: 1838c2ecf20Sopenharmony_ci { 1848c2ecf20Sopenharmony_ci u32 src_code[] = { 1858c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_UYVY8_2X8, 1868c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_UYVY8_1_5X8, 1878c2ecf20Sopenharmony_ci }; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci return vfe_find_code(src_code, ARRAY_SIZE(src_code), 1908c2ecf20Sopenharmony_ci index, src_req_code); 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_VYUY8_2X8: 1938c2ecf20Sopenharmony_ci { 1948c2ecf20Sopenharmony_ci u32 src_code[] = { 1958c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_VYUY8_2X8, 1968c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_VYUY8_1_5X8, 1978c2ecf20Sopenharmony_ci }; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci return vfe_find_code(src_code, ARRAY_SIZE(src_code), 2008c2ecf20Sopenharmony_ci index, src_req_code); 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci default: 2038c2ecf20Sopenharmony_ci if (index > 0) 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci return sink_code; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci else if (vfe->camss->version == CAMSS_8x96) 2098c2ecf20Sopenharmony_ci switch (sink_code) { 2108c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_YUYV8_2X8: 2118c2ecf20Sopenharmony_ci { 2128c2ecf20Sopenharmony_ci u32 src_code[] = { 2138c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_YUYV8_2X8, 2148c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_YVYU8_2X8, 2158c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_UYVY8_2X8, 2168c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_VYUY8_2X8, 2178c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_YUYV8_1_5X8, 2188c2ecf20Sopenharmony_ci }; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci return vfe_find_code(src_code, ARRAY_SIZE(src_code), 2218c2ecf20Sopenharmony_ci index, src_req_code); 2228c2ecf20Sopenharmony_ci } 2238c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_YVYU8_2X8: 2248c2ecf20Sopenharmony_ci { 2258c2ecf20Sopenharmony_ci u32 src_code[] = { 2268c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_YVYU8_2X8, 2278c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_YUYV8_2X8, 2288c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_UYVY8_2X8, 2298c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_VYUY8_2X8, 2308c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_YVYU8_1_5X8, 2318c2ecf20Sopenharmony_ci }; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return vfe_find_code(src_code, ARRAY_SIZE(src_code), 2348c2ecf20Sopenharmony_ci index, src_req_code); 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_UYVY8_2X8: 2378c2ecf20Sopenharmony_ci { 2388c2ecf20Sopenharmony_ci u32 src_code[] = { 2398c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_UYVY8_2X8, 2408c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_YUYV8_2X8, 2418c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_YVYU8_2X8, 2428c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_VYUY8_2X8, 2438c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_UYVY8_1_5X8, 2448c2ecf20Sopenharmony_ci }; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci return vfe_find_code(src_code, ARRAY_SIZE(src_code), 2478c2ecf20Sopenharmony_ci index, src_req_code); 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci case MEDIA_BUS_FMT_VYUY8_2X8: 2508c2ecf20Sopenharmony_ci { 2518c2ecf20Sopenharmony_ci u32 src_code[] = { 2528c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_VYUY8_2X8, 2538c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_YUYV8_2X8, 2548c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_YVYU8_2X8, 2558c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_UYVY8_2X8, 2568c2ecf20Sopenharmony_ci MEDIA_BUS_FMT_VYUY8_1_5X8, 2578c2ecf20Sopenharmony_ci }; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci return vfe_find_code(src_code, ARRAY_SIZE(src_code), 2608c2ecf20Sopenharmony_ci index, src_req_code); 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci default: 2638c2ecf20Sopenharmony_ci if (index > 0) 2648c2ecf20Sopenharmony_ci return 0; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci return sink_code; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci else 2698c2ecf20Sopenharmony_ci return 0; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci/* 2738c2ecf20Sopenharmony_ci * vfe_reset - Trigger reset on VFE module and wait to complete 2748c2ecf20Sopenharmony_ci * @vfe: VFE device 2758c2ecf20Sopenharmony_ci * 2768c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise 2778c2ecf20Sopenharmony_ci */ 2788c2ecf20Sopenharmony_cistatic int vfe_reset(struct vfe_device *vfe) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci unsigned long time; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci reinit_completion(&vfe->reset_complete); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci vfe->ops->global_reset(vfe); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci time = wait_for_completion_timeout(&vfe->reset_complete, 2878c2ecf20Sopenharmony_ci msecs_to_jiffies(VFE_RESET_TIMEOUT_MS)); 2888c2ecf20Sopenharmony_ci if (!time) { 2898c2ecf20Sopenharmony_ci dev_err(vfe->camss->dev, "VFE reset timeout\n"); 2908c2ecf20Sopenharmony_ci return -EIO; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci return 0; 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci/* 2978c2ecf20Sopenharmony_ci * vfe_halt - Trigger halt on VFE module and wait to complete 2988c2ecf20Sopenharmony_ci * @vfe: VFE device 2998c2ecf20Sopenharmony_ci * 3008c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise 3018c2ecf20Sopenharmony_ci */ 3028c2ecf20Sopenharmony_cistatic int vfe_halt(struct vfe_device *vfe) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci unsigned long time; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci reinit_completion(&vfe->halt_complete); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci vfe->ops->halt_request(vfe); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci time = wait_for_completion_timeout(&vfe->halt_complete, 3118c2ecf20Sopenharmony_ci msecs_to_jiffies(VFE_HALT_TIMEOUT_MS)); 3128c2ecf20Sopenharmony_ci if (!time) { 3138c2ecf20Sopenharmony_ci dev_err(vfe->camss->dev, "VFE halt timeout\n"); 3148c2ecf20Sopenharmony_ci return -EIO; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return 0; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic void vfe_init_outputs(struct vfe_device *vfe) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci int i; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vfe->line); i++) { 3258c2ecf20Sopenharmony_ci struct vfe_output *output = &vfe->line[i].output; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci output->state = VFE_OUTPUT_OFF; 3288c2ecf20Sopenharmony_ci output->buf[0] = NULL; 3298c2ecf20Sopenharmony_ci output->buf[1] = NULL; 3308c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&output->pending_bufs); 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_cistatic void vfe_reset_output_maps(struct vfe_device *vfe) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci int i; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++) 3398c2ecf20Sopenharmony_ci vfe->wm_output_map[i] = VFE_LINE_NONE; 3408c2ecf20Sopenharmony_ci} 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_cistatic void vfe_output_init_addrs(struct vfe_device *vfe, 3438c2ecf20Sopenharmony_ci struct vfe_output *output, u8 sync) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci u32 ping_addr; 3468c2ecf20Sopenharmony_ci u32 pong_addr; 3478c2ecf20Sopenharmony_ci unsigned int i; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci output->active_buf = 0; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci for (i = 0; i < output->wm_num; i++) { 3528c2ecf20Sopenharmony_ci if (output->buf[0]) 3538c2ecf20Sopenharmony_ci ping_addr = output->buf[0]->addr[i]; 3548c2ecf20Sopenharmony_ci else 3558c2ecf20Sopenharmony_ci ping_addr = 0; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (output->buf[1]) 3588c2ecf20Sopenharmony_ci pong_addr = output->buf[1]->addr[i]; 3598c2ecf20Sopenharmony_ci else 3608c2ecf20Sopenharmony_ci pong_addr = ping_addr; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci vfe->ops->wm_set_ping_addr(vfe, output->wm_idx[i], ping_addr); 3638c2ecf20Sopenharmony_ci vfe->ops->wm_set_pong_addr(vfe, output->wm_idx[i], pong_addr); 3648c2ecf20Sopenharmony_ci if (sync) 3658c2ecf20Sopenharmony_ci vfe->ops->bus_reload_wm(vfe, output->wm_idx[i]); 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic void vfe_output_update_ping_addr(struct vfe_device *vfe, 3708c2ecf20Sopenharmony_ci struct vfe_output *output, u8 sync) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci u32 addr; 3738c2ecf20Sopenharmony_ci unsigned int i; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci for (i = 0; i < output->wm_num; i++) { 3768c2ecf20Sopenharmony_ci if (output->buf[0]) 3778c2ecf20Sopenharmony_ci addr = output->buf[0]->addr[i]; 3788c2ecf20Sopenharmony_ci else 3798c2ecf20Sopenharmony_ci addr = 0; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci vfe->ops->wm_set_ping_addr(vfe, output->wm_idx[i], addr); 3828c2ecf20Sopenharmony_ci if (sync) 3838c2ecf20Sopenharmony_ci vfe->ops->bus_reload_wm(vfe, output->wm_idx[i]); 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_cistatic void vfe_output_update_pong_addr(struct vfe_device *vfe, 3888c2ecf20Sopenharmony_ci struct vfe_output *output, u8 sync) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci u32 addr; 3918c2ecf20Sopenharmony_ci unsigned int i; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci for (i = 0; i < output->wm_num; i++) { 3948c2ecf20Sopenharmony_ci if (output->buf[1]) 3958c2ecf20Sopenharmony_ci addr = output->buf[1]->addr[i]; 3968c2ecf20Sopenharmony_ci else 3978c2ecf20Sopenharmony_ci addr = 0; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci vfe->ops->wm_set_pong_addr(vfe, output->wm_idx[i], addr); 4008c2ecf20Sopenharmony_ci if (sync) 4018c2ecf20Sopenharmony_ci vfe->ops->bus_reload_wm(vfe, output->wm_idx[i]); 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci} 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_cistatic int vfe_reserve_wm(struct vfe_device *vfe, enum vfe_line_id line_id) 4078c2ecf20Sopenharmony_ci{ 4088c2ecf20Sopenharmony_ci int ret = -EBUSY; 4098c2ecf20Sopenharmony_ci int i; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++) { 4128c2ecf20Sopenharmony_ci if (vfe->wm_output_map[i] == VFE_LINE_NONE) { 4138c2ecf20Sopenharmony_ci vfe->wm_output_map[i] = line_id; 4148c2ecf20Sopenharmony_ci ret = i; 4158c2ecf20Sopenharmony_ci break; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci return ret; 4208c2ecf20Sopenharmony_ci} 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cistatic int vfe_release_wm(struct vfe_device *vfe, u8 wm) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci if (wm >= ARRAY_SIZE(vfe->wm_output_map)) 4258c2ecf20Sopenharmony_ci return -EINVAL; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci vfe->wm_output_map[wm] = VFE_LINE_NONE; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci return 0; 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic void vfe_output_frame_drop(struct vfe_device *vfe, 4338c2ecf20Sopenharmony_ci struct vfe_output *output, 4348c2ecf20Sopenharmony_ci u32 drop_pattern) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci u8 drop_period; 4378c2ecf20Sopenharmony_ci unsigned int i; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci /* We need to toggle update period to be valid on next frame */ 4408c2ecf20Sopenharmony_ci output->drop_update_idx++; 4418c2ecf20Sopenharmony_ci output->drop_update_idx %= VFE_FRAME_DROP_UPDATES; 4428c2ecf20Sopenharmony_ci drop_period = VFE_FRAME_DROP_VAL + output->drop_update_idx; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci for (i = 0; i < output->wm_num; i++) { 4458c2ecf20Sopenharmony_ci vfe->ops->wm_set_framedrop_period(vfe, output->wm_idx[i], 4468c2ecf20Sopenharmony_ci drop_period); 4478c2ecf20Sopenharmony_ci vfe->ops->wm_set_framedrop_pattern(vfe, output->wm_idx[i], 4488c2ecf20Sopenharmony_ci drop_pattern); 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci vfe->ops->reg_update(vfe, 4518c2ecf20Sopenharmony_ci container_of(output, struct vfe_line, output)->id); 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_cistatic struct camss_buffer *vfe_buf_get_pending(struct vfe_output *output) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci struct camss_buffer *buffer = NULL; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (!list_empty(&output->pending_bufs)) { 4598c2ecf20Sopenharmony_ci buffer = list_first_entry(&output->pending_bufs, 4608c2ecf20Sopenharmony_ci struct camss_buffer, 4618c2ecf20Sopenharmony_ci queue); 4628c2ecf20Sopenharmony_ci list_del(&buffer->queue); 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci return buffer; 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci/* 4698c2ecf20Sopenharmony_ci * vfe_buf_add_pending - Add output buffer to list of pending 4708c2ecf20Sopenharmony_ci * @output: VFE output 4718c2ecf20Sopenharmony_ci * @buffer: Video buffer 4728c2ecf20Sopenharmony_ci */ 4738c2ecf20Sopenharmony_cistatic void vfe_buf_add_pending(struct vfe_output *output, 4748c2ecf20Sopenharmony_ci struct camss_buffer *buffer) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&buffer->queue); 4778c2ecf20Sopenharmony_ci list_add_tail(&buffer->queue, &output->pending_bufs); 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci/* 4818c2ecf20Sopenharmony_ci * vfe_buf_flush_pending - Flush all pending buffers. 4828c2ecf20Sopenharmony_ci * @output: VFE output 4838c2ecf20Sopenharmony_ci * @state: vb2 buffer state 4848c2ecf20Sopenharmony_ci */ 4858c2ecf20Sopenharmony_cistatic void vfe_buf_flush_pending(struct vfe_output *output, 4868c2ecf20Sopenharmony_ci enum vb2_buffer_state state) 4878c2ecf20Sopenharmony_ci{ 4888c2ecf20Sopenharmony_ci struct camss_buffer *buf; 4898c2ecf20Sopenharmony_ci struct camss_buffer *t; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci list_for_each_entry_safe(buf, t, &output->pending_bufs, queue) { 4928c2ecf20Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, state); 4938c2ecf20Sopenharmony_ci list_del(&buf->queue); 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci} 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_cistatic void vfe_buf_update_wm_on_next(struct vfe_device *vfe, 4988c2ecf20Sopenharmony_ci struct vfe_output *output) 4998c2ecf20Sopenharmony_ci{ 5008c2ecf20Sopenharmony_ci switch (output->state) { 5018c2ecf20Sopenharmony_ci case VFE_OUTPUT_CONTINUOUS: 5028c2ecf20Sopenharmony_ci vfe_output_frame_drop(vfe, output, 3); 5038c2ecf20Sopenharmony_ci break; 5048c2ecf20Sopenharmony_ci case VFE_OUTPUT_SINGLE: 5058c2ecf20Sopenharmony_ci default: 5068c2ecf20Sopenharmony_ci dev_err_ratelimited(vfe->camss->dev, 5078c2ecf20Sopenharmony_ci "Next buf in wrong state! %d\n", 5088c2ecf20Sopenharmony_ci output->state); 5098c2ecf20Sopenharmony_ci break; 5108c2ecf20Sopenharmony_ci } 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic void vfe_buf_update_wm_on_last(struct vfe_device *vfe, 5148c2ecf20Sopenharmony_ci struct vfe_output *output) 5158c2ecf20Sopenharmony_ci{ 5168c2ecf20Sopenharmony_ci switch (output->state) { 5178c2ecf20Sopenharmony_ci case VFE_OUTPUT_CONTINUOUS: 5188c2ecf20Sopenharmony_ci output->state = VFE_OUTPUT_SINGLE; 5198c2ecf20Sopenharmony_ci vfe_output_frame_drop(vfe, output, 1); 5208c2ecf20Sopenharmony_ci break; 5218c2ecf20Sopenharmony_ci case VFE_OUTPUT_SINGLE: 5228c2ecf20Sopenharmony_ci output->state = VFE_OUTPUT_STOPPING; 5238c2ecf20Sopenharmony_ci vfe_output_frame_drop(vfe, output, 0); 5248c2ecf20Sopenharmony_ci break; 5258c2ecf20Sopenharmony_ci default: 5268c2ecf20Sopenharmony_ci dev_err_ratelimited(vfe->camss->dev, 5278c2ecf20Sopenharmony_ci "Last buff in wrong state! %d\n", 5288c2ecf20Sopenharmony_ci output->state); 5298c2ecf20Sopenharmony_ci break; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_cistatic void vfe_buf_update_wm_on_new(struct vfe_device *vfe, 5348c2ecf20Sopenharmony_ci struct vfe_output *output, 5358c2ecf20Sopenharmony_ci struct camss_buffer *new_buf) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci int inactive_idx; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci switch (output->state) { 5408c2ecf20Sopenharmony_ci case VFE_OUTPUT_SINGLE: 5418c2ecf20Sopenharmony_ci inactive_idx = !output->active_buf; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci if (!output->buf[inactive_idx]) { 5448c2ecf20Sopenharmony_ci output->buf[inactive_idx] = new_buf; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci if (inactive_idx) 5478c2ecf20Sopenharmony_ci vfe_output_update_pong_addr(vfe, output, 0); 5488c2ecf20Sopenharmony_ci else 5498c2ecf20Sopenharmony_ci vfe_output_update_ping_addr(vfe, output, 0); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci vfe_output_frame_drop(vfe, output, 3); 5528c2ecf20Sopenharmony_ci output->state = VFE_OUTPUT_CONTINUOUS; 5538c2ecf20Sopenharmony_ci } else { 5548c2ecf20Sopenharmony_ci vfe_buf_add_pending(output, new_buf); 5558c2ecf20Sopenharmony_ci dev_err_ratelimited(vfe->camss->dev, 5568c2ecf20Sopenharmony_ci "Inactive buffer is busy\n"); 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci break; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci case VFE_OUTPUT_IDLE: 5618c2ecf20Sopenharmony_ci if (!output->buf[0]) { 5628c2ecf20Sopenharmony_ci output->buf[0] = new_buf; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci vfe_output_init_addrs(vfe, output, 1); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci vfe_output_frame_drop(vfe, output, 1); 5678c2ecf20Sopenharmony_ci output->state = VFE_OUTPUT_SINGLE; 5688c2ecf20Sopenharmony_ci } else { 5698c2ecf20Sopenharmony_ci vfe_buf_add_pending(output, new_buf); 5708c2ecf20Sopenharmony_ci dev_err_ratelimited(vfe->camss->dev, 5718c2ecf20Sopenharmony_ci "Output idle with buffer set!\n"); 5728c2ecf20Sopenharmony_ci } 5738c2ecf20Sopenharmony_ci break; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci case VFE_OUTPUT_CONTINUOUS: 5768c2ecf20Sopenharmony_ci default: 5778c2ecf20Sopenharmony_ci vfe_buf_add_pending(output, new_buf); 5788c2ecf20Sopenharmony_ci break; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci} 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_cistatic int vfe_get_output(struct vfe_line *line) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci struct vfe_device *vfe = to_vfe(line); 5858c2ecf20Sopenharmony_ci struct vfe_output *output; 5868c2ecf20Sopenharmony_ci struct v4l2_format *f = &line->video_out.active_fmt; 5878c2ecf20Sopenharmony_ci unsigned long flags; 5888c2ecf20Sopenharmony_ci int i; 5898c2ecf20Sopenharmony_ci int wm_idx; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci spin_lock_irqsave(&vfe->output_lock, flags); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci output = &line->output; 5948c2ecf20Sopenharmony_ci if (output->state != VFE_OUTPUT_OFF) { 5958c2ecf20Sopenharmony_ci dev_err(vfe->camss->dev, "Output is running\n"); 5968c2ecf20Sopenharmony_ci goto error; 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci output->state = VFE_OUTPUT_RESERVED; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci output->active_buf = 0; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci switch (f->fmt.pix_mp.pixelformat) { 6038c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV12: 6048c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV21: 6058c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV16: 6068c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV61: 6078c2ecf20Sopenharmony_ci output->wm_num = 2; 6088c2ecf20Sopenharmony_ci break; 6098c2ecf20Sopenharmony_ci default: 6108c2ecf20Sopenharmony_ci output->wm_num = 1; 6118c2ecf20Sopenharmony_ci break; 6128c2ecf20Sopenharmony_ci } 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci for (i = 0; i < output->wm_num; i++) { 6158c2ecf20Sopenharmony_ci wm_idx = vfe_reserve_wm(vfe, line->id); 6168c2ecf20Sopenharmony_ci if (wm_idx < 0) { 6178c2ecf20Sopenharmony_ci dev_err(vfe->camss->dev, "Can not reserve wm\n"); 6188c2ecf20Sopenharmony_ci goto error_get_wm; 6198c2ecf20Sopenharmony_ci } 6208c2ecf20Sopenharmony_ci output->wm_idx[i] = wm_idx; 6218c2ecf20Sopenharmony_ci } 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci output->drop_update_idx = 0; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vfe->output_lock, flags); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci return 0; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_cierror_get_wm: 6308c2ecf20Sopenharmony_ci for (i--; i >= 0; i--) 6318c2ecf20Sopenharmony_ci vfe_release_wm(vfe, output->wm_idx[i]); 6328c2ecf20Sopenharmony_ci output->state = VFE_OUTPUT_OFF; 6338c2ecf20Sopenharmony_cierror: 6348c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vfe->output_lock, flags); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci return -EINVAL; 6378c2ecf20Sopenharmony_ci} 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_cistatic int vfe_put_output(struct vfe_line *line) 6408c2ecf20Sopenharmony_ci{ 6418c2ecf20Sopenharmony_ci struct vfe_device *vfe = to_vfe(line); 6428c2ecf20Sopenharmony_ci struct vfe_output *output = &line->output; 6438c2ecf20Sopenharmony_ci unsigned long flags; 6448c2ecf20Sopenharmony_ci unsigned int i; 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci spin_lock_irqsave(&vfe->output_lock, flags); 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci for (i = 0; i < output->wm_num; i++) 6498c2ecf20Sopenharmony_ci vfe_release_wm(vfe, output->wm_idx[i]); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci output->state = VFE_OUTPUT_OFF; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vfe->output_lock, flags); 6548c2ecf20Sopenharmony_ci return 0; 6558c2ecf20Sopenharmony_ci} 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_cistatic int vfe_enable_output(struct vfe_line *line) 6588c2ecf20Sopenharmony_ci{ 6598c2ecf20Sopenharmony_ci struct vfe_device *vfe = to_vfe(line); 6608c2ecf20Sopenharmony_ci struct vfe_output *output = &line->output; 6618c2ecf20Sopenharmony_ci const struct vfe_hw_ops *ops = vfe->ops; 6628c2ecf20Sopenharmony_ci struct media_entity *sensor; 6638c2ecf20Sopenharmony_ci unsigned long flags; 6648c2ecf20Sopenharmony_ci unsigned int frame_skip = 0; 6658c2ecf20Sopenharmony_ci unsigned int i; 6668c2ecf20Sopenharmony_ci u16 ub_size; 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci ub_size = ops->get_ub_size(vfe->id); 6698c2ecf20Sopenharmony_ci if (!ub_size) 6708c2ecf20Sopenharmony_ci return -EINVAL; 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci sensor = camss_find_sensor(&line->subdev.entity); 6738c2ecf20Sopenharmony_ci if (sensor) { 6748c2ecf20Sopenharmony_ci struct v4l2_subdev *subdev = 6758c2ecf20Sopenharmony_ci media_entity_to_v4l2_subdev(sensor); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci v4l2_subdev_call(subdev, sensor, g_skip_frames, &frame_skip); 6788c2ecf20Sopenharmony_ci /* Max frame skip is 29 frames */ 6798c2ecf20Sopenharmony_ci if (frame_skip > VFE_FRAME_DROP_VAL - 1) 6808c2ecf20Sopenharmony_ci frame_skip = VFE_FRAME_DROP_VAL - 1; 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci spin_lock_irqsave(&vfe->output_lock, flags); 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci ops->reg_update_clear(vfe, line->id); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci if (output->state != VFE_OUTPUT_RESERVED) { 6888c2ecf20Sopenharmony_ci dev_err(vfe->camss->dev, "Output is not in reserved state %d\n", 6898c2ecf20Sopenharmony_ci output->state); 6908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vfe->output_lock, flags); 6918c2ecf20Sopenharmony_ci return -EINVAL; 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci output->state = VFE_OUTPUT_IDLE; 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci output->buf[0] = vfe_buf_get_pending(output); 6968c2ecf20Sopenharmony_ci output->buf[1] = vfe_buf_get_pending(output); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci if (!output->buf[0] && output->buf[1]) { 6998c2ecf20Sopenharmony_ci output->buf[0] = output->buf[1]; 7008c2ecf20Sopenharmony_ci output->buf[1] = NULL; 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci if (output->buf[0]) 7048c2ecf20Sopenharmony_ci output->state = VFE_OUTPUT_SINGLE; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci if (output->buf[1]) 7078c2ecf20Sopenharmony_ci output->state = VFE_OUTPUT_CONTINUOUS; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci switch (output->state) { 7108c2ecf20Sopenharmony_ci case VFE_OUTPUT_SINGLE: 7118c2ecf20Sopenharmony_ci vfe_output_frame_drop(vfe, output, 1 << frame_skip); 7128c2ecf20Sopenharmony_ci break; 7138c2ecf20Sopenharmony_ci case VFE_OUTPUT_CONTINUOUS: 7148c2ecf20Sopenharmony_ci vfe_output_frame_drop(vfe, output, 3 << frame_skip); 7158c2ecf20Sopenharmony_ci break; 7168c2ecf20Sopenharmony_ci default: 7178c2ecf20Sopenharmony_ci vfe_output_frame_drop(vfe, output, 0); 7188c2ecf20Sopenharmony_ci break; 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci output->sequence = 0; 7228c2ecf20Sopenharmony_ci output->wait_sof = 0; 7238c2ecf20Sopenharmony_ci output->wait_reg_update = 0; 7248c2ecf20Sopenharmony_ci reinit_completion(&output->sof); 7258c2ecf20Sopenharmony_ci reinit_completion(&output->reg_update); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci vfe_output_init_addrs(vfe, output, 0); 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci if (line->id != VFE_LINE_PIX) { 7308c2ecf20Sopenharmony_ci ops->set_cgc_override(vfe, output->wm_idx[0], 1); 7318c2ecf20Sopenharmony_ci ops->enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 1); 7328c2ecf20Sopenharmony_ci ops->bus_connect_wm_to_rdi(vfe, output->wm_idx[0], line->id); 7338c2ecf20Sopenharmony_ci ops->wm_set_subsample(vfe, output->wm_idx[0]); 7348c2ecf20Sopenharmony_ci ops->set_rdi_cid(vfe, line->id, 0); 7358c2ecf20Sopenharmony_ci ops->wm_set_ub_cfg(vfe, output->wm_idx[0], 7368c2ecf20Sopenharmony_ci (ub_size + 1) * output->wm_idx[0], ub_size); 7378c2ecf20Sopenharmony_ci ops->wm_frame_based(vfe, output->wm_idx[0], 1); 7388c2ecf20Sopenharmony_ci ops->wm_enable(vfe, output->wm_idx[0], 1); 7398c2ecf20Sopenharmony_ci ops->bus_reload_wm(vfe, output->wm_idx[0]); 7408c2ecf20Sopenharmony_ci } else { 7418c2ecf20Sopenharmony_ci ub_size /= output->wm_num; 7428c2ecf20Sopenharmony_ci for (i = 0; i < output->wm_num; i++) { 7438c2ecf20Sopenharmony_ci ops->set_cgc_override(vfe, output->wm_idx[i], 1); 7448c2ecf20Sopenharmony_ci ops->wm_set_subsample(vfe, output->wm_idx[i]); 7458c2ecf20Sopenharmony_ci ops->wm_set_ub_cfg(vfe, output->wm_idx[i], 7468c2ecf20Sopenharmony_ci (ub_size + 1) * output->wm_idx[i], 7478c2ecf20Sopenharmony_ci ub_size); 7488c2ecf20Sopenharmony_ci ops->wm_line_based(vfe, output->wm_idx[i], 7498c2ecf20Sopenharmony_ci &line->video_out.active_fmt.fmt.pix_mp, 7508c2ecf20Sopenharmony_ci i, 1); 7518c2ecf20Sopenharmony_ci ops->wm_enable(vfe, output->wm_idx[i], 1); 7528c2ecf20Sopenharmony_ci ops->bus_reload_wm(vfe, output->wm_idx[i]); 7538c2ecf20Sopenharmony_ci } 7548c2ecf20Sopenharmony_ci ops->enable_irq_pix_line(vfe, 0, line->id, 1); 7558c2ecf20Sopenharmony_ci ops->set_module_cfg(vfe, 1); 7568c2ecf20Sopenharmony_ci ops->set_camif_cfg(vfe, line); 7578c2ecf20Sopenharmony_ci ops->set_realign_cfg(vfe, line, 1); 7588c2ecf20Sopenharmony_ci ops->set_xbar_cfg(vfe, output, 1); 7598c2ecf20Sopenharmony_ci ops->set_demux_cfg(vfe, line); 7608c2ecf20Sopenharmony_ci ops->set_scale_cfg(vfe, line); 7618c2ecf20Sopenharmony_ci ops->set_crop_cfg(vfe, line); 7628c2ecf20Sopenharmony_ci ops->set_clamp_cfg(vfe); 7638c2ecf20Sopenharmony_ci ops->set_camif_cmd(vfe, 1); 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci ops->reg_update(vfe, line->id); 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vfe->output_lock, flags); 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci return 0; 7718c2ecf20Sopenharmony_ci} 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_cistatic int vfe_disable_output(struct vfe_line *line) 7748c2ecf20Sopenharmony_ci{ 7758c2ecf20Sopenharmony_ci struct vfe_device *vfe = to_vfe(line); 7768c2ecf20Sopenharmony_ci struct vfe_output *output = &line->output; 7778c2ecf20Sopenharmony_ci const struct vfe_hw_ops *ops = vfe->ops; 7788c2ecf20Sopenharmony_ci unsigned long flags; 7798c2ecf20Sopenharmony_ci unsigned long time; 7808c2ecf20Sopenharmony_ci unsigned int i; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci spin_lock_irqsave(&vfe->output_lock, flags); 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci output->wait_sof = 1; 7858c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vfe->output_lock, flags); 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci time = wait_for_completion_timeout(&output->sof, 7888c2ecf20Sopenharmony_ci msecs_to_jiffies(VFE_NEXT_SOF_MS)); 7898c2ecf20Sopenharmony_ci if (!time) 7908c2ecf20Sopenharmony_ci dev_err(vfe->camss->dev, "VFE sof timeout\n"); 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci spin_lock_irqsave(&vfe->output_lock, flags); 7938c2ecf20Sopenharmony_ci for (i = 0; i < output->wm_num; i++) 7948c2ecf20Sopenharmony_ci ops->wm_enable(vfe, output->wm_idx[i], 0); 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci ops->reg_update(vfe, line->id); 7978c2ecf20Sopenharmony_ci output->wait_reg_update = 1; 7988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vfe->output_lock, flags); 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci time = wait_for_completion_timeout(&output->reg_update, 8018c2ecf20Sopenharmony_ci msecs_to_jiffies(VFE_NEXT_SOF_MS)); 8028c2ecf20Sopenharmony_ci if (!time) 8038c2ecf20Sopenharmony_ci dev_err(vfe->camss->dev, "VFE reg update timeout\n"); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci spin_lock_irqsave(&vfe->output_lock, flags); 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci if (line->id != VFE_LINE_PIX) { 8088c2ecf20Sopenharmony_ci ops->wm_frame_based(vfe, output->wm_idx[0], 0); 8098c2ecf20Sopenharmony_ci ops->bus_disconnect_wm_from_rdi(vfe, output->wm_idx[0], 8108c2ecf20Sopenharmony_ci line->id); 8118c2ecf20Sopenharmony_ci ops->enable_irq_wm_line(vfe, output->wm_idx[0], line->id, 0); 8128c2ecf20Sopenharmony_ci ops->set_cgc_override(vfe, output->wm_idx[0], 0); 8138c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vfe->output_lock, flags); 8148c2ecf20Sopenharmony_ci } else { 8158c2ecf20Sopenharmony_ci for (i = 0; i < output->wm_num; i++) { 8168c2ecf20Sopenharmony_ci ops->wm_line_based(vfe, output->wm_idx[i], NULL, i, 0); 8178c2ecf20Sopenharmony_ci ops->set_cgc_override(vfe, output->wm_idx[i], 0); 8188c2ecf20Sopenharmony_ci } 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci ops->enable_irq_pix_line(vfe, 0, line->id, 0); 8218c2ecf20Sopenharmony_ci ops->set_module_cfg(vfe, 0); 8228c2ecf20Sopenharmony_ci ops->set_realign_cfg(vfe, line, 0); 8238c2ecf20Sopenharmony_ci ops->set_xbar_cfg(vfe, output, 0); 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci ops->set_camif_cmd(vfe, 0); 8268c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vfe->output_lock, flags); 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci ops->camif_wait_for_stop(vfe, vfe->camss->dev); 8298c2ecf20Sopenharmony_ci } 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci return 0; 8328c2ecf20Sopenharmony_ci} 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci/* 8358c2ecf20Sopenharmony_ci * vfe_enable - Enable streaming on VFE line 8368c2ecf20Sopenharmony_ci * @line: VFE line 8378c2ecf20Sopenharmony_ci * 8388c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise 8398c2ecf20Sopenharmony_ci */ 8408c2ecf20Sopenharmony_cistatic int vfe_enable(struct vfe_line *line) 8418c2ecf20Sopenharmony_ci{ 8428c2ecf20Sopenharmony_ci struct vfe_device *vfe = to_vfe(line); 8438c2ecf20Sopenharmony_ci int ret; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci mutex_lock(&vfe->stream_lock); 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci if (!vfe->stream_count) { 8488c2ecf20Sopenharmony_ci vfe->ops->enable_irq_common(vfe); 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci vfe->ops->bus_enable_wr_if(vfe, 1); 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci vfe->ops->set_qos(vfe); 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci vfe->ops->set_ds(vfe); 8558c2ecf20Sopenharmony_ci } 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci vfe->stream_count++; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci mutex_unlock(&vfe->stream_lock); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci ret = vfe_get_output(line); 8628c2ecf20Sopenharmony_ci if (ret < 0) 8638c2ecf20Sopenharmony_ci goto error_get_output; 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci ret = vfe_enable_output(line); 8668c2ecf20Sopenharmony_ci if (ret < 0) 8678c2ecf20Sopenharmony_ci goto error_enable_output; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci vfe->was_streaming = 1; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci return 0; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_cierror_enable_output: 8758c2ecf20Sopenharmony_ci vfe_put_output(line); 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_cierror_get_output: 8788c2ecf20Sopenharmony_ci mutex_lock(&vfe->stream_lock); 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci if (vfe->stream_count == 1) 8818c2ecf20Sopenharmony_ci vfe->ops->bus_enable_wr_if(vfe, 0); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci vfe->stream_count--; 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci mutex_unlock(&vfe->stream_lock); 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci return ret; 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci/* 8918c2ecf20Sopenharmony_ci * vfe_disable - Disable streaming on VFE line 8928c2ecf20Sopenharmony_ci * @line: VFE line 8938c2ecf20Sopenharmony_ci * 8948c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise 8958c2ecf20Sopenharmony_ci */ 8968c2ecf20Sopenharmony_cistatic int vfe_disable(struct vfe_line *line) 8978c2ecf20Sopenharmony_ci{ 8988c2ecf20Sopenharmony_ci struct vfe_device *vfe = to_vfe(line); 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci vfe_disable_output(line); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci vfe_put_output(line); 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci mutex_lock(&vfe->stream_lock); 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci if (vfe->stream_count == 1) 9078c2ecf20Sopenharmony_ci vfe->ops->bus_enable_wr_if(vfe, 0); 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci vfe->stream_count--; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci mutex_unlock(&vfe->stream_lock); 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci return 0; 9148c2ecf20Sopenharmony_ci} 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci/* 9178c2ecf20Sopenharmony_ci * vfe_isr_sof - Process start of frame interrupt 9188c2ecf20Sopenharmony_ci * @vfe: VFE Device 9198c2ecf20Sopenharmony_ci * @line_id: VFE line 9208c2ecf20Sopenharmony_ci */ 9218c2ecf20Sopenharmony_cistatic void vfe_isr_sof(struct vfe_device *vfe, enum vfe_line_id line_id) 9228c2ecf20Sopenharmony_ci{ 9238c2ecf20Sopenharmony_ci struct vfe_output *output; 9248c2ecf20Sopenharmony_ci unsigned long flags; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci spin_lock_irqsave(&vfe->output_lock, flags); 9278c2ecf20Sopenharmony_ci output = &vfe->line[line_id].output; 9288c2ecf20Sopenharmony_ci if (output->wait_sof) { 9298c2ecf20Sopenharmony_ci output->wait_sof = 0; 9308c2ecf20Sopenharmony_ci complete(&output->sof); 9318c2ecf20Sopenharmony_ci } 9328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vfe->output_lock, flags); 9338c2ecf20Sopenharmony_ci} 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci/* 9368c2ecf20Sopenharmony_ci * vfe_isr_reg_update - Process reg update interrupt 9378c2ecf20Sopenharmony_ci * @vfe: VFE Device 9388c2ecf20Sopenharmony_ci * @line_id: VFE line 9398c2ecf20Sopenharmony_ci */ 9408c2ecf20Sopenharmony_cistatic void vfe_isr_reg_update(struct vfe_device *vfe, enum vfe_line_id line_id) 9418c2ecf20Sopenharmony_ci{ 9428c2ecf20Sopenharmony_ci struct vfe_output *output; 9438c2ecf20Sopenharmony_ci unsigned long flags; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci spin_lock_irqsave(&vfe->output_lock, flags); 9468c2ecf20Sopenharmony_ci vfe->ops->reg_update_clear(vfe, line_id); 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci output = &vfe->line[line_id].output; 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci if (output->wait_reg_update) { 9518c2ecf20Sopenharmony_ci output->wait_reg_update = 0; 9528c2ecf20Sopenharmony_ci complete(&output->reg_update); 9538c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vfe->output_lock, flags); 9548c2ecf20Sopenharmony_ci return; 9558c2ecf20Sopenharmony_ci } 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci if (output->state == VFE_OUTPUT_STOPPING) { 9588c2ecf20Sopenharmony_ci /* Release last buffer when hw is idle */ 9598c2ecf20Sopenharmony_ci if (output->last_buffer) { 9608c2ecf20Sopenharmony_ci vb2_buffer_done(&output->last_buffer->vb.vb2_buf, 9618c2ecf20Sopenharmony_ci VB2_BUF_STATE_DONE); 9628c2ecf20Sopenharmony_ci output->last_buffer = NULL; 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci output->state = VFE_OUTPUT_IDLE; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci /* Buffers received in stopping state are queued in */ 9678c2ecf20Sopenharmony_ci /* dma pending queue, start next capture here */ 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci output->buf[0] = vfe_buf_get_pending(output); 9708c2ecf20Sopenharmony_ci output->buf[1] = vfe_buf_get_pending(output); 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci if (!output->buf[0] && output->buf[1]) { 9738c2ecf20Sopenharmony_ci output->buf[0] = output->buf[1]; 9748c2ecf20Sopenharmony_ci output->buf[1] = NULL; 9758c2ecf20Sopenharmony_ci } 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci if (output->buf[0]) 9788c2ecf20Sopenharmony_ci output->state = VFE_OUTPUT_SINGLE; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci if (output->buf[1]) 9818c2ecf20Sopenharmony_ci output->state = VFE_OUTPUT_CONTINUOUS; 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci switch (output->state) { 9848c2ecf20Sopenharmony_ci case VFE_OUTPUT_SINGLE: 9858c2ecf20Sopenharmony_ci vfe_output_frame_drop(vfe, output, 2); 9868c2ecf20Sopenharmony_ci break; 9878c2ecf20Sopenharmony_ci case VFE_OUTPUT_CONTINUOUS: 9888c2ecf20Sopenharmony_ci vfe_output_frame_drop(vfe, output, 3); 9898c2ecf20Sopenharmony_ci break; 9908c2ecf20Sopenharmony_ci default: 9918c2ecf20Sopenharmony_ci vfe_output_frame_drop(vfe, output, 0); 9928c2ecf20Sopenharmony_ci break; 9938c2ecf20Sopenharmony_ci } 9948c2ecf20Sopenharmony_ci 9958c2ecf20Sopenharmony_ci vfe_output_init_addrs(vfe, output, 1); 9968c2ecf20Sopenharmony_ci } 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vfe->output_lock, flags); 9998c2ecf20Sopenharmony_ci} 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci/* 10028c2ecf20Sopenharmony_ci * vfe_isr_wm_done - Process write master done interrupt 10038c2ecf20Sopenharmony_ci * @vfe: VFE Device 10048c2ecf20Sopenharmony_ci * @wm: Write master id 10058c2ecf20Sopenharmony_ci */ 10068c2ecf20Sopenharmony_cistatic void vfe_isr_wm_done(struct vfe_device *vfe, u8 wm) 10078c2ecf20Sopenharmony_ci{ 10088c2ecf20Sopenharmony_ci struct camss_buffer *ready_buf; 10098c2ecf20Sopenharmony_ci struct vfe_output *output; 10108c2ecf20Sopenharmony_ci dma_addr_t *new_addr; 10118c2ecf20Sopenharmony_ci unsigned long flags; 10128c2ecf20Sopenharmony_ci u32 active_index; 10138c2ecf20Sopenharmony_ci u64 ts = ktime_get_ns(); 10148c2ecf20Sopenharmony_ci unsigned int i; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci active_index = vfe->ops->wm_get_ping_pong_status(vfe, wm); 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci spin_lock_irqsave(&vfe->output_lock, flags); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci if (vfe->wm_output_map[wm] == VFE_LINE_NONE) { 10218c2ecf20Sopenharmony_ci dev_err_ratelimited(vfe->camss->dev, 10228c2ecf20Sopenharmony_ci "Received wm done for unmapped index\n"); 10238c2ecf20Sopenharmony_ci goto out_unlock; 10248c2ecf20Sopenharmony_ci } 10258c2ecf20Sopenharmony_ci output = &vfe->line[vfe->wm_output_map[wm]].output; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci if (output->active_buf == active_index) { 10288c2ecf20Sopenharmony_ci dev_err_ratelimited(vfe->camss->dev, 10298c2ecf20Sopenharmony_ci "Active buffer mismatch!\n"); 10308c2ecf20Sopenharmony_ci goto out_unlock; 10318c2ecf20Sopenharmony_ci } 10328c2ecf20Sopenharmony_ci output->active_buf = active_index; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci ready_buf = output->buf[!active_index]; 10358c2ecf20Sopenharmony_ci if (!ready_buf) { 10368c2ecf20Sopenharmony_ci dev_err_ratelimited(vfe->camss->dev, 10378c2ecf20Sopenharmony_ci "Missing ready buf %d %d!\n", 10388c2ecf20Sopenharmony_ci !active_index, output->state); 10398c2ecf20Sopenharmony_ci goto out_unlock; 10408c2ecf20Sopenharmony_ci } 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci ready_buf->vb.vb2_buf.timestamp = ts; 10438c2ecf20Sopenharmony_ci ready_buf->vb.sequence = output->sequence++; 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci /* Get next buffer */ 10468c2ecf20Sopenharmony_ci output->buf[!active_index] = vfe_buf_get_pending(output); 10478c2ecf20Sopenharmony_ci if (!output->buf[!active_index]) { 10488c2ecf20Sopenharmony_ci /* No next buffer - set same address */ 10498c2ecf20Sopenharmony_ci new_addr = ready_buf->addr; 10508c2ecf20Sopenharmony_ci vfe_buf_update_wm_on_last(vfe, output); 10518c2ecf20Sopenharmony_ci } else { 10528c2ecf20Sopenharmony_ci new_addr = output->buf[!active_index]->addr; 10538c2ecf20Sopenharmony_ci vfe_buf_update_wm_on_next(vfe, output); 10548c2ecf20Sopenharmony_ci } 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci if (active_index) 10578c2ecf20Sopenharmony_ci for (i = 0; i < output->wm_num; i++) 10588c2ecf20Sopenharmony_ci vfe->ops->wm_set_ping_addr(vfe, output->wm_idx[i], 10598c2ecf20Sopenharmony_ci new_addr[i]); 10608c2ecf20Sopenharmony_ci else 10618c2ecf20Sopenharmony_ci for (i = 0; i < output->wm_num; i++) 10628c2ecf20Sopenharmony_ci vfe->ops->wm_set_pong_addr(vfe, output->wm_idx[i], 10638c2ecf20Sopenharmony_ci new_addr[i]); 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vfe->output_lock, flags); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci if (output->state == VFE_OUTPUT_STOPPING) 10688c2ecf20Sopenharmony_ci output->last_buffer = ready_buf; 10698c2ecf20Sopenharmony_ci else 10708c2ecf20Sopenharmony_ci vb2_buffer_done(&ready_buf->vb.vb2_buf, VB2_BUF_STATE_DONE); 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_ci return; 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ciout_unlock: 10758c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vfe->output_lock, flags); 10768c2ecf20Sopenharmony_ci} 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci/* 10798c2ecf20Sopenharmony_ci * vfe_isr_wm_done - Process composite image done interrupt 10808c2ecf20Sopenharmony_ci * @vfe: VFE Device 10818c2ecf20Sopenharmony_ci * @comp: Composite image id 10828c2ecf20Sopenharmony_ci */ 10838c2ecf20Sopenharmony_cistatic void vfe_isr_comp_done(struct vfe_device *vfe, u8 comp) 10848c2ecf20Sopenharmony_ci{ 10858c2ecf20Sopenharmony_ci unsigned int i; 10868c2ecf20Sopenharmony_ci 10878c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++) 10888c2ecf20Sopenharmony_ci if (vfe->wm_output_map[i] == VFE_LINE_PIX) { 10898c2ecf20Sopenharmony_ci vfe_isr_wm_done(vfe, i); 10908c2ecf20Sopenharmony_ci break; 10918c2ecf20Sopenharmony_ci } 10928c2ecf20Sopenharmony_ci} 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_cistatic inline void vfe_isr_reset_ack(struct vfe_device *vfe) 10958c2ecf20Sopenharmony_ci{ 10968c2ecf20Sopenharmony_ci complete(&vfe->reset_complete); 10978c2ecf20Sopenharmony_ci} 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_cistatic inline void vfe_isr_halt_ack(struct vfe_device *vfe) 11008c2ecf20Sopenharmony_ci{ 11018c2ecf20Sopenharmony_ci complete(&vfe->halt_complete); 11028c2ecf20Sopenharmony_ci vfe->ops->halt_clear(vfe); 11038c2ecf20Sopenharmony_ci} 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci/* 11068c2ecf20Sopenharmony_ci * vfe_set_clock_rates - Calculate and set clock rates on VFE module 11078c2ecf20Sopenharmony_ci * @vfe: VFE device 11088c2ecf20Sopenharmony_ci * 11098c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise 11108c2ecf20Sopenharmony_ci */ 11118c2ecf20Sopenharmony_cistatic int vfe_set_clock_rates(struct vfe_device *vfe) 11128c2ecf20Sopenharmony_ci{ 11138c2ecf20Sopenharmony_ci struct device *dev = vfe->camss->dev; 11148c2ecf20Sopenharmony_ci u32 pixel_clock[MSM_VFE_LINE_NUM]; 11158c2ecf20Sopenharmony_ci int i, j; 11168c2ecf20Sopenharmony_ci int ret; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) { 11198c2ecf20Sopenharmony_ci ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity, 11208c2ecf20Sopenharmony_ci &pixel_clock[i]); 11218c2ecf20Sopenharmony_ci if (ret) 11228c2ecf20Sopenharmony_ci pixel_clock[i] = 0; 11238c2ecf20Sopenharmony_ci } 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci for (i = 0; i < vfe->nclocks; i++) { 11268c2ecf20Sopenharmony_ci struct camss_clock *clock = &vfe->clock[i]; 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci if (!strcmp(clock->name, "vfe0") || 11298c2ecf20Sopenharmony_ci !strcmp(clock->name, "vfe1")) { 11308c2ecf20Sopenharmony_ci u64 min_rate = 0; 11318c2ecf20Sopenharmony_ci long rate; 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci for (j = VFE_LINE_RDI0; j <= VFE_LINE_PIX; j++) { 11348c2ecf20Sopenharmony_ci u32 tmp; 11358c2ecf20Sopenharmony_ci u8 bpp; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci if (j == VFE_LINE_PIX) { 11388c2ecf20Sopenharmony_ci tmp = pixel_clock[j]; 11398c2ecf20Sopenharmony_ci } else { 11408c2ecf20Sopenharmony_ci struct vfe_line *l = &vfe->line[j]; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci bpp = vfe_get_bpp(l->formats, 11438c2ecf20Sopenharmony_ci l->nformats, 11448c2ecf20Sopenharmony_ci l->fmt[MSM_VFE_PAD_SINK].code); 11458c2ecf20Sopenharmony_ci tmp = pixel_clock[j] * bpp / 64; 11468c2ecf20Sopenharmony_ci } 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci if (min_rate < tmp) 11498c2ecf20Sopenharmony_ci min_rate = tmp; 11508c2ecf20Sopenharmony_ci } 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci camss_add_clock_margin(&min_rate); 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci for (j = 0; j < clock->nfreqs; j++) 11558c2ecf20Sopenharmony_ci if (min_rate < clock->freq[j]) 11568c2ecf20Sopenharmony_ci break; 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci if (j == clock->nfreqs) { 11598c2ecf20Sopenharmony_ci dev_err(dev, 11608c2ecf20Sopenharmony_ci "Pixel clock is too high for VFE"); 11618c2ecf20Sopenharmony_ci return -EINVAL; 11628c2ecf20Sopenharmony_ci } 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci /* if sensor pixel clock is not available */ 11658c2ecf20Sopenharmony_ci /* set highest possible VFE clock rate */ 11668c2ecf20Sopenharmony_ci if (min_rate == 0) 11678c2ecf20Sopenharmony_ci j = clock->nfreqs - 1; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci rate = clk_round_rate(clock->clk, clock->freq[j]); 11708c2ecf20Sopenharmony_ci if (rate < 0) { 11718c2ecf20Sopenharmony_ci dev_err(dev, "clk round rate failed: %ld\n", 11728c2ecf20Sopenharmony_ci rate); 11738c2ecf20Sopenharmony_ci return -EINVAL; 11748c2ecf20Sopenharmony_ci } 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci ret = clk_set_rate(clock->clk, rate); 11778c2ecf20Sopenharmony_ci if (ret < 0) { 11788c2ecf20Sopenharmony_ci dev_err(dev, "clk set rate failed: %d\n", ret); 11798c2ecf20Sopenharmony_ci return ret; 11808c2ecf20Sopenharmony_ci } 11818c2ecf20Sopenharmony_ci } 11828c2ecf20Sopenharmony_ci } 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci return 0; 11858c2ecf20Sopenharmony_ci} 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci/* 11888c2ecf20Sopenharmony_ci * vfe_check_clock_rates - Check current clock rates on VFE module 11898c2ecf20Sopenharmony_ci * @vfe: VFE device 11908c2ecf20Sopenharmony_ci * 11918c2ecf20Sopenharmony_ci * Return 0 if current clock rates are suitable for a new pipeline 11928c2ecf20Sopenharmony_ci * or a negative error code otherwise 11938c2ecf20Sopenharmony_ci */ 11948c2ecf20Sopenharmony_cistatic int vfe_check_clock_rates(struct vfe_device *vfe) 11958c2ecf20Sopenharmony_ci{ 11968c2ecf20Sopenharmony_ci u32 pixel_clock[MSM_VFE_LINE_NUM]; 11978c2ecf20Sopenharmony_ci int i, j; 11988c2ecf20Sopenharmony_ci int ret; 11998c2ecf20Sopenharmony_ci 12008c2ecf20Sopenharmony_ci for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) { 12018c2ecf20Sopenharmony_ci ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity, 12028c2ecf20Sopenharmony_ci &pixel_clock[i]); 12038c2ecf20Sopenharmony_ci if (ret) 12048c2ecf20Sopenharmony_ci pixel_clock[i] = 0; 12058c2ecf20Sopenharmony_ci } 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci for (i = 0; i < vfe->nclocks; i++) { 12088c2ecf20Sopenharmony_ci struct camss_clock *clock = &vfe->clock[i]; 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci if (!strcmp(clock->name, "vfe0") || 12118c2ecf20Sopenharmony_ci !strcmp(clock->name, "vfe1")) { 12128c2ecf20Sopenharmony_ci u64 min_rate = 0; 12138c2ecf20Sopenharmony_ci unsigned long rate; 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci for (j = VFE_LINE_RDI0; j <= VFE_LINE_PIX; j++) { 12168c2ecf20Sopenharmony_ci u32 tmp; 12178c2ecf20Sopenharmony_ci u8 bpp; 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci if (j == VFE_LINE_PIX) { 12208c2ecf20Sopenharmony_ci tmp = pixel_clock[j]; 12218c2ecf20Sopenharmony_ci } else { 12228c2ecf20Sopenharmony_ci struct vfe_line *l = &vfe->line[j]; 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci bpp = vfe_get_bpp(l->formats, 12258c2ecf20Sopenharmony_ci l->nformats, 12268c2ecf20Sopenharmony_ci l->fmt[MSM_VFE_PAD_SINK].code); 12278c2ecf20Sopenharmony_ci tmp = pixel_clock[j] * bpp / 64; 12288c2ecf20Sopenharmony_ci } 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci if (min_rate < tmp) 12318c2ecf20Sopenharmony_ci min_rate = tmp; 12328c2ecf20Sopenharmony_ci } 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci camss_add_clock_margin(&min_rate); 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci rate = clk_get_rate(clock->clk); 12378c2ecf20Sopenharmony_ci if (rate < min_rate) 12388c2ecf20Sopenharmony_ci return -EBUSY; 12398c2ecf20Sopenharmony_ci } 12408c2ecf20Sopenharmony_ci } 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci return 0; 12438c2ecf20Sopenharmony_ci} 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci/* 12468c2ecf20Sopenharmony_ci * vfe_get - Power up and reset VFE module 12478c2ecf20Sopenharmony_ci * @vfe: VFE Device 12488c2ecf20Sopenharmony_ci * 12498c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise 12508c2ecf20Sopenharmony_ci */ 12518c2ecf20Sopenharmony_cistatic int vfe_get(struct vfe_device *vfe) 12528c2ecf20Sopenharmony_ci{ 12538c2ecf20Sopenharmony_ci int ret; 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci mutex_lock(&vfe->power_lock); 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci if (vfe->power_count == 0) { 12588c2ecf20Sopenharmony_ci ret = camss_pm_domain_on(vfe->camss, vfe->id); 12598c2ecf20Sopenharmony_ci if (ret < 0) 12608c2ecf20Sopenharmony_ci goto error_pm_domain; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(vfe->camss->dev); 12638c2ecf20Sopenharmony_ci if (ret < 0) 12648c2ecf20Sopenharmony_ci goto error_pm_runtime_get; 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci ret = vfe_set_clock_rates(vfe); 12678c2ecf20Sopenharmony_ci if (ret < 0) 12688c2ecf20Sopenharmony_ci goto error_pm_runtime_get; 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci ret = camss_enable_clocks(vfe->nclocks, vfe->clock, 12718c2ecf20Sopenharmony_ci vfe->camss->dev); 12728c2ecf20Sopenharmony_ci if (ret < 0) 12738c2ecf20Sopenharmony_ci goto error_pm_runtime_get; 12748c2ecf20Sopenharmony_ci 12758c2ecf20Sopenharmony_ci ret = vfe_reset(vfe); 12768c2ecf20Sopenharmony_ci if (ret < 0) 12778c2ecf20Sopenharmony_ci goto error_reset; 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci vfe_reset_output_maps(vfe); 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci vfe_init_outputs(vfe); 12828c2ecf20Sopenharmony_ci } else { 12838c2ecf20Sopenharmony_ci ret = vfe_check_clock_rates(vfe); 12848c2ecf20Sopenharmony_ci if (ret < 0) 12858c2ecf20Sopenharmony_ci goto error_pm_domain; 12868c2ecf20Sopenharmony_ci } 12878c2ecf20Sopenharmony_ci vfe->power_count++; 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci mutex_unlock(&vfe->power_lock); 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci return 0; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_cierror_reset: 12948c2ecf20Sopenharmony_ci camss_disable_clocks(vfe->nclocks, vfe->clock); 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_cierror_pm_runtime_get: 12978c2ecf20Sopenharmony_ci pm_runtime_put_sync(vfe->camss->dev); 12988c2ecf20Sopenharmony_ci camss_pm_domain_off(vfe->camss, vfe->id); 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_cierror_pm_domain: 13018c2ecf20Sopenharmony_ci mutex_unlock(&vfe->power_lock); 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci return ret; 13048c2ecf20Sopenharmony_ci} 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_ci/* 13078c2ecf20Sopenharmony_ci * vfe_put - Power down VFE module 13088c2ecf20Sopenharmony_ci * @vfe: VFE Device 13098c2ecf20Sopenharmony_ci */ 13108c2ecf20Sopenharmony_cistatic void vfe_put(struct vfe_device *vfe) 13118c2ecf20Sopenharmony_ci{ 13128c2ecf20Sopenharmony_ci mutex_lock(&vfe->power_lock); 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci if (vfe->power_count == 0) { 13158c2ecf20Sopenharmony_ci dev_err(vfe->camss->dev, "vfe power off on power_count == 0\n"); 13168c2ecf20Sopenharmony_ci goto exit; 13178c2ecf20Sopenharmony_ci } else if (vfe->power_count == 1) { 13188c2ecf20Sopenharmony_ci if (vfe->was_streaming) { 13198c2ecf20Sopenharmony_ci vfe->was_streaming = 0; 13208c2ecf20Sopenharmony_ci vfe_halt(vfe); 13218c2ecf20Sopenharmony_ci } 13228c2ecf20Sopenharmony_ci camss_disable_clocks(vfe->nclocks, vfe->clock); 13238c2ecf20Sopenharmony_ci pm_runtime_put_sync(vfe->camss->dev); 13248c2ecf20Sopenharmony_ci camss_pm_domain_off(vfe->camss, vfe->id); 13258c2ecf20Sopenharmony_ci } 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci vfe->power_count--; 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ciexit: 13308c2ecf20Sopenharmony_ci mutex_unlock(&vfe->power_lock); 13318c2ecf20Sopenharmony_ci} 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci/* 13348c2ecf20Sopenharmony_ci * vfe_queue_buffer - Add empty buffer 13358c2ecf20Sopenharmony_ci * @vid: Video device structure 13368c2ecf20Sopenharmony_ci * @buf: Buffer to be enqueued 13378c2ecf20Sopenharmony_ci * 13388c2ecf20Sopenharmony_ci * Add an empty buffer - depending on the current number of buffers it will be 13398c2ecf20Sopenharmony_ci * put in pending buffer queue or directly given to the hardware to be filled. 13408c2ecf20Sopenharmony_ci * 13418c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise 13428c2ecf20Sopenharmony_ci */ 13438c2ecf20Sopenharmony_cistatic int vfe_queue_buffer(struct camss_video *vid, 13448c2ecf20Sopenharmony_ci struct camss_buffer *buf) 13458c2ecf20Sopenharmony_ci{ 13468c2ecf20Sopenharmony_ci struct vfe_line *line = container_of(vid, struct vfe_line, video_out); 13478c2ecf20Sopenharmony_ci struct vfe_device *vfe = to_vfe(line); 13488c2ecf20Sopenharmony_ci struct vfe_output *output; 13498c2ecf20Sopenharmony_ci unsigned long flags; 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci output = &line->output; 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci spin_lock_irqsave(&vfe->output_lock, flags); 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci vfe_buf_update_wm_on_new(vfe, output, buf); 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vfe->output_lock, flags); 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci return 0; 13608c2ecf20Sopenharmony_ci} 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci/* 13638c2ecf20Sopenharmony_ci * vfe_flush_buffers - Return all vb2 buffers 13648c2ecf20Sopenharmony_ci * @vid: Video device structure 13658c2ecf20Sopenharmony_ci * @state: vb2 buffer state of the returned buffers 13668c2ecf20Sopenharmony_ci * 13678c2ecf20Sopenharmony_ci * Return all buffers to vb2. This includes queued pending buffers (still 13688c2ecf20Sopenharmony_ci * unused) and any buffers given to the hardware but again still not used. 13698c2ecf20Sopenharmony_ci * 13708c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise 13718c2ecf20Sopenharmony_ci */ 13728c2ecf20Sopenharmony_cistatic int vfe_flush_buffers(struct camss_video *vid, 13738c2ecf20Sopenharmony_ci enum vb2_buffer_state state) 13748c2ecf20Sopenharmony_ci{ 13758c2ecf20Sopenharmony_ci struct vfe_line *line = container_of(vid, struct vfe_line, video_out); 13768c2ecf20Sopenharmony_ci struct vfe_device *vfe = to_vfe(line); 13778c2ecf20Sopenharmony_ci struct vfe_output *output; 13788c2ecf20Sopenharmony_ci unsigned long flags; 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci output = &line->output; 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci spin_lock_irqsave(&vfe->output_lock, flags); 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci vfe_buf_flush_pending(output, state); 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci if (output->buf[0]) 13878c2ecf20Sopenharmony_ci vb2_buffer_done(&output->buf[0]->vb.vb2_buf, state); 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci if (output->buf[1]) 13908c2ecf20Sopenharmony_ci vb2_buffer_done(&output->buf[1]->vb.vb2_buf, state); 13918c2ecf20Sopenharmony_ci 13928c2ecf20Sopenharmony_ci if (output->last_buffer) { 13938c2ecf20Sopenharmony_ci vb2_buffer_done(&output->last_buffer->vb.vb2_buf, state); 13948c2ecf20Sopenharmony_ci output->last_buffer = NULL; 13958c2ecf20Sopenharmony_ci } 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vfe->output_lock, flags); 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci return 0; 14008c2ecf20Sopenharmony_ci} 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci/* 14038c2ecf20Sopenharmony_ci * vfe_set_power - Power on/off VFE module 14048c2ecf20Sopenharmony_ci * @sd: VFE V4L2 subdevice 14058c2ecf20Sopenharmony_ci * @on: Requested power state 14068c2ecf20Sopenharmony_ci * 14078c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise 14088c2ecf20Sopenharmony_ci */ 14098c2ecf20Sopenharmony_cistatic int vfe_set_power(struct v4l2_subdev *sd, int on) 14108c2ecf20Sopenharmony_ci{ 14118c2ecf20Sopenharmony_ci struct vfe_line *line = v4l2_get_subdevdata(sd); 14128c2ecf20Sopenharmony_ci struct vfe_device *vfe = to_vfe(line); 14138c2ecf20Sopenharmony_ci int ret; 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci if (on) { 14168c2ecf20Sopenharmony_ci ret = vfe_get(vfe); 14178c2ecf20Sopenharmony_ci if (ret < 0) 14188c2ecf20Sopenharmony_ci return ret; 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci vfe->ops->hw_version_read(vfe, vfe->camss->dev); 14218c2ecf20Sopenharmony_ci } else { 14228c2ecf20Sopenharmony_ci vfe_put(vfe); 14238c2ecf20Sopenharmony_ci } 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci return 0; 14268c2ecf20Sopenharmony_ci} 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci/* 14298c2ecf20Sopenharmony_ci * vfe_set_stream - Enable/disable streaming on VFE module 14308c2ecf20Sopenharmony_ci * @sd: VFE V4L2 subdevice 14318c2ecf20Sopenharmony_ci * @enable: Requested streaming state 14328c2ecf20Sopenharmony_ci * 14338c2ecf20Sopenharmony_ci * Main configuration of VFE module is triggered here. 14348c2ecf20Sopenharmony_ci * 14358c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise 14368c2ecf20Sopenharmony_ci */ 14378c2ecf20Sopenharmony_cistatic int vfe_set_stream(struct v4l2_subdev *sd, int enable) 14388c2ecf20Sopenharmony_ci{ 14398c2ecf20Sopenharmony_ci struct vfe_line *line = v4l2_get_subdevdata(sd); 14408c2ecf20Sopenharmony_ci struct vfe_device *vfe = to_vfe(line); 14418c2ecf20Sopenharmony_ci int ret; 14428c2ecf20Sopenharmony_ci 14438c2ecf20Sopenharmony_ci if (enable) { 14448c2ecf20Sopenharmony_ci ret = vfe_enable(line); 14458c2ecf20Sopenharmony_ci if (ret < 0) 14468c2ecf20Sopenharmony_ci dev_err(vfe->camss->dev, 14478c2ecf20Sopenharmony_ci "Failed to enable vfe outputs\n"); 14488c2ecf20Sopenharmony_ci } else { 14498c2ecf20Sopenharmony_ci ret = vfe_disable(line); 14508c2ecf20Sopenharmony_ci if (ret < 0) 14518c2ecf20Sopenharmony_ci dev_err(vfe->camss->dev, 14528c2ecf20Sopenharmony_ci "Failed to disable vfe outputs\n"); 14538c2ecf20Sopenharmony_ci } 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci return ret; 14568c2ecf20Sopenharmony_ci} 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_ci/* 14598c2ecf20Sopenharmony_ci * __vfe_get_format - Get pointer to format structure 14608c2ecf20Sopenharmony_ci * @line: VFE line 14618c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 14628c2ecf20Sopenharmony_ci * @pad: pad from which format is requested 14638c2ecf20Sopenharmony_ci * @which: TRY or ACTIVE format 14648c2ecf20Sopenharmony_ci * 14658c2ecf20Sopenharmony_ci * Return pointer to TRY or ACTIVE format structure 14668c2ecf20Sopenharmony_ci */ 14678c2ecf20Sopenharmony_cistatic struct v4l2_mbus_framefmt * 14688c2ecf20Sopenharmony_ci__vfe_get_format(struct vfe_line *line, 14698c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 14708c2ecf20Sopenharmony_ci unsigned int pad, 14718c2ecf20Sopenharmony_ci enum v4l2_subdev_format_whence which) 14728c2ecf20Sopenharmony_ci{ 14738c2ecf20Sopenharmony_ci if (which == V4L2_SUBDEV_FORMAT_TRY) 14748c2ecf20Sopenharmony_ci return v4l2_subdev_get_try_format(&line->subdev, cfg, pad); 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci return &line->fmt[pad]; 14778c2ecf20Sopenharmony_ci} 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci/* 14808c2ecf20Sopenharmony_ci * __vfe_get_compose - Get pointer to compose selection structure 14818c2ecf20Sopenharmony_ci * @line: VFE line 14828c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 14838c2ecf20Sopenharmony_ci * @which: TRY or ACTIVE format 14848c2ecf20Sopenharmony_ci * 14858c2ecf20Sopenharmony_ci * Return pointer to TRY or ACTIVE compose rectangle structure 14868c2ecf20Sopenharmony_ci */ 14878c2ecf20Sopenharmony_cistatic struct v4l2_rect * 14888c2ecf20Sopenharmony_ci__vfe_get_compose(struct vfe_line *line, 14898c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 14908c2ecf20Sopenharmony_ci enum v4l2_subdev_format_whence which) 14918c2ecf20Sopenharmony_ci{ 14928c2ecf20Sopenharmony_ci if (which == V4L2_SUBDEV_FORMAT_TRY) 14938c2ecf20Sopenharmony_ci return v4l2_subdev_get_try_compose(&line->subdev, cfg, 14948c2ecf20Sopenharmony_ci MSM_VFE_PAD_SINK); 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci return &line->compose; 14978c2ecf20Sopenharmony_ci} 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci/* 15008c2ecf20Sopenharmony_ci * __vfe_get_crop - Get pointer to crop selection structure 15018c2ecf20Sopenharmony_ci * @line: VFE line 15028c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 15038c2ecf20Sopenharmony_ci * @which: TRY or ACTIVE format 15048c2ecf20Sopenharmony_ci * 15058c2ecf20Sopenharmony_ci * Return pointer to TRY or ACTIVE crop rectangle structure 15068c2ecf20Sopenharmony_ci */ 15078c2ecf20Sopenharmony_cistatic struct v4l2_rect * 15088c2ecf20Sopenharmony_ci__vfe_get_crop(struct vfe_line *line, 15098c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 15108c2ecf20Sopenharmony_ci enum v4l2_subdev_format_whence which) 15118c2ecf20Sopenharmony_ci{ 15128c2ecf20Sopenharmony_ci if (which == V4L2_SUBDEV_FORMAT_TRY) 15138c2ecf20Sopenharmony_ci return v4l2_subdev_get_try_crop(&line->subdev, cfg, 15148c2ecf20Sopenharmony_ci MSM_VFE_PAD_SRC); 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci return &line->crop; 15178c2ecf20Sopenharmony_ci} 15188c2ecf20Sopenharmony_ci 15198c2ecf20Sopenharmony_ci/* 15208c2ecf20Sopenharmony_ci * vfe_try_format - Handle try format by pad subdev method 15218c2ecf20Sopenharmony_ci * @line: VFE line 15228c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 15238c2ecf20Sopenharmony_ci * @pad: pad on which format is requested 15248c2ecf20Sopenharmony_ci * @fmt: pointer to v4l2 format structure 15258c2ecf20Sopenharmony_ci * @which: wanted subdev format 15268c2ecf20Sopenharmony_ci */ 15278c2ecf20Sopenharmony_cistatic void vfe_try_format(struct vfe_line *line, 15288c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 15298c2ecf20Sopenharmony_ci unsigned int pad, 15308c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *fmt, 15318c2ecf20Sopenharmony_ci enum v4l2_subdev_format_whence which) 15328c2ecf20Sopenharmony_ci{ 15338c2ecf20Sopenharmony_ci unsigned int i; 15348c2ecf20Sopenharmony_ci u32 code; 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci switch (pad) { 15378c2ecf20Sopenharmony_ci case MSM_VFE_PAD_SINK: 15388c2ecf20Sopenharmony_ci /* Set format on sink pad */ 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci for (i = 0; i < line->nformats; i++) 15418c2ecf20Sopenharmony_ci if (fmt->code == line->formats[i].code) 15428c2ecf20Sopenharmony_ci break; 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci /* If not found, use UYVY as default */ 15458c2ecf20Sopenharmony_ci if (i >= line->nformats) 15468c2ecf20Sopenharmony_ci fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; 15478c2ecf20Sopenharmony_ci 15488c2ecf20Sopenharmony_ci fmt->width = clamp_t(u32, fmt->width, 1, 8191); 15498c2ecf20Sopenharmony_ci fmt->height = clamp_t(u32, fmt->height, 1, 8191); 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci fmt->field = V4L2_FIELD_NONE; 15528c2ecf20Sopenharmony_ci fmt->colorspace = V4L2_COLORSPACE_SRGB; 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci break; 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci case MSM_VFE_PAD_SRC: 15578c2ecf20Sopenharmony_ci /* Set and return a format same as sink pad */ 15588c2ecf20Sopenharmony_ci code = fmt->code; 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci *fmt = *__vfe_get_format(line, cfg, MSM_VFE_PAD_SINK, which); 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci fmt->code = vfe_src_pad_code(line, fmt->code, 0, code); 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci if (line->id == VFE_LINE_PIX) { 15658c2ecf20Sopenharmony_ci struct v4l2_rect *rect; 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci rect = __vfe_get_crop(line, cfg, which); 15688c2ecf20Sopenharmony_ci 15698c2ecf20Sopenharmony_ci fmt->width = rect->width; 15708c2ecf20Sopenharmony_ci fmt->height = rect->height; 15718c2ecf20Sopenharmony_ci } 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci break; 15748c2ecf20Sopenharmony_ci } 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci fmt->colorspace = V4L2_COLORSPACE_SRGB; 15778c2ecf20Sopenharmony_ci} 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci/* 15808c2ecf20Sopenharmony_ci * vfe_try_compose - Handle try compose selection by pad subdev method 15818c2ecf20Sopenharmony_ci * @line: VFE line 15828c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 15838c2ecf20Sopenharmony_ci * @rect: pointer to v4l2 rect structure 15848c2ecf20Sopenharmony_ci * @which: wanted subdev format 15858c2ecf20Sopenharmony_ci */ 15868c2ecf20Sopenharmony_cistatic void vfe_try_compose(struct vfe_line *line, 15878c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 15888c2ecf20Sopenharmony_ci struct v4l2_rect *rect, 15898c2ecf20Sopenharmony_ci enum v4l2_subdev_format_whence which) 15908c2ecf20Sopenharmony_ci{ 15918c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *fmt; 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci fmt = __vfe_get_format(line, cfg, MSM_VFE_PAD_SINK, which); 15948c2ecf20Sopenharmony_ci 15958c2ecf20Sopenharmony_ci if (rect->width > fmt->width) 15968c2ecf20Sopenharmony_ci rect->width = fmt->width; 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci if (rect->height > fmt->height) 15998c2ecf20Sopenharmony_ci rect->height = fmt->height; 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci if (fmt->width > rect->width * SCALER_RATIO_MAX) 16028c2ecf20Sopenharmony_ci rect->width = (fmt->width + SCALER_RATIO_MAX - 1) / 16038c2ecf20Sopenharmony_ci SCALER_RATIO_MAX; 16048c2ecf20Sopenharmony_ci 16058c2ecf20Sopenharmony_ci rect->width &= ~0x1; 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci if (fmt->height > rect->height * SCALER_RATIO_MAX) 16088c2ecf20Sopenharmony_ci rect->height = (fmt->height + SCALER_RATIO_MAX - 1) / 16098c2ecf20Sopenharmony_ci SCALER_RATIO_MAX; 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci if (rect->width < 16) 16128c2ecf20Sopenharmony_ci rect->width = 16; 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci if (rect->height < 4) 16158c2ecf20Sopenharmony_ci rect->height = 4; 16168c2ecf20Sopenharmony_ci} 16178c2ecf20Sopenharmony_ci 16188c2ecf20Sopenharmony_ci/* 16198c2ecf20Sopenharmony_ci * vfe_try_crop - Handle try crop selection by pad subdev method 16208c2ecf20Sopenharmony_ci * @line: VFE line 16218c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 16228c2ecf20Sopenharmony_ci * @rect: pointer to v4l2 rect structure 16238c2ecf20Sopenharmony_ci * @which: wanted subdev format 16248c2ecf20Sopenharmony_ci */ 16258c2ecf20Sopenharmony_cistatic void vfe_try_crop(struct vfe_line *line, 16268c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 16278c2ecf20Sopenharmony_ci struct v4l2_rect *rect, 16288c2ecf20Sopenharmony_ci enum v4l2_subdev_format_whence which) 16298c2ecf20Sopenharmony_ci{ 16308c2ecf20Sopenharmony_ci struct v4l2_rect *compose; 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci compose = __vfe_get_compose(line, cfg, which); 16338c2ecf20Sopenharmony_ci 16348c2ecf20Sopenharmony_ci if (rect->width > compose->width) 16358c2ecf20Sopenharmony_ci rect->width = compose->width; 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci if (rect->width + rect->left > compose->width) 16388c2ecf20Sopenharmony_ci rect->left = compose->width - rect->width; 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci if (rect->height > compose->height) 16418c2ecf20Sopenharmony_ci rect->height = compose->height; 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci if (rect->height + rect->top > compose->height) 16448c2ecf20Sopenharmony_ci rect->top = compose->height - rect->height; 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_ci /* wm in line based mode writes multiple of 16 horizontally */ 16478c2ecf20Sopenharmony_ci rect->left += (rect->width & 0xf) >> 1; 16488c2ecf20Sopenharmony_ci rect->width &= ~0xf; 16498c2ecf20Sopenharmony_ci 16508c2ecf20Sopenharmony_ci if (rect->width < 16) { 16518c2ecf20Sopenharmony_ci rect->left = 0; 16528c2ecf20Sopenharmony_ci rect->width = 16; 16538c2ecf20Sopenharmony_ci } 16548c2ecf20Sopenharmony_ci 16558c2ecf20Sopenharmony_ci if (rect->height < 4) { 16568c2ecf20Sopenharmony_ci rect->top = 0; 16578c2ecf20Sopenharmony_ci rect->height = 4; 16588c2ecf20Sopenharmony_ci } 16598c2ecf20Sopenharmony_ci} 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_ci/* 16628c2ecf20Sopenharmony_ci * vfe_enum_mbus_code - Handle pixel format enumeration 16638c2ecf20Sopenharmony_ci * @sd: VFE V4L2 subdevice 16648c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 16658c2ecf20Sopenharmony_ci * @code: pointer to v4l2_subdev_mbus_code_enum structure 16668c2ecf20Sopenharmony_ci * 16678c2ecf20Sopenharmony_ci * return -EINVAL or zero on success 16688c2ecf20Sopenharmony_ci */ 16698c2ecf20Sopenharmony_cistatic int vfe_enum_mbus_code(struct v4l2_subdev *sd, 16708c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 16718c2ecf20Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 16728c2ecf20Sopenharmony_ci{ 16738c2ecf20Sopenharmony_ci struct vfe_line *line = v4l2_get_subdevdata(sd); 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci if (code->pad == MSM_VFE_PAD_SINK) { 16768c2ecf20Sopenharmony_ci if (code->index >= line->nformats) 16778c2ecf20Sopenharmony_ci return -EINVAL; 16788c2ecf20Sopenharmony_ci 16798c2ecf20Sopenharmony_ci code->code = line->formats[code->index].code; 16808c2ecf20Sopenharmony_ci } else { 16818c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *sink_fmt; 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci sink_fmt = __vfe_get_format(line, cfg, MSM_VFE_PAD_SINK, 16848c2ecf20Sopenharmony_ci code->which); 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci code->code = vfe_src_pad_code(line, sink_fmt->code, 16878c2ecf20Sopenharmony_ci code->index, 0); 16888c2ecf20Sopenharmony_ci if (!code->code) 16898c2ecf20Sopenharmony_ci return -EINVAL; 16908c2ecf20Sopenharmony_ci } 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_ci return 0; 16938c2ecf20Sopenharmony_ci} 16948c2ecf20Sopenharmony_ci 16958c2ecf20Sopenharmony_ci/* 16968c2ecf20Sopenharmony_ci * vfe_enum_frame_size - Handle frame size enumeration 16978c2ecf20Sopenharmony_ci * @sd: VFE V4L2 subdevice 16988c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 16998c2ecf20Sopenharmony_ci * @fse: pointer to v4l2_subdev_frame_size_enum structure 17008c2ecf20Sopenharmony_ci * 17018c2ecf20Sopenharmony_ci * Return -EINVAL or zero on success 17028c2ecf20Sopenharmony_ci */ 17038c2ecf20Sopenharmony_cistatic int vfe_enum_frame_size(struct v4l2_subdev *sd, 17048c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 17058c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_size_enum *fse) 17068c2ecf20Sopenharmony_ci{ 17078c2ecf20Sopenharmony_ci struct vfe_line *line = v4l2_get_subdevdata(sd); 17088c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt format; 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci if (fse->index != 0) 17118c2ecf20Sopenharmony_ci return -EINVAL; 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci format.code = fse->code; 17148c2ecf20Sopenharmony_ci format.width = 1; 17158c2ecf20Sopenharmony_ci format.height = 1; 17168c2ecf20Sopenharmony_ci vfe_try_format(line, cfg, fse->pad, &format, fse->which); 17178c2ecf20Sopenharmony_ci fse->min_width = format.width; 17188c2ecf20Sopenharmony_ci fse->min_height = format.height; 17198c2ecf20Sopenharmony_ci 17208c2ecf20Sopenharmony_ci if (format.code != fse->code) 17218c2ecf20Sopenharmony_ci return -EINVAL; 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci format.code = fse->code; 17248c2ecf20Sopenharmony_ci format.width = -1; 17258c2ecf20Sopenharmony_ci format.height = -1; 17268c2ecf20Sopenharmony_ci vfe_try_format(line, cfg, fse->pad, &format, fse->which); 17278c2ecf20Sopenharmony_ci fse->max_width = format.width; 17288c2ecf20Sopenharmony_ci fse->max_height = format.height; 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci return 0; 17318c2ecf20Sopenharmony_ci} 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci/* 17348c2ecf20Sopenharmony_ci * vfe_get_format - Handle get format by pads subdev method 17358c2ecf20Sopenharmony_ci * @sd: VFE V4L2 subdevice 17368c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 17378c2ecf20Sopenharmony_ci * @fmt: pointer to v4l2 subdev format structure 17388c2ecf20Sopenharmony_ci * 17398c2ecf20Sopenharmony_ci * Return -EINVAL or zero on success 17408c2ecf20Sopenharmony_ci */ 17418c2ecf20Sopenharmony_cistatic int vfe_get_format(struct v4l2_subdev *sd, 17428c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 17438c2ecf20Sopenharmony_ci struct v4l2_subdev_format *fmt) 17448c2ecf20Sopenharmony_ci{ 17458c2ecf20Sopenharmony_ci struct vfe_line *line = v4l2_get_subdevdata(sd); 17468c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *format; 17478c2ecf20Sopenharmony_ci 17488c2ecf20Sopenharmony_ci format = __vfe_get_format(line, cfg, fmt->pad, fmt->which); 17498c2ecf20Sopenharmony_ci if (format == NULL) 17508c2ecf20Sopenharmony_ci return -EINVAL; 17518c2ecf20Sopenharmony_ci 17528c2ecf20Sopenharmony_ci fmt->format = *format; 17538c2ecf20Sopenharmony_ci 17548c2ecf20Sopenharmony_ci return 0; 17558c2ecf20Sopenharmony_ci} 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_cistatic int vfe_set_selection(struct v4l2_subdev *sd, 17588c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 17598c2ecf20Sopenharmony_ci struct v4l2_subdev_selection *sel); 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci/* 17628c2ecf20Sopenharmony_ci * vfe_set_format - Handle set format by pads subdev method 17638c2ecf20Sopenharmony_ci * @sd: VFE V4L2 subdevice 17648c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 17658c2ecf20Sopenharmony_ci * @fmt: pointer to v4l2 subdev format structure 17668c2ecf20Sopenharmony_ci * 17678c2ecf20Sopenharmony_ci * Return -EINVAL or zero on success 17688c2ecf20Sopenharmony_ci */ 17698c2ecf20Sopenharmony_cistatic int vfe_set_format(struct v4l2_subdev *sd, 17708c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 17718c2ecf20Sopenharmony_ci struct v4l2_subdev_format *fmt) 17728c2ecf20Sopenharmony_ci{ 17738c2ecf20Sopenharmony_ci struct vfe_line *line = v4l2_get_subdevdata(sd); 17748c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *format; 17758c2ecf20Sopenharmony_ci 17768c2ecf20Sopenharmony_ci format = __vfe_get_format(line, cfg, fmt->pad, fmt->which); 17778c2ecf20Sopenharmony_ci if (format == NULL) 17788c2ecf20Sopenharmony_ci return -EINVAL; 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_ci vfe_try_format(line, cfg, fmt->pad, &fmt->format, fmt->which); 17818c2ecf20Sopenharmony_ci *format = fmt->format; 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_ci if (fmt->pad == MSM_VFE_PAD_SINK) { 17848c2ecf20Sopenharmony_ci struct v4l2_subdev_selection sel = { 0 }; 17858c2ecf20Sopenharmony_ci int ret; 17868c2ecf20Sopenharmony_ci 17878c2ecf20Sopenharmony_ci /* Propagate the format from sink to source */ 17888c2ecf20Sopenharmony_ci format = __vfe_get_format(line, cfg, MSM_VFE_PAD_SRC, 17898c2ecf20Sopenharmony_ci fmt->which); 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci *format = fmt->format; 17928c2ecf20Sopenharmony_ci vfe_try_format(line, cfg, MSM_VFE_PAD_SRC, format, 17938c2ecf20Sopenharmony_ci fmt->which); 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci if (line->id != VFE_LINE_PIX) 17968c2ecf20Sopenharmony_ci return 0; 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci /* Reset sink pad compose selection */ 17998c2ecf20Sopenharmony_ci sel.which = fmt->which; 18008c2ecf20Sopenharmony_ci sel.pad = MSM_VFE_PAD_SINK; 18018c2ecf20Sopenharmony_ci sel.target = V4L2_SEL_TGT_COMPOSE; 18028c2ecf20Sopenharmony_ci sel.r.width = fmt->format.width; 18038c2ecf20Sopenharmony_ci sel.r.height = fmt->format.height; 18048c2ecf20Sopenharmony_ci ret = vfe_set_selection(sd, cfg, &sel); 18058c2ecf20Sopenharmony_ci if (ret < 0) 18068c2ecf20Sopenharmony_ci return ret; 18078c2ecf20Sopenharmony_ci } 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci return 0; 18108c2ecf20Sopenharmony_ci} 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci/* 18138c2ecf20Sopenharmony_ci * vfe_get_selection - Handle get selection by pads subdev method 18148c2ecf20Sopenharmony_ci * @sd: VFE V4L2 subdevice 18158c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 18168c2ecf20Sopenharmony_ci * @sel: pointer to v4l2 subdev selection structure 18178c2ecf20Sopenharmony_ci * 18188c2ecf20Sopenharmony_ci * Return -EINVAL or zero on success 18198c2ecf20Sopenharmony_ci */ 18208c2ecf20Sopenharmony_cistatic int vfe_get_selection(struct v4l2_subdev *sd, 18218c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 18228c2ecf20Sopenharmony_ci struct v4l2_subdev_selection *sel) 18238c2ecf20Sopenharmony_ci{ 18248c2ecf20Sopenharmony_ci struct vfe_line *line = v4l2_get_subdevdata(sd); 18258c2ecf20Sopenharmony_ci struct v4l2_subdev_format fmt = { 0 }; 18268c2ecf20Sopenharmony_ci struct v4l2_rect *rect; 18278c2ecf20Sopenharmony_ci int ret; 18288c2ecf20Sopenharmony_ci 18298c2ecf20Sopenharmony_ci if (line->id != VFE_LINE_PIX) 18308c2ecf20Sopenharmony_ci return -EINVAL; 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci if (sel->pad == MSM_VFE_PAD_SINK) 18338c2ecf20Sopenharmony_ci switch (sel->target) { 18348c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_BOUNDS: 18358c2ecf20Sopenharmony_ci fmt.pad = sel->pad; 18368c2ecf20Sopenharmony_ci fmt.which = sel->which; 18378c2ecf20Sopenharmony_ci ret = vfe_get_format(sd, cfg, &fmt); 18388c2ecf20Sopenharmony_ci if (ret < 0) 18398c2ecf20Sopenharmony_ci return ret; 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci sel->r.left = 0; 18428c2ecf20Sopenharmony_ci sel->r.top = 0; 18438c2ecf20Sopenharmony_ci sel->r.width = fmt.format.width; 18448c2ecf20Sopenharmony_ci sel->r.height = fmt.format.height; 18458c2ecf20Sopenharmony_ci break; 18468c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 18478c2ecf20Sopenharmony_ci rect = __vfe_get_compose(line, cfg, sel->which); 18488c2ecf20Sopenharmony_ci if (rect == NULL) 18498c2ecf20Sopenharmony_ci return -EINVAL; 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci sel->r = *rect; 18528c2ecf20Sopenharmony_ci break; 18538c2ecf20Sopenharmony_ci default: 18548c2ecf20Sopenharmony_ci return -EINVAL; 18558c2ecf20Sopenharmony_ci } 18568c2ecf20Sopenharmony_ci else if (sel->pad == MSM_VFE_PAD_SRC) 18578c2ecf20Sopenharmony_ci switch (sel->target) { 18588c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 18598c2ecf20Sopenharmony_ci rect = __vfe_get_compose(line, cfg, sel->which); 18608c2ecf20Sopenharmony_ci if (rect == NULL) 18618c2ecf20Sopenharmony_ci return -EINVAL; 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci sel->r.left = rect->left; 18648c2ecf20Sopenharmony_ci sel->r.top = rect->top; 18658c2ecf20Sopenharmony_ci sel->r.width = rect->width; 18668c2ecf20Sopenharmony_ci sel->r.height = rect->height; 18678c2ecf20Sopenharmony_ci break; 18688c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP: 18698c2ecf20Sopenharmony_ci rect = __vfe_get_crop(line, cfg, sel->which); 18708c2ecf20Sopenharmony_ci if (rect == NULL) 18718c2ecf20Sopenharmony_ci return -EINVAL; 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci sel->r = *rect; 18748c2ecf20Sopenharmony_ci break; 18758c2ecf20Sopenharmony_ci default: 18768c2ecf20Sopenharmony_ci return -EINVAL; 18778c2ecf20Sopenharmony_ci } 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_ci return 0; 18808c2ecf20Sopenharmony_ci} 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci/* 18838c2ecf20Sopenharmony_ci * vfe_set_selection - Handle set selection by pads subdev method 18848c2ecf20Sopenharmony_ci * @sd: VFE V4L2 subdevice 18858c2ecf20Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 18868c2ecf20Sopenharmony_ci * @sel: pointer to v4l2 subdev selection structure 18878c2ecf20Sopenharmony_ci * 18888c2ecf20Sopenharmony_ci * Return -EINVAL or zero on success 18898c2ecf20Sopenharmony_ci */ 18908c2ecf20Sopenharmony_cistatic int vfe_set_selection(struct v4l2_subdev *sd, 18918c2ecf20Sopenharmony_ci struct v4l2_subdev_pad_config *cfg, 18928c2ecf20Sopenharmony_ci struct v4l2_subdev_selection *sel) 18938c2ecf20Sopenharmony_ci{ 18948c2ecf20Sopenharmony_ci struct vfe_line *line = v4l2_get_subdevdata(sd); 18958c2ecf20Sopenharmony_ci struct v4l2_rect *rect; 18968c2ecf20Sopenharmony_ci int ret; 18978c2ecf20Sopenharmony_ci 18988c2ecf20Sopenharmony_ci if (line->id != VFE_LINE_PIX) 18998c2ecf20Sopenharmony_ci return -EINVAL; 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci if (sel->target == V4L2_SEL_TGT_COMPOSE && 19028c2ecf20Sopenharmony_ci sel->pad == MSM_VFE_PAD_SINK) { 19038c2ecf20Sopenharmony_ci struct v4l2_subdev_selection crop = { 0 }; 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_ci rect = __vfe_get_compose(line, cfg, sel->which); 19068c2ecf20Sopenharmony_ci if (rect == NULL) 19078c2ecf20Sopenharmony_ci return -EINVAL; 19088c2ecf20Sopenharmony_ci 19098c2ecf20Sopenharmony_ci vfe_try_compose(line, cfg, &sel->r, sel->which); 19108c2ecf20Sopenharmony_ci *rect = sel->r; 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci /* Reset source crop selection */ 19138c2ecf20Sopenharmony_ci crop.which = sel->which; 19148c2ecf20Sopenharmony_ci crop.pad = MSM_VFE_PAD_SRC; 19158c2ecf20Sopenharmony_ci crop.target = V4L2_SEL_TGT_CROP; 19168c2ecf20Sopenharmony_ci crop.r = *rect; 19178c2ecf20Sopenharmony_ci ret = vfe_set_selection(sd, cfg, &crop); 19188c2ecf20Sopenharmony_ci } else if (sel->target == V4L2_SEL_TGT_CROP && 19198c2ecf20Sopenharmony_ci sel->pad == MSM_VFE_PAD_SRC) { 19208c2ecf20Sopenharmony_ci struct v4l2_subdev_format fmt = { 0 }; 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci rect = __vfe_get_crop(line, cfg, sel->which); 19238c2ecf20Sopenharmony_ci if (rect == NULL) 19248c2ecf20Sopenharmony_ci return -EINVAL; 19258c2ecf20Sopenharmony_ci 19268c2ecf20Sopenharmony_ci vfe_try_crop(line, cfg, &sel->r, sel->which); 19278c2ecf20Sopenharmony_ci *rect = sel->r; 19288c2ecf20Sopenharmony_ci 19298c2ecf20Sopenharmony_ci /* Reset source pad format width and height */ 19308c2ecf20Sopenharmony_ci fmt.which = sel->which; 19318c2ecf20Sopenharmony_ci fmt.pad = MSM_VFE_PAD_SRC; 19328c2ecf20Sopenharmony_ci ret = vfe_get_format(sd, cfg, &fmt); 19338c2ecf20Sopenharmony_ci if (ret < 0) 19348c2ecf20Sopenharmony_ci return ret; 19358c2ecf20Sopenharmony_ci 19368c2ecf20Sopenharmony_ci fmt.format.width = rect->width; 19378c2ecf20Sopenharmony_ci fmt.format.height = rect->height; 19388c2ecf20Sopenharmony_ci ret = vfe_set_format(sd, cfg, &fmt); 19398c2ecf20Sopenharmony_ci } else { 19408c2ecf20Sopenharmony_ci ret = -EINVAL; 19418c2ecf20Sopenharmony_ci } 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_ci return ret; 19448c2ecf20Sopenharmony_ci} 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_ci/* 19478c2ecf20Sopenharmony_ci * vfe_init_formats - Initialize formats on all pads 19488c2ecf20Sopenharmony_ci * @sd: VFE V4L2 subdevice 19498c2ecf20Sopenharmony_ci * @fh: V4L2 subdev file handle 19508c2ecf20Sopenharmony_ci * 19518c2ecf20Sopenharmony_ci * Initialize all pad formats with default values. 19528c2ecf20Sopenharmony_ci * 19538c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise 19548c2ecf20Sopenharmony_ci */ 19558c2ecf20Sopenharmony_cistatic int vfe_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 19568c2ecf20Sopenharmony_ci{ 19578c2ecf20Sopenharmony_ci struct v4l2_subdev_format format = { 19588c2ecf20Sopenharmony_ci .pad = MSM_VFE_PAD_SINK, 19598c2ecf20Sopenharmony_ci .which = fh ? V4L2_SUBDEV_FORMAT_TRY : 19608c2ecf20Sopenharmony_ci V4L2_SUBDEV_FORMAT_ACTIVE, 19618c2ecf20Sopenharmony_ci .format = { 19628c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_UYVY8_2X8, 19638c2ecf20Sopenharmony_ci .width = 1920, 19648c2ecf20Sopenharmony_ci .height = 1080 19658c2ecf20Sopenharmony_ci } 19668c2ecf20Sopenharmony_ci }; 19678c2ecf20Sopenharmony_ci 19688c2ecf20Sopenharmony_ci return vfe_set_format(sd, fh ? fh->pad : NULL, &format); 19698c2ecf20Sopenharmony_ci} 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci/* 19728c2ecf20Sopenharmony_ci * msm_vfe_subdev_init - Initialize VFE device structure and resources 19738c2ecf20Sopenharmony_ci * @vfe: VFE device 19748c2ecf20Sopenharmony_ci * @res: VFE module resources table 19758c2ecf20Sopenharmony_ci * 19768c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise 19778c2ecf20Sopenharmony_ci */ 19788c2ecf20Sopenharmony_ciint msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, 19798c2ecf20Sopenharmony_ci const struct resources *res, u8 id) 19808c2ecf20Sopenharmony_ci{ 19818c2ecf20Sopenharmony_ci struct device *dev = camss->dev; 19828c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 19838c2ecf20Sopenharmony_ci struct resource *r; 19848c2ecf20Sopenharmony_ci int i, j; 19858c2ecf20Sopenharmony_ci int ret; 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci vfe->isr_ops.reset_ack = vfe_isr_reset_ack; 19888c2ecf20Sopenharmony_ci vfe->isr_ops.halt_ack = vfe_isr_halt_ack; 19898c2ecf20Sopenharmony_ci vfe->isr_ops.reg_update = vfe_isr_reg_update; 19908c2ecf20Sopenharmony_ci vfe->isr_ops.sof = vfe_isr_sof; 19918c2ecf20Sopenharmony_ci vfe->isr_ops.comp_done = vfe_isr_comp_done; 19928c2ecf20Sopenharmony_ci vfe->isr_ops.wm_done = vfe_isr_wm_done; 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_ci if (camss->version == CAMSS_8x16) 19958c2ecf20Sopenharmony_ci vfe->ops = &vfe_ops_4_1; 19968c2ecf20Sopenharmony_ci else if (camss->version == CAMSS_8x96) 19978c2ecf20Sopenharmony_ci vfe->ops = &vfe_ops_4_7; 19988c2ecf20Sopenharmony_ci else 19998c2ecf20Sopenharmony_ci return -EINVAL; 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci /* Memory */ 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ci r = platform_get_resource_byname(pdev, IORESOURCE_MEM, res->reg[0]); 20048c2ecf20Sopenharmony_ci vfe->base = devm_ioremap_resource(dev, r); 20058c2ecf20Sopenharmony_ci if (IS_ERR(vfe->base)) { 20068c2ecf20Sopenharmony_ci dev_err(dev, "could not map memory\n"); 20078c2ecf20Sopenharmony_ci return PTR_ERR(vfe->base); 20088c2ecf20Sopenharmony_ci } 20098c2ecf20Sopenharmony_ci 20108c2ecf20Sopenharmony_ci /* Interrupt */ 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_ci r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, 20138c2ecf20Sopenharmony_ci res->interrupt[0]); 20148c2ecf20Sopenharmony_ci if (!r) { 20158c2ecf20Sopenharmony_ci dev_err(dev, "missing IRQ\n"); 20168c2ecf20Sopenharmony_ci return -EINVAL; 20178c2ecf20Sopenharmony_ci } 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_ci vfe->irq = r->start; 20208c2ecf20Sopenharmony_ci snprintf(vfe->irq_name, sizeof(vfe->irq_name), "%s_%s%d", 20218c2ecf20Sopenharmony_ci dev_name(dev), MSM_VFE_NAME, vfe->id); 20228c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, vfe->irq, vfe->ops->isr, 20238c2ecf20Sopenharmony_ci IRQF_TRIGGER_RISING, vfe->irq_name, vfe); 20248c2ecf20Sopenharmony_ci if (ret < 0) { 20258c2ecf20Sopenharmony_ci dev_err(dev, "request_irq failed: %d\n", ret); 20268c2ecf20Sopenharmony_ci return ret; 20278c2ecf20Sopenharmony_ci } 20288c2ecf20Sopenharmony_ci 20298c2ecf20Sopenharmony_ci /* Clocks */ 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_ci vfe->nclocks = 0; 20328c2ecf20Sopenharmony_ci while (res->clock[vfe->nclocks]) 20338c2ecf20Sopenharmony_ci vfe->nclocks++; 20348c2ecf20Sopenharmony_ci 20358c2ecf20Sopenharmony_ci vfe->clock = devm_kcalloc(dev, vfe->nclocks, sizeof(*vfe->clock), 20368c2ecf20Sopenharmony_ci GFP_KERNEL); 20378c2ecf20Sopenharmony_ci if (!vfe->clock) 20388c2ecf20Sopenharmony_ci return -ENOMEM; 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_ci for (i = 0; i < vfe->nclocks; i++) { 20418c2ecf20Sopenharmony_ci struct camss_clock *clock = &vfe->clock[i]; 20428c2ecf20Sopenharmony_ci 20438c2ecf20Sopenharmony_ci clock->clk = devm_clk_get(dev, res->clock[i]); 20448c2ecf20Sopenharmony_ci if (IS_ERR(clock->clk)) 20458c2ecf20Sopenharmony_ci return PTR_ERR(clock->clk); 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_ci clock->name = res->clock[i]; 20488c2ecf20Sopenharmony_ci 20498c2ecf20Sopenharmony_ci clock->nfreqs = 0; 20508c2ecf20Sopenharmony_ci while (res->clock_rate[i][clock->nfreqs]) 20518c2ecf20Sopenharmony_ci clock->nfreqs++; 20528c2ecf20Sopenharmony_ci 20538c2ecf20Sopenharmony_ci if (!clock->nfreqs) { 20548c2ecf20Sopenharmony_ci clock->freq = NULL; 20558c2ecf20Sopenharmony_ci continue; 20568c2ecf20Sopenharmony_ci } 20578c2ecf20Sopenharmony_ci 20588c2ecf20Sopenharmony_ci clock->freq = devm_kcalloc(dev, 20598c2ecf20Sopenharmony_ci clock->nfreqs, 20608c2ecf20Sopenharmony_ci sizeof(*clock->freq), 20618c2ecf20Sopenharmony_ci GFP_KERNEL); 20628c2ecf20Sopenharmony_ci if (!clock->freq) 20638c2ecf20Sopenharmony_ci return -ENOMEM; 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_ci for (j = 0; j < clock->nfreqs; j++) 20668c2ecf20Sopenharmony_ci clock->freq[j] = res->clock_rate[i][j]; 20678c2ecf20Sopenharmony_ci } 20688c2ecf20Sopenharmony_ci 20698c2ecf20Sopenharmony_ci mutex_init(&vfe->power_lock); 20708c2ecf20Sopenharmony_ci vfe->power_count = 0; 20718c2ecf20Sopenharmony_ci 20728c2ecf20Sopenharmony_ci mutex_init(&vfe->stream_lock); 20738c2ecf20Sopenharmony_ci vfe->stream_count = 0; 20748c2ecf20Sopenharmony_ci 20758c2ecf20Sopenharmony_ci spin_lock_init(&vfe->output_lock); 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci vfe->camss = camss; 20788c2ecf20Sopenharmony_ci vfe->id = id; 20798c2ecf20Sopenharmony_ci vfe->reg_update = 0; 20808c2ecf20Sopenharmony_ci 20818c2ecf20Sopenharmony_ci for (i = VFE_LINE_RDI0; i <= VFE_LINE_PIX; i++) { 20828c2ecf20Sopenharmony_ci struct vfe_line *l = &vfe->line[i]; 20838c2ecf20Sopenharmony_ci 20848c2ecf20Sopenharmony_ci l->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 20858c2ecf20Sopenharmony_ci l->video_out.camss = camss; 20868c2ecf20Sopenharmony_ci l->id = i; 20878c2ecf20Sopenharmony_ci init_completion(&l->output.sof); 20888c2ecf20Sopenharmony_ci init_completion(&l->output.reg_update); 20898c2ecf20Sopenharmony_ci 20908c2ecf20Sopenharmony_ci if (camss->version == CAMSS_8x16) { 20918c2ecf20Sopenharmony_ci if (i == VFE_LINE_PIX) { 20928c2ecf20Sopenharmony_ci l->formats = formats_pix_8x16; 20938c2ecf20Sopenharmony_ci l->nformats = ARRAY_SIZE(formats_pix_8x16); 20948c2ecf20Sopenharmony_ci } else { 20958c2ecf20Sopenharmony_ci l->formats = formats_rdi_8x16; 20968c2ecf20Sopenharmony_ci l->nformats = ARRAY_SIZE(formats_rdi_8x16); 20978c2ecf20Sopenharmony_ci } 20988c2ecf20Sopenharmony_ci } else if (camss->version == CAMSS_8x96) { 20998c2ecf20Sopenharmony_ci if (i == VFE_LINE_PIX) { 21008c2ecf20Sopenharmony_ci l->formats = formats_pix_8x96; 21018c2ecf20Sopenharmony_ci l->nformats = ARRAY_SIZE(formats_pix_8x96); 21028c2ecf20Sopenharmony_ci } else { 21038c2ecf20Sopenharmony_ci l->formats = formats_rdi_8x96; 21048c2ecf20Sopenharmony_ci l->nformats = ARRAY_SIZE(formats_rdi_8x96); 21058c2ecf20Sopenharmony_ci } 21068c2ecf20Sopenharmony_ci } else { 21078c2ecf20Sopenharmony_ci return -EINVAL; 21088c2ecf20Sopenharmony_ci } 21098c2ecf20Sopenharmony_ci } 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_ci init_completion(&vfe->reset_complete); 21128c2ecf20Sopenharmony_ci init_completion(&vfe->halt_complete); 21138c2ecf20Sopenharmony_ci 21148c2ecf20Sopenharmony_ci return 0; 21158c2ecf20Sopenharmony_ci} 21168c2ecf20Sopenharmony_ci 21178c2ecf20Sopenharmony_ci/* 21188c2ecf20Sopenharmony_ci * msm_vfe_get_vfe_id - Get VFE HW module id 21198c2ecf20Sopenharmony_ci * @entity: Pointer to VFE media entity structure 21208c2ecf20Sopenharmony_ci * @id: Return CSID HW module id here 21218c2ecf20Sopenharmony_ci */ 21228c2ecf20Sopenharmony_civoid msm_vfe_get_vfe_id(struct media_entity *entity, u8 *id) 21238c2ecf20Sopenharmony_ci{ 21248c2ecf20Sopenharmony_ci struct v4l2_subdev *sd; 21258c2ecf20Sopenharmony_ci struct vfe_line *line; 21268c2ecf20Sopenharmony_ci struct vfe_device *vfe; 21278c2ecf20Sopenharmony_ci 21288c2ecf20Sopenharmony_ci sd = media_entity_to_v4l2_subdev(entity); 21298c2ecf20Sopenharmony_ci line = v4l2_get_subdevdata(sd); 21308c2ecf20Sopenharmony_ci vfe = to_vfe(line); 21318c2ecf20Sopenharmony_ci 21328c2ecf20Sopenharmony_ci *id = vfe->id; 21338c2ecf20Sopenharmony_ci} 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci/* 21368c2ecf20Sopenharmony_ci * msm_vfe_get_vfe_line_id - Get VFE line id by media entity 21378c2ecf20Sopenharmony_ci * @entity: Pointer to VFE media entity structure 21388c2ecf20Sopenharmony_ci * @id: Return VFE line id here 21398c2ecf20Sopenharmony_ci */ 21408c2ecf20Sopenharmony_civoid msm_vfe_get_vfe_line_id(struct media_entity *entity, enum vfe_line_id *id) 21418c2ecf20Sopenharmony_ci{ 21428c2ecf20Sopenharmony_ci struct v4l2_subdev *sd; 21438c2ecf20Sopenharmony_ci struct vfe_line *line; 21448c2ecf20Sopenharmony_ci 21458c2ecf20Sopenharmony_ci sd = media_entity_to_v4l2_subdev(entity); 21468c2ecf20Sopenharmony_ci line = v4l2_get_subdevdata(sd); 21478c2ecf20Sopenharmony_ci 21488c2ecf20Sopenharmony_ci *id = line->id; 21498c2ecf20Sopenharmony_ci} 21508c2ecf20Sopenharmony_ci 21518c2ecf20Sopenharmony_ci/* 21528c2ecf20Sopenharmony_ci * vfe_link_setup - Setup VFE connections 21538c2ecf20Sopenharmony_ci * @entity: Pointer to media entity structure 21548c2ecf20Sopenharmony_ci * @local: Pointer to local pad 21558c2ecf20Sopenharmony_ci * @remote: Pointer to remote pad 21568c2ecf20Sopenharmony_ci * @flags: Link flags 21578c2ecf20Sopenharmony_ci * 21588c2ecf20Sopenharmony_ci * Return 0 on success 21598c2ecf20Sopenharmony_ci */ 21608c2ecf20Sopenharmony_cistatic int vfe_link_setup(struct media_entity *entity, 21618c2ecf20Sopenharmony_ci const struct media_pad *local, 21628c2ecf20Sopenharmony_ci const struct media_pad *remote, u32 flags) 21638c2ecf20Sopenharmony_ci{ 21648c2ecf20Sopenharmony_ci if (flags & MEDIA_LNK_FL_ENABLED) 21658c2ecf20Sopenharmony_ci if (media_entity_remote_pad(local)) 21668c2ecf20Sopenharmony_ci return -EBUSY; 21678c2ecf20Sopenharmony_ci 21688c2ecf20Sopenharmony_ci return 0; 21698c2ecf20Sopenharmony_ci} 21708c2ecf20Sopenharmony_ci 21718c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_core_ops vfe_core_ops = { 21728c2ecf20Sopenharmony_ci .s_power = vfe_set_power, 21738c2ecf20Sopenharmony_ci}; 21748c2ecf20Sopenharmony_ci 21758c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_video_ops vfe_video_ops = { 21768c2ecf20Sopenharmony_ci .s_stream = vfe_set_stream, 21778c2ecf20Sopenharmony_ci}; 21788c2ecf20Sopenharmony_ci 21798c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_pad_ops vfe_pad_ops = { 21808c2ecf20Sopenharmony_ci .enum_mbus_code = vfe_enum_mbus_code, 21818c2ecf20Sopenharmony_ci .enum_frame_size = vfe_enum_frame_size, 21828c2ecf20Sopenharmony_ci .get_fmt = vfe_get_format, 21838c2ecf20Sopenharmony_ci .set_fmt = vfe_set_format, 21848c2ecf20Sopenharmony_ci .get_selection = vfe_get_selection, 21858c2ecf20Sopenharmony_ci .set_selection = vfe_set_selection, 21868c2ecf20Sopenharmony_ci}; 21878c2ecf20Sopenharmony_ci 21888c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_ops vfe_v4l2_ops = { 21898c2ecf20Sopenharmony_ci .core = &vfe_core_ops, 21908c2ecf20Sopenharmony_ci .video = &vfe_video_ops, 21918c2ecf20Sopenharmony_ci .pad = &vfe_pad_ops, 21928c2ecf20Sopenharmony_ci}; 21938c2ecf20Sopenharmony_ci 21948c2ecf20Sopenharmony_cistatic const struct v4l2_subdev_internal_ops vfe_v4l2_internal_ops = { 21958c2ecf20Sopenharmony_ci .open = vfe_init_formats, 21968c2ecf20Sopenharmony_ci}; 21978c2ecf20Sopenharmony_ci 21988c2ecf20Sopenharmony_cistatic const struct media_entity_operations vfe_media_ops = { 21998c2ecf20Sopenharmony_ci .link_setup = vfe_link_setup, 22008c2ecf20Sopenharmony_ci .link_validate = v4l2_subdev_link_validate, 22018c2ecf20Sopenharmony_ci}; 22028c2ecf20Sopenharmony_ci 22038c2ecf20Sopenharmony_cistatic const struct camss_video_ops camss_vfe_video_ops = { 22048c2ecf20Sopenharmony_ci .queue_buffer = vfe_queue_buffer, 22058c2ecf20Sopenharmony_ci .flush_buffers = vfe_flush_buffers, 22068c2ecf20Sopenharmony_ci}; 22078c2ecf20Sopenharmony_ci 22088c2ecf20Sopenharmony_ci/* 22098c2ecf20Sopenharmony_ci * msm_vfe_register_entities - Register subdev node for VFE module 22108c2ecf20Sopenharmony_ci * @vfe: VFE device 22118c2ecf20Sopenharmony_ci * @v4l2_dev: V4L2 device 22128c2ecf20Sopenharmony_ci * 22138c2ecf20Sopenharmony_ci * Initialize and register a subdev node for the VFE module. Then 22148c2ecf20Sopenharmony_ci * call msm_video_register() to register the video device node which 22158c2ecf20Sopenharmony_ci * will be connected to this subdev node. Then actually create the 22168c2ecf20Sopenharmony_ci * media link between them. 22178c2ecf20Sopenharmony_ci * 22188c2ecf20Sopenharmony_ci * Return 0 on success or a negative error code otherwise 22198c2ecf20Sopenharmony_ci */ 22208c2ecf20Sopenharmony_ciint msm_vfe_register_entities(struct vfe_device *vfe, 22218c2ecf20Sopenharmony_ci struct v4l2_device *v4l2_dev) 22228c2ecf20Sopenharmony_ci{ 22238c2ecf20Sopenharmony_ci struct device *dev = vfe->camss->dev; 22248c2ecf20Sopenharmony_ci struct v4l2_subdev *sd; 22258c2ecf20Sopenharmony_ci struct media_pad *pads; 22268c2ecf20Sopenharmony_ci struct camss_video *video_out; 22278c2ecf20Sopenharmony_ci int ret; 22288c2ecf20Sopenharmony_ci int i; 22298c2ecf20Sopenharmony_ci 22308c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vfe->line); i++) { 22318c2ecf20Sopenharmony_ci char name[32]; 22328c2ecf20Sopenharmony_ci 22338c2ecf20Sopenharmony_ci sd = &vfe->line[i].subdev; 22348c2ecf20Sopenharmony_ci pads = vfe->line[i].pads; 22358c2ecf20Sopenharmony_ci video_out = &vfe->line[i].video_out; 22368c2ecf20Sopenharmony_ci 22378c2ecf20Sopenharmony_ci v4l2_subdev_init(sd, &vfe_v4l2_ops); 22388c2ecf20Sopenharmony_ci sd->internal_ops = &vfe_v4l2_internal_ops; 22398c2ecf20Sopenharmony_ci sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 22408c2ecf20Sopenharmony_ci if (i == VFE_LINE_PIX) 22418c2ecf20Sopenharmony_ci snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s", 22428c2ecf20Sopenharmony_ci MSM_VFE_NAME, vfe->id, "pix"); 22438c2ecf20Sopenharmony_ci else 22448c2ecf20Sopenharmony_ci snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s%d", 22458c2ecf20Sopenharmony_ci MSM_VFE_NAME, vfe->id, "rdi", i); 22468c2ecf20Sopenharmony_ci 22478c2ecf20Sopenharmony_ci v4l2_set_subdevdata(sd, &vfe->line[i]); 22488c2ecf20Sopenharmony_ci 22498c2ecf20Sopenharmony_ci ret = vfe_init_formats(sd, NULL); 22508c2ecf20Sopenharmony_ci if (ret < 0) { 22518c2ecf20Sopenharmony_ci dev_err(dev, "Failed to init format: %d\n", ret); 22528c2ecf20Sopenharmony_ci goto error_init; 22538c2ecf20Sopenharmony_ci } 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_ci pads[MSM_VFE_PAD_SINK].flags = MEDIA_PAD_FL_SINK; 22568c2ecf20Sopenharmony_ci pads[MSM_VFE_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; 22578c2ecf20Sopenharmony_ci 22588c2ecf20Sopenharmony_ci sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; 22598c2ecf20Sopenharmony_ci sd->entity.ops = &vfe_media_ops; 22608c2ecf20Sopenharmony_ci ret = media_entity_pads_init(&sd->entity, MSM_VFE_PADS_NUM, 22618c2ecf20Sopenharmony_ci pads); 22628c2ecf20Sopenharmony_ci if (ret < 0) { 22638c2ecf20Sopenharmony_ci dev_err(dev, "Failed to init media entity: %d\n", ret); 22648c2ecf20Sopenharmony_ci goto error_init; 22658c2ecf20Sopenharmony_ci } 22668c2ecf20Sopenharmony_ci 22678c2ecf20Sopenharmony_ci ret = v4l2_device_register_subdev(v4l2_dev, sd); 22688c2ecf20Sopenharmony_ci if (ret < 0) { 22698c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register subdev: %d\n", ret); 22708c2ecf20Sopenharmony_ci goto error_reg_subdev; 22718c2ecf20Sopenharmony_ci } 22728c2ecf20Sopenharmony_ci 22738c2ecf20Sopenharmony_ci video_out->ops = &camss_vfe_video_ops; 22748c2ecf20Sopenharmony_ci video_out->bpl_alignment = 8; 22758c2ecf20Sopenharmony_ci video_out->line_based = 0; 22768c2ecf20Sopenharmony_ci if (i == VFE_LINE_PIX) { 22778c2ecf20Sopenharmony_ci video_out->bpl_alignment = 16; 22788c2ecf20Sopenharmony_ci video_out->line_based = 1; 22798c2ecf20Sopenharmony_ci } 22808c2ecf20Sopenharmony_ci snprintf(name, ARRAY_SIZE(name), "%s%d_%s%d", 22818c2ecf20Sopenharmony_ci MSM_VFE_NAME, vfe->id, "video", i); 22828c2ecf20Sopenharmony_ci ret = msm_video_register(video_out, v4l2_dev, name, 22838c2ecf20Sopenharmony_ci i == VFE_LINE_PIX ? 1 : 0); 22848c2ecf20Sopenharmony_ci if (ret < 0) { 22858c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register video node: %d\n", 22868c2ecf20Sopenharmony_ci ret); 22878c2ecf20Sopenharmony_ci goto error_reg_video; 22888c2ecf20Sopenharmony_ci } 22898c2ecf20Sopenharmony_ci 22908c2ecf20Sopenharmony_ci ret = media_create_pad_link( 22918c2ecf20Sopenharmony_ci &sd->entity, MSM_VFE_PAD_SRC, 22928c2ecf20Sopenharmony_ci &video_out->vdev.entity, 0, 22938c2ecf20Sopenharmony_ci MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); 22948c2ecf20Sopenharmony_ci if (ret < 0) { 22958c2ecf20Sopenharmony_ci dev_err(dev, "Failed to link %s->%s entities: %d\n", 22968c2ecf20Sopenharmony_ci sd->entity.name, video_out->vdev.entity.name, 22978c2ecf20Sopenharmony_ci ret); 22988c2ecf20Sopenharmony_ci goto error_link; 22998c2ecf20Sopenharmony_ci } 23008c2ecf20Sopenharmony_ci } 23018c2ecf20Sopenharmony_ci 23028c2ecf20Sopenharmony_ci return 0; 23038c2ecf20Sopenharmony_ci 23048c2ecf20Sopenharmony_cierror_link: 23058c2ecf20Sopenharmony_ci msm_video_unregister(video_out); 23068c2ecf20Sopenharmony_ci 23078c2ecf20Sopenharmony_cierror_reg_video: 23088c2ecf20Sopenharmony_ci v4l2_device_unregister_subdev(sd); 23098c2ecf20Sopenharmony_ci 23108c2ecf20Sopenharmony_cierror_reg_subdev: 23118c2ecf20Sopenharmony_ci media_entity_cleanup(&sd->entity); 23128c2ecf20Sopenharmony_ci 23138c2ecf20Sopenharmony_cierror_init: 23148c2ecf20Sopenharmony_ci for (i--; i >= 0; i--) { 23158c2ecf20Sopenharmony_ci sd = &vfe->line[i].subdev; 23168c2ecf20Sopenharmony_ci video_out = &vfe->line[i].video_out; 23178c2ecf20Sopenharmony_ci 23188c2ecf20Sopenharmony_ci msm_video_unregister(video_out); 23198c2ecf20Sopenharmony_ci v4l2_device_unregister_subdev(sd); 23208c2ecf20Sopenharmony_ci media_entity_cleanup(&sd->entity); 23218c2ecf20Sopenharmony_ci } 23228c2ecf20Sopenharmony_ci 23238c2ecf20Sopenharmony_ci return ret; 23248c2ecf20Sopenharmony_ci} 23258c2ecf20Sopenharmony_ci 23268c2ecf20Sopenharmony_ci/* 23278c2ecf20Sopenharmony_ci * msm_vfe_unregister_entities - Unregister VFE module subdev node 23288c2ecf20Sopenharmony_ci * @vfe: VFE device 23298c2ecf20Sopenharmony_ci */ 23308c2ecf20Sopenharmony_civoid msm_vfe_unregister_entities(struct vfe_device *vfe) 23318c2ecf20Sopenharmony_ci{ 23328c2ecf20Sopenharmony_ci int i; 23338c2ecf20Sopenharmony_ci 23348c2ecf20Sopenharmony_ci mutex_destroy(&vfe->power_lock); 23358c2ecf20Sopenharmony_ci mutex_destroy(&vfe->stream_lock); 23368c2ecf20Sopenharmony_ci 23378c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vfe->line); i++) { 23388c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = &vfe->line[i].subdev; 23398c2ecf20Sopenharmony_ci struct camss_video *video_out = &vfe->line[i].video_out; 23408c2ecf20Sopenharmony_ci 23418c2ecf20Sopenharmony_ci msm_video_unregister(video_out); 23428c2ecf20Sopenharmony_ci v4l2_device_unregister_subdev(sd); 23438c2ecf20Sopenharmony_ci media_entity_cleanup(&sd->entity); 23448c2ecf20Sopenharmony_ci } 23458c2ecf20Sopenharmony_ci} 2346