162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * camss-vfe.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Qualcomm MSM Camera Subsystem - VFE (Video Front End) Module 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. 862306a36Sopenharmony_ci * Copyright (C) 2015-2018 Linaro Ltd. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci#include <linux/clk.h> 1162306a36Sopenharmony_ci#include <linux/completion.h> 1262306a36Sopenharmony_ci#include <linux/interrupt.h> 1362306a36Sopenharmony_ci#include <linux/iommu.h> 1462306a36Sopenharmony_ci#include <linux/mutex.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1862306a36Sopenharmony_ci#include <linux/spinlock_types.h> 1962306a36Sopenharmony_ci#include <linux/spinlock.h> 2062306a36Sopenharmony_ci#include <media/media-entity.h> 2162306a36Sopenharmony_ci#include <media/v4l2-device.h> 2262306a36Sopenharmony_ci#include <media/v4l2-subdev.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "camss-vfe.h" 2562306a36Sopenharmony_ci#include "camss.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define MSM_VFE_NAME "msm_vfe" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* VFE reset timeout */ 3062306a36Sopenharmony_ci#define VFE_RESET_TIMEOUT_MS 50 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define SCALER_RATIO_MAX 16 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistruct vfe_format { 3562306a36Sopenharmony_ci u32 code; 3662306a36Sopenharmony_ci u8 bpp; 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic const struct vfe_format formats_rdi_8x16[] = { 4062306a36Sopenharmony_ci { MEDIA_BUS_FMT_UYVY8_2X8, 8 }, 4162306a36Sopenharmony_ci { MEDIA_BUS_FMT_VYUY8_2X8, 8 }, 4262306a36Sopenharmony_ci { MEDIA_BUS_FMT_YUYV8_2X8, 8 }, 4362306a36Sopenharmony_ci { MEDIA_BUS_FMT_YVYU8_2X8, 8 }, 4462306a36Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, 4562306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, 4662306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, 4762306a36Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, 4862306a36Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, 4962306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, 5062306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, 5162306a36Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, 5262306a36Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, 5362306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, 5462306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, 5562306a36Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, 5662306a36Sopenharmony_ci { MEDIA_BUS_FMT_Y10_1X10, 10 }, 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic const struct vfe_format formats_pix_8x16[] = { 6062306a36Sopenharmony_ci { MEDIA_BUS_FMT_UYVY8_2X8, 8 }, 6162306a36Sopenharmony_ci { MEDIA_BUS_FMT_VYUY8_2X8, 8 }, 6262306a36Sopenharmony_ci { MEDIA_BUS_FMT_YUYV8_2X8, 8 }, 6362306a36Sopenharmony_ci { MEDIA_BUS_FMT_YVYU8_2X8, 8 }, 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic const struct vfe_format formats_rdi_8x96[] = { 6762306a36Sopenharmony_ci { MEDIA_BUS_FMT_UYVY8_2X8, 8 }, 6862306a36Sopenharmony_ci { MEDIA_BUS_FMT_VYUY8_2X8, 8 }, 6962306a36Sopenharmony_ci { MEDIA_BUS_FMT_YUYV8_2X8, 8 }, 7062306a36Sopenharmony_ci { MEDIA_BUS_FMT_YVYU8_2X8, 8 }, 7162306a36Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, 7262306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, 7362306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, 7462306a36Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, 7562306a36Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, 7662306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, 7762306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, 7862306a36Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, 7962306a36Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16 }, 8062306a36Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, 8162306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, 8262306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, 8362306a36Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, 8462306a36Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR14_1X14, 14 }, 8562306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG14_1X14, 14 }, 8662306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG14_1X14, 14 }, 8762306a36Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB14_1X14, 14 }, 8862306a36Sopenharmony_ci { MEDIA_BUS_FMT_Y10_1X10, 10 }, 8962306a36Sopenharmony_ci { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16 }, 9062306a36Sopenharmony_ci}; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic const struct vfe_format formats_pix_8x96[] = { 9362306a36Sopenharmony_ci { MEDIA_BUS_FMT_UYVY8_2X8, 8 }, 9462306a36Sopenharmony_ci { MEDIA_BUS_FMT_VYUY8_2X8, 8 }, 9562306a36Sopenharmony_ci { MEDIA_BUS_FMT_YUYV8_2X8, 8 }, 9662306a36Sopenharmony_ci { MEDIA_BUS_FMT_YVYU8_2X8, 8 }, 9762306a36Sopenharmony_ci}; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic const struct vfe_format formats_rdi_845[] = { 10062306a36Sopenharmony_ci { MEDIA_BUS_FMT_UYVY8_2X8, 8 }, 10162306a36Sopenharmony_ci { MEDIA_BUS_FMT_VYUY8_2X8, 8 }, 10262306a36Sopenharmony_ci { MEDIA_BUS_FMT_YUYV8_2X8, 8 }, 10362306a36Sopenharmony_ci { MEDIA_BUS_FMT_YVYU8_2X8, 8 }, 10462306a36Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR8_1X8, 8 }, 10562306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG8_1X8, 8 }, 10662306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG8_1X8, 8 }, 10762306a36Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB8_1X8, 8 }, 10862306a36Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR10_1X10, 10 }, 10962306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG10_1X10, 10 }, 11062306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG10_1X10, 10 }, 11162306a36Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB10_1X10, 10 }, 11262306a36Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR10_2X8_PADHI_LE, 16 }, 11362306a36Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR12_1X12, 12 }, 11462306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG12_1X12, 12 }, 11562306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG12_1X12, 12 }, 11662306a36Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB12_1X12, 12 }, 11762306a36Sopenharmony_ci { MEDIA_BUS_FMT_SBGGR14_1X14, 14 }, 11862306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGBRG14_1X14, 14 }, 11962306a36Sopenharmony_ci { MEDIA_BUS_FMT_SGRBG14_1X14, 14 }, 12062306a36Sopenharmony_ci { MEDIA_BUS_FMT_SRGGB14_1X14, 14 }, 12162306a36Sopenharmony_ci { MEDIA_BUS_FMT_Y8_1X8, 8 }, 12262306a36Sopenharmony_ci { MEDIA_BUS_FMT_Y10_1X10, 10 }, 12362306a36Sopenharmony_ci { MEDIA_BUS_FMT_Y10_2X8_PADHI_LE, 16 }, 12462306a36Sopenharmony_ci}; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/* 12762306a36Sopenharmony_ci * vfe_get_bpp - map media bus format to bits per pixel 12862306a36Sopenharmony_ci * @formats: supported media bus formats array 12962306a36Sopenharmony_ci * @nformats: size of @formats array 13062306a36Sopenharmony_ci * @code: media bus format code 13162306a36Sopenharmony_ci * 13262306a36Sopenharmony_ci * Return number of bits per pixel 13362306a36Sopenharmony_ci */ 13462306a36Sopenharmony_cistatic u8 vfe_get_bpp(const struct vfe_format *formats, 13562306a36Sopenharmony_ci unsigned int nformats, u32 code) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci unsigned int i; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci for (i = 0; i < nformats; i++) 14062306a36Sopenharmony_ci if (code == formats[i].code) 14162306a36Sopenharmony_ci return formats[i].bpp; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci WARN(1, "Unknown format\n"); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return formats[0].bpp; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic u32 vfe_find_code(u32 *code, unsigned int n_code, 14962306a36Sopenharmony_ci unsigned int index, u32 req_code) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci int i; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (!req_code && (index >= n_code)) 15462306a36Sopenharmony_ci return 0; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci for (i = 0; i < n_code; i++) 15762306a36Sopenharmony_ci if (req_code) { 15862306a36Sopenharmony_ci if (req_code == code[i]) 15962306a36Sopenharmony_ci return req_code; 16062306a36Sopenharmony_ci } else { 16162306a36Sopenharmony_ci if (i == index) 16262306a36Sopenharmony_ci return code[i]; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci return code[0]; 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic u32 vfe_src_pad_code(struct vfe_line *line, u32 sink_code, 16962306a36Sopenharmony_ci unsigned int index, u32 src_req_code) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci struct vfe_device *vfe = to_vfe(line); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (vfe->camss->version == CAMSS_8x16) 17462306a36Sopenharmony_ci switch (sink_code) { 17562306a36Sopenharmony_ci case MEDIA_BUS_FMT_YUYV8_2X8: 17662306a36Sopenharmony_ci { 17762306a36Sopenharmony_ci u32 src_code[] = { 17862306a36Sopenharmony_ci MEDIA_BUS_FMT_YUYV8_2X8, 17962306a36Sopenharmony_ci MEDIA_BUS_FMT_YUYV8_1_5X8, 18062306a36Sopenharmony_ci }; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci return vfe_find_code(src_code, ARRAY_SIZE(src_code), 18362306a36Sopenharmony_ci index, src_req_code); 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci case MEDIA_BUS_FMT_YVYU8_2X8: 18662306a36Sopenharmony_ci { 18762306a36Sopenharmony_ci u32 src_code[] = { 18862306a36Sopenharmony_ci MEDIA_BUS_FMT_YVYU8_2X8, 18962306a36Sopenharmony_ci MEDIA_BUS_FMT_YVYU8_1_5X8, 19062306a36Sopenharmony_ci }; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci return vfe_find_code(src_code, ARRAY_SIZE(src_code), 19362306a36Sopenharmony_ci index, src_req_code); 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci case MEDIA_BUS_FMT_UYVY8_2X8: 19662306a36Sopenharmony_ci { 19762306a36Sopenharmony_ci u32 src_code[] = { 19862306a36Sopenharmony_ci MEDIA_BUS_FMT_UYVY8_2X8, 19962306a36Sopenharmony_ci MEDIA_BUS_FMT_UYVY8_1_5X8, 20062306a36Sopenharmony_ci }; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci return vfe_find_code(src_code, ARRAY_SIZE(src_code), 20362306a36Sopenharmony_ci index, src_req_code); 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci case MEDIA_BUS_FMT_VYUY8_2X8: 20662306a36Sopenharmony_ci { 20762306a36Sopenharmony_ci u32 src_code[] = { 20862306a36Sopenharmony_ci MEDIA_BUS_FMT_VYUY8_2X8, 20962306a36Sopenharmony_ci MEDIA_BUS_FMT_VYUY8_1_5X8, 21062306a36Sopenharmony_ci }; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci return vfe_find_code(src_code, ARRAY_SIZE(src_code), 21362306a36Sopenharmony_ci index, src_req_code); 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci default: 21662306a36Sopenharmony_ci if (index > 0) 21762306a36Sopenharmony_ci return 0; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci return sink_code; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci else if (vfe->camss->version == CAMSS_8x96 || 22262306a36Sopenharmony_ci vfe->camss->version == CAMSS_660 || 22362306a36Sopenharmony_ci vfe->camss->version == CAMSS_845 || 22462306a36Sopenharmony_ci vfe->camss->version == CAMSS_8250) 22562306a36Sopenharmony_ci switch (sink_code) { 22662306a36Sopenharmony_ci case MEDIA_BUS_FMT_YUYV8_2X8: 22762306a36Sopenharmony_ci { 22862306a36Sopenharmony_ci u32 src_code[] = { 22962306a36Sopenharmony_ci MEDIA_BUS_FMT_YUYV8_2X8, 23062306a36Sopenharmony_ci MEDIA_BUS_FMT_YVYU8_2X8, 23162306a36Sopenharmony_ci MEDIA_BUS_FMT_UYVY8_2X8, 23262306a36Sopenharmony_ci MEDIA_BUS_FMT_VYUY8_2X8, 23362306a36Sopenharmony_ci MEDIA_BUS_FMT_YUYV8_1_5X8, 23462306a36Sopenharmony_ci }; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci return vfe_find_code(src_code, ARRAY_SIZE(src_code), 23762306a36Sopenharmony_ci index, src_req_code); 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci case MEDIA_BUS_FMT_YVYU8_2X8: 24062306a36Sopenharmony_ci { 24162306a36Sopenharmony_ci u32 src_code[] = { 24262306a36Sopenharmony_ci MEDIA_BUS_FMT_YVYU8_2X8, 24362306a36Sopenharmony_ci MEDIA_BUS_FMT_YUYV8_2X8, 24462306a36Sopenharmony_ci MEDIA_BUS_FMT_UYVY8_2X8, 24562306a36Sopenharmony_ci MEDIA_BUS_FMT_VYUY8_2X8, 24662306a36Sopenharmony_ci MEDIA_BUS_FMT_YVYU8_1_5X8, 24762306a36Sopenharmony_ci }; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci return vfe_find_code(src_code, ARRAY_SIZE(src_code), 25062306a36Sopenharmony_ci index, src_req_code); 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci case MEDIA_BUS_FMT_UYVY8_2X8: 25362306a36Sopenharmony_ci { 25462306a36Sopenharmony_ci u32 src_code[] = { 25562306a36Sopenharmony_ci MEDIA_BUS_FMT_UYVY8_2X8, 25662306a36Sopenharmony_ci MEDIA_BUS_FMT_YUYV8_2X8, 25762306a36Sopenharmony_ci MEDIA_BUS_FMT_YVYU8_2X8, 25862306a36Sopenharmony_ci MEDIA_BUS_FMT_VYUY8_2X8, 25962306a36Sopenharmony_ci MEDIA_BUS_FMT_UYVY8_1_5X8, 26062306a36Sopenharmony_ci }; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci return vfe_find_code(src_code, ARRAY_SIZE(src_code), 26362306a36Sopenharmony_ci index, src_req_code); 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci case MEDIA_BUS_FMT_VYUY8_2X8: 26662306a36Sopenharmony_ci { 26762306a36Sopenharmony_ci u32 src_code[] = { 26862306a36Sopenharmony_ci MEDIA_BUS_FMT_VYUY8_2X8, 26962306a36Sopenharmony_ci MEDIA_BUS_FMT_YUYV8_2X8, 27062306a36Sopenharmony_ci MEDIA_BUS_FMT_YVYU8_2X8, 27162306a36Sopenharmony_ci MEDIA_BUS_FMT_UYVY8_2X8, 27262306a36Sopenharmony_ci MEDIA_BUS_FMT_VYUY8_1_5X8, 27362306a36Sopenharmony_ci }; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci return vfe_find_code(src_code, ARRAY_SIZE(src_code), 27662306a36Sopenharmony_ci index, src_req_code); 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci default: 27962306a36Sopenharmony_ci if (index > 0) 28062306a36Sopenharmony_ci return 0; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci return sink_code; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci else 28562306a36Sopenharmony_ci return 0; 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ciint vfe_reset(struct vfe_device *vfe) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci unsigned long time; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci reinit_completion(&vfe->reset_complete); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci vfe->ops->global_reset(vfe); 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci time = wait_for_completion_timeout(&vfe->reset_complete, 29762306a36Sopenharmony_ci msecs_to_jiffies(VFE_RESET_TIMEOUT_MS)); 29862306a36Sopenharmony_ci if (!time) { 29962306a36Sopenharmony_ci dev_err(vfe->camss->dev, "VFE reset timeout\n"); 30062306a36Sopenharmony_ci return -EIO; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci return 0; 30462306a36Sopenharmony_ci} 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic void vfe_init_outputs(struct vfe_device *vfe) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci int i; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci for (i = 0; i < vfe->line_num; i++) { 31162306a36Sopenharmony_ci struct vfe_output *output = &vfe->line[i].output; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci output->state = VFE_OUTPUT_OFF; 31462306a36Sopenharmony_ci output->buf[0] = NULL; 31562306a36Sopenharmony_ci output->buf[1] = NULL; 31662306a36Sopenharmony_ci INIT_LIST_HEAD(&output->pending_bufs); 31762306a36Sopenharmony_ci } 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic void vfe_reset_output_maps(struct vfe_device *vfe) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci int i; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++) 32562306a36Sopenharmony_ci vfe->wm_output_map[i] = VFE_LINE_NONE; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ciint vfe_reserve_wm(struct vfe_device *vfe, enum vfe_line_id line_id) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci int ret = -EBUSY; 33162306a36Sopenharmony_ci int i; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++) { 33462306a36Sopenharmony_ci if (vfe->wm_output_map[i] == VFE_LINE_NONE) { 33562306a36Sopenharmony_ci vfe->wm_output_map[i] = line_id; 33662306a36Sopenharmony_ci ret = i; 33762306a36Sopenharmony_ci break; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci return ret; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ciint vfe_release_wm(struct vfe_device *vfe, u8 wm) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci if (wm >= ARRAY_SIZE(vfe->wm_output_map)) 34762306a36Sopenharmony_ci return -EINVAL; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci vfe->wm_output_map[wm] = VFE_LINE_NONE; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci return 0; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistruct camss_buffer *vfe_buf_get_pending(struct vfe_output *output) 35562306a36Sopenharmony_ci{ 35662306a36Sopenharmony_ci struct camss_buffer *buffer = NULL; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (!list_empty(&output->pending_bufs)) { 35962306a36Sopenharmony_ci buffer = list_first_entry(&output->pending_bufs, 36062306a36Sopenharmony_ci struct camss_buffer, 36162306a36Sopenharmony_ci queue); 36262306a36Sopenharmony_ci list_del(&buffer->queue); 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci return buffer; 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_civoid vfe_buf_add_pending(struct vfe_output *output, 36962306a36Sopenharmony_ci struct camss_buffer *buffer) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci INIT_LIST_HEAD(&buffer->queue); 37262306a36Sopenharmony_ci list_add_tail(&buffer->queue, &output->pending_bufs); 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci/* 37662306a36Sopenharmony_ci * vfe_buf_flush_pending - Flush all pending buffers. 37762306a36Sopenharmony_ci * @output: VFE output 37862306a36Sopenharmony_ci * @state: vb2 buffer state 37962306a36Sopenharmony_ci */ 38062306a36Sopenharmony_cistatic void vfe_buf_flush_pending(struct vfe_output *output, 38162306a36Sopenharmony_ci enum vb2_buffer_state state) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci struct camss_buffer *buf; 38462306a36Sopenharmony_ci struct camss_buffer *t; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci list_for_each_entry_safe(buf, t, &output->pending_bufs, queue) { 38762306a36Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, state); 38862306a36Sopenharmony_ci list_del(&buf->queue); 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ciint vfe_put_output(struct vfe_line *line) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci struct vfe_device *vfe = to_vfe(line); 39562306a36Sopenharmony_ci struct vfe_output *output = &line->output; 39662306a36Sopenharmony_ci unsigned long flags; 39762306a36Sopenharmony_ci unsigned int i; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci spin_lock_irqsave(&vfe->output_lock, flags); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci for (i = 0; i < output->wm_num; i++) 40262306a36Sopenharmony_ci vfe_release_wm(vfe, output->wm_idx[i]); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci output->state = VFE_OUTPUT_OFF; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci spin_unlock_irqrestore(&vfe->output_lock, flags); 40762306a36Sopenharmony_ci return 0; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci/** 41162306a36Sopenharmony_ci * vfe_isr_comp_done() - Process composite image done interrupt 41262306a36Sopenharmony_ci * @vfe: VFE Device 41362306a36Sopenharmony_ci * @comp: Composite image id 41462306a36Sopenharmony_ci */ 41562306a36Sopenharmony_civoid vfe_isr_comp_done(struct vfe_device *vfe, u8 comp) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci unsigned int i; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vfe->wm_output_map); i++) 42062306a36Sopenharmony_ci if (vfe->wm_output_map[i] == VFE_LINE_PIX) { 42162306a36Sopenharmony_ci vfe->isr_ops.wm_done(vfe, i); 42262306a36Sopenharmony_ci break; 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_civoid vfe_isr_reset_ack(struct vfe_device *vfe) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci complete(&vfe->reset_complete); 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci/* 43262306a36Sopenharmony_ci * vfe_set_clock_rates - Calculate and set clock rates on VFE module 43362306a36Sopenharmony_ci * @vfe: VFE device 43462306a36Sopenharmony_ci * 43562306a36Sopenharmony_ci * Return 0 on success or a negative error code otherwise 43662306a36Sopenharmony_ci */ 43762306a36Sopenharmony_cistatic int vfe_set_clock_rates(struct vfe_device *vfe) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci struct device *dev = vfe->camss->dev; 44062306a36Sopenharmony_ci u64 pixel_clock[VFE_LINE_NUM_MAX]; 44162306a36Sopenharmony_ci int i, j; 44262306a36Sopenharmony_ci int ret; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) { 44562306a36Sopenharmony_ci ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity, 44662306a36Sopenharmony_ci &pixel_clock[i]); 44762306a36Sopenharmony_ci if (ret) 44862306a36Sopenharmony_ci pixel_clock[i] = 0; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci for (i = 0; i < vfe->nclocks; i++) { 45262306a36Sopenharmony_ci struct camss_clock *clock = &vfe->clock[i]; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci if (!strcmp(clock->name, "vfe0") || 45562306a36Sopenharmony_ci !strcmp(clock->name, "vfe1") || 45662306a36Sopenharmony_ci !strcmp(clock->name, "vfe_lite")) { 45762306a36Sopenharmony_ci u64 min_rate = 0; 45862306a36Sopenharmony_ci long rate; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci for (j = VFE_LINE_RDI0; j < vfe->line_num; j++) { 46162306a36Sopenharmony_ci u32 tmp; 46262306a36Sopenharmony_ci u8 bpp; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (j == VFE_LINE_PIX) { 46562306a36Sopenharmony_ci tmp = pixel_clock[j]; 46662306a36Sopenharmony_ci } else { 46762306a36Sopenharmony_ci struct vfe_line *l = &vfe->line[j]; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci bpp = vfe_get_bpp(l->formats, 47062306a36Sopenharmony_ci l->nformats, 47162306a36Sopenharmony_ci l->fmt[MSM_VFE_PAD_SINK].code); 47262306a36Sopenharmony_ci tmp = pixel_clock[j] * bpp / 64; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci if (min_rate < tmp) 47662306a36Sopenharmony_ci min_rate = tmp; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci camss_add_clock_margin(&min_rate); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci for (j = 0; j < clock->nfreqs; j++) 48262306a36Sopenharmony_ci if (min_rate < clock->freq[j]) 48362306a36Sopenharmony_ci break; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci if (j == clock->nfreqs) { 48662306a36Sopenharmony_ci dev_err(dev, 48762306a36Sopenharmony_ci "Pixel clock is too high for VFE"); 48862306a36Sopenharmony_ci return -EINVAL; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci /* if sensor pixel clock is not available */ 49262306a36Sopenharmony_ci /* set highest possible VFE clock rate */ 49362306a36Sopenharmony_ci if (min_rate == 0) 49462306a36Sopenharmony_ci j = clock->nfreqs - 1; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci rate = clk_round_rate(clock->clk, clock->freq[j]); 49762306a36Sopenharmony_ci if (rate < 0) { 49862306a36Sopenharmony_ci dev_err(dev, "clk round rate failed: %ld\n", 49962306a36Sopenharmony_ci rate); 50062306a36Sopenharmony_ci return -EINVAL; 50162306a36Sopenharmony_ci } 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci ret = clk_set_rate(clock->clk, rate); 50462306a36Sopenharmony_ci if (ret < 0) { 50562306a36Sopenharmony_ci dev_err(dev, "clk set rate failed: %d\n", ret); 50662306a36Sopenharmony_ci return ret; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci return 0; 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci/* 51562306a36Sopenharmony_ci * vfe_check_clock_rates - Check current clock rates on VFE module 51662306a36Sopenharmony_ci * @vfe: VFE device 51762306a36Sopenharmony_ci * 51862306a36Sopenharmony_ci * Return 0 if current clock rates are suitable for a new pipeline 51962306a36Sopenharmony_ci * or a negative error code otherwise 52062306a36Sopenharmony_ci */ 52162306a36Sopenharmony_cistatic int vfe_check_clock_rates(struct vfe_device *vfe) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci u64 pixel_clock[VFE_LINE_NUM_MAX]; 52462306a36Sopenharmony_ci int i, j; 52562306a36Sopenharmony_ci int ret; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) { 52862306a36Sopenharmony_ci ret = camss_get_pixel_clock(&vfe->line[i].subdev.entity, 52962306a36Sopenharmony_ci &pixel_clock[i]); 53062306a36Sopenharmony_ci if (ret) 53162306a36Sopenharmony_ci pixel_clock[i] = 0; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci for (i = 0; i < vfe->nclocks; i++) { 53562306a36Sopenharmony_ci struct camss_clock *clock = &vfe->clock[i]; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (!strcmp(clock->name, "vfe0") || 53862306a36Sopenharmony_ci !strcmp(clock->name, "vfe1") || 53962306a36Sopenharmony_ci !strcmp(clock->name, "vfe_lite")) { 54062306a36Sopenharmony_ci u64 min_rate = 0; 54162306a36Sopenharmony_ci unsigned long rate; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci for (j = VFE_LINE_RDI0; j < vfe->line_num; j++) { 54462306a36Sopenharmony_ci u32 tmp; 54562306a36Sopenharmony_ci u8 bpp; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci if (j == VFE_LINE_PIX) { 54862306a36Sopenharmony_ci tmp = pixel_clock[j]; 54962306a36Sopenharmony_ci } else { 55062306a36Sopenharmony_ci struct vfe_line *l = &vfe->line[j]; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci bpp = vfe_get_bpp(l->formats, 55362306a36Sopenharmony_ci l->nformats, 55462306a36Sopenharmony_ci l->fmt[MSM_VFE_PAD_SINK].code); 55562306a36Sopenharmony_ci tmp = pixel_clock[j] * bpp / 64; 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (min_rate < tmp) 55962306a36Sopenharmony_ci min_rate = tmp; 56062306a36Sopenharmony_ci } 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci camss_add_clock_margin(&min_rate); 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci rate = clk_get_rate(clock->clk); 56562306a36Sopenharmony_ci if (rate < min_rate) 56662306a36Sopenharmony_ci return -EBUSY; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci return 0; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci/* 57462306a36Sopenharmony_ci * vfe_get - Power up and reset VFE module 57562306a36Sopenharmony_ci * @vfe: VFE Device 57662306a36Sopenharmony_ci * 57762306a36Sopenharmony_ci * Return 0 on success or a negative error code otherwise 57862306a36Sopenharmony_ci */ 57962306a36Sopenharmony_ciint vfe_get(struct vfe_device *vfe) 58062306a36Sopenharmony_ci{ 58162306a36Sopenharmony_ci int ret; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci mutex_lock(&vfe->power_lock); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (vfe->power_count == 0) { 58662306a36Sopenharmony_ci ret = vfe->ops->pm_domain_on(vfe); 58762306a36Sopenharmony_ci if (ret < 0) 58862306a36Sopenharmony_ci goto error_pm_domain; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(vfe->camss->dev); 59162306a36Sopenharmony_ci if (ret < 0) 59262306a36Sopenharmony_ci goto error_domain_off; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci ret = vfe_set_clock_rates(vfe); 59562306a36Sopenharmony_ci if (ret < 0) 59662306a36Sopenharmony_ci goto error_pm_runtime_get; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci ret = camss_enable_clocks(vfe->nclocks, vfe->clock, 59962306a36Sopenharmony_ci vfe->camss->dev); 60062306a36Sopenharmony_ci if (ret < 0) 60162306a36Sopenharmony_ci goto error_pm_runtime_get; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci ret = vfe_reset(vfe); 60462306a36Sopenharmony_ci if (ret < 0) 60562306a36Sopenharmony_ci goto error_reset; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci vfe_reset_output_maps(vfe); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci vfe_init_outputs(vfe); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci vfe->ops->hw_version(vfe); 61262306a36Sopenharmony_ci } else { 61362306a36Sopenharmony_ci ret = vfe_check_clock_rates(vfe); 61462306a36Sopenharmony_ci if (ret < 0) 61562306a36Sopenharmony_ci goto error_pm_domain; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci vfe->power_count++; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci mutex_unlock(&vfe->power_lock); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci return 0; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_cierror_reset: 62462306a36Sopenharmony_ci camss_disable_clocks(vfe->nclocks, vfe->clock); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_cierror_pm_runtime_get: 62762306a36Sopenharmony_ci pm_runtime_put_sync(vfe->camss->dev); 62862306a36Sopenharmony_cierror_domain_off: 62962306a36Sopenharmony_ci vfe->ops->pm_domain_off(vfe); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_cierror_pm_domain: 63262306a36Sopenharmony_ci mutex_unlock(&vfe->power_lock); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci return ret; 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci/* 63862306a36Sopenharmony_ci * vfe_put - Power down VFE module 63962306a36Sopenharmony_ci * @vfe: VFE Device 64062306a36Sopenharmony_ci */ 64162306a36Sopenharmony_civoid vfe_put(struct vfe_device *vfe) 64262306a36Sopenharmony_ci{ 64362306a36Sopenharmony_ci mutex_lock(&vfe->power_lock); 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci if (vfe->power_count == 0) { 64662306a36Sopenharmony_ci dev_err(vfe->camss->dev, "vfe power off on power_count == 0\n"); 64762306a36Sopenharmony_ci goto exit; 64862306a36Sopenharmony_ci } else if (vfe->power_count == 1) { 64962306a36Sopenharmony_ci if (vfe->was_streaming) { 65062306a36Sopenharmony_ci vfe->was_streaming = 0; 65162306a36Sopenharmony_ci vfe->ops->vfe_halt(vfe); 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci camss_disable_clocks(vfe->nclocks, vfe->clock); 65462306a36Sopenharmony_ci pm_runtime_put_sync(vfe->camss->dev); 65562306a36Sopenharmony_ci vfe->ops->pm_domain_off(vfe); 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci vfe->power_count--; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ciexit: 66162306a36Sopenharmony_ci mutex_unlock(&vfe->power_lock); 66262306a36Sopenharmony_ci} 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci/* 66562306a36Sopenharmony_ci * vfe_flush_buffers - Return all vb2 buffers 66662306a36Sopenharmony_ci * @vid: Video device structure 66762306a36Sopenharmony_ci * @state: vb2 buffer state of the returned buffers 66862306a36Sopenharmony_ci * 66962306a36Sopenharmony_ci * Return all buffers to vb2. This includes queued pending buffers (still 67062306a36Sopenharmony_ci * unused) and any buffers given to the hardware but again still not used. 67162306a36Sopenharmony_ci * 67262306a36Sopenharmony_ci * Return 0 on success or a negative error code otherwise 67362306a36Sopenharmony_ci */ 67462306a36Sopenharmony_ciint vfe_flush_buffers(struct camss_video *vid, 67562306a36Sopenharmony_ci enum vb2_buffer_state state) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci struct vfe_line *line = container_of(vid, struct vfe_line, video_out); 67862306a36Sopenharmony_ci struct vfe_device *vfe = to_vfe(line); 67962306a36Sopenharmony_ci struct vfe_output *output; 68062306a36Sopenharmony_ci unsigned long flags; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci output = &line->output; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci spin_lock_irqsave(&vfe->output_lock, flags); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci vfe_buf_flush_pending(output, state); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci if (output->buf[0]) 68962306a36Sopenharmony_ci vb2_buffer_done(&output->buf[0]->vb.vb2_buf, state); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci if (output->buf[1]) 69262306a36Sopenharmony_ci vb2_buffer_done(&output->buf[1]->vb.vb2_buf, state); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci if (output->last_buffer) { 69562306a36Sopenharmony_ci vb2_buffer_done(&output->last_buffer->vb.vb2_buf, state); 69662306a36Sopenharmony_ci output->last_buffer = NULL; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci spin_unlock_irqrestore(&vfe->output_lock, flags); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci return 0; 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci/* 70562306a36Sopenharmony_ci * vfe_set_power - Power on/off VFE module 70662306a36Sopenharmony_ci * @sd: VFE V4L2 subdevice 70762306a36Sopenharmony_ci * @on: Requested power state 70862306a36Sopenharmony_ci * 70962306a36Sopenharmony_ci * Return 0 on success or a negative error code otherwise 71062306a36Sopenharmony_ci */ 71162306a36Sopenharmony_cistatic int vfe_set_power(struct v4l2_subdev *sd, int on) 71262306a36Sopenharmony_ci{ 71362306a36Sopenharmony_ci struct vfe_line *line = v4l2_get_subdevdata(sd); 71462306a36Sopenharmony_ci struct vfe_device *vfe = to_vfe(line); 71562306a36Sopenharmony_ci int ret; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci if (on) { 71862306a36Sopenharmony_ci ret = vfe_get(vfe); 71962306a36Sopenharmony_ci if (ret < 0) 72062306a36Sopenharmony_ci return ret; 72162306a36Sopenharmony_ci } else { 72262306a36Sopenharmony_ci vfe_put(vfe); 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci return 0; 72662306a36Sopenharmony_ci} 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci/* 72962306a36Sopenharmony_ci * vfe_set_stream - Enable/disable streaming on VFE module 73062306a36Sopenharmony_ci * @sd: VFE V4L2 subdevice 73162306a36Sopenharmony_ci * @enable: Requested streaming state 73262306a36Sopenharmony_ci * 73362306a36Sopenharmony_ci * Main configuration of VFE module is triggered here. 73462306a36Sopenharmony_ci * 73562306a36Sopenharmony_ci * Return 0 on success or a negative error code otherwise 73662306a36Sopenharmony_ci */ 73762306a36Sopenharmony_cistatic int vfe_set_stream(struct v4l2_subdev *sd, int enable) 73862306a36Sopenharmony_ci{ 73962306a36Sopenharmony_ci struct vfe_line *line = v4l2_get_subdevdata(sd); 74062306a36Sopenharmony_ci struct vfe_device *vfe = to_vfe(line); 74162306a36Sopenharmony_ci int ret; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci if (enable) { 74462306a36Sopenharmony_ci line->output.state = VFE_OUTPUT_RESERVED; 74562306a36Sopenharmony_ci ret = vfe->ops->vfe_enable(line); 74662306a36Sopenharmony_ci if (ret < 0) 74762306a36Sopenharmony_ci dev_err(vfe->camss->dev, 74862306a36Sopenharmony_ci "Failed to enable vfe outputs\n"); 74962306a36Sopenharmony_ci } else { 75062306a36Sopenharmony_ci ret = vfe->ops->vfe_disable(line); 75162306a36Sopenharmony_ci if (ret < 0) 75262306a36Sopenharmony_ci dev_err(vfe->camss->dev, 75362306a36Sopenharmony_ci "Failed to disable vfe outputs\n"); 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci return ret; 75762306a36Sopenharmony_ci} 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci/* 76062306a36Sopenharmony_ci * __vfe_get_format - Get pointer to format structure 76162306a36Sopenharmony_ci * @line: VFE line 76262306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 76362306a36Sopenharmony_ci * @pad: pad from which format is requested 76462306a36Sopenharmony_ci * @which: TRY or ACTIVE format 76562306a36Sopenharmony_ci * 76662306a36Sopenharmony_ci * Return pointer to TRY or ACTIVE format structure 76762306a36Sopenharmony_ci */ 76862306a36Sopenharmony_cistatic struct v4l2_mbus_framefmt * 76962306a36Sopenharmony_ci__vfe_get_format(struct vfe_line *line, 77062306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 77162306a36Sopenharmony_ci unsigned int pad, 77262306a36Sopenharmony_ci enum v4l2_subdev_format_whence which) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci if (which == V4L2_SUBDEV_FORMAT_TRY) 77562306a36Sopenharmony_ci return v4l2_subdev_get_try_format(&line->subdev, sd_state, 77662306a36Sopenharmony_ci pad); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci return &line->fmt[pad]; 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci/* 78262306a36Sopenharmony_ci * __vfe_get_compose - Get pointer to compose selection structure 78362306a36Sopenharmony_ci * @line: VFE line 78462306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 78562306a36Sopenharmony_ci * @which: TRY or ACTIVE format 78662306a36Sopenharmony_ci * 78762306a36Sopenharmony_ci * Return pointer to TRY or ACTIVE compose rectangle structure 78862306a36Sopenharmony_ci */ 78962306a36Sopenharmony_cistatic struct v4l2_rect * 79062306a36Sopenharmony_ci__vfe_get_compose(struct vfe_line *line, 79162306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 79262306a36Sopenharmony_ci enum v4l2_subdev_format_whence which) 79362306a36Sopenharmony_ci{ 79462306a36Sopenharmony_ci if (which == V4L2_SUBDEV_FORMAT_TRY) 79562306a36Sopenharmony_ci return v4l2_subdev_get_try_compose(&line->subdev, sd_state, 79662306a36Sopenharmony_ci MSM_VFE_PAD_SINK); 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci return &line->compose; 79962306a36Sopenharmony_ci} 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci/* 80262306a36Sopenharmony_ci * __vfe_get_crop - Get pointer to crop selection structure 80362306a36Sopenharmony_ci * @line: VFE line 80462306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 80562306a36Sopenharmony_ci * @which: TRY or ACTIVE format 80662306a36Sopenharmony_ci * 80762306a36Sopenharmony_ci * Return pointer to TRY or ACTIVE crop rectangle structure 80862306a36Sopenharmony_ci */ 80962306a36Sopenharmony_cistatic struct v4l2_rect * 81062306a36Sopenharmony_ci__vfe_get_crop(struct vfe_line *line, 81162306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 81262306a36Sopenharmony_ci enum v4l2_subdev_format_whence which) 81362306a36Sopenharmony_ci{ 81462306a36Sopenharmony_ci if (which == V4L2_SUBDEV_FORMAT_TRY) 81562306a36Sopenharmony_ci return v4l2_subdev_get_try_crop(&line->subdev, sd_state, 81662306a36Sopenharmony_ci MSM_VFE_PAD_SRC); 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci return &line->crop; 81962306a36Sopenharmony_ci} 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci/* 82262306a36Sopenharmony_ci * vfe_try_format - Handle try format by pad subdev method 82362306a36Sopenharmony_ci * @line: VFE line 82462306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 82562306a36Sopenharmony_ci * @pad: pad on which format is requested 82662306a36Sopenharmony_ci * @fmt: pointer to v4l2 format structure 82762306a36Sopenharmony_ci * @which: wanted subdev format 82862306a36Sopenharmony_ci */ 82962306a36Sopenharmony_cistatic void vfe_try_format(struct vfe_line *line, 83062306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 83162306a36Sopenharmony_ci unsigned int pad, 83262306a36Sopenharmony_ci struct v4l2_mbus_framefmt *fmt, 83362306a36Sopenharmony_ci enum v4l2_subdev_format_whence which) 83462306a36Sopenharmony_ci{ 83562306a36Sopenharmony_ci unsigned int i; 83662306a36Sopenharmony_ci u32 code; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci switch (pad) { 83962306a36Sopenharmony_ci case MSM_VFE_PAD_SINK: 84062306a36Sopenharmony_ci /* Set format on sink pad */ 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci for (i = 0; i < line->nformats; i++) 84362306a36Sopenharmony_ci if (fmt->code == line->formats[i].code) 84462306a36Sopenharmony_ci break; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci /* If not found, use UYVY as default */ 84762306a36Sopenharmony_ci if (i >= line->nformats) 84862306a36Sopenharmony_ci fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci fmt->width = clamp_t(u32, fmt->width, 1, 8191); 85162306a36Sopenharmony_ci fmt->height = clamp_t(u32, fmt->height, 1, 8191); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci fmt->field = V4L2_FIELD_NONE; 85462306a36Sopenharmony_ci fmt->colorspace = V4L2_COLORSPACE_SRGB; 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci break; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci case MSM_VFE_PAD_SRC: 85962306a36Sopenharmony_ci /* Set and return a format same as sink pad */ 86062306a36Sopenharmony_ci code = fmt->code; 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci *fmt = *__vfe_get_format(line, sd_state, MSM_VFE_PAD_SINK, 86362306a36Sopenharmony_ci which); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci fmt->code = vfe_src_pad_code(line, fmt->code, 0, code); 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci if (line->id == VFE_LINE_PIX) { 86862306a36Sopenharmony_ci struct v4l2_rect *rect; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci rect = __vfe_get_crop(line, sd_state, which); 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci fmt->width = rect->width; 87362306a36Sopenharmony_ci fmt->height = rect->height; 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci break; 87762306a36Sopenharmony_ci } 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci fmt->colorspace = V4L2_COLORSPACE_SRGB; 88062306a36Sopenharmony_ci} 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci/* 88362306a36Sopenharmony_ci * vfe_try_compose - Handle try compose selection by pad subdev method 88462306a36Sopenharmony_ci * @line: VFE line 88562306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 88662306a36Sopenharmony_ci * @rect: pointer to v4l2 rect structure 88762306a36Sopenharmony_ci * @which: wanted subdev format 88862306a36Sopenharmony_ci */ 88962306a36Sopenharmony_cistatic void vfe_try_compose(struct vfe_line *line, 89062306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 89162306a36Sopenharmony_ci struct v4l2_rect *rect, 89262306a36Sopenharmony_ci enum v4l2_subdev_format_whence which) 89362306a36Sopenharmony_ci{ 89462306a36Sopenharmony_ci struct v4l2_mbus_framefmt *fmt; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci fmt = __vfe_get_format(line, sd_state, MSM_VFE_PAD_SINK, which); 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci if (rect->width > fmt->width) 89962306a36Sopenharmony_ci rect->width = fmt->width; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci if (rect->height > fmt->height) 90262306a36Sopenharmony_ci rect->height = fmt->height; 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci if (fmt->width > rect->width * SCALER_RATIO_MAX) 90562306a36Sopenharmony_ci rect->width = (fmt->width + SCALER_RATIO_MAX - 1) / 90662306a36Sopenharmony_ci SCALER_RATIO_MAX; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci rect->width &= ~0x1; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci if (fmt->height > rect->height * SCALER_RATIO_MAX) 91162306a36Sopenharmony_ci rect->height = (fmt->height + SCALER_RATIO_MAX - 1) / 91262306a36Sopenharmony_ci SCALER_RATIO_MAX; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci if (rect->width < 16) 91562306a36Sopenharmony_ci rect->width = 16; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci if (rect->height < 4) 91862306a36Sopenharmony_ci rect->height = 4; 91962306a36Sopenharmony_ci} 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci/* 92262306a36Sopenharmony_ci * vfe_try_crop - Handle try crop selection by pad subdev method 92362306a36Sopenharmony_ci * @line: VFE line 92462306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 92562306a36Sopenharmony_ci * @rect: pointer to v4l2 rect structure 92662306a36Sopenharmony_ci * @which: wanted subdev format 92762306a36Sopenharmony_ci */ 92862306a36Sopenharmony_cistatic void vfe_try_crop(struct vfe_line *line, 92962306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 93062306a36Sopenharmony_ci struct v4l2_rect *rect, 93162306a36Sopenharmony_ci enum v4l2_subdev_format_whence which) 93262306a36Sopenharmony_ci{ 93362306a36Sopenharmony_ci struct v4l2_rect *compose; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci compose = __vfe_get_compose(line, sd_state, which); 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci if (rect->width > compose->width) 93862306a36Sopenharmony_ci rect->width = compose->width; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci if (rect->width + rect->left > compose->width) 94162306a36Sopenharmony_ci rect->left = compose->width - rect->width; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci if (rect->height > compose->height) 94462306a36Sopenharmony_ci rect->height = compose->height; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci if (rect->height + rect->top > compose->height) 94762306a36Sopenharmony_ci rect->top = compose->height - rect->height; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci /* wm in line based mode writes multiple of 16 horizontally */ 95062306a36Sopenharmony_ci rect->left += (rect->width & 0xf) >> 1; 95162306a36Sopenharmony_ci rect->width &= ~0xf; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci if (rect->width < 16) { 95462306a36Sopenharmony_ci rect->left = 0; 95562306a36Sopenharmony_ci rect->width = 16; 95662306a36Sopenharmony_ci } 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci if (rect->height < 4) { 95962306a36Sopenharmony_ci rect->top = 0; 96062306a36Sopenharmony_ci rect->height = 4; 96162306a36Sopenharmony_ci } 96262306a36Sopenharmony_ci} 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci/* 96562306a36Sopenharmony_ci * vfe_enum_mbus_code - Handle pixel format enumeration 96662306a36Sopenharmony_ci * @sd: VFE V4L2 subdevice 96762306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 96862306a36Sopenharmony_ci * @code: pointer to v4l2_subdev_mbus_code_enum structure 96962306a36Sopenharmony_ci * 97062306a36Sopenharmony_ci * return -EINVAL or zero on success 97162306a36Sopenharmony_ci */ 97262306a36Sopenharmony_cistatic int vfe_enum_mbus_code(struct v4l2_subdev *sd, 97362306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 97462306a36Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 97562306a36Sopenharmony_ci{ 97662306a36Sopenharmony_ci struct vfe_line *line = v4l2_get_subdevdata(sd); 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci if (code->pad == MSM_VFE_PAD_SINK) { 97962306a36Sopenharmony_ci if (code->index >= line->nformats) 98062306a36Sopenharmony_ci return -EINVAL; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci code->code = line->formats[code->index].code; 98362306a36Sopenharmony_ci } else { 98462306a36Sopenharmony_ci struct v4l2_mbus_framefmt *sink_fmt; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci sink_fmt = __vfe_get_format(line, sd_state, MSM_VFE_PAD_SINK, 98762306a36Sopenharmony_ci code->which); 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci code->code = vfe_src_pad_code(line, sink_fmt->code, 99062306a36Sopenharmony_ci code->index, 0); 99162306a36Sopenharmony_ci if (!code->code) 99262306a36Sopenharmony_ci return -EINVAL; 99362306a36Sopenharmony_ci } 99462306a36Sopenharmony_ci 99562306a36Sopenharmony_ci return 0; 99662306a36Sopenharmony_ci} 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci/* 99962306a36Sopenharmony_ci * vfe_enum_frame_size - Handle frame size enumeration 100062306a36Sopenharmony_ci * @sd: VFE V4L2 subdevice 100162306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 100262306a36Sopenharmony_ci * @fse: pointer to v4l2_subdev_frame_size_enum structure 100362306a36Sopenharmony_ci * 100462306a36Sopenharmony_ci * Return -EINVAL or zero on success 100562306a36Sopenharmony_ci */ 100662306a36Sopenharmony_cistatic int vfe_enum_frame_size(struct v4l2_subdev *sd, 100762306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 100862306a36Sopenharmony_ci struct v4l2_subdev_frame_size_enum *fse) 100962306a36Sopenharmony_ci{ 101062306a36Sopenharmony_ci struct vfe_line *line = v4l2_get_subdevdata(sd); 101162306a36Sopenharmony_ci struct v4l2_mbus_framefmt format; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci if (fse->index != 0) 101462306a36Sopenharmony_ci return -EINVAL; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci format.code = fse->code; 101762306a36Sopenharmony_ci format.width = 1; 101862306a36Sopenharmony_ci format.height = 1; 101962306a36Sopenharmony_ci vfe_try_format(line, sd_state, fse->pad, &format, fse->which); 102062306a36Sopenharmony_ci fse->min_width = format.width; 102162306a36Sopenharmony_ci fse->min_height = format.height; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci if (format.code != fse->code) 102462306a36Sopenharmony_ci return -EINVAL; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci format.code = fse->code; 102762306a36Sopenharmony_ci format.width = -1; 102862306a36Sopenharmony_ci format.height = -1; 102962306a36Sopenharmony_ci vfe_try_format(line, sd_state, fse->pad, &format, fse->which); 103062306a36Sopenharmony_ci fse->max_width = format.width; 103162306a36Sopenharmony_ci fse->max_height = format.height; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci return 0; 103462306a36Sopenharmony_ci} 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci/* 103762306a36Sopenharmony_ci * vfe_get_format - Handle get format by pads subdev method 103862306a36Sopenharmony_ci * @sd: VFE V4L2 subdevice 103962306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 104062306a36Sopenharmony_ci * @fmt: pointer to v4l2 subdev format structure 104162306a36Sopenharmony_ci * 104262306a36Sopenharmony_ci * Return -EINVAL or zero on success 104362306a36Sopenharmony_ci */ 104462306a36Sopenharmony_cistatic int vfe_get_format(struct v4l2_subdev *sd, 104562306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 104662306a36Sopenharmony_ci struct v4l2_subdev_format *fmt) 104762306a36Sopenharmony_ci{ 104862306a36Sopenharmony_ci struct vfe_line *line = v4l2_get_subdevdata(sd); 104962306a36Sopenharmony_ci struct v4l2_mbus_framefmt *format; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci format = __vfe_get_format(line, sd_state, fmt->pad, fmt->which); 105262306a36Sopenharmony_ci if (format == NULL) 105362306a36Sopenharmony_ci return -EINVAL; 105462306a36Sopenharmony_ci 105562306a36Sopenharmony_ci fmt->format = *format; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci return 0; 105862306a36Sopenharmony_ci} 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_cistatic int vfe_set_selection(struct v4l2_subdev *sd, 106162306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 106262306a36Sopenharmony_ci struct v4l2_subdev_selection *sel); 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci/* 106562306a36Sopenharmony_ci * vfe_set_format - Handle set format by pads subdev method 106662306a36Sopenharmony_ci * @sd: VFE V4L2 subdevice 106762306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 106862306a36Sopenharmony_ci * @fmt: pointer to v4l2 subdev format structure 106962306a36Sopenharmony_ci * 107062306a36Sopenharmony_ci * Return -EINVAL or zero on success 107162306a36Sopenharmony_ci */ 107262306a36Sopenharmony_cistatic int vfe_set_format(struct v4l2_subdev *sd, 107362306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 107462306a36Sopenharmony_ci struct v4l2_subdev_format *fmt) 107562306a36Sopenharmony_ci{ 107662306a36Sopenharmony_ci struct vfe_line *line = v4l2_get_subdevdata(sd); 107762306a36Sopenharmony_ci struct v4l2_mbus_framefmt *format; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci format = __vfe_get_format(line, sd_state, fmt->pad, fmt->which); 108062306a36Sopenharmony_ci if (format == NULL) 108162306a36Sopenharmony_ci return -EINVAL; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci vfe_try_format(line, sd_state, fmt->pad, &fmt->format, fmt->which); 108462306a36Sopenharmony_ci *format = fmt->format; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci if (fmt->pad == MSM_VFE_PAD_SINK) { 108762306a36Sopenharmony_ci struct v4l2_subdev_selection sel = { 0 }; 108862306a36Sopenharmony_ci int ret; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci /* Propagate the format from sink to source */ 109162306a36Sopenharmony_ci format = __vfe_get_format(line, sd_state, MSM_VFE_PAD_SRC, 109262306a36Sopenharmony_ci fmt->which); 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci *format = fmt->format; 109562306a36Sopenharmony_ci vfe_try_format(line, sd_state, MSM_VFE_PAD_SRC, format, 109662306a36Sopenharmony_ci fmt->which); 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci if (line->id != VFE_LINE_PIX) 109962306a36Sopenharmony_ci return 0; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci /* Reset sink pad compose selection */ 110262306a36Sopenharmony_ci sel.which = fmt->which; 110362306a36Sopenharmony_ci sel.pad = MSM_VFE_PAD_SINK; 110462306a36Sopenharmony_ci sel.target = V4L2_SEL_TGT_COMPOSE; 110562306a36Sopenharmony_ci sel.r.width = fmt->format.width; 110662306a36Sopenharmony_ci sel.r.height = fmt->format.height; 110762306a36Sopenharmony_ci ret = vfe_set_selection(sd, sd_state, &sel); 110862306a36Sopenharmony_ci if (ret < 0) 110962306a36Sopenharmony_ci return ret; 111062306a36Sopenharmony_ci } 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci return 0; 111362306a36Sopenharmony_ci} 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci/* 111662306a36Sopenharmony_ci * vfe_get_selection - Handle get selection by pads subdev method 111762306a36Sopenharmony_ci * @sd: VFE V4L2 subdevice 111862306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 111962306a36Sopenharmony_ci * @sel: pointer to v4l2 subdev selection structure 112062306a36Sopenharmony_ci * 112162306a36Sopenharmony_ci * Return -EINVAL or zero on success 112262306a36Sopenharmony_ci */ 112362306a36Sopenharmony_cistatic int vfe_get_selection(struct v4l2_subdev *sd, 112462306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 112562306a36Sopenharmony_ci struct v4l2_subdev_selection *sel) 112662306a36Sopenharmony_ci{ 112762306a36Sopenharmony_ci struct vfe_line *line = v4l2_get_subdevdata(sd); 112862306a36Sopenharmony_ci struct v4l2_subdev_format fmt = { 0 }; 112962306a36Sopenharmony_ci struct v4l2_rect *rect; 113062306a36Sopenharmony_ci int ret; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci if (line->id != VFE_LINE_PIX) 113362306a36Sopenharmony_ci return -EINVAL; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci if (sel->pad == MSM_VFE_PAD_SINK) 113662306a36Sopenharmony_ci switch (sel->target) { 113762306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE_BOUNDS: 113862306a36Sopenharmony_ci fmt.pad = sel->pad; 113962306a36Sopenharmony_ci fmt.which = sel->which; 114062306a36Sopenharmony_ci ret = vfe_get_format(sd, sd_state, &fmt); 114162306a36Sopenharmony_ci if (ret < 0) 114262306a36Sopenharmony_ci return ret; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci sel->r.left = 0; 114562306a36Sopenharmony_ci sel->r.top = 0; 114662306a36Sopenharmony_ci sel->r.width = fmt.format.width; 114762306a36Sopenharmony_ci sel->r.height = fmt.format.height; 114862306a36Sopenharmony_ci break; 114962306a36Sopenharmony_ci case V4L2_SEL_TGT_COMPOSE: 115062306a36Sopenharmony_ci rect = __vfe_get_compose(line, sd_state, sel->which); 115162306a36Sopenharmony_ci if (rect == NULL) 115262306a36Sopenharmony_ci return -EINVAL; 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci sel->r = *rect; 115562306a36Sopenharmony_ci break; 115662306a36Sopenharmony_ci default: 115762306a36Sopenharmony_ci return -EINVAL; 115862306a36Sopenharmony_ci } 115962306a36Sopenharmony_ci else if (sel->pad == MSM_VFE_PAD_SRC) 116062306a36Sopenharmony_ci switch (sel->target) { 116162306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 116262306a36Sopenharmony_ci rect = __vfe_get_compose(line, sd_state, sel->which); 116362306a36Sopenharmony_ci if (rect == NULL) 116462306a36Sopenharmony_ci return -EINVAL; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci sel->r.left = rect->left; 116762306a36Sopenharmony_ci sel->r.top = rect->top; 116862306a36Sopenharmony_ci sel->r.width = rect->width; 116962306a36Sopenharmony_ci sel->r.height = rect->height; 117062306a36Sopenharmony_ci break; 117162306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP: 117262306a36Sopenharmony_ci rect = __vfe_get_crop(line, sd_state, sel->which); 117362306a36Sopenharmony_ci if (rect == NULL) 117462306a36Sopenharmony_ci return -EINVAL; 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci sel->r = *rect; 117762306a36Sopenharmony_ci break; 117862306a36Sopenharmony_ci default: 117962306a36Sopenharmony_ci return -EINVAL; 118062306a36Sopenharmony_ci } 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci return 0; 118362306a36Sopenharmony_ci} 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci/* 118662306a36Sopenharmony_ci * vfe_set_selection - Handle set selection by pads subdev method 118762306a36Sopenharmony_ci * @sd: VFE V4L2 subdevice 118862306a36Sopenharmony_ci * @cfg: V4L2 subdev pad configuration 118962306a36Sopenharmony_ci * @sel: pointer to v4l2 subdev selection structure 119062306a36Sopenharmony_ci * 119162306a36Sopenharmony_ci * Return -EINVAL or zero on success 119262306a36Sopenharmony_ci */ 119362306a36Sopenharmony_cistatic int vfe_set_selection(struct v4l2_subdev *sd, 119462306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 119562306a36Sopenharmony_ci struct v4l2_subdev_selection *sel) 119662306a36Sopenharmony_ci{ 119762306a36Sopenharmony_ci struct vfe_line *line = v4l2_get_subdevdata(sd); 119862306a36Sopenharmony_ci struct v4l2_rect *rect; 119962306a36Sopenharmony_ci int ret; 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci if (line->id != VFE_LINE_PIX) 120262306a36Sopenharmony_ci return -EINVAL; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci if (sel->target == V4L2_SEL_TGT_COMPOSE && 120562306a36Sopenharmony_ci sel->pad == MSM_VFE_PAD_SINK) { 120662306a36Sopenharmony_ci struct v4l2_subdev_selection crop = { 0 }; 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci rect = __vfe_get_compose(line, sd_state, sel->which); 120962306a36Sopenharmony_ci if (rect == NULL) 121062306a36Sopenharmony_ci return -EINVAL; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci vfe_try_compose(line, sd_state, &sel->r, sel->which); 121362306a36Sopenharmony_ci *rect = sel->r; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci /* Reset source crop selection */ 121662306a36Sopenharmony_ci crop.which = sel->which; 121762306a36Sopenharmony_ci crop.pad = MSM_VFE_PAD_SRC; 121862306a36Sopenharmony_ci crop.target = V4L2_SEL_TGT_CROP; 121962306a36Sopenharmony_ci crop.r = *rect; 122062306a36Sopenharmony_ci ret = vfe_set_selection(sd, sd_state, &crop); 122162306a36Sopenharmony_ci } else if (sel->target == V4L2_SEL_TGT_CROP && 122262306a36Sopenharmony_ci sel->pad == MSM_VFE_PAD_SRC) { 122362306a36Sopenharmony_ci struct v4l2_subdev_format fmt = { 0 }; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci rect = __vfe_get_crop(line, sd_state, sel->which); 122662306a36Sopenharmony_ci if (rect == NULL) 122762306a36Sopenharmony_ci return -EINVAL; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci vfe_try_crop(line, sd_state, &sel->r, sel->which); 123062306a36Sopenharmony_ci *rect = sel->r; 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci /* Reset source pad format width and height */ 123362306a36Sopenharmony_ci fmt.which = sel->which; 123462306a36Sopenharmony_ci fmt.pad = MSM_VFE_PAD_SRC; 123562306a36Sopenharmony_ci ret = vfe_get_format(sd, sd_state, &fmt); 123662306a36Sopenharmony_ci if (ret < 0) 123762306a36Sopenharmony_ci return ret; 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci fmt.format.width = rect->width; 124062306a36Sopenharmony_ci fmt.format.height = rect->height; 124162306a36Sopenharmony_ci ret = vfe_set_format(sd, sd_state, &fmt); 124262306a36Sopenharmony_ci } else { 124362306a36Sopenharmony_ci ret = -EINVAL; 124462306a36Sopenharmony_ci } 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci return ret; 124762306a36Sopenharmony_ci} 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci/* 125062306a36Sopenharmony_ci * vfe_init_formats - Initialize formats on all pads 125162306a36Sopenharmony_ci * @sd: VFE V4L2 subdevice 125262306a36Sopenharmony_ci * @fh: V4L2 subdev file handle 125362306a36Sopenharmony_ci * 125462306a36Sopenharmony_ci * Initialize all pad formats with default values. 125562306a36Sopenharmony_ci * 125662306a36Sopenharmony_ci * Return 0 on success or a negative error code otherwise 125762306a36Sopenharmony_ci */ 125862306a36Sopenharmony_cistatic int vfe_init_formats(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) 125962306a36Sopenharmony_ci{ 126062306a36Sopenharmony_ci struct v4l2_subdev_format format = { 126162306a36Sopenharmony_ci .pad = MSM_VFE_PAD_SINK, 126262306a36Sopenharmony_ci .which = fh ? V4L2_SUBDEV_FORMAT_TRY : 126362306a36Sopenharmony_ci V4L2_SUBDEV_FORMAT_ACTIVE, 126462306a36Sopenharmony_ci .format = { 126562306a36Sopenharmony_ci .code = MEDIA_BUS_FMT_UYVY8_2X8, 126662306a36Sopenharmony_ci .width = 1920, 126762306a36Sopenharmony_ci .height = 1080 126862306a36Sopenharmony_ci } 126962306a36Sopenharmony_ci }; 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci return vfe_set_format(sd, fh ? fh->state : NULL, &format); 127262306a36Sopenharmony_ci} 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_ci/* 127562306a36Sopenharmony_ci * msm_vfe_subdev_init - Initialize VFE device structure and resources 127662306a36Sopenharmony_ci * @vfe: VFE device 127762306a36Sopenharmony_ci * @res: VFE module resources table 127862306a36Sopenharmony_ci * 127962306a36Sopenharmony_ci * Return 0 on success or a negative error code otherwise 128062306a36Sopenharmony_ci */ 128162306a36Sopenharmony_ciint msm_vfe_subdev_init(struct camss *camss, struct vfe_device *vfe, 128262306a36Sopenharmony_ci const struct resources *res, u8 id) 128362306a36Sopenharmony_ci{ 128462306a36Sopenharmony_ci struct device *dev = camss->dev; 128562306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 128662306a36Sopenharmony_ci int i, j; 128762306a36Sopenharmony_ci int ret; 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci switch (camss->version) { 129062306a36Sopenharmony_ci case CAMSS_8x16: 129162306a36Sopenharmony_ci vfe->ops = &vfe_ops_4_1; 129262306a36Sopenharmony_ci break; 129362306a36Sopenharmony_ci case CAMSS_8x96: 129462306a36Sopenharmony_ci vfe->ops = &vfe_ops_4_7; 129562306a36Sopenharmony_ci break; 129662306a36Sopenharmony_ci case CAMSS_660: 129762306a36Sopenharmony_ci vfe->ops = &vfe_ops_4_8; 129862306a36Sopenharmony_ci break; 129962306a36Sopenharmony_ci case CAMSS_845: 130062306a36Sopenharmony_ci vfe->ops = &vfe_ops_170; 130162306a36Sopenharmony_ci break; 130262306a36Sopenharmony_ci case CAMSS_8250: 130362306a36Sopenharmony_ci vfe->ops = &vfe_ops_480; 130462306a36Sopenharmony_ci break; 130562306a36Sopenharmony_ci default: 130662306a36Sopenharmony_ci return -EINVAL; 130762306a36Sopenharmony_ci } 130862306a36Sopenharmony_ci vfe->ops->subdev_init(dev, vfe); 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci /* Memory */ 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci vfe->base = devm_platform_ioremap_resource_byname(pdev, res->reg[0]); 131362306a36Sopenharmony_ci if (IS_ERR(vfe->base)) { 131462306a36Sopenharmony_ci dev_err(dev, "could not map memory\n"); 131562306a36Sopenharmony_ci return PTR_ERR(vfe->base); 131662306a36Sopenharmony_ci } 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci /* Interrupt */ 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci ret = platform_get_irq_byname(pdev, res->interrupt[0]); 132162306a36Sopenharmony_ci if (ret < 0) 132262306a36Sopenharmony_ci return ret; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci vfe->irq = ret; 132562306a36Sopenharmony_ci snprintf(vfe->irq_name, sizeof(vfe->irq_name), "%s_%s%d", 132662306a36Sopenharmony_ci dev_name(dev), MSM_VFE_NAME, id); 132762306a36Sopenharmony_ci ret = devm_request_irq(dev, vfe->irq, vfe->ops->isr, 132862306a36Sopenharmony_ci IRQF_TRIGGER_RISING, vfe->irq_name, vfe); 132962306a36Sopenharmony_ci if (ret < 0) { 133062306a36Sopenharmony_ci dev_err(dev, "request_irq failed: %d\n", ret); 133162306a36Sopenharmony_ci return ret; 133262306a36Sopenharmony_ci } 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci /* Clocks */ 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci vfe->nclocks = 0; 133762306a36Sopenharmony_ci while (res->clock[vfe->nclocks]) 133862306a36Sopenharmony_ci vfe->nclocks++; 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci vfe->clock = devm_kcalloc(dev, vfe->nclocks, sizeof(*vfe->clock), 134162306a36Sopenharmony_ci GFP_KERNEL); 134262306a36Sopenharmony_ci if (!vfe->clock) 134362306a36Sopenharmony_ci return -ENOMEM; 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci for (i = 0; i < vfe->nclocks; i++) { 134662306a36Sopenharmony_ci struct camss_clock *clock = &vfe->clock[i]; 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci clock->clk = devm_clk_get(dev, res->clock[i]); 134962306a36Sopenharmony_ci if (IS_ERR(clock->clk)) 135062306a36Sopenharmony_ci return PTR_ERR(clock->clk); 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci clock->name = res->clock[i]; 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci clock->nfreqs = 0; 135562306a36Sopenharmony_ci while (res->clock_rate[i][clock->nfreqs]) 135662306a36Sopenharmony_ci clock->nfreqs++; 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci if (!clock->nfreqs) { 135962306a36Sopenharmony_ci clock->freq = NULL; 136062306a36Sopenharmony_ci continue; 136162306a36Sopenharmony_ci } 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci clock->freq = devm_kcalloc(dev, 136462306a36Sopenharmony_ci clock->nfreqs, 136562306a36Sopenharmony_ci sizeof(*clock->freq), 136662306a36Sopenharmony_ci GFP_KERNEL); 136762306a36Sopenharmony_ci if (!clock->freq) 136862306a36Sopenharmony_ci return -ENOMEM; 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci for (j = 0; j < clock->nfreqs; j++) 137162306a36Sopenharmony_ci clock->freq[j] = res->clock_rate[i][j]; 137262306a36Sopenharmony_ci } 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci mutex_init(&vfe->power_lock); 137562306a36Sopenharmony_ci vfe->power_count = 0; 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci mutex_init(&vfe->stream_lock); 137862306a36Sopenharmony_ci vfe->stream_count = 0; 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci spin_lock_init(&vfe->output_lock); 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci vfe->camss = camss; 138362306a36Sopenharmony_ci vfe->id = id; 138462306a36Sopenharmony_ci vfe->reg_update = 0; 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci for (i = VFE_LINE_RDI0; i < vfe->line_num; i++) { 138762306a36Sopenharmony_ci struct vfe_line *l = &vfe->line[i]; 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci l->video_out.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 139062306a36Sopenharmony_ci l->video_out.camss = camss; 139162306a36Sopenharmony_ci l->id = i; 139262306a36Sopenharmony_ci init_completion(&l->output.sof); 139362306a36Sopenharmony_ci init_completion(&l->output.reg_update); 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci if (camss->version == CAMSS_8x16) { 139662306a36Sopenharmony_ci if (i == VFE_LINE_PIX) { 139762306a36Sopenharmony_ci l->formats = formats_pix_8x16; 139862306a36Sopenharmony_ci l->nformats = ARRAY_SIZE(formats_pix_8x16); 139962306a36Sopenharmony_ci } else { 140062306a36Sopenharmony_ci l->formats = formats_rdi_8x16; 140162306a36Sopenharmony_ci l->nformats = ARRAY_SIZE(formats_rdi_8x16); 140262306a36Sopenharmony_ci } 140362306a36Sopenharmony_ci } else if (camss->version == CAMSS_8x96 || 140462306a36Sopenharmony_ci camss->version == CAMSS_660) { 140562306a36Sopenharmony_ci if (i == VFE_LINE_PIX) { 140662306a36Sopenharmony_ci l->formats = formats_pix_8x96; 140762306a36Sopenharmony_ci l->nformats = ARRAY_SIZE(formats_pix_8x96); 140862306a36Sopenharmony_ci } else { 140962306a36Sopenharmony_ci l->formats = formats_rdi_8x96; 141062306a36Sopenharmony_ci l->nformats = ARRAY_SIZE(formats_rdi_8x96); 141162306a36Sopenharmony_ci } 141262306a36Sopenharmony_ci } else if (camss->version == CAMSS_845 || 141362306a36Sopenharmony_ci camss->version == CAMSS_8250) { 141462306a36Sopenharmony_ci l->formats = formats_rdi_845; 141562306a36Sopenharmony_ci l->nformats = ARRAY_SIZE(formats_rdi_845); 141662306a36Sopenharmony_ci } else { 141762306a36Sopenharmony_ci return -EINVAL; 141862306a36Sopenharmony_ci } 141962306a36Sopenharmony_ci } 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci init_completion(&vfe->reset_complete); 142262306a36Sopenharmony_ci init_completion(&vfe->halt_complete); 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci return 0; 142562306a36Sopenharmony_ci} 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci/* 142862306a36Sopenharmony_ci * vfe_link_setup - Setup VFE connections 142962306a36Sopenharmony_ci * @entity: Pointer to media entity structure 143062306a36Sopenharmony_ci * @local: Pointer to local pad 143162306a36Sopenharmony_ci * @remote: Pointer to remote pad 143262306a36Sopenharmony_ci * @flags: Link flags 143362306a36Sopenharmony_ci * 143462306a36Sopenharmony_ci * Return 0 on success 143562306a36Sopenharmony_ci */ 143662306a36Sopenharmony_cistatic int vfe_link_setup(struct media_entity *entity, 143762306a36Sopenharmony_ci const struct media_pad *local, 143862306a36Sopenharmony_ci const struct media_pad *remote, u32 flags) 143962306a36Sopenharmony_ci{ 144062306a36Sopenharmony_ci if (flags & MEDIA_LNK_FL_ENABLED) 144162306a36Sopenharmony_ci if (media_pad_remote_pad_first(local)) 144262306a36Sopenharmony_ci return -EBUSY; 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci return 0; 144562306a36Sopenharmony_ci} 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_cistatic const struct v4l2_subdev_core_ops vfe_core_ops = { 144862306a36Sopenharmony_ci .s_power = vfe_set_power, 144962306a36Sopenharmony_ci}; 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_cistatic const struct v4l2_subdev_video_ops vfe_video_ops = { 145262306a36Sopenharmony_ci .s_stream = vfe_set_stream, 145362306a36Sopenharmony_ci}; 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_cistatic const struct v4l2_subdev_pad_ops vfe_pad_ops = { 145662306a36Sopenharmony_ci .enum_mbus_code = vfe_enum_mbus_code, 145762306a36Sopenharmony_ci .enum_frame_size = vfe_enum_frame_size, 145862306a36Sopenharmony_ci .get_fmt = vfe_get_format, 145962306a36Sopenharmony_ci .set_fmt = vfe_set_format, 146062306a36Sopenharmony_ci .get_selection = vfe_get_selection, 146162306a36Sopenharmony_ci .set_selection = vfe_set_selection, 146262306a36Sopenharmony_ci}; 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_cistatic const struct v4l2_subdev_ops vfe_v4l2_ops = { 146562306a36Sopenharmony_ci .core = &vfe_core_ops, 146662306a36Sopenharmony_ci .video = &vfe_video_ops, 146762306a36Sopenharmony_ci .pad = &vfe_pad_ops, 146862306a36Sopenharmony_ci}; 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_cistatic const struct v4l2_subdev_internal_ops vfe_v4l2_internal_ops = { 147162306a36Sopenharmony_ci .open = vfe_init_formats, 147262306a36Sopenharmony_ci}; 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_cistatic const struct media_entity_operations vfe_media_ops = { 147562306a36Sopenharmony_ci .link_setup = vfe_link_setup, 147662306a36Sopenharmony_ci .link_validate = v4l2_subdev_link_validate, 147762306a36Sopenharmony_ci}; 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci/* 148062306a36Sopenharmony_ci * msm_vfe_register_entities - Register subdev node for VFE module 148162306a36Sopenharmony_ci * @vfe: VFE device 148262306a36Sopenharmony_ci * @v4l2_dev: V4L2 device 148362306a36Sopenharmony_ci * 148462306a36Sopenharmony_ci * Initialize and register a subdev node for the VFE module. Then 148562306a36Sopenharmony_ci * call msm_video_register() to register the video device node which 148662306a36Sopenharmony_ci * will be connected to this subdev node. Then actually create the 148762306a36Sopenharmony_ci * media link between them. 148862306a36Sopenharmony_ci * 148962306a36Sopenharmony_ci * Return 0 on success or a negative error code otherwise 149062306a36Sopenharmony_ci */ 149162306a36Sopenharmony_ciint msm_vfe_register_entities(struct vfe_device *vfe, 149262306a36Sopenharmony_ci struct v4l2_device *v4l2_dev) 149362306a36Sopenharmony_ci{ 149462306a36Sopenharmony_ci struct device *dev = vfe->camss->dev; 149562306a36Sopenharmony_ci struct v4l2_subdev *sd; 149662306a36Sopenharmony_ci struct media_pad *pads; 149762306a36Sopenharmony_ci struct camss_video *video_out; 149862306a36Sopenharmony_ci int ret; 149962306a36Sopenharmony_ci int i; 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci for (i = 0; i < vfe->line_num; i++) { 150262306a36Sopenharmony_ci char name[32]; 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci sd = &vfe->line[i].subdev; 150562306a36Sopenharmony_ci pads = vfe->line[i].pads; 150662306a36Sopenharmony_ci video_out = &vfe->line[i].video_out; 150762306a36Sopenharmony_ci 150862306a36Sopenharmony_ci v4l2_subdev_init(sd, &vfe_v4l2_ops); 150962306a36Sopenharmony_ci sd->internal_ops = &vfe_v4l2_internal_ops; 151062306a36Sopenharmony_ci sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; 151162306a36Sopenharmony_ci if (i == VFE_LINE_PIX) 151262306a36Sopenharmony_ci snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s", 151362306a36Sopenharmony_ci MSM_VFE_NAME, vfe->id, "pix"); 151462306a36Sopenharmony_ci else 151562306a36Sopenharmony_ci snprintf(sd->name, ARRAY_SIZE(sd->name), "%s%d_%s%d", 151662306a36Sopenharmony_ci MSM_VFE_NAME, vfe->id, "rdi", i); 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci v4l2_set_subdevdata(sd, &vfe->line[i]); 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci ret = vfe_init_formats(sd, NULL); 152162306a36Sopenharmony_ci if (ret < 0) { 152262306a36Sopenharmony_ci dev_err(dev, "Failed to init format: %d\n", ret); 152362306a36Sopenharmony_ci goto error_init; 152462306a36Sopenharmony_ci } 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_ci pads[MSM_VFE_PAD_SINK].flags = MEDIA_PAD_FL_SINK; 152762306a36Sopenharmony_ci pads[MSM_VFE_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE; 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER; 153062306a36Sopenharmony_ci sd->entity.ops = &vfe_media_ops; 153162306a36Sopenharmony_ci ret = media_entity_pads_init(&sd->entity, MSM_VFE_PADS_NUM, 153262306a36Sopenharmony_ci pads); 153362306a36Sopenharmony_ci if (ret < 0) { 153462306a36Sopenharmony_ci dev_err(dev, "Failed to init media entity: %d\n", ret); 153562306a36Sopenharmony_ci goto error_init; 153662306a36Sopenharmony_ci } 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci ret = v4l2_device_register_subdev(v4l2_dev, sd); 153962306a36Sopenharmony_ci if (ret < 0) { 154062306a36Sopenharmony_ci dev_err(dev, "Failed to register subdev: %d\n", ret); 154162306a36Sopenharmony_ci goto error_reg_subdev; 154262306a36Sopenharmony_ci } 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci video_out->ops = &vfe->video_ops; 154562306a36Sopenharmony_ci if (vfe->camss->version == CAMSS_845 || 154662306a36Sopenharmony_ci vfe->camss->version == CAMSS_8250) 154762306a36Sopenharmony_ci video_out->bpl_alignment = 16; 154862306a36Sopenharmony_ci else 154962306a36Sopenharmony_ci video_out->bpl_alignment = 8; 155062306a36Sopenharmony_ci video_out->line_based = 0; 155162306a36Sopenharmony_ci if (i == VFE_LINE_PIX) { 155262306a36Sopenharmony_ci video_out->bpl_alignment = 16; 155362306a36Sopenharmony_ci video_out->line_based = 1; 155462306a36Sopenharmony_ci } 155562306a36Sopenharmony_ci snprintf(name, ARRAY_SIZE(name), "%s%d_%s%d", 155662306a36Sopenharmony_ci MSM_VFE_NAME, vfe->id, "video", i); 155762306a36Sopenharmony_ci ret = msm_video_register(video_out, v4l2_dev, name, 155862306a36Sopenharmony_ci i == VFE_LINE_PIX ? 1 : 0); 155962306a36Sopenharmony_ci if (ret < 0) { 156062306a36Sopenharmony_ci dev_err(dev, "Failed to register video node: %d\n", 156162306a36Sopenharmony_ci ret); 156262306a36Sopenharmony_ci goto error_reg_video; 156362306a36Sopenharmony_ci } 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci ret = media_create_pad_link( 156662306a36Sopenharmony_ci &sd->entity, MSM_VFE_PAD_SRC, 156762306a36Sopenharmony_ci &video_out->vdev.entity, 0, 156862306a36Sopenharmony_ci MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED); 156962306a36Sopenharmony_ci if (ret < 0) { 157062306a36Sopenharmony_ci dev_err(dev, "Failed to link %s->%s entities: %d\n", 157162306a36Sopenharmony_ci sd->entity.name, video_out->vdev.entity.name, 157262306a36Sopenharmony_ci ret); 157362306a36Sopenharmony_ci goto error_link; 157462306a36Sopenharmony_ci } 157562306a36Sopenharmony_ci } 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci return 0; 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_cierror_link: 158062306a36Sopenharmony_ci msm_video_unregister(video_out); 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_cierror_reg_video: 158362306a36Sopenharmony_ci v4l2_device_unregister_subdev(sd); 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_cierror_reg_subdev: 158662306a36Sopenharmony_ci media_entity_cleanup(&sd->entity); 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_cierror_init: 158962306a36Sopenharmony_ci for (i--; i >= 0; i--) { 159062306a36Sopenharmony_ci sd = &vfe->line[i].subdev; 159162306a36Sopenharmony_ci video_out = &vfe->line[i].video_out; 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci msm_video_unregister(video_out); 159462306a36Sopenharmony_ci v4l2_device_unregister_subdev(sd); 159562306a36Sopenharmony_ci media_entity_cleanup(&sd->entity); 159662306a36Sopenharmony_ci } 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci return ret; 159962306a36Sopenharmony_ci} 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci/* 160262306a36Sopenharmony_ci * msm_vfe_unregister_entities - Unregister VFE module subdev node 160362306a36Sopenharmony_ci * @vfe: VFE device 160462306a36Sopenharmony_ci */ 160562306a36Sopenharmony_civoid msm_vfe_unregister_entities(struct vfe_device *vfe) 160662306a36Sopenharmony_ci{ 160762306a36Sopenharmony_ci int i; 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci mutex_destroy(&vfe->power_lock); 161062306a36Sopenharmony_ci mutex_destroy(&vfe->stream_lock); 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci for (i = 0; i < vfe->line_num; i++) { 161362306a36Sopenharmony_ci struct v4l2_subdev *sd = &vfe->line[i].subdev; 161462306a36Sopenharmony_ci struct camss_video *video_out = &vfe->line[i].video_out; 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_ci msm_video_unregister(video_out); 161762306a36Sopenharmony_ci v4l2_device_unregister_subdev(sd); 161862306a36Sopenharmony_ci media_entity_cleanup(&sd->entity); 161962306a36Sopenharmony_ci } 162062306a36Sopenharmony_ci} 1621