18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * TI VPFE capture Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2013 - 2014 Texas Instruments, Inc. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Benoit Parrot <bparrot@ti.com> 88c2ecf20Sopenharmony_ci * Lad, Prabhakar <prabhakar.csengg@gmail.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/err.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/of_graph.h> 188c2ecf20Sopenharmony_ci#include <linux/pinctrl/consumer.h> 198c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 208c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 238c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <media/v4l2-common.h> 268c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 278c2ecf20Sopenharmony_ci#include <media/v4l2-event.h> 288c2ecf20Sopenharmony_ci#include <media/v4l2-fwnode.h> 298c2ecf20Sopenharmony_ci#include <media/v4l2-rect.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include "am437x-vpfe.h" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define VPFE_MODULE_NAME "vpfe" 348c2ecf20Sopenharmony_ci#define VPFE_VERSION "0.1.0" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic int debug; 378c2ecf20Sopenharmony_cimodule_param(debug, int, 0644); 388c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Debug level 0-8"); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define vpfe_dbg(level, dev, fmt, arg...) \ 418c2ecf20Sopenharmony_ci v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ##arg) 428c2ecf20Sopenharmony_ci#define vpfe_info(dev, fmt, arg...) \ 438c2ecf20Sopenharmony_ci v4l2_info(&dev->v4l2_dev, fmt, ##arg) 448c2ecf20Sopenharmony_ci#define vpfe_err(dev, fmt, arg...) \ 458c2ecf20Sopenharmony_ci v4l2_err(&dev->v4l2_dev, fmt, ##arg) 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci/* standard information */ 488c2ecf20Sopenharmony_cistruct vpfe_standard { 498c2ecf20Sopenharmony_ci v4l2_std_id std_id; 508c2ecf20Sopenharmony_ci unsigned int width; 518c2ecf20Sopenharmony_ci unsigned int height; 528c2ecf20Sopenharmony_ci struct v4l2_fract pixelaspect; 538c2ecf20Sopenharmony_ci int frame_format; 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic const struct vpfe_standard vpfe_standards[] = { 578c2ecf20Sopenharmony_ci {V4L2_STD_525_60, 720, 480, {11, 10}, 1}, 588c2ecf20Sopenharmony_ci {V4L2_STD_625_50, 720, 576, {54, 59}, 1}, 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic struct vpfe_fmt formats[VPFE_NUM_FORMATS] = { 628c2ecf20Sopenharmony_ci { 638c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_YUYV, 648c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_YUYV8_2X8, 658c2ecf20Sopenharmony_ci .bitsperpixel = 16, 668c2ecf20Sopenharmony_ci }, { 678c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_UYVY, 688c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_UYVY8_2X8, 698c2ecf20Sopenharmony_ci .bitsperpixel = 16, 708c2ecf20Sopenharmony_ci }, { 718c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_YVYU, 728c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_YVYU8_2X8, 738c2ecf20Sopenharmony_ci .bitsperpixel = 16, 748c2ecf20Sopenharmony_ci }, { 758c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_VYUY, 768c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_VYUY8_2X8, 778c2ecf20Sopenharmony_ci .bitsperpixel = 16, 788c2ecf20Sopenharmony_ci }, { 798c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SBGGR8, 808c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_SBGGR8_1X8, 818c2ecf20Sopenharmony_ci .bitsperpixel = 8, 828c2ecf20Sopenharmony_ci }, { 838c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SGBRG8, 848c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_SGBRG8_1X8, 858c2ecf20Sopenharmony_ci .bitsperpixel = 8, 868c2ecf20Sopenharmony_ci }, { 878c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SGRBG8, 888c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_SGRBG8_1X8, 898c2ecf20Sopenharmony_ci .bitsperpixel = 8, 908c2ecf20Sopenharmony_ci }, { 918c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SRGGB8, 928c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_SRGGB8_1X8, 938c2ecf20Sopenharmony_ci .bitsperpixel = 8, 948c2ecf20Sopenharmony_ci }, { 958c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB565, 968c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_RGB565_2X8_LE, 978c2ecf20Sopenharmony_ci .bitsperpixel = 16, 988c2ecf20Sopenharmony_ci }, { 998c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB565X, 1008c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_RGB565_2X8_BE, 1018c2ecf20Sopenharmony_ci .bitsperpixel = 16, 1028c2ecf20Sopenharmony_ci }, 1038c2ecf20Sopenharmony_ci}; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic int __subdev_get_format(struct vpfe_device *vpfe, 1068c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *fmt); 1078c2ecf20Sopenharmony_cistatic int vpfe_calc_format_size(struct vpfe_device *vpfe, 1088c2ecf20Sopenharmony_ci const struct vpfe_fmt *fmt, 1098c2ecf20Sopenharmony_ci struct v4l2_format *f); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic struct vpfe_fmt *find_format_by_code(struct vpfe_device *vpfe, 1128c2ecf20Sopenharmony_ci unsigned int code) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci struct vpfe_fmt *fmt; 1158c2ecf20Sopenharmony_ci unsigned int k; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci for (k = 0; k < vpfe->num_active_fmt; k++) { 1188c2ecf20Sopenharmony_ci fmt = vpfe->active_fmt[k]; 1198c2ecf20Sopenharmony_ci if (fmt->code == code) 1208c2ecf20Sopenharmony_ci return fmt; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return NULL; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic struct vpfe_fmt *find_format_by_pix(struct vpfe_device *vpfe, 1278c2ecf20Sopenharmony_ci unsigned int pixelformat) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci struct vpfe_fmt *fmt; 1308c2ecf20Sopenharmony_ci unsigned int k; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci for (k = 0; k < vpfe->num_active_fmt; k++) { 1338c2ecf20Sopenharmony_ci fmt = vpfe->active_fmt[k]; 1348c2ecf20Sopenharmony_ci if (fmt->fourcc == pixelformat) 1358c2ecf20Sopenharmony_ci return fmt; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci return NULL; 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic unsigned int __get_bytesperpixel(struct vpfe_device *vpfe, 1428c2ecf20Sopenharmony_ci const struct vpfe_fmt *fmt) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct vpfe_subdev_info *sdinfo = vpfe->current_subdev; 1458c2ecf20Sopenharmony_ci unsigned int bus_width = sdinfo->vpfe_param.bus_width; 1468c2ecf20Sopenharmony_ci u32 bpp, bus_width_bytes, clocksperpixel; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci bus_width_bytes = ALIGN(bus_width, 8) >> 3; 1498c2ecf20Sopenharmony_ci clocksperpixel = DIV_ROUND_UP(fmt->bitsperpixel, bus_width); 1508c2ecf20Sopenharmony_ci bpp = clocksperpixel * bus_width_bytes; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci return bpp; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci/* Print Four-character-code (FOURCC) */ 1568c2ecf20Sopenharmony_cistatic char *print_fourcc(u32 fmt) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci static char code[5]; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci code[0] = (unsigned char)(fmt & 0xff); 1618c2ecf20Sopenharmony_ci code[1] = (unsigned char)((fmt >> 8) & 0xff); 1628c2ecf20Sopenharmony_ci code[2] = (unsigned char)((fmt >> 16) & 0xff); 1638c2ecf20Sopenharmony_ci code[3] = (unsigned char)((fmt >> 24) & 0xff); 1648c2ecf20Sopenharmony_ci code[4] = '\0'; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci return code; 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic inline u32 vpfe_reg_read(struct vpfe_ccdc *ccdc, u32 offset) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci return ioread32(ccdc->ccdc_cfg.base_addr + offset); 1728c2ecf20Sopenharmony_ci} 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_cistatic inline void vpfe_reg_write(struct vpfe_ccdc *ccdc, u32 val, u32 offset) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci iowrite32(val, ccdc->ccdc_cfg.base_addr + offset); 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic inline struct vpfe_device *to_vpfe(struct vpfe_ccdc *ccdc) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci return container_of(ccdc, struct vpfe_device, ccdc); 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic inline 1858c2ecf20Sopenharmony_cistruct vpfe_cap_buffer *to_vpfe_buffer(struct vb2_v4l2_buffer *vb) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci return container_of(vb, struct vpfe_cap_buffer, vb); 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic inline void vpfe_pcr_enable(struct vpfe_ccdc *ccdc, int flag) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, !!flag, VPFE_PCR); 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic void vpfe_config_enable(struct vpfe_ccdc *ccdc, int flag) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci unsigned int cfg; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (!flag) { 2008c2ecf20Sopenharmony_ci cfg = vpfe_reg_read(ccdc, VPFE_CONFIG); 2018c2ecf20Sopenharmony_ci cfg &= ~(VPFE_CONFIG_EN_ENABLE << VPFE_CONFIG_EN_SHIFT); 2028c2ecf20Sopenharmony_ci } else { 2038c2ecf20Sopenharmony_ci cfg = VPFE_CONFIG_EN_ENABLE << VPFE_CONFIG_EN_SHIFT; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, cfg, VPFE_CONFIG); 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic void vpfe_ccdc_setwin(struct vpfe_ccdc *ccdc, 2108c2ecf20Sopenharmony_ci struct v4l2_rect *image_win, 2118c2ecf20Sopenharmony_ci enum ccdc_frmfmt frm_fmt, 2128c2ecf20Sopenharmony_ci int bpp) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci int horz_start, horz_nr_pixels; 2158c2ecf20Sopenharmony_ci int vert_start, vert_nr_lines; 2168c2ecf20Sopenharmony_ci int val, mid_img; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci /* 2198c2ecf20Sopenharmony_ci * ppc - per pixel count. indicates how many pixels per cell 2208c2ecf20Sopenharmony_ci * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. 2218c2ecf20Sopenharmony_ci * raw capture this is 1 2228c2ecf20Sopenharmony_ci */ 2238c2ecf20Sopenharmony_ci horz_start = image_win->left * bpp; 2248c2ecf20Sopenharmony_ci horz_nr_pixels = (image_win->width * bpp) - 1; 2258c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, (horz_start << VPFE_HORZ_INFO_SPH_SHIFT) | 2268c2ecf20Sopenharmony_ci horz_nr_pixels, VPFE_HORZ_INFO); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci vert_start = image_win->top; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (frm_fmt == CCDC_FRMFMT_INTERLACED) { 2318c2ecf20Sopenharmony_ci vert_nr_lines = (image_win->height >> 1) - 1; 2328c2ecf20Sopenharmony_ci vert_start >>= 1; 2338c2ecf20Sopenharmony_ci /* configure VDINT0 */ 2348c2ecf20Sopenharmony_ci val = (vert_start << VPFE_VDINT_VDINT0_SHIFT); 2358c2ecf20Sopenharmony_ci } else { 2368c2ecf20Sopenharmony_ci vert_nr_lines = image_win->height - 1; 2378c2ecf20Sopenharmony_ci /* 2388c2ecf20Sopenharmony_ci * configure VDINT0 and VDINT1. VDINT1 will be at half 2398c2ecf20Sopenharmony_ci * of image height 2408c2ecf20Sopenharmony_ci */ 2418c2ecf20Sopenharmony_ci mid_img = vert_start + (image_win->height / 2); 2428c2ecf20Sopenharmony_ci val = (vert_start << VPFE_VDINT_VDINT0_SHIFT) | 2438c2ecf20Sopenharmony_ci (mid_img & VPFE_VDINT_VDINT1_MASK); 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, val, VPFE_VDINT); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, (vert_start << VPFE_VERT_START_SLV0_SHIFT) | 2498c2ecf20Sopenharmony_ci vert_start, VPFE_VERT_START); 2508c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, vert_nr_lines, VPFE_VERT_LINES); 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic void vpfe_reg_dump(struct vpfe_ccdc *ccdc) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = to_vpfe(ccdc); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci vpfe_dbg(3, vpfe, "ALAW: 0x%x\n", vpfe_reg_read(ccdc, VPFE_ALAW)); 2588c2ecf20Sopenharmony_ci vpfe_dbg(3, vpfe, "CLAMP: 0x%x\n", vpfe_reg_read(ccdc, VPFE_CLAMP)); 2598c2ecf20Sopenharmony_ci vpfe_dbg(3, vpfe, "DCSUB: 0x%x\n", vpfe_reg_read(ccdc, VPFE_DCSUB)); 2608c2ecf20Sopenharmony_ci vpfe_dbg(3, vpfe, "BLKCMP: 0x%x\n", vpfe_reg_read(ccdc, VPFE_BLKCMP)); 2618c2ecf20Sopenharmony_ci vpfe_dbg(3, vpfe, "COLPTN: 0x%x\n", vpfe_reg_read(ccdc, VPFE_COLPTN)); 2628c2ecf20Sopenharmony_ci vpfe_dbg(3, vpfe, "SDOFST: 0x%x\n", vpfe_reg_read(ccdc, VPFE_SDOFST)); 2638c2ecf20Sopenharmony_ci vpfe_dbg(3, vpfe, "SYN_MODE: 0x%x\n", 2648c2ecf20Sopenharmony_ci vpfe_reg_read(ccdc, VPFE_SYNMODE)); 2658c2ecf20Sopenharmony_ci vpfe_dbg(3, vpfe, "HSIZE_OFF: 0x%x\n", 2668c2ecf20Sopenharmony_ci vpfe_reg_read(ccdc, VPFE_HSIZE_OFF)); 2678c2ecf20Sopenharmony_ci vpfe_dbg(3, vpfe, "HORZ_INFO: 0x%x\n", 2688c2ecf20Sopenharmony_ci vpfe_reg_read(ccdc, VPFE_HORZ_INFO)); 2698c2ecf20Sopenharmony_ci vpfe_dbg(3, vpfe, "VERT_START: 0x%x\n", 2708c2ecf20Sopenharmony_ci vpfe_reg_read(ccdc, VPFE_VERT_START)); 2718c2ecf20Sopenharmony_ci vpfe_dbg(3, vpfe, "VERT_LINES: 0x%x\n", 2728c2ecf20Sopenharmony_ci vpfe_reg_read(ccdc, VPFE_VERT_LINES)); 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic int 2768c2ecf20Sopenharmony_civpfe_ccdc_validate_param(struct vpfe_ccdc *ccdc, 2778c2ecf20Sopenharmony_ci struct vpfe_ccdc_config_params_raw *ccdcparam) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = to_vpfe(ccdc); 2808c2ecf20Sopenharmony_ci u8 max_gamma, max_data; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (!ccdcparam->alaw.enable) 2838c2ecf20Sopenharmony_ci return 0; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci max_gamma = ccdc_gamma_width_max_bit(ccdcparam->alaw.gamma_wd); 2868c2ecf20Sopenharmony_ci max_data = ccdc_data_size_max_bit(ccdcparam->data_sz); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (ccdcparam->alaw.gamma_wd > VPFE_CCDC_GAMMA_BITS_09_0 || 2898c2ecf20Sopenharmony_ci ccdcparam->data_sz > VPFE_CCDC_DATA_8BITS || 2908c2ecf20Sopenharmony_ci max_gamma > max_data) { 2918c2ecf20Sopenharmony_ci vpfe_dbg(1, vpfe, "Invalid data line select\n"); 2928c2ecf20Sopenharmony_ci return -EINVAL; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci return 0; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic void 2998c2ecf20Sopenharmony_civpfe_ccdc_update_raw_params(struct vpfe_ccdc *ccdc, 3008c2ecf20Sopenharmony_ci struct vpfe_ccdc_config_params_raw *raw_params) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci struct vpfe_ccdc_config_params_raw *config_params = 3038c2ecf20Sopenharmony_ci &ccdc->ccdc_cfg.bayer.config_params; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci *config_params = *raw_params; 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci/* 3098c2ecf20Sopenharmony_ci * vpfe_ccdc_restore_defaults() 3108c2ecf20Sopenharmony_ci * This function will write defaults to all CCDC registers 3118c2ecf20Sopenharmony_ci */ 3128c2ecf20Sopenharmony_cistatic void vpfe_ccdc_restore_defaults(struct vpfe_ccdc *ccdc) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci int i; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci /* Disable CCDC */ 3178c2ecf20Sopenharmony_ci vpfe_pcr_enable(ccdc, 0); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci /* set all registers to default value */ 3208c2ecf20Sopenharmony_ci for (i = 4; i <= 0x94; i += 4) 3218c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, 0, i); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, VPFE_NO_CULLING, VPFE_CULLING); 3248c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, VPFE_CCDC_GAMMA_BITS_11_2, VPFE_ALAW); 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_cistatic int vpfe_ccdc_close(struct vpfe_ccdc *ccdc, struct device *dev) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = to_vpfe(ccdc); 3308c2ecf20Sopenharmony_ci u32 dma_cntl, pcr; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci pcr = vpfe_reg_read(ccdc, VPFE_PCR); 3338c2ecf20Sopenharmony_ci if (pcr) 3348c2ecf20Sopenharmony_ci vpfe_dbg(1, vpfe, "VPFE_PCR is still set (%x)", pcr); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci dma_cntl = vpfe_reg_read(ccdc, VPFE_DMA_CNTL); 3378c2ecf20Sopenharmony_ci if ((dma_cntl & VPFE_DMA_CNTL_OVERFLOW)) 3388c2ecf20Sopenharmony_ci vpfe_dbg(1, vpfe, "VPFE_DMA_CNTL_OVERFLOW is still set (%x)", 3398c2ecf20Sopenharmony_ci dma_cntl); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci /* Disable CCDC by resetting all register to default POR values */ 3428c2ecf20Sopenharmony_ci vpfe_ccdc_restore_defaults(ccdc); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci /* Disabled the module at the CONFIG level */ 3458c2ecf20Sopenharmony_ci vpfe_config_enable(ccdc, 0); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci pm_runtime_put_sync(dev); 3488c2ecf20Sopenharmony_ci return 0; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic int vpfe_ccdc_set_params(struct vpfe_ccdc *ccdc, void __user *params) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = to_vpfe(ccdc); 3548c2ecf20Sopenharmony_ci struct vpfe_ccdc_config_params_raw raw_params; 3558c2ecf20Sopenharmony_ci int x; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (ccdc->ccdc_cfg.if_type != VPFE_RAW_BAYER) 3588c2ecf20Sopenharmony_ci return -EINVAL; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci x = copy_from_user(&raw_params, params, sizeof(raw_params)); 3618c2ecf20Sopenharmony_ci if (x) { 3628c2ecf20Sopenharmony_ci vpfe_dbg(1, vpfe, 3638c2ecf20Sopenharmony_ci "%s: error in copying ccdc params, %d\n", 3648c2ecf20Sopenharmony_ci __func__, x); 3658c2ecf20Sopenharmony_ci return -EFAULT; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (!vpfe_ccdc_validate_param(ccdc, &raw_params)) { 3698c2ecf20Sopenharmony_ci vpfe_ccdc_update_raw_params(ccdc, &raw_params); 3708c2ecf20Sopenharmony_ci return 0; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci return -EINVAL; 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci/* 3778c2ecf20Sopenharmony_ci * vpfe_ccdc_config_ycbcr() 3788c2ecf20Sopenharmony_ci * This function will configure CCDC for YCbCr video capture 3798c2ecf20Sopenharmony_ci */ 3808c2ecf20Sopenharmony_cistatic void vpfe_ccdc_config_ycbcr(struct vpfe_ccdc *ccdc) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci struct ccdc_params_ycbcr *params = &ccdc->ccdc_cfg.ycbcr; 3838c2ecf20Sopenharmony_ci u32 syn_mode; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci /* 3868c2ecf20Sopenharmony_ci * first restore the CCDC registers to default values 3878c2ecf20Sopenharmony_ci * This is important since we assume default values to be set in 3888c2ecf20Sopenharmony_ci * a lot of registers that we didn't touch 3898c2ecf20Sopenharmony_ci */ 3908c2ecf20Sopenharmony_ci vpfe_ccdc_restore_defaults(ccdc); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci /* 3938c2ecf20Sopenharmony_ci * configure pixel format, frame format, configure video frame 3948c2ecf20Sopenharmony_ci * format, enable output to SDRAM, enable internal timing generator 3958c2ecf20Sopenharmony_ci * and 8bit pack mode 3968c2ecf20Sopenharmony_ci */ 3978c2ecf20Sopenharmony_ci syn_mode = (((params->pix_fmt & VPFE_SYN_MODE_INPMOD_MASK) << 3988c2ecf20Sopenharmony_ci VPFE_SYN_MODE_INPMOD_SHIFT) | 3998c2ecf20Sopenharmony_ci ((params->frm_fmt & VPFE_SYN_FLDMODE_MASK) << 4008c2ecf20Sopenharmony_ci VPFE_SYN_FLDMODE_SHIFT) | VPFE_VDHDEN_ENABLE | 4018c2ecf20Sopenharmony_ci VPFE_WEN_ENABLE | VPFE_DATA_PACK_ENABLE); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci /* setup BT.656 sync mode */ 4048c2ecf20Sopenharmony_ci if (params->bt656_enable) { 4058c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, VPFE_REC656IF_BT656_EN, VPFE_REC656IF); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* 4088c2ecf20Sopenharmony_ci * configure the FID, VD, HD pin polarity, 4098c2ecf20Sopenharmony_ci * fld,hd pol positive, vd negative, 8-bit data 4108c2ecf20Sopenharmony_ci */ 4118c2ecf20Sopenharmony_ci syn_mode |= VPFE_SYN_MODE_VD_POL_NEGATIVE; 4128c2ecf20Sopenharmony_ci if (ccdc->ccdc_cfg.if_type == VPFE_BT656_10BIT) 4138c2ecf20Sopenharmony_ci syn_mode |= VPFE_SYN_MODE_10BITS; 4148c2ecf20Sopenharmony_ci else 4158c2ecf20Sopenharmony_ci syn_mode |= VPFE_SYN_MODE_8BITS; 4168c2ecf20Sopenharmony_ci } else { 4178c2ecf20Sopenharmony_ci /* y/c external sync mode */ 4188c2ecf20Sopenharmony_ci syn_mode |= (((params->fid_pol & VPFE_FID_POL_MASK) << 4198c2ecf20Sopenharmony_ci VPFE_FID_POL_SHIFT) | 4208c2ecf20Sopenharmony_ci ((params->hd_pol & VPFE_HD_POL_MASK) << 4218c2ecf20Sopenharmony_ci VPFE_HD_POL_SHIFT) | 4228c2ecf20Sopenharmony_ci ((params->vd_pol & VPFE_VD_POL_MASK) << 4238c2ecf20Sopenharmony_ci VPFE_VD_POL_SHIFT)); 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, syn_mode, VPFE_SYNMODE); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci /* configure video window */ 4288c2ecf20Sopenharmony_ci vpfe_ccdc_setwin(ccdc, ¶ms->win, 4298c2ecf20Sopenharmony_ci params->frm_fmt, params->bytesperpixel); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* 4328c2ecf20Sopenharmony_ci * configure the order of y cb cr in SDRAM, and disable latch 4338c2ecf20Sopenharmony_ci * internal register on vsync 4348c2ecf20Sopenharmony_ci */ 4358c2ecf20Sopenharmony_ci if (ccdc->ccdc_cfg.if_type == VPFE_BT656_10BIT) 4368c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, 4378c2ecf20Sopenharmony_ci (params->pix_order << VPFE_CCDCFG_Y8POS_SHIFT) | 4388c2ecf20Sopenharmony_ci VPFE_LATCH_ON_VSYNC_DISABLE | 4398c2ecf20Sopenharmony_ci VPFE_CCDCFG_BW656_10BIT, VPFE_CCDCFG); 4408c2ecf20Sopenharmony_ci else 4418c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, 4428c2ecf20Sopenharmony_ci (params->pix_order << VPFE_CCDCFG_Y8POS_SHIFT) | 4438c2ecf20Sopenharmony_ci VPFE_LATCH_ON_VSYNC_DISABLE, VPFE_CCDCFG); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci /* 4468c2ecf20Sopenharmony_ci * configure the horizontal line offset. This should be a 4478c2ecf20Sopenharmony_ci * on 32 byte boundary. So clear LSB 5 bits 4488c2ecf20Sopenharmony_ci */ 4498c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, params->bytesperline, VPFE_HSIZE_OFF); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci /* configure the memory line offset */ 4528c2ecf20Sopenharmony_ci if (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) 4538c2ecf20Sopenharmony_ci /* two fields are interleaved in memory */ 4548c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, VPFE_SDOFST_FIELD_INTERLEAVED, 4558c2ecf20Sopenharmony_ci VPFE_SDOFST); 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic void 4598c2ecf20Sopenharmony_civpfe_ccdc_config_black_clamp(struct vpfe_ccdc *ccdc, 4608c2ecf20Sopenharmony_ci struct vpfe_ccdc_black_clamp *bclamp) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci u32 val; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (!bclamp->enable) { 4658c2ecf20Sopenharmony_ci /* configure DCSub */ 4668c2ecf20Sopenharmony_ci val = (bclamp->dc_sub) & VPFE_BLK_DC_SUB_MASK; 4678c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, val, VPFE_DCSUB); 4688c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, VPFE_CLAMP_DEFAULT_VAL, VPFE_CLAMP); 4698c2ecf20Sopenharmony_ci return; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci /* 4728c2ecf20Sopenharmony_ci * Configure gain, Start pixel, No of line to be avg, 4738c2ecf20Sopenharmony_ci * No of pixel/line to be avg, & Enable the Black clamping 4748c2ecf20Sopenharmony_ci */ 4758c2ecf20Sopenharmony_ci val = ((bclamp->sgain & VPFE_BLK_SGAIN_MASK) | 4768c2ecf20Sopenharmony_ci ((bclamp->start_pixel & VPFE_BLK_ST_PXL_MASK) << 4778c2ecf20Sopenharmony_ci VPFE_BLK_ST_PXL_SHIFT) | 4788c2ecf20Sopenharmony_ci ((bclamp->sample_ln & VPFE_BLK_SAMPLE_LINE_MASK) << 4798c2ecf20Sopenharmony_ci VPFE_BLK_SAMPLE_LINE_SHIFT) | 4808c2ecf20Sopenharmony_ci ((bclamp->sample_pixel & VPFE_BLK_SAMPLE_LN_MASK) << 4818c2ecf20Sopenharmony_ci VPFE_BLK_SAMPLE_LN_SHIFT) | VPFE_BLK_CLAMP_ENABLE); 4828c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, val, VPFE_CLAMP); 4838c2ecf20Sopenharmony_ci /* If Black clamping is enable then make dcsub 0 */ 4848c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, VPFE_DCSUB_DEFAULT_VAL, VPFE_DCSUB); 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic void 4888c2ecf20Sopenharmony_civpfe_ccdc_config_black_compense(struct vpfe_ccdc *ccdc, 4898c2ecf20Sopenharmony_ci struct vpfe_ccdc_black_compensation *bcomp) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci u32 val; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci val = ((bcomp->b & VPFE_BLK_COMP_MASK) | 4948c2ecf20Sopenharmony_ci ((bcomp->gb & VPFE_BLK_COMP_MASK) << 4958c2ecf20Sopenharmony_ci VPFE_BLK_COMP_GB_COMP_SHIFT) | 4968c2ecf20Sopenharmony_ci ((bcomp->gr & VPFE_BLK_COMP_MASK) << 4978c2ecf20Sopenharmony_ci VPFE_BLK_COMP_GR_COMP_SHIFT) | 4988c2ecf20Sopenharmony_ci ((bcomp->r & VPFE_BLK_COMP_MASK) << 4998c2ecf20Sopenharmony_ci VPFE_BLK_COMP_R_COMP_SHIFT)); 5008c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, val, VPFE_BLKCMP); 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci/* 5048c2ecf20Sopenharmony_ci * vpfe_ccdc_config_raw() 5058c2ecf20Sopenharmony_ci * This function will configure CCDC for Raw capture mode 5068c2ecf20Sopenharmony_ci */ 5078c2ecf20Sopenharmony_cistatic void vpfe_ccdc_config_raw(struct vpfe_ccdc *ccdc) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = to_vpfe(ccdc); 5108c2ecf20Sopenharmony_ci struct vpfe_ccdc_config_params_raw *config_params = 5118c2ecf20Sopenharmony_ci &ccdc->ccdc_cfg.bayer.config_params; 5128c2ecf20Sopenharmony_ci struct ccdc_params_raw *params = &ccdc->ccdc_cfg.bayer; 5138c2ecf20Sopenharmony_ci unsigned int syn_mode; 5148c2ecf20Sopenharmony_ci unsigned int val; 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci /* Reset CCDC */ 5178c2ecf20Sopenharmony_ci vpfe_ccdc_restore_defaults(ccdc); 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci /* Disable latching function registers on VSYNC */ 5208c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, VPFE_LATCH_ON_VSYNC_DISABLE, VPFE_CCDCFG); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci /* 5238c2ecf20Sopenharmony_ci * Configure the vertical sync polarity(SYN_MODE.VDPOL), 5248c2ecf20Sopenharmony_ci * horizontal sync polarity (SYN_MODE.HDPOL), frame id polarity 5258c2ecf20Sopenharmony_ci * (SYN_MODE.FLDPOL), frame format(progressive or interlace), 5268c2ecf20Sopenharmony_ci * data size(SYNMODE.DATSIZ), &pixel format (Input mode), output 5278c2ecf20Sopenharmony_ci * SDRAM, enable internal timing generator 5288c2ecf20Sopenharmony_ci */ 5298c2ecf20Sopenharmony_ci syn_mode = (((params->vd_pol & VPFE_VD_POL_MASK) << VPFE_VD_POL_SHIFT) | 5308c2ecf20Sopenharmony_ci ((params->hd_pol & VPFE_HD_POL_MASK) << VPFE_HD_POL_SHIFT) | 5318c2ecf20Sopenharmony_ci ((params->fid_pol & VPFE_FID_POL_MASK) << 5328c2ecf20Sopenharmony_ci VPFE_FID_POL_SHIFT) | ((params->frm_fmt & 5338c2ecf20Sopenharmony_ci VPFE_FRM_FMT_MASK) << VPFE_FRM_FMT_SHIFT) | 5348c2ecf20Sopenharmony_ci ((config_params->data_sz & VPFE_DATA_SZ_MASK) << 5358c2ecf20Sopenharmony_ci VPFE_DATA_SZ_SHIFT) | ((params->pix_fmt & 5368c2ecf20Sopenharmony_ci VPFE_PIX_FMT_MASK) << VPFE_PIX_FMT_SHIFT) | 5378c2ecf20Sopenharmony_ci VPFE_WEN_ENABLE | VPFE_VDHDEN_ENABLE); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci /* Enable and configure aLaw register if needed */ 5408c2ecf20Sopenharmony_ci if (config_params->alaw.enable) { 5418c2ecf20Sopenharmony_ci val = ((config_params->alaw.gamma_wd & 5428c2ecf20Sopenharmony_ci VPFE_ALAW_GAMMA_WD_MASK) | VPFE_ALAW_ENABLE); 5438c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, val, VPFE_ALAW); 5448c2ecf20Sopenharmony_ci vpfe_dbg(3, vpfe, "\nWriting 0x%x to ALAW...\n", val); 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci /* Configure video window */ 5488c2ecf20Sopenharmony_ci vpfe_ccdc_setwin(ccdc, ¶ms->win, params->frm_fmt, 5498c2ecf20Sopenharmony_ci params->bytesperpixel); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci /* Configure Black Clamp */ 5528c2ecf20Sopenharmony_ci vpfe_ccdc_config_black_clamp(ccdc, &config_params->blk_clamp); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci /* Configure Black level compensation */ 5558c2ecf20Sopenharmony_ci vpfe_ccdc_config_black_compense(ccdc, &config_params->blk_comp); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci /* If data size is 8 bit then pack the data */ 5588c2ecf20Sopenharmony_ci if ((config_params->data_sz == VPFE_CCDC_DATA_8BITS) || 5598c2ecf20Sopenharmony_ci config_params->alaw.enable) 5608c2ecf20Sopenharmony_ci syn_mode |= VPFE_DATA_PACK_ENABLE; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci /* 5638c2ecf20Sopenharmony_ci * Configure Horizontal offset register. If pack 8 is enabled then 5648c2ecf20Sopenharmony_ci * 1 pixel will take 1 byte 5658c2ecf20Sopenharmony_ci */ 5668c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, params->bytesperline, VPFE_HSIZE_OFF); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci vpfe_dbg(3, vpfe, "Writing %d (%x) to HSIZE_OFF\n", 5698c2ecf20Sopenharmony_ci params->bytesperline, params->bytesperline); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci /* Set value for SDOFST */ 5728c2ecf20Sopenharmony_ci if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) { 5738c2ecf20Sopenharmony_ci if (params->image_invert_enable) { 5748c2ecf20Sopenharmony_ci /* For interlace inverse mode */ 5758c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, VPFE_INTERLACED_IMAGE_INVERT, 5768c2ecf20Sopenharmony_ci VPFE_SDOFST); 5778c2ecf20Sopenharmony_ci } else { 5788c2ecf20Sopenharmony_ci /* For interlace non inverse mode */ 5798c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, VPFE_INTERLACED_NO_IMAGE_INVERT, 5808c2ecf20Sopenharmony_ci VPFE_SDOFST); 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { 5838c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, VPFE_PROGRESSIVE_NO_IMAGE_INVERT, 5848c2ecf20Sopenharmony_ci VPFE_SDOFST); 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, syn_mode, VPFE_SYNMODE); 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci vpfe_reg_dump(ccdc); 5908c2ecf20Sopenharmony_ci} 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_cistatic inline int 5938c2ecf20Sopenharmony_civpfe_ccdc_set_buftype(struct vpfe_ccdc *ccdc, 5948c2ecf20Sopenharmony_ci enum ccdc_buftype buf_type) 5958c2ecf20Sopenharmony_ci{ 5968c2ecf20Sopenharmony_ci if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) 5978c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.bayer.buf_type = buf_type; 5988c2ecf20Sopenharmony_ci else 5998c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.ycbcr.buf_type = buf_type; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci return 0; 6028c2ecf20Sopenharmony_ci} 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_cistatic inline enum ccdc_buftype vpfe_ccdc_get_buftype(struct vpfe_ccdc *ccdc) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) 6078c2ecf20Sopenharmony_ci return ccdc->ccdc_cfg.bayer.buf_type; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci return ccdc->ccdc_cfg.ycbcr.buf_type; 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_cistatic int vpfe_ccdc_set_pixel_format(struct vpfe_ccdc *ccdc, u32 pixfmt) 6138c2ecf20Sopenharmony_ci{ 6148c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = to_vpfe(ccdc); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci vpfe_dbg(1, vpfe, "%s: if_type: %d, pixfmt:%s\n", 6178c2ecf20Sopenharmony_ci __func__, ccdc->ccdc_cfg.if_type, print_fourcc(pixfmt)); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) { 6208c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; 6218c2ecf20Sopenharmony_ci /* 6228c2ecf20Sopenharmony_ci * Need to clear it in case it was left on 6238c2ecf20Sopenharmony_ci * after the last capture. 6248c2ecf20Sopenharmony_ci */ 6258c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.bayer.config_params.alaw.enable = 0; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci switch (pixfmt) { 6288c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_SBGGR8: 6298c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.bayer.config_params.alaw.enable = 1; 6308c2ecf20Sopenharmony_ci break; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_YUYV: 6338c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_UYVY: 6348c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_YUV420: 6358c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_NV12: 6368c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_RGB565X: 6378c2ecf20Sopenharmony_ci break; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_SBGGR16: 6408c2ecf20Sopenharmony_ci default: 6418c2ecf20Sopenharmony_ci return -EINVAL; 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci } else { 6448c2ecf20Sopenharmony_ci switch (pixfmt) { 6458c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_YUYV: 6468c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; 6478c2ecf20Sopenharmony_ci break; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci case V4L2_PIX_FMT_UYVY: 6508c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; 6518c2ecf20Sopenharmony_ci break; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci default: 6548c2ecf20Sopenharmony_ci return -EINVAL; 6558c2ecf20Sopenharmony_ci } 6568c2ecf20Sopenharmony_ci } 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci return 0; 6598c2ecf20Sopenharmony_ci} 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_cistatic u32 vpfe_ccdc_get_pixel_format(struct vpfe_ccdc *ccdc) 6628c2ecf20Sopenharmony_ci{ 6638c2ecf20Sopenharmony_ci u32 pixfmt; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) { 6668c2ecf20Sopenharmony_ci pixfmt = V4L2_PIX_FMT_YUYV; 6678c2ecf20Sopenharmony_ci } else { 6688c2ecf20Sopenharmony_ci if (ccdc->ccdc_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) 6698c2ecf20Sopenharmony_ci pixfmt = V4L2_PIX_FMT_YUYV; 6708c2ecf20Sopenharmony_ci else 6718c2ecf20Sopenharmony_ci pixfmt = V4L2_PIX_FMT_UYVY; 6728c2ecf20Sopenharmony_ci } 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci return pixfmt; 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_cistatic int 6788c2ecf20Sopenharmony_civpfe_ccdc_set_image_window(struct vpfe_ccdc *ccdc, 6798c2ecf20Sopenharmony_ci struct v4l2_rect *win, unsigned int bpp) 6808c2ecf20Sopenharmony_ci{ 6818c2ecf20Sopenharmony_ci if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) { 6828c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.bayer.win = *win; 6838c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.bayer.bytesperpixel = bpp; 6848c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.bayer.bytesperline = ALIGN(win->width * bpp, 32); 6858c2ecf20Sopenharmony_ci } else { 6868c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.ycbcr.win = *win; 6878c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.ycbcr.bytesperpixel = bpp; 6888c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.ycbcr.bytesperline = ALIGN(win->width * bpp, 32); 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci return 0; 6928c2ecf20Sopenharmony_ci} 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_cistatic inline void 6958c2ecf20Sopenharmony_civpfe_ccdc_get_image_window(struct vpfe_ccdc *ccdc, 6968c2ecf20Sopenharmony_ci struct v4l2_rect *win) 6978c2ecf20Sopenharmony_ci{ 6988c2ecf20Sopenharmony_ci if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) 6998c2ecf20Sopenharmony_ci *win = ccdc->ccdc_cfg.bayer.win; 7008c2ecf20Sopenharmony_ci else 7018c2ecf20Sopenharmony_ci *win = ccdc->ccdc_cfg.ycbcr.win; 7028c2ecf20Sopenharmony_ci} 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_cistatic inline unsigned int vpfe_ccdc_get_line_length(struct vpfe_ccdc *ccdc) 7058c2ecf20Sopenharmony_ci{ 7068c2ecf20Sopenharmony_ci if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) 7078c2ecf20Sopenharmony_ci return ccdc->ccdc_cfg.bayer.bytesperline; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci return ccdc->ccdc_cfg.ycbcr.bytesperline; 7108c2ecf20Sopenharmony_ci} 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_cistatic inline int 7138c2ecf20Sopenharmony_civpfe_ccdc_set_frame_format(struct vpfe_ccdc *ccdc, 7148c2ecf20Sopenharmony_ci enum ccdc_frmfmt frm_fmt) 7158c2ecf20Sopenharmony_ci{ 7168c2ecf20Sopenharmony_ci if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) 7178c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.bayer.frm_fmt = frm_fmt; 7188c2ecf20Sopenharmony_ci else 7198c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.ycbcr.frm_fmt = frm_fmt; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci return 0; 7228c2ecf20Sopenharmony_ci} 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_cistatic inline enum ccdc_frmfmt 7258c2ecf20Sopenharmony_civpfe_ccdc_get_frame_format(struct vpfe_ccdc *ccdc) 7268c2ecf20Sopenharmony_ci{ 7278c2ecf20Sopenharmony_ci if (ccdc->ccdc_cfg.if_type == VPFE_RAW_BAYER) 7288c2ecf20Sopenharmony_ci return ccdc->ccdc_cfg.bayer.frm_fmt; 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci return ccdc->ccdc_cfg.ycbcr.frm_fmt; 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_cistatic inline int vpfe_ccdc_getfid(struct vpfe_ccdc *ccdc) 7348c2ecf20Sopenharmony_ci{ 7358c2ecf20Sopenharmony_ci return (vpfe_reg_read(ccdc, VPFE_SYNMODE) >> 15) & 1; 7368c2ecf20Sopenharmony_ci} 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_cistatic inline void vpfe_set_sdr_addr(struct vpfe_ccdc *ccdc, unsigned long addr) 7398c2ecf20Sopenharmony_ci{ 7408c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, addr & 0xffffffe0, VPFE_SDR_ADDR); 7418c2ecf20Sopenharmony_ci} 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_cistatic int vpfe_ccdc_set_hw_if_params(struct vpfe_ccdc *ccdc, 7448c2ecf20Sopenharmony_ci struct vpfe_hw_if_param *params) 7458c2ecf20Sopenharmony_ci{ 7468c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = to_vpfe(ccdc); 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.if_type = params->if_type; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci switch (params->if_type) { 7518c2ecf20Sopenharmony_ci case VPFE_BT656: 7528c2ecf20Sopenharmony_ci case VPFE_YCBCR_SYNC_16: 7538c2ecf20Sopenharmony_ci case VPFE_YCBCR_SYNC_8: 7548c2ecf20Sopenharmony_ci case VPFE_BT656_10BIT: 7558c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.ycbcr.vd_pol = params->vdpol; 7568c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.ycbcr.hd_pol = params->hdpol; 7578c2ecf20Sopenharmony_ci break; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci case VPFE_RAW_BAYER: 7608c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.bayer.vd_pol = params->vdpol; 7618c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.bayer.hd_pol = params->hdpol; 7628c2ecf20Sopenharmony_ci if (params->bus_width == 10) 7638c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.bayer.config_params.data_sz = 7648c2ecf20Sopenharmony_ci VPFE_CCDC_DATA_10BITS; 7658c2ecf20Sopenharmony_ci else 7668c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.bayer.config_params.data_sz = 7678c2ecf20Sopenharmony_ci VPFE_CCDC_DATA_8BITS; 7688c2ecf20Sopenharmony_ci vpfe_dbg(1, vpfe, "params.bus_width: %d\n", 7698c2ecf20Sopenharmony_ci params->bus_width); 7708c2ecf20Sopenharmony_ci vpfe_dbg(1, vpfe, "config_params.data_sz: %d\n", 7718c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.bayer.config_params.data_sz); 7728c2ecf20Sopenharmony_ci break; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci default: 7758c2ecf20Sopenharmony_ci return -EINVAL; 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci return 0; 7798c2ecf20Sopenharmony_ci} 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_cistatic void vpfe_clear_intr(struct vpfe_ccdc *ccdc, int vdint) 7828c2ecf20Sopenharmony_ci{ 7838c2ecf20Sopenharmony_ci unsigned int vpfe_int_status; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci vpfe_int_status = vpfe_reg_read(ccdc, VPFE_IRQ_STS); 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci switch (vdint) { 7888c2ecf20Sopenharmony_ci /* VD0 interrupt */ 7898c2ecf20Sopenharmony_ci case VPFE_VDINT0: 7908c2ecf20Sopenharmony_ci vpfe_int_status &= ~VPFE_VDINT0; 7918c2ecf20Sopenharmony_ci vpfe_int_status |= VPFE_VDINT0; 7928c2ecf20Sopenharmony_ci break; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci /* VD1 interrupt */ 7958c2ecf20Sopenharmony_ci case VPFE_VDINT1: 7968c2ecf20Sopenharmony_ci vpfe_int_status &= ~VPFE_VDINT1; 7978c2ecf20Sopenharmony_ci vpfe_int_status |= VPFE_VDINT1; 7988c2ecf20Sopenharmony_ci break; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci /* VD2 interrupt */ 8018c2ecf20Sopenharmony_ci case VPFE_VDINT2: 8028c2ecf20Sopenharmony_ci vpfe_int_status &= ~VPFE_VDINT2; 8038c2ecf20Sopenharmony_ci vpfe_int_status |= VPFE_VDINT2; 8048c2ecf20Sopenharmony_ci break; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci /* Clear all interrupts */ 8078c2ecf20Sopenharmony_ci default: 8088c2ecf20Sopenharmony_ci vpfe_int_status &= ~(VPFE_VDINT0 | 8098c2ecf20Sopenharmony_ci VPFE_VDINT1 | 8108c2ecf20Sopenharmony_ci VPFE_VDINT2); 8118c2ecf20Sopenharmony_ci vpfe_int_status |= (VPFE_VDINT0 | 8128c2ecf20Sopenharmony_ci VPFE_VDINT1 | 8138c2ecf20Sopenharmony_ci VPFE_VDINT2); 8148c2ecf20Sopenharmony_ci break; 8158c2ecf20Sopenharmony_ci } 8168c2ecf20Sopenharmony_ci /* Clear specific VDINT from the status register */ 8178c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, vpfe_int_status, VPFE_IRQ_STS); 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci vpfe_int_status = vpfe_reg_read(ccdc, VPFE_IRQ_STS); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci /* Acknowledge that we are done with all interrupts */ 8228c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, 1, VPFE_IRQ_EOI); 8238c2ecf20Sopenharmony_ci} 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_cistatic void vpfe_ccdc_config_defaults(struct vpfe_ccdc *ccdc) 8268c2ecf20Sopenharmony_ci{ 8278c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.if_type = VPFE_RAW_BAYER; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_8BIT; 8308c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.ycbcr.frm_fmt = CCDC_FRMFMT_INTERLACED; 8318c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.ycbcr.fid_pol = VPFE_PINPOL_POSITIVE; 8328c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.ycbcr.vd_pol = VPFE_PINPOL_POSITIVE; 8338c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.ycbcr.hd_pol = VPFE_PINPOL_POSITIVE; 8348c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; 8358c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.ycbcr.buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.ycbcr.win.left = 0; 8388c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.ycbcr.win.top = 0; 8398c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.ycbcr.win.width = 720; 8408c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.ycbcr.win.height = 576; 8418c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.ycbcr.bt656_enable = 1; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; 8448c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.bayer.frm_fmt = CCDC_FRMFMT_PROGRESSIVE; 8458c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.bayer.fid_pol = VPFE_PINPOL_POSITIVE; 8468c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.bayer.vd_pol = VPFE_PINPOL_POSITIVE; 8478c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.bayer.hd_pol = VPFE_PINPOL_POSITIVE; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.bayer.win.left = 0; 8508c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.bayer.win.top = 0; 8518c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.bayer.win.width = 800; 8528c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.bayer.win.height = 600; 8538c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.bayer.config_params.data_sz = VPFE_CCDC_DATA_8BITS; 8548c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.bayer.config_params.alaw.gamma_wd = 8558c2ecf20Sopenharmony_ci VPFE_CCDC_GAMMA_BITS_09_0; 8568c2ecf20Sopenharmony_ci} 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci/* 8598c2ecf20Sopenharmony_ci * vpfe_get_ccdc_image_format - Get image parameters based on CCDC settings 8608c2ecf20Sopenharmony_ci */ 8618c2ecf20Sopenharmony_cistatic int vpfe_get_ccdc_image_format(struct vpfe_device *vpfe, 8628c2ecf20Sopenharmony_ci struct v4l2_format *f) 8638c2ecf20Sopenharmony_ci{ 8648c2ecf20Sopenharmony_ci struct v4l2_rect image_win; 8658c2ecf20Sopenharmony_ci enum ccdc_buftype buf_type; 8668c2ecf20Sopenharmony_ci enum ccdc_frmfmt frm_fmt; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci memset(f, 0, sizeof(*f)); 8698c2ecf20Sopenharmony_ci f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 8708c2ecf20Sopenharmony_ci vpfe_ccdc_get_image_window(&vpfe->ccdc, &image_win); 8718c2ecf20Sopenharmony_ci f->fmt.pix.width = image_win.width; 8728c2ecf20Sopenharmony_ci f->fmt.pix.height = image_win.height; 8738c2ecf20Sopenharmony_ci f->fmt.pix.bytesperline = vpfe_ccdc_get_line_length(&vpfe->ccdc); 8748c2ecf20Sopenharmony_ci f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * 8758c2ecf20Sopenharmony_ci f->fmt.pix.height; 8768c2ecf20Sopenharmony_ci buf_type = vpfe_ccdc_get_buftype(&vpfe->ccdc); 8778c2ecf20Sopenharmony_ci f->fmt.pix.pixelformat = vpfe_ccdc_get_pixel_format(&vpfe->ccdc); 8788c2ecf20Sopenharmony_ci frm_fmt = vpfe_ccdc_get_frame_format(&vpfe->ccdc); 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci if (frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { 8818c2ecf20Sopenharmony_ci f->fmt.pix.field = V4L2_FIELD_NONE; 8828c2ecf20Sopenharmony_ci } else if (frm_fmt == CCDC_FRMFMT_INTERLACED) { 8838c2ecf20Sopenharmony_ci if (buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED) { 8848c2ecf20Sopenharmony_ci f->fmt.pix.field = V4L2_FIELD_INTERLACED; 8858c2ecf20Sopenharmony_ci } else if (buf_type == CCDC_BUFTYPE_FLD_SEPARATED) { 8868c2ecf20Sopenharmony_ci f->fmt.pix.field = V4L2_FIELD_SEQ_TB; 8878c2ecf20Sopenharmony_ci } else { 8888c2ecf20Sopenharmony_ci vpfe_err(vpfe, "Invalid buf_type\n"); 8898c2ecf20Sopenharmony_ci return -EINVAL; 8908c2ecf20Sopenharmony_ci } 8918c2ecf20Sopenharmony_ci } else { 8928c2ecf20Sopenharmony_ci vpfe_err(vpfe, "Invalid frm_fmt\n"); 8938c2ecf20Sopenharmony_ci return -EINVAL; 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci return 0; 8968c2ecf20Sopenharmony_ci} 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_cistatic int vpfe_config_ccdc_image_format(struct vpfe_device *vpfe) 8998c2ecf20Sopenharmony_ci{ 9008c2ecf20Sopenharmony_ci enum ccdc_frmfmt frm_fmt = CCDC_FRMFMT_INTERLACED; 9018c2ecf20Sopenharmony_ci u32 bpp; 9028c2ecf20Sopenharmony_ci int ret = 0; 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci vpfe_dbg(1, vpfe, "pixelformat: %s\n", 9058c2ecf20Sopenharmony_ci print_fourcc(vpfe->fmt.fmt.pix.pixelformat)); 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci if (vpfe_ccdc_set_pixel_format(&vpfe->ccdc, 9088c2ecf20Sopenharmony_ci vpfe->fmt.fmt.pix.pixelformat) < 0) { 9098c2ecf20Sopenharmony_ci vpfe_err(vpfe, "couldn't set pix format in ccdc\n"); 9108c2ecf20Sopenharmony_ci return -EINVAL; 9118c2ecf20Sopenharmony_ci } 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci /* configure the image window */ 9148c2ecf20Sopenharmony_ci bpp = __get_bytesperpixel(vpfe, vpfe->current_vpfe_fmt); 9158c2ecf20Sopenharmony_ci vpfe_ccdc_set_image_window(&vpfe->ccdc, &vpfe->crop, bpp); 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci switch (vpfe->fmt.fmt.pix.field) { 9188c2ecf20Sopenharmony_ci case V4L2_FIELD_INTERLACED: 9198c2ecf20Sopenharmony_ci /* do nothing, since it is default */ 9208c2ecf20Sopenharmony_ci ret = vpfe_ccdc_set_buftype( 9218c2ecf20Sopenharmony_ci &vpfe->ccdc, 9228c2ecf20Sopenharmony_ci CCDC_BUFTYPE_FLD_INTERLEAVED); 9238c2ecf20Sopenharmony_ci break; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci case V4L2_FIELD_NONE: 9268c2ecf20Sopenharmony_ci frm_fmt = CCDC_FRMFMT_PROGRESSIVE; 9278c2ecf20Sopenharmony_ci /* buffer type only applicable for interlaced scan */ 9288c2ecf20Sopenharmony_ci break; 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci case V4L2_FIELD_SEQ_TB: 9318c2ecf20Sopenharmony_ci ret = vpfe_ccdc_set_buftype( 9328c2ecf20Sopenharmony_ci &vpfe->ccdc, 9338c2ecf20Sopenharmony_ci CCDC_BUFTYPE_FLD_SEPARATED); 9348c2ecf20Sopenharmony_ci break; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci default: 9378c2ecf20Sopenharmony_ci return -EINVAL; 9388c2ecf20Sopenharmony_ci } 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci if (ret) 9418c2ecf20Sopenharmony_ci return ret; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci return vpfe_ccdc_set_frame_format(&vpfe->ccdc, frm_fmt); 9448c2ecf20Sopenharmony_ci} 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci/* 9478c2ecf20Sopenharmony_ci * vpfe_config_image_format() 9488c2ecf20Sopenharmony_ci * For a given standard, this functions sets up the default 9498c2ecf20Sopenharmony_ci * pix format & crop values in the vpfe device and ccdc. It first 9508c2ecf20Sopenharmony_ci * starts with defaults based values from the standard table. 9518c2ecf20Sopenharmony_ci * It then checks if sub device supports get_fmt and then override the 9528c2ecf20Sopenharmony_ci * values based on that.Sets crop values to match with scan resolution 9538c2ecf20Sopenharmony_ci * starting at 0,0. It calls vpfe_config_ccdc_image_format() set the 9548c2ecf20Sopenharmony_ci * values in ccdc 9558c2ecf20Sopenharmony_ci */ 9568c2ecf20Sopenharmony_cistatic int vpfe_config_image_format(struct vpfe_device *vpfe, 9578c2ecf20Sopenharmony_ci v4l2_std_id std_id) 9588c2ecf20Sopenharmony_ci{ 9598c2ecf20Sopenharmony_ci struct vpfe_fmt *fmt; 9608c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt mbus_fmt; 9618c2ecf20Sopenharmony_ci int i, ret; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vpfe_standards); i++) { 9648c2ecf20Sopenharmony_ci if (vpfe_standards[i].std_id & std_id) { 9658c2ecf20Sopenharmony_ci vpfe->std_info.active_pixels = 9668c2ecf20Sopenharmony_ci vpfe_standards[i].width; 9678c2ecf20Sopenharmony_ci vpfe->std_info.active_lines = 9688c2ecf20Sopenharmony_ci vpfe_standards[i].height; 9698c2ecf20Sopenharmony_ci vpfe->std_info.frame_format = 9708c2ecf20Sopenharmony_ci vpfe_standards[i].frame_format; 9718c2ecf20Sopenharmony_ci vpfe->std_index = i; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci break; 9748c2ecf20Sopenharmony_ci } 9758c2ecf20Sopenharmony_ci } 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(vpfe_standards)) { 9788c2ecf20Sopenharmony_ci vpfe_err(vpfe, "standard not supported\n"); 9798c2ecf20Sopenharmony_ci return -EINVAL; 9808c2ecf20Sopenharmony_ci } 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci ret = __subdev_get_format(vpfe, &mbus_fmt); 9838c2ecf20Sopenharmony_ci if (ret) 9848c2ecf20Sopenharmony_ci return ret; 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci fmt = find_format_by_code(vpfe, mbus_fmt.code); 9878c2ecf20Sopenharmony_ci if (!fmt) { 9888c2ecf20Sopenharmony_ci vpfe_dbg(3, vpfe, "mbus code format (0x%08x) not found.\n", 9898c2ecf20Sopenharmony_ci mbus_fmt.code); 9908c2ecf20Sopenharmony_ci return -EINVAL; 9918c2ecf20Sopenharmony_ci } 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci /* Save current subdev format */ 9948c2ecf20Sopenharmony_ci v4l2_fill_pix_format(&vpfe->fmt.fmt.pix, &mbus_fmt); 9958c2ecf20Sopenharmony_ci vpfe->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 9968c2ecf20Sopenharmony_ci vpfe->fmt.fmt.pix.pixelformat = fmt->fourcc; 9978c2ecf20Sopenharmony_ci vpfe_calc_format_size(vpfe, fmt, &vpfe->fmt); 9988c2ecf20Sopenharmony_ci vpfe->current_vpfe_fmt = fmt; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci /* Update the crop window based on found values */ 10018c2ecf20Sopenharmony_ci vpfe->crop.top = 0; 10028c2ecf20Sopenharmony_ci vpfe->crop.left = 0; 10038c2ecf20Sopenharmony_ci vpfe->crop.width = mbus_fmt.width; 10048c2ecf20Sopenharmony_ci vpfe->crop.height = mbus_fmt.height; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci return vpfe_config_ccdc_image_format(vpfe); 10078c2ecf20Sopenharmony_ci} 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_cistatic int vpfe_initialize_device(struct vpfe_device *vpfe) 10108c2ecf20Sopenharmony_ci{ 10118c2ecf20Sopenharmony_ci struct vpfe_subdev_info *sdinfo; 10128c2ecf20Sopenharmony_ci int ret; 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci sdinfo = &vpfe->cfg->sub_devs[0]; 10158c2ecf20Sopenharmony_ci sdinfo->sd = vpfe->sd[0]; 10168c2ecf20Sopenharmony_ci vpfe->current_input = 0; 10178c2ecf20Sopenharmony_ci vpfe->std_index = 0; 10188c2ecf20Sopenharmony_ci /* Configure the default format information */ 10198c2ecf20Sopenharmony_ci ret = vpfe_config_image_format(vpfe, 10208c2ecf20Sopenharmony_ci vpfe_standards[vpfe->std_index].std_id); 10218c2ecf20Sopenharmony_ci if (ret) 10228c2ecf20Sopenharmony_ci return ret; 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci ret = pm_runtime_resume_and_get(vpfe->pdev); 10258c2ecf20Sopenharmony_ci if (ret < 0) 10268c2ecf20Sopenharmony_ci return ret; 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci vpfe_config_enable(&vpfe->ccdc, 1); 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci vpfe_ccdc_restore_defaults(&vpfe->ccdc); 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci /* Clear all VPFE interrupts */ 10338c2ecf20Sopenharmony_ci vpfe_clear_intr(&vpfe->ccdc, -1); 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci return ret; 10368c2ecf20Sopenharmony_ci} 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci/* 10398c2ecf20Sopenharmony_ci * vpfe_release : This function is based on the vb2_fop_release 10408c2ecf20Sopenharmony_ci * helper function. 10418c2ecf20Sopenharmony_ci * It has been augmented to handle module power management, 10428c2ecf20Sopenharmony_ci * by disabling/enabling h/w module fcntl clock when necessary. 10438c2ecf20Sopenharmony_ci */ 10448c2ecf20Sopenharmony_cistatic int vpfe_release(struct file *file) 10458c2ecf20Sopenharmony_ci{ 10468c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = video_drvdata(file); 10478c2ecf20Sopenharmony_ci bool fh_singular; 10488c2ecf20Sopenharmony_ci int ret; 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci mutex_lock(&vpfe->lock); 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci /* Save the singular status before we call the clean-up helper */ 10538c2ecf20Sopenharmony_ci fh_singular = v4l2_fh_is_singular_file(file); 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci /* the release helper will cleanup any on-going streaming */ 10568c2ecf20Sopenharmony_ci ret = _vb2_fop_release(file, NULL); 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci /* 10598c2ecf20Sopenharmony_ci * If this was the last open file. 10608c2ecf20Sopenharmony_ci * Then de-initialize hw module. 10618c2ecf20Sopenharmony_ci */ 10628c2ecf20Sopenharmony_ci if (fh_singular) 10638c2ecf20Sopenharmony_ci vpfe_ccdc_close(&vpfe->ccdc, vpfe->pdev); 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci mutex_unlock(&vpfe->lock); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci return ret; 10688c2ecf20Sopenharmony_ci} 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci/* 10718c2ecf20Sopenharmony_ci * vpfe_open : This function is based on the v4l2_fh_open helper function. 10728c2ecf20Sopenharmony_ci * It has been augmented to handle module power management, 10738c2ecf20Sopenharmony_ci * by disabling/enabling h/w module fcntl clock when necessary. 10748c2ecf20Sopenharmony_ci */ 10758c2ecf20Sopenharmony_cistatic int vpfe_open(struct file *file) 10768c2ecf20Sopenharmony_ci{ 10778c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = video_drvdata(file); 10788c2ecf20Sopenharmony_ci int ret; 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci mutex_lock(&vpfe->lock); 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci ret = v4l2_fh_open(file); 10838c2ecf20Sopenharmony_ci if (ret) { 10848c2ecf20Sopenharmony_ci vpfe_err(vpfe, "v4l2_fh_open failed\n"); 10858c2ecf20Sopenharmony_ci goto unlock; 10868c2ecf20Sopenharmony_ci } 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci if (!v4l2_fh_is_singular_file(file)) 10898c2ecf20Sopenharmony_ci goto unlock; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci if (vpfe_initialize_device(vpfe)) { 10928c2ecf20Sopenharmony_ci v4l2_fh_release(file); 10938c2ecf20Sopenharmony_ci ret = -ENODEV; 10948c2ecf20Sopenharmony_ci } 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ciunlock: 10978c2ecf20Sopenharmony_ci mutex_unlock(&vpfe->lock); 10988c2ecf20Sopenharmony_ci return ret; 10998c2ecf20Sopenharmony_ci} 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_ci/** 11028c2ecf20Sopenharmony_ci * vpfe_schedule_next_buffer: set next buffer address for capture 11038c2ecf20Sopenharmony_ci * @vpfe : ptr to vpfe device 11048c2ecf20Sopenharmony_ci * 11058c2ecf20Sopenharmony_ci * This function will get next buffer from the dma queue and 11068c2ecf20Sopenharmony_ci * set the buffer address in the vpfe register for capture. 11078c2ecf20Sopenharmony_ci * the buffer is marked active 11088c2ecf20Sopenharmony_ci */ 11098c2ecf20Sopenharmony_cistatic void vpfe_schedule_next_buffer(struct vpfe_device *vpfe) 11108c2ecf20Sopenharmony_ci{ 11118c2ecf20Sopenharmony_ci dma_addr_t addr; 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_ci spin_lock(&vpfe->dma_queue_lock); 11148c2ecf20Sopenharmony_ci if (list_empty(&vpfe->dma_queue)) { 11158c2ecf20Sopenharmony_ci spin_unlock(&vpfe->dma_queue_lock); 11168c2ecf20Sopenharmony_ci return; 11178c2ecf20Sopenharmony_ci } 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci vpfe->next_frm = list_entry(vpfe->dma_queue.next, 11208c2ecf20Sopenharmony_ci struct vpfe_cap_buffer, list); 11218c2ecf20Sopenharmony_ci list_del(&vpfe->next_frm->list); 11228c2ecf20Sopenharmony_ci spin_unlock(&vpfe->dma_queue_lock); 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci addr = vb2_dma_contig_plane_dma_addr(&vpfe->next_frm->vb.vb2_buf, 0); 11258c2ecf20Sopenharmony_ci vpfe_set_sdr_addr(&vpfe->ccdc, addr); 11268c2ecf20Sopenharmony_ci} 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_cistatic inline void vpfe_schedule_bottom_field(struct vpfe_device *vpfe) 11298c2ecf20Sopenharmony_ci{ 11308c2ecf20Sopenharmony_ci dma_addr_t addr; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci addr = vb2_dma_contig_plane_dma_addr(&vpfe->next_frm->vb.vb2_buf, 0) + 11338c2ecf20Sopenharmony_ci vpfe->field_off; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci vpfe_set_sdr_addr(&vpfe->ccdc, addr); 11368c2ecf20Sopenharmony_ci} 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci/* 11398c2ecf20Sopenharmony_ci * vpfe_process_buffer_complete: process a completed buffer 11408c2ecf20Sopenharmony_ci * @vpfe : ptr to vpfe device 11418c2ecf20Sopenharmony_ci * 11428c2ecf20Sopenharmony_ci * This function time stamp the buffer and mark it as DONE. It also 11438c2ecf20Sopenharmony_ci * wake up any process waiting on the QUEUE and set the next buffer 11448c2ecf20Sopenharmony_ci * as current 11458c2ecf20Sopenharmony_ci */ 11468c2ecf20Sopenharmony_cistatic inline void vpfe_process_buffer_complete(struct vpfe_device *vpfe) 11478c2ecf20Sopenharmony_ci{ 11488c2ecf20Sopenharmony_ci vpfe->cur_frm->vb.vb2_buf.timestamp = ktime_get_ns(); 11498c2ecf20Sopenharmony_ci vpfe->cur_frm->vb.field = vpfe->fmt.fmt.pix.field; 11508c2ecf20Sopenharmony_ci vpfe->cur_frm->vb.sequence = vpfe->sequence++; 11518c2ecf20Sopenharmony_ci vb2_buffer_done(&vpfe->cur_frm->vb.vb2_buf, VB2_BUF_STATE_DONE); 11528c2ecf20Sopenharmony_ci vpfe->cur_frm = vpfe->next_frm; 11538c2ecf20Sopenharmony_ci} 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_cistatic void vpfe_handle_interlaced_irq(struct vpfe_device *vpfe, 11568c2ecf20Sopenharmony_ci enum v4l2_field field) 11578c2ecf20Sopenharmony_ci{ 11588c2ecf20Sopenharmony_ci int fid; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci /* interlaced or TB capture check which field 11618c2ecf20Sopenharmony_ci * we are in hardware 11628c2ecf20Sopenharmony_ci */ 11638c2ecf20Sopenharmony_ci fid = vpfe_ccdc_getfid(&vpfe->ccdc); 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci /* switch the software maintained field id */ 11668c2ecf20Sopenharmony_ci vpfe->field ^= 1; 11678c2ecf20Sopenharmony_ci if (fid == vpfe->field) { 11688c2ecf20Sopenharmony_ci /* we are in-sync here,continue */ 11698c2ecf20Sopenharmony_ci if (fid == 0) { 11708c2ecf20Sopenharmony_ci /* 11718c2ecf20Sopenharmony_ci * One frame is just being captured. If the 11728c2ecf20Sopenharmony_ci * next frame is available, release the 11738c2ecf20Sopenharmony_ci * current frame and move on 11748c2ecf20Sopenharmony_ci */ 11758c2ecf20Sopenharmony_ci if (vpfe->cur_frm != vpfe->next_frm) 11768c2ecf20Sopenharmony_ci vpfe_process_buffer_complete(vpfe); 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci if (vpfe->stopping) 11798c2ecf20Sopenharmony_ci return; 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci /* 11828c2ecf20Sopenharmony_ci * based on whether the two fields are stored 11838c2ecf20Sopenharmony_ci * interleave or separately in memory, 11848c2ecf20Sopenharmony_ci * reconfigure the CCDC memory address 11858c2ecf20Sopenharmony_ci */ 11868c2ecf20Sopenharmony_ci if (field == V4L2_FIELD_SEQ_TB) 11878c2ecf20Sopenharmony_ci vpfe_schedule_bottom_field(vpfe); 11888c2ecf20Sopenharmony_ci } else { 11898c2ecf20Sopenharmony_ci /* 11908c2ecf20Sopenharmony_ci * if one field is just being captured configure 11918c2ecf20Sopenharmony_ci * the next frame get the next frame from the empty 11928c2ecf20Sopenharmony_ci * queue if no frame is available hold on to the 11938c2ecf20Sopenharmony_ci * current buffer 11948c2ecf20Sopenharmony_ci */ 11958c2ecf20Sopenharmony_ci if (vpfe->cur_frm == vpfe->next_frm) 11968c2ecf20Sopenharmony_ci vpfe_schedule_next_buffer(vpfe); 11978c2ecf20Sopenharmony_ci } 11988c2ecf20Sopenharmony_ci } else if (fid == 0) { 11998c2ecf20Sopenharmony_ci /* 12008c2ecf20Sopenharmony_ci * out of sync. Recover from any hardware out-of-sync. 12018c2ecf20Sopenharmony_ci * May loose one frame 12028c2ecf20Sopenharmony_ci */ 12038c2ecf20Sopenharmony_ci vpfe->field = fid; 12048c2ecf20Sopenharmony_ci } 12058c2ecf20Sopenharmony_ci} 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci/* 12088c2ecf20Sopenharmony_ci * vpfe_isr : ISR handler for vpfe capture (VINT0) 12098c2ecf20Sopenharmony_ci * @irq: irq number 12108c2ecf20Sopenharmony_ci * @dev_id: dev_id ptr 12118c2ecf20Sopenharmony_ci * 12128c2ecf20Sopenharmony_ci * It changes status of the captured buffer, takes next buffer from the queue 12138c2ecf20Sopenharmony_ci * and sets its address in VPFE registers 12148c2ecf20Sopenharmony_ci */ 12158c2ecf20Sopenharmony_cistatic irqreturn_t vpfe_isr(int irq, void *dev) 12168c2ecf20Sopenharmony_ci{ 12178c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = (struct vpfe_device *)dev; 12188c2ecf20Sopenharmony_ci enum v4l2_field field = vpfe->fmt.fmt.pix.field; 12198c2ecf20Sopenharmony_ci int intr_status, stopping = vpfe->stopping; 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci intr_status = vpfe_reg_read(&vpfe->ccdc, VPFE_IRQ_STS); 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci if (intr_status & VPFE_VDINT0) { 12248c2ecf20Sopenharmony_ci if (field == V4L2_FIELD_NONE) { 12258c2ecf20Sopenharmony_ci if (vpfe->cur_frm != vpfe->next_frm) 12268c2ecf20Sopenharmony_ci vpfe_process_buffer_complete(vpfe); 12278c2ecf20Sopenharmony_ci } else { 12288c2ecf20Sopenharmony_ci vpfe_handle_interlaced_irq(vpfe, field); 12298c2ecf20Sopenharmony_ci } 12308c2ecf20Sopenharmony_ci if (stopping) { 12318c2ecf20Sopenharmony_ci vpfe->stopping = false; 12328c2ecf20Sopenharmony_ci complete(&vpfe->capture_stop); 12338c2ecf20Sopenharmony_ci } 12348c2ecf20Sopenharmony_ci } 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci if (intr_status & VPFE_VDINT1 && !stopping) { 12378c2ecf20Sopenharmony_ci if (field == V4L2_FIELD_NONE && 12388c2ecf20Sopenharmony_ci vpfe->cur_frm == vpfe->next_frm) 12398c2ecf20Sopenharmony_ci vpfe_schedule_next_buffer(vpfe); 12408c2ecf20Sopenharmony_ci } 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci vpfe_clear_intr(&vpfe->ccdc, intr_status); 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci return IRQ_HANDLED; 12458c2ecf20Sopenharmony_ci} 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_cistatic inline void vpfe_detach_irq(struct vpfe_device *vpfe) 12488c2ecf20Sopenharmony_ci{ 12498c2ecf20Sopenharmony_ci unsigned int intr = VPFE_VDINT0; 12508c2ecf20Sopenharmony_ci enum ccdc_frmfmt frame_format; 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci frame_format = vpfe_ccdc_get_frame_format(&vpfe->ccdc); 12538c2ecf20Sopenharmony_ci if (frame_format == CCDC_FRMFMT_PROGRESSIVE) 12548c2ecf20Sopenharmony_ci intr |= VPFE_VDINT1; 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci vpfe_reg_write(&vpfe->ccdc, intr, VPFE_IRQ_EN_CLR); 12578c2ecf20Sopenharmony_ci} 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_cistatic inline void vpfe_attach_irq(struct vpfe_device *vpfe) 12608c2ecf20Sopenharmony_ci{ 12618c2ecf20Sopenharmony_ci unsigned int intr = VPFE_VDINT0; 12628c2ecf20Sopenharmony_ci enum ccdc_frmfmt frame_format; 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_ci frame_format = vpfe_ccdc_get_frame_format(&vpfe->ccdc); 12658c2ecf20Sopenharmony_ci if (frame_format == CCDC_FRMFMT_PROGRESSIVE) 12668c2ecf20Sopenharmony_ci intr |= VPFE_VDINT1; 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci vpfe_reg_write(&vpfe->ccdc, intr, VPFE_IRQ_EN_SET); 12698c2ecf20Sopenharmony_ci} 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_cistatic int vpfe_querycap(struct file *file, void *priv, 12728c2ecf20Sopenharmony_ci struct v4l2_capability *cap) 12738c2ecf20Sopenharmony_ci{ 12748c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = video_drvdata(file); 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci strscpy(cap->driver, VPFE_MODULE_NAME, sizeof(cap->driver)); 12778c2ecf20Sopenharmony_ci strscpy(cap->card, "TI AM437x VPFE", sizeof(cap->card)); 12788c2ecf20Sopenharmony_ci snprintf(cap->bus_info, sizeof(cap->bus_info), 12798c2ecf20Sopenharmony_ci "platform:%s", vpfe->v4l2_dev.name); 12808c2ecf20Sopenharmony_ci return 0; 12818c2ecf20Sopenharmony_ci} 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci/* get the format set at output pad of the adjacent subdev */ 12848c2ecf20Sopenharmony_cistatic int __subdev_get_format(struct vpfe_device *vpfe, 12858c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *fmt) 12868c2ecf20Sopenharmony_ci{ 12878c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = vpfe->current_subdev->sd; 12888c2ecf20Sopenharmony_ci struct v4l2_subdev_format sd_fmt; 12898c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; 12908c2ecf20Sopenharmony_ci int ret; 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_ci sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; 12938c2ecf20Sopenharmony_ci sd_fmt.pad = 0; 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &sd_fmt); 12968c2ecf20Sopenharmony_ci if (ret) 12978c2ecf20Sopenharmony_ci return ret; 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci *fmt = *mbus_fmt; 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci vpfe_dbg(1, vpfe, "%s: %dx%d code:%04X\n", __func__, 13028c2ecf20Sopenharmony_ci fmt->width, fmt->height, fmt->code); 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci return 0; 13058c2ecf20Sopenharmony_ci} 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci/* set the format at output pad of the adjacent subdev */ 13088c2ecf20Sopenharmony_cistatic int __subdev_set_format(struct vpfe_device *vpfe, 13098c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *fmt) 13108c2ecf20Sopenharmony_ci{ 13118c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = vpfe->current_subdev->sd; 13128c2ecf20Sopenharmony_ci struct v4l2_subdev_format sd_fmt; 13138c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; 13148c2ecf20Sopenharmony_ci int ret; 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; 13178c2ecf20Sopenharmony_ci sd_fmt.pad = 0; 13188c2ecf20Sopenharmony_ci *mbus_fmt = *fmt; 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(sd, pad, set_fmt, NULL, &sd_fmt); 13218c2ecf20Sopenharmony_ci if (ret) 13228c2ecf20Sopenharmony_ci return ret; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci vpfe_dbg(1, vpfe, "%s %dx%d code:%04X\n", __func__, 13258c2ecf20Sopenharmony_ci fmt->width, fmt->height, fmt->code); 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci return 0; 13288c2ecf20Sopenharmony_ci} 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_cistatic int vpfe_calc_format_size(struct vpfe_device *vpfe, 13318c2ecf20Sopenharmony_ci const struct vpfe_fmt *fmt, 13328c2ecf20Sopenharmony_ci struct v4l2_format *f) 13338c2ecf20Sopenharmony_ci{ 13348c2ecf20Sopenharmony_ci u32 bpp; 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci if (!fmt) { 13378c2ecf20Sopenharmony_ci vpfe_dbg(3, vpfe, "No vpfe_fmt provided!\n"); 13388c2ecf20Sopenharmony_ci return -EINVAL; 13398c2ecf20Sopenharmony_ci } 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci bpp = __get_bytesperpixel(vpfe, fmt); 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci /* pitch should be 32 bytes aligned */ 13448c2ecf20Sopenharmony_ci f->fmt.pix.bytesperline = ALIGN(f->fmt.pix.width * bpp, 32); 13458c2ecf20Sopenharmony_ci f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * 13468c2ecf20Sopenharmony_ci f->fmt.pix.height; 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci vpfe_dbg(3, vpfe, "%s: fourcc: %s size: %dx%d bpl:%d img_size:%d\n", 13498c2ecf20Sopenharmony_ci __func__, print_fourcc(f->fmt.pix.pixelformat), 13508c2ecf20Sopenharmony_ci f->fmt.pix.width, f->fmt.pix.height, 13518c2ecf20Sopenharmony_ci f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci return 0; 13548c2ecf20Sopenharmony_ci} 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_cistatic int vpfe_g_fmt(struct file *file, void *priv, 13578c2ecf20Sopenharmony_ci struct v4l2_format *fmt) 13588c2ecf20Sopenharmony_ci{ 13598c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = video_drvdata(file); 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci *fmt = vpfe->fmt; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci return 0; 13648c2ecf20Sopenharmony_ci} 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_cistatic int vpfe_enum_fmt(struct file *file, void *priv, 13678c2ecf20Sopenharmony_ci struct v4l2_fmtdesc *f) 13688c2ecf20Sopenharmony_ci{ 13698c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = video_drvdata(file); 13708c2ecf20Sopenharmony_ci struct vpfe_subdev_info *sdinfo; 13718c2ecf20Sopenharmony_ci struct vpfe_fmt *fmt; 13728c2ecf20Sopenharmony_ci 13738c2ecf20Sopenharmony_ci sdinfo = vpfe->current_subdev; 13748c2ecf20Sopenharmony_ci if (!sdinfo->sd) 13758c2ecf20Sopenharmony_ci return -EINVAL; 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci if (f->index >= vpfe->num_active_fmt) 13788c2ecf20Sopenharmony_ci return -EINVAL; 13798c2ecf20Sopenharmony_ci 13808c2ecf20Sopenharmony_ci fmt = vpfe->active_fmt[f->index]; 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci f->pixelformat = fmt->fourcc; 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci vpfe_dbg(1, vpfe, "%s: mbus index: %d code: %x pixelformat: %s\n", 13858c2ecf20Sopenharmony_ci __func__, f->index, fmt->code, print_fourcc(fmt->fourcc)); 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci return 0; 13888c2ecf20Sopenharmony_ci} 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_cistatic int vpfe_try_fmt(struct file *file, void *priv, 13918c2ecf20Sopenharmony_ci struct v4l2_format *f) 13928c2ecf20Sopenharmony_ci{ 13938c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = video_drvdata(file); 13948c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = vpfe->current_subdev->sd; 13958c2ecf20Sopenharmony_ci const struct vpfe_fmt *fmt; 13968c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_size_enum fse; 13978c2ecf20Sopenharmony_ci int ret, found; 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci fmt = find_format_by_pix(vpfe, f->fmt.pix.pixelformat); 14008c2ecf20Sopenharmony_ci if (!fmt) { 14018c2ecf20Sopenharmony_ci /* default to first entry */ 14028c2ecf20Sopenharmony_ci vpfe_dbg(3, vpfe, "Invalid pixel code: %x, default used instead\n", 14038c2ecf20Sopenharmony_ci f->fmt.pix.pixelformat); 14048c2ecf20Sopenharmony_ci fmt = vpfe->active_fmt[0]; 14058c2ecf20Sopenharmony_ci f->fmt.pix.pixelformat = fmt->fourcc; 14068c2ecf20Sopenharmony_ci } 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci f->fmt.pix.field = vpfe->fmt.fmt.pix.field; 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci /* check for/find a valid width/height */ 14118c2ecf20Sopenharmony_ci ret = 0; 14128c2ecf20Sopenharmony_ci found = false; 14138c2ecf20Sopenharmony_ci fse.pad = 0; 14148c2ecf20Sopenharmony_ci fse.code = fmt->code; 14158c2ecf20Sopenharmony_ci fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; 14168c2ecf20Sopenharmony_ci for (fse.index = 0; ; fse.index++) { 14178c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(sd, pad, enum_frame_size, 14188c2ecf20Sopenharmony_ci NULL, &fse); 14198c2ecf20Sopenharmony_ci if (ret) 14208c2ecf20Sopenharmony_ci break; 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci if (f->fmt.pix.width == fse.max_width && 14238c2ecf20Sopenharmony_ci f->fmt.pix.height == fse.max_height) { 14248c2ecf20Sopenharmony_ci found = true; 14258c2ecf20Sopenharmony_ci break; 14268c2ecf20Sopenharmony_ci } else if (f->fmt.pix.width >= fse.min_width && 14278c2ecf20Sopenharmony_ci f->fmt.pix.width <= fse.max_width && 14288c2ecf20Sopenharmony_ci f->fmt.pix.height >= fse.min_height && 14298c2ecf20Sopenharmony_ci f->fmt.pix.height <= fse.max_height) { 14308c2ecf20Sopenharmony_ci found = true; 14318c2ecf20Sopenharmony_ci break; 14328c2ecf20Sopenharmony_ci } 14338c2ecf20Sopenharmony_ci } 14348c2ecf20Sopenharmony_ci 14358c2ecf20Sopenharmony_ci if (!found) { 14368c2ecf20Sopenharmony_ci /* use existing values as default */ 14378c2ecf20Sopenharmony_ci f->fmt.pix.width = vpfe->fmt.fmt.pix.width; 14388c2ecf20Sopenharmony_ci f->fmt.pix.height = vpfe->fmt.fmt.pix.height; 14398c2ecf20Sopenharmony_ci } 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci /* 14428c2ecf20Sopenharmony_ci * Use current colorspace for now, it will get 14438c2ecf20Sopenharmony_ci * updated properly during s_fmt 14448c2ecf20Sopenharmony_ci */ 14458c2ecf20Sopenharmony_ci f->fmt.pix.colorspace = vpfe->fmt.fmt.pix.colorspace; 14468c2ecf20Sopenharmony_ci return vpfe_calc_format_size(vpfe, fmt, f); 14478c2ecf20Sopenharmony_ci} 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_cistatic int vpfe_s_fmt(struct file *file, void *priv, 14508c2ecf20Sopenharmony_ci struct v4l2_format *fmt) 14518c2ecf20Sopenharmony_ci{ 14528c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = video_drvdata(file); 14538c2ecf20Sopenharmony_ci struct vpfe_fmt *f; 14548c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt mbus_fmt; 14558c2ecf20Sopenharmony_ci int ret; 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci /* If streaming is started, return error */ 14588c2ecf20Sopenharmony_ci if (vb2_is_busy(&vpfe->buffer_queue)) { 14598c2ecf20Sopenharmony_ci vpfe_err(vpfe, "%s device busy\n", __func__); 14608c2ecf20Sopenharmony_ci return -EBUSY; 14618c2ecf20Sopenharmony_ci } 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci ret = vpfe_try_fmt(file, priv, fmt); 14648c2ecf20Sopenharmony_ci if (ret < 0) 14658c2ecf20Sopenharmony_ci return ret; 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci f = find_format_by_pix(vpfe, fmt->fmt.pix.pixelformat); 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci v4l2_fill_mbus_format(&mbus_fmt, &fmt->fmt.pix, f->code); 14708c2ecf20Sopenharmony_ci 14718c2ecf20Sopenharmony_ci ret = __subdev_set_format(vpfe, &mbus_fmt); 14728c2ecf20Sopenharmony_ci if (ret) 14738c2ecf20Sopenharmony_ci return ret; 14748c2ecf20Sopenharmony_ci 14758c2ecf20Sopenharmony_ci /* Just double check nothing has gone wrong */ 14768c2ecf20Sopenharmony_ci if (mbus_fmt.code != f->code) { 14778c2ecf20Sopenharmony_ci vpfe_dbg(3, vpfe, 14788c2ecf20Sopenharmony_ci "%s subdev changed format on us, this should not happen\n", 14798c2ecf20Sopenharmony_ci __func__); 14808c2ecf20Sopenharmony_ci return -EINVAL; 14818c2ecf20Sopenharmony_ci } 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_ci v4l2_fill_pix_format(&vpfe->fmt.fmt.pix, &mbus_fmt); 14848c2ecf20Sopenharmony_ci vpfe->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 14858c2ecf20Sopenharmony_ci vpfe->fmt.fmt.pix.pixelformat = f->fourcc; 14868c2ecf20Sopenharmony_ci vpfe_calc_format_size(vpfe, f, &vpfe->fmt); 14878c2ecf20Sopenharmony_ci *fmt = vpfe->fmt; 14888c2ecf20Sopenharmony_ci vpfe->current_vpfe_fmt = f; 14898c2ecf20Sopenharmony_ci 14908c2ecf20Sopenharmony_ci /* Update the crop window based on found values */ 14918c2ecf20Sopenharmony_ci vpfe->crop.width = fmt->fmt.pix.width; 14928c2ecf20Sopenharmony_ci vpfe->crop.height = fmt->fmt.pix.height; 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci /* set image capture parameters in the ccdc */ 14958c2ecf20Sopenharmony_ci return vpfe_config_ccdc_image_format(vpfe); 14968c2ecf20Sopenharmony_ci} 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_cistatic int vpfe_enum_size(struct file *file, void *priv, 14998c2ecf20Sopenharmony_ci struct v4l2_frmsizeenum *fsize) 15008c2ecf20Sopenharmony_ci{ 15018c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = video_drvdata(file); 15028c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_size_enum fse; 15038c2ecf20Sopenharmony_ci struct v4l2_subdev *sd = vpfe->current_subdev->sd; 15048c2ecf20Sopenharmony_ci struct vpfe_fmt *fmt; 15058c2ecf20Sopenharmony_ci int ret; 15068c2ecf20Sopenharmony_ci 15078c2ecf20Sopenharmony_ci /* check for valid format */ 15088c2ecf20Sopenharmony_ci fmt = find_format_by_pix(vpfe, fsize->pixel_format); 15098c2ecf20Sopenharmony_ci if (!fmt) { 15108c2ecf20Sopenharmony_ci vpfe_dbg(3, vpfe, "Invalid pixel code: %x\n", 15118c2ecf20Sopenharmony_ci fsize->pixel_format); 15128c2ecf20Sopenharmony_ci return -EINVAL; 15138c2ecf20Sopenharmony_ci } 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci memset(fsize->reserved, 0x0, sizeof(fsize->reserved)); 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci memset(&fse, 0x0, sizeof(fse)); 15188c2ecf20Sopenharmony_ci fse.index = fsize->index; 15198c2ecf20Sopenharmony_ci fse.pad = 0; 15208c2ecf20Sopenharmony_ci fse.code = fmt->code; 15218c2ecf20Sopenharmony_ci fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; 15228c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(sd, pad, enum_frame_size, NULL, &fse); 15238c2ecf20Sopenharmony_ci if (ret) 15248c2ecf20Sopenharmony_ci return ret; 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci vpfe_dbg(1, vpfe, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n", 15278c2ecf20Sopenharmony_ci __func__, fse.index, fse.code, fse.min_width, fse.max_width, 15288c2ecf20Sopenharmony_ci fse.min_height, fse.max_height); 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; 15318c2ecf20Sopenharmony_ci fsize->discrete.width = fse.max_width; 15328c2ecf20Sopenharmony_ci fsize->discrete.height = fse.max_height; 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci vpfe_dbg(1, vpfe, "%s: index: %d pixformat: %s size: %dx%d\n", 15358c2ecf20Sopenharmony_ci __func__, fsize->index, print_fourcc(fsize->pixel_format), 15368c2ecf20Sopenharmony_ci fsize->discrete.width, fsize->discrete.height); 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci return 0; 15398c2ecf20Sopenharmony_ci} 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci/* 15428c2ecf20Sopenharmony_ci * vpfe_get_subdev_input_index - Get subdev index and subdev input index for a 15438c2ecf20Sopenharmony_ci * given app input index 15448c2ecf20Sopenharmony_ci */ 15458c2ecf20Sopenharmony_cistatic int 15468c2ecf20Sopenharmony_civpfe_get_subdev_input_index(struct vpfe_device *vpfe, 15478c2ecf20Sopenharmony_ci int *subdev_index, 15488c2ecf20Sopenharmony_ci int *subdev_input_index, 15498c2ecf20Sopenharmony_ci int app_input_index) 15508c2ecf20Sopenharmony_ci{ 15518c2ecf20Sopenharmony_ci int i, j = 0; 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vpfe->cfg->asd); i++) { 15548c2ecf20Sopenharmony_ci if (app_input_index < (j + 1)) { 15558c2ecf20Sopenharmony_ci *subdev_index = i; 15568c2ecf20Sopenharmony_ci *subdev_input_index = app_input_index - j; 15578c2ecf20Sopenharmony_ci return 0; 15588c2ecf20Sopenharmony_ci } 15598c2ecf20Sopenharmony_ci j++; 15608c2ecf20Sopenharmony_ci } 15618c2ecf20Sopenharmony_ci return -EINVAL; 15628c2ecf20Sopenharmony_ci} 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci/* 15658c2ecf20Sopenharmony_ci * vpfe_get_app_input - Get app input index for a given subdev input index 15668c2ecf20Sopenharmony_ci * driver stores the input index of the current sub device and translate it 15678c2ecf20Sopenharmony_ci * when application request the current input 15688c2ecf20Sopenharmony_ci */ 15698c2ecf20Sopenharmony_cistatic int vpfe_get_app_input_index(struct vpfe_device *vpfe, 15708c2ecf20Sopenharmony_ci int *app_input_index) 15718c2ecf20Sopenharmony_ci{ 15728c2ecf20Sopenharmony_ci struct vpfe_config *cfg = vpfe->cfg; 15738c2ecf20Sopenharmony_ci struct vpfe_subdev_info *sdinfo; 15748c2ecf20Sopenharmony_ci struct i2c_client *client; 15758c2ecf20Sopenharmony_ci struct i2c_client *curr_client; 15768c2ecf20Sopenharmony_ci int i, j = 0; 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_ci curr_client = v4l2_get_subdevdata(vpfe->current_subdev->sd); 15798c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vpfe->cfg->asd); i++) { 15808c2ecf20Sopenharmony_ci sdinfo = &cfg->sub_devs[i]; 15818c2ecf20Sopenharmony_ci client = v4l2_get_subdevdata(sdinfo->sd); 15828c2ecf20Sopenharmony_ci if (client->addr == curr_client->addr && 15838c2ecf20Sopenharmony_ci client->adapter->nr == curr_client->adapter->nr) { 15848c2ecf20Sopenharmony_ci if (vpfe->current_input >= 1) 15858c2ecf20Sopenharmony_ci return -1; 15868c2ecf20Sopenharmony_ci *app_input_index = j + vpfe->current_input; 15878c2ecf20Sopenharmony_ci return 0; 15888c2ecf20Sopenharmony_ci } 15898c2ecf20Sopenharmony_ci j++; 15908c2ecf20Sopenharmony_ci } 15918c2ecf20Sopenharmony_ci return -EINVAL; 15928c2ecf20Sopenharmony_ci} 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_cistatic int vpfe_enum_input(struct file *file, void *priv, 15958c2ecf20Sopenharmony_ci struct v4l2_input *inp) 15968c2ecf20Sopenharmony_ci{ 15978c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = video_drvdata(file); 15988c2ecf20Sopenharmony_ci struct vpfe_subdev_info *sdinfo; 15998c2ecf20Sopenharmony_ci int subdev, index; 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci if (vpfe_get_subdev_input_index(vpfe, &subdev, &index, 16028c2ecf20Sopenharmony_ci inp->index) < 0) { 16038c2ecf20Sopenharmony_ci vpfe_dbg(1, vpfe, 16048c2ecf20Sopenharmony_ci "input information not found for the subdev\n"); 16058c2ecf20Sopenharmony_ci return -EINVAL; 16068c2ecf20Sopenharmony_ci } 16078c2ecf20Sopenharmony_ci sdinfo = &vpfe->cfg->sub_devs[subdev]; 16088c2ecf20Sopenharmony_ci *inp = sdinfo->inputs[index]; 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci return 0; 16118c2ecf20Sopenharmony_ci} 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_cistatic int vpfe_g_input(struct file *file, void *priv, unsigned int *index) 16148c2ecf20Sopenharmony_ci{ 16158c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = video_drvdata(file); 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci return vpfe_get_app_input_index(vpfe, index); 16188c2ecf20Sopenharmony_ci} 16198c2ecf20Sopenharmony_ci 16208c2ecf20Sopenharmony_ci/* Assumes caller is holding vpfe_dev->lock */ 16218c2ecf20Sopenharmony_cistatic int vpfe_set_input(struct vpfe_device *vpfe, unsigned int index) 16228c2ecf20Sopenharmony_ci{ 16238c2ecf20Sopenharmony_ci int subdev_index = 0, inp_index = 0; 16248c2ecf20Sopenharmony_ci struct vpfe_subdev_info *sdinfo; 16258c2ecf20Sopenharmony_ci struct vpfe_route *route; 16268c2ecf20Sopenharmony_ci u32 input, output; 16278c2ecf20Sopenharmony_ci int ret; 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci /* If streaming is started, return error */ 16308c2ecf20Sopenharmony_ci if (vb2_is_busy(&vpfe->buffer_queue)) { 16318c2ecf20Sopenharmony_ci vpfe_err(vpfe, "%s device busy\n", __func__); 16328c2ecf20Sopenharmony_ci return -EBUSY; 16338c2ecf20Sopenharmony_ci } 16348c2ecf20Sopenharmony_ci ret = vpfe_get_subdev_input_index(vpfe, 16358c2ecf20Sopenharmony_ci &subdev_index, 16368c2ecf20Sopenharmony_ci &inp_index, 16378c2ecf20Sopenharmony_ci index); 16388c2ecf20Sopenharmony_ci if (ret < 0) { 16398c2ecf20Sopenharmony_ci vpfe_err(vpfe, "invalid input index: %d\n", index); 16408c2ecf20Sopenharmony_ci goto get_out; 16418c2ecf20Sopenharmony_ci } 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci sdinfo = &vpfe->cfg->sub_devs[subdev_index]; 16448c2ecf20Sopenharmony_ci sdinfo->sd = vpfe->sd[subdev_index]; 16458c2ecf20Sopenharmony_ci route = &sdinfo->routes[inp_index]; 16468c2ecf20Sopenharmony_ci if (route && sdinfo->can_route) { 16478c2ecf20Sopenharmony_ci input = route->input; 16488c2ecf20Sopenharmony_ci output = route->output; 16498c2ecf20Sopenharmony_ci if (sdinfo->sd) { 16508c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(sdinfo->sd, video, 16518c2ecf20Sopenharmony_ci s_routing, input, output, 0); 16528c2ecf20Sopenharmony_ci if (ret) { 16538c2ecf20Sopenharmony_ci vpfe_err(vpfe, "s_routing failed\n"); 16548c2ecf20Sopenharmony_ci ret = -EINVAL; 16558c2ecf20Sopenharmony_ci goto get_out; 16568c2ecf20Sopenharmony_ci } 16578c2ecf20Sopenharmony_ci } 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_ci } 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_ci vpfe->current_subdev = sdinfo; 16628c2ecf20Sopenharmony_ci if (sdinfo->sd) 16638c2ecf20Sopenharmony_ci vpfe->v4l2_dev.ctrl_handler = sdinfo->sd->ctrl_handler; 16648c2ecf20Sopenharmony_ci vpfe->current_input = index; 16658c2ecf20Sopenharmony_ci vpfe->std_index = 0; 16668c2ecf20Sopenharmony_ci 16678c2ecf20Sopenharmony_ci /* set the bus/interface parameter for the sub device in ccdc */ 16688c2ecf20Sopenharmony_ci ret = vpfe_ccdc_set_hw_if_params(&vpfe->ccdc, &sdinfo->vpfe_param); 16698c2ecf20Sopenharmony_ci if (ret) 16708c2ecf20Sopenharmony_ci return ret; 16718c2ecf20Sopenharmony_ci 16728c2ecf20Sopenharmony_ci /* set the default image parameters in the device */ 16738c2ecf20Sopenharmony_ci return vpfe_config_image_format(vpfe, 16748c2ecf20Sopenharmony_ci vpfe_standards[vpfe->std_index].std_id); 16758c2ecf20Sopenharmony_ci 16768c2ecf20Sopenharmony_ciget_out: 16778c2ecf20Sopenharmony_ci return ret; 16788c2ecf20Sopenharmony_ci} 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_cistatic int vpfe_s_input(struct file *file, void *priv, unsigned int index) 16818c2ecf20Sopenharmony_ci{ 16828c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = video_drvdata(file); 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci return vpfe_set_input(vpfe, index); 16858c2ecf20Sopenharmony_ci} 16868c2ecf20Sopenharmony_ci 16878c2ecf20Sopenharmony_cistatic int vpfe_querystd(struct file *file, void *priv, v4l2_std_id *std_id) 16888c2ecf20Sopenharmony_ci{ 16898c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = video_drvdata(file); 16908c2ecf20Sopenharmony_ci struct vpfe_subdev_info *sdinfo; 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_ci sdinfo = vpfe->current_subdev; 16938c2ecf20Sopenharmony_ci if (!(sdinfo->inputs[0].capabilities & V4L2_IN_CAP_STD)) 16948c2ecf20Sopenharmony_ci return -ENODATA; 16958c2ecf20Sopenharmony_ci 16968c2ecf20Sopenharmony_ci /* Call querystd function of decoder device */ 16978c2ecf20Sopenharmony_ci return v4l2_device_call_until_err(&vpfe->v4l2_dev, sdinfo->grp_id, 16988c2ecf20Sopenharmony_ci video, querystd, std_id); 16998c2ecf20Sopenharmony_ci} 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_cistatic int vpfe_s_std(struct file *file, void *priv, v4l2_std_id std_id) 17028c2ecf20Sopenharmony_ci{ 17038c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = video_drvdata(file); 17048c2ecf20Sopenharmony_ci struct vpfe_subdev_info *sdinfo; 17058c2ecf20Sopenharmony_ci int ret; 17068c2ecf20Sopenharmony_ci 17078c2ecf20Sopenharmony_ci sdinfo = vpfe->current_subdev; 17088c2ecf20Sopenharmony_ci if (!(sdinfo->inputs[0].capabilities & V4L2_IN_CAP_STD)) 17098c2ecf20Sopenharmony_ci return -ENODATA; 17108c2ecf20Sopenharmony_ci 17118c2ecf20Sopenharmony_ci /* if trying to set the same std then nothing to do */ 17128c2ecf20Sopenharmony_ci if (vpfe_standards[vpfe->std_index].std_id == std_id) 17138c2ecf20Sopenharmony_ci return 0; 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_ci /* If streaming is started, return error */ 17168c2ecf20Sopenharmony_ci if (vb2_is_busy(&vpfe->buffer_queue)) { 17178c2ecf20Sopenharmony_ci vpfe_err(vpfe, "%s device busy\n", __func__); 17188c2ecf20Sopenharmony_ci ret = -EBUSY; 17198c2ecf20Sopenharmony_ci return ret; 17208c2ecf20Sopenharmony_ci } 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci ret = v4l2_device_call_until_err(&vpfe->v4l2_dev, sdinfo->grp_id, 17238c2ecf20Sopenharmony_ci video, s_std, std_id); 17248c2ecf20Sopenharmony_ci if (ret < 0) { 17258c2ecf20Sopenharmony_ci vpfe_err(vpfe, "Failed to set standard\n"); 17268c2ecf20Sopenharmony_ci return ret; 17278c2ecf20Sopenharmony_ci } 17288c2ecf20Sopenharmony_ci ret = vpfe_config_image_format(vpfe, std_id); 17298c2ecf20Sopenharmony_ci 17308c2ecf20Sopenharmony_ci return ret; 17318c2ecf20Sopenharmony_ci} 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_cistatic int vpfe_g_std(struct file *file, void *priv, v4l2_std_id *std_id) 17348c2ecf20Sopenharmony_ci{ 17358c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = video_drvdata(file); 17368c2ecf20Sopenharmony_ci struct vpfe_subdev_info *sdinfo; 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci sdinfo = vpfe->current_subdev; 17398c2ecf20Sopenharmony_ci if (sdinfo->inputs[0].capabilities != V4L2_IN_CAP_STD) 17408c2ecf20Sopenharmony_ci return -ENODATA; 17418c2ecf20Sopenharmony_ci 17428c2ecf20Sopenharmony_ci *std_id = vpfe_standards[vpfe->std_index].std_id; 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_ci return 0; 17458c2ecf20Sopenharmony_ci} 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci/* 17488c2ecf20Sopenharmony_ci * vpfe_calculate_offsets : This function calculates buffers offset 17498c2ecf20Sopenharmony_ci * for top and bottom field 17508c2ecf20Sopenharmony_ci */ 17518c2ecf20Sopenharmony_cistatic void vpfe_calculate_offsets(struct vpfe_device *vpfe) 17528c2ecf20Sopenharmony_ci{ 17538c2ecf20Sopenharmony_ci struct v4l2_rect image_win; 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci vpfe_ccdc_get_image_window(&vpfe->ccdc, &image_win); 17568c2ecf20Sopenharmony_ci vpfe->field_off = image_win.height * image_win.width; 17578c2ecf20Sopenharmony_ci} 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_ci/* 17608c2ecf20Sopenharmony_ci * vpfe_queue_setup - Callback function for buffer setup. 17618c2ecf20Sopenharmony_ci * @vq: vb2_queue ptr 17628c2ecf20Sopenharmony_ci * @nbuffers: ptr to number of buffers requested by application 17638c2ecf20Sopenharmony_ci * @nplanes:: contains number of distinct video planes needed to hold a frame 17648c2ecf20Sopenharmony_ci * @sizes[]: contains the size (in bytes) of each plane. 17658c2ecf20Sopenharmony_ci * @alloc_devs: ptr to allocation context 17668c2ecf20Sopenharmony_ci * 17678c2ecf20Sopenharmony_ci * This callback function is called when reqbuf() is called to adjust 17688c2ecf20Sopenharmony_ci * the buffer count and buffer size 17698c2ecf20Sopenharmony_ci */ 17708c2ecf20Sopenharmony_cistatic int vpfe_queue_setup(struct vb2_queue *vq, 17718c2ecf20Sopenharmony_ci unsigned int *nbuffers, unsigned int *nplanes, 17728c2ecf20Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 17738c2ecf20Sopenharmony_ci{ 17748c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = vb2_get_drv_priv(vq); 17758c2ecf20Sopenharmony_ci unsigned size = vpfe->fmt.fmt.pix.sizeimage; 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_ci if (vq->num_buffers + *nbuffers < 3) 17788c2ecf20Sopenharmony_ci *nbuffers = 3 - vq->num_buffers; 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_ci if (*nplanes) { 17818c2ecf20Sopenharmony_ci if (sizes[0] < size) 17828c2ecf20Sopenharmony_ci return -EINVAL; 17838c2ecf20Sopenharmony_ci size = sizes[0]; 17848c2ecf20Sopenharmony_ci } 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci *nplanes = 1; 17878c2ecf20Sopenharmony_ci sizes[0] = size; 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci vpfe_dbg(1, vpfe, 17908c2ecf20Sopenharmony_ci "nbuffers=%d, size=%u\n", *nbuffers, sizes[0]); 17918c2ecf20Sopenharmony_ci 17928c2ecf20Sopenharmony_ci /* Calculate field offset */ 17938c2ecf20Sopenharmony_ci vpfe_calculate_offsets(vpfe); 17948c2ecf20Sopenharmony_ci 17958c2ecf20Sopenharmony_ci return 0; 17968c2ecf20Sopenharmony_ci} 17978c2ecf20Sopenharmony_ci 17988c2ecf20Sopenharmony_ci/* 17998c2ecf20Sopenharmony_ci * vpfe_buffer_prepare : callback function for buffer prepare 18008c2ecf20Sopenharmony_ci * @vb: ptr to vb2_buffer 18018c2ecf20Sopenharmony_ci * 18028c2ecf20Sopenharmony_ci * This is the callback function for buffer prepare when vb2_qbuf() 18038c2ecf20Sopenharmony_ci * function is called. The buffer is prepared and user space virtual address 18048c2ecf20Sopenharmony_ci * or user address is converted into physical address 18058c2ecf20Sopenharmony_ci */ 18068c2ecf20Sopenharmony_cistatic int vpfe_buffer_prepare(struct vb2_buffer *vb) 18078c2ecf20Sopenharmony_ci{ 18088c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 18098c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = vb2_get_drv_priv(vb->vb2_queue); 18108c2ecf20Sopenharmony_ci 18118c2ecf20Sopenharmony_ci vb2_set_plane_payload(vb, 0, vpfe->fmt.fmt.pix.sizeimage); 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci if (vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) 18148c2ecf20Sopenharmony_ci return -EINVAL; 18158c2ecf20Sopenharmony_ci 18168c2ecf20Sopenharmony_ci vbuf->field = vpfe->fmt.fmt.pix.field; 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci return 0; 18198c2ecf20Sopenharmony_ci} 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci/* 18228c2ecf20Sopenharmony_ci * vpfe_buffer_queue : Callback function to add buffer to DMA queue 18238c2ecf20Sopenharmony_ci * @vb: ptr to vb2_buffer 18248c2ecf20Sopenharmony_ci */ 18258c2ecf20Sopenharmony_cistatic void vpfe_buffer_queue(struct vb2_buffer *vb) 18268c2ecf20Sopenharmony_ci{ 18278c2ecf20Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 18288c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = vb2_get_drv_priv(vb->vb2_queue); 18298c2ecf20Sopenharmony_ci struct vpfe_cap_buffer *buf = to_vpfe_buffer(vbuf); 18308c2ecf20Sopenharmony_ci unsigned long flags = 0; 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci /* add the buffer to the DMA queue */ 18338c2ecf20Sopenharmony_ci spin_lock_irqsave(&vpfe->dma_queue_lock, flags); 18348c2ecf20Sopenharmony_ci list_add_tail(&buf->list, &vpfe->dma_queue); 18358c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vpfe->dma_queue_lock, flags); 18368c2ecf20Sopenharmony_ci} 18378c2ecf20Sopenharmony_ci 18388c2ecf20Sopenharmony_cistatic void vpfe_return_all_buffers(struct vpfe_device *vpfe, 18398c2ecf20Sopenharmony_ci enum vb2_buffer_state state) 18408c2ecf20Sopenharmony_ci{ 18418c2ecf20Sopenharmony_ci struct vpfe_cap_buffer *buf, *node; 18428c2ecf20Sopenharmony_ci unsigned long flags; 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_ci spin_lock_irqsave(&vpfe->dma_queue_lock, flags); 18458c2ecf20Sopenharmony_ci list_for_each_entry_safe(buf, node, &vpfe->dma_queue, list) { 18468c2ecf20Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, state); 18478c2ecf20Sopenharmony_ci list_del(&buf->list); 18488c2ecf20Sopenharmony_ci } 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_ci if (vpfe->cur_frm) 18518c2ecf20Sopenharmony_ci vb2_buffer_done(&vpfe->cur_frm->vb.vb2_buf, state); 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_ci if (vpfe->next_frm && vpfe->next_frm != vpfe->cur_frm) 18548c2ecf20Sopenharmony_ci vb2_buffer_done(&vpfe->next_frm->vb.vb2_buf, state); 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_ci vpfe->cur_frm = NULL; 18578c2ecf20Sopenharmony_ci vpfe->next_frm = NULL; 18588c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vpfe->dma_queue_lock, flags); 18598c2ecf20Sopenharmony_ci} 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci/* 18628c2ecf20Sopenharmony_ci * vpfe_start_streaming : Starts the DMA engine for streaming 18638c2ecf20Sopenharmony_ci * @vb: ptr to vb2_buffer 18648c2ecf20Sopenharmony_ci * @count: number of buffers 18658c2ecf20Sopenharmony_ci */ 18668c2ecf20Sopenharmony_cistatic int vpfe_start_streaming(struct vb2_queue *vq, unsigned int count) 18678c2ecf20Sopenharmony_ci{ 18688c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = vb2_get_drv_priv(vq); 18698c2ecf20Sopenharmony_ci struct vpfe_subdev_info *sdinfo; 18708c2ecf20Sopenharmony_ci unsigned long flags; 18718c2ecf20Sopenharmony_ci unsigned long addr; 18728c2ecf20Sopenharmony_ci int ret; 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci spin_lock_irqsave(&vpfe->dma_queue_lock, flags); 18758c2ecf20Sopenharmony_ci 18768c2ecf20Sopenharmony_ci vpfe->field = 0; 18778c2ecf20Sopenharmony_ci vpfe->sequence = 0; 18788c2ecf20Sopenharmony_ci 18798c2ecf20Sopenharmony_ci sdinfo = vpfe->current_subdev; 18808c2ecf20Sopenharmony_ci 18818c2ecf20Sopenharmony_ci vpfe_attach_irq(vpfe); 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_ci vpfe->stopping = false; 18848c2ecf20Sopenharmony_ci init_completion(&vpfe->capture_stop); 18858c2ecf20Sopenharmony_ci 18868c2ecf20Sopenharmony_ci if (vpfe->ccdc.ccdc_cfg.if_type == VPFE_RAW_BAYER) 18878c2ecf20Sopenharmony_ci vpfe_ccdc_config_raw(&vpfe->ccdc); 18888c2ecf20Sopenharmony_ci else 18898c2ecf20Sopenharmony_ci vpfe_ccdc_config_ycbcr(&vpfe->ccdc); 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ci /* Get the next frame from the buffer queue */ 18928c2ecf20Sopenharmony_ci vpfe->next_frm = list_entry(vpfe->dma_queue.next, 18938c2ecf20Sopenharmony_ci struct vpfe_cap_buffer, list); 18948c2ecf20Sopenharmony_ci vpfe->cur_frm = vpfe->next_frm; 18958c2ecf20Sopenharmony_ci /* Remove buffer from the buffer queue */ 18968c2ecf20Sopenharmony_ci list_del(&vpfe->cur_frm->list); 18978c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vpfe->dma_queue_lock, flags); 18988c2ecf20Sopenharmony_ci 18998c2ecf20Sopenharmony_ci addr = vb2_dma_contig_plane_dma_addr(&vpfe->cur_frm->vb.vb2_buf, 0); 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci vpfe_set_sdr_addr(&vpfe->ccdc, (unsigned long)(addr)); 19028c2ecf20Sopenharmony_ci 19038c2ecf20Sopenharmony_ci vpfe_pcr_enable(&vpfe->ccdc, 1); 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(sdinfo->sd, video, s_stream, 1); 19068c2ecf20Sopenharmony_ci if (ret < 0) { 19078c2ecf20Sopenharmony_ci vpfe_err(vpfe, "Error in attaching interrupt handle\n"); 19088c2ecf20Sopenharmony_ci goto err; 19098c2ecf20Sopenharmony_ci } 19108c2ecf20Sopenharmony_ci 19118c2ecf20Sopenharmony_ci return 0; 19128c2ecf20Sopenharmony_ci 19138c2ecf20Sopenharmony_cierr: 19148c2ecf20Sopenharmony_ci vpfe_return_all_buffers(vpfe, VB2_BUF_STATE_QUEUED); 19158c2ecf20Sopenharmony_ci vpfe_pcr_enable(&vpfe->ccdc, 0); 19168c2ecf20Sopenharmony_ci return ret; 19178c2ecf20Sopenharmony_ci} 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci/* 19208c2ecf20Sopenharmony_ci * vpfe_stop_streaming : Stop the DMA engine 19218c2ecf20Sopenharmony_ci * @vq: ptr to vb2_queue 19228c2ecf20Sopenharmony_ci * 19238c2ecf20Sopenharmony_ci * This callback stops the DMA engine and any remaining buffers 19248c2ecf20Sopenharmony_ci * in the DMA queue are released. 19258c2ecf20Sopenharmony_ci */ 19268c2ecf20Sopenharmony_cistatic void vpfe_stop_streaming(struct vb2_queue *vq) 19278c2ecf20Sopenharmony_ci{ 19288c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = vb2_get_drv_priv(vq); 19298c2ecf20Sopenharmony_ci struct vpfe_subdev_info *sdinfo; 19308c2ecf20Sopenharmony_ci int ret; 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_ci vpfe_pcr_enable(&vpfe->ccdc, 0); 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci /* Wait for the last frame to be captured */ 19358c2ecf20Sopenharmony_ci vpfe->stopping = true; 19368c2ecf20Sopenharmony_ci wait_for_completion_timeout(&vpfe->capture_stop, 19378c2ecf20Sopenharmony_ci msecs_to_jiffies(250)); 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ci vpfe_detach_irq(vpfe); 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci sdinfo = vpfe->current_subdev; 19428c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(sdinfo->sd, video, s_stream, 0); 19438c2ecf20Sopenharmony_ci if (ret && ret != -ENOIOCTLCMD && ret != -ENODEV) 19448c2ecf20Sopenharmony_ci vpfe_dbg(1, vpfe, "stream off failed in subdev\n"); 19458c2ecf20Sopenharmony_ci 19468c2ecf20Sopenharmony_ci /* release all active buffers */ 19478c2ecf20Sopenharmony_ci vpfe_return_all_buffers(vpfe, VB2_BUF_STATE_ERROR); 19488c2ecf20Sopenharmony_ci} 19498c2ecf20Sopenharmony_ci 19508c2ecf20Sopenharmony_cistatic int vpfe_g_pixelaspect(struct file *file, void *priv, 19518c2ecf20Sopenharmony_ci int type, struct v4l2_fract *f) 19528c2ecf20Sopenharmony_ci{ 19538c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = video_drvdata(file); 19548c2ecf20Sopenharmony_ci 19558c2ecf20Sopenharmony_ci if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || 19568c2ecf20Sopenharmony_ci vpfe->std_index >= ARRAY_SIZE(vpfe_standards)) 19578c2ecf20Sopenharmony_ci return -EINVAL; 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_ci *f = vpfe_standards[vpfe->std_index].pixelaspect; 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_ci return 0; 19628c2ecf20Sopenharmony_ci} 19638c2ecf20Sopenharmony_ci 19648c2ecf20Sopenharmony_cistatic int 19658c2ecf20Sopenharmony_civpfe_g_selection(struct file *file, void *fh, struct v4l2_selection *s) 19668c2ecf20Sopenharmony_ci{ 19678c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = video_drvdata(file); 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || 19708c2ecf20Sopenharmony_ci vpfe->std_index >= ARRAY_SIZE(vpfe_standards)) 19718c2ecf20Sopenharmony_ci return -EINVAL; 19728c2ecf20Sopenharmony_ci 19738c2ecf20Sopenharmony_ci switch (s->target) { 19748c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 19758c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP_DEFAULT: 19768c2ecf20Sopenharmony_ci s->r.left = 0; 19778c2ecf20Sopenharmony_ci s->r.top = 0; 19788c2ecf20Sopenharmony_ci s->r.width = vpfe_standards[vpfe->std_index].width; 19798c2ecf20Sopenharmony_ci s->r.height = vpfe_standards[vpfe->std_index].height; 19808c2ecf20Sopenharmony_ci break; 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_ci case V4L2_SEL_TGT_CROP: 19838c2ecf20Sopenharmony_ci s->r = vpfe->crop; 19848c2ecf20Sopenharmony_ci break; 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ci default: 19878c2ecf20Sopenharmony_ci return -EINVAL; 19888c2ecf20Sopenharmony_ci } 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_ci return 0; 19918c2ecf20Sopenharmony_ci} 19928c2ecf20Sopenharmony_ci 19938c2ecf20Sopenharmony_cistatic int 19948c2ecf20Sopenharmony_civpfe_s_selection(struct file *file, void *fh, struct v4l2_selection *s) 19958c2ecf20Sopenharmony_ci{ 19968c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = video_drvdata(file); 19978c2ecf20Sopenharmony_ci struct v4l2_rect cr = vpfe->crop; 19988c2ecf20Sopenharmony_ci struct v4l2_rect r = s->r; 19998c2ecf20Sopenharmony_ci u32 bpp; 20008c2ecf20Sopenharmony_ci 20018c2ecf20Sopenharmony_ci /* If streaming is started, return error */ 20028c2ecf20Sopenharmony_ci if (vb2_is_busy(&vpfe->buffer_queue)) { 20038c2ecf20Sopenharmony_ci vpfe_err(vpfe, "%s device busy\n", __func__); 20048c2ecf20Sopenharmony_ci return -EBUSY; 20058c2ecf20Sopenharmony_ci } 20068c2ecf20Sopenharmony_ci 20078c2ecf20Sopenharmony_ci if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || 20088c2ecf20Sopenharmony_ci s->target != V4L2_SEL_TGT_CROP) 20098c2ecf20Sopenharmony_ci return -EINVAL; 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_ci v4l_bound_align_image(&r.width, 0, cr.width, 0, 20128c2ecf20Sopenharmony_ci &r.height, 0, cr.height, 0, 0); 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_ci r.left = clamp_t(unsigned int, r.left, 0, cr.width - r.width); 20158c2ecf20Sopenharmony_ci r.top = clamp_t(unsigned int, r.top, 0, cr.height - r.height); 20168c2ecf20Sopenharmony_ci 20178c2ecf20Sopenharmony_ci if (s->flags & V4L2_SEL_FLAG_LE && !v4l2_rect_enclosed(&r, &s->r)) 20188c2ecf20Sopenharmony_ci return -ERANGE; 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci if (s->flags & V4L2_SEL_FLAG_GE && !v4l2_rect_enclosed(&s->r, &r)) 20218c2ecf20Sopenharmony_ci return -ERANGE; 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_ci s->r = vpfe->crop = r; 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_ci bpp = __get_bytesperpixel(vpfe, vpfe->current_vpfe_fmt); 20268c2ecf20Sopenharmony_ci vpfe_ccdc_set_image_window(&vpfe->ccdc, &r, bpp); 20278c2ecf20Sopenharmony_ci vpfe->fmt.fmt.pix.width = r.width; 20288c2ecf20Sopenharmony_ci vpfe->fmt.fmt.pix.height = r.height; 20298c2ecf20Sopenharmony_ci vpfe->fmt.fmt.pix.bytesperline = 20308c2ecf20Sopenharmony_ci vpfe_ccdc_get_line_length(&vpfe->ccdc); 20318c2ecf20Sopenharmony_ci vpfe->fmt.fmt.pix.sizeimage = vpfe->fmt.fmt.pix.bytesperline * 20328c2ecf20Sopenharmony_ci vpfe->fmt.fmt.pix.height; 20338c2ecf20Sopenharmony_ci 20348c2ecf20Sopenharmony_ci vpfe_dbg(1, vpfe, "cropped (%d,%d)/%dx%d of %dx%d\n", 20358c2ecf20Sopenharmony_ci r.left, r.top, r.width, r.height, cr.width, cr.height); 20368c2ecf20Sopenharmony_ci 20378c2ecf20Sopenharmony_ci return 0; 20388c2ecf20Sopenharmony_ci} 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_cistatic long vpfe_ioctl_default(struct file *file, void *priv, 20418c2ecf20Sopenharmony_ci bool valid_prio, unsigned int cmd, void *param) 20428c2ecf20Sopenharmony_ci{ 20438c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = video_drvdata(file); 20448c2ecf20Sopenharmony_ci int ret; 20458c2ecf20Sopenharmony_ci 20468c2ecf20Sopenharmony_ci if (!valid_prio) { 20478c2ecf20Sopenharmony_ci vpfe_err(vpfe, "%s device busy\n", __func__); 20488c2ecf20Sopenharmony_ci return -EBUSY; 20498c2ecf20Sopenharmony_ci } 20508c2ecf20Sopenharmony_ci 20518c2ecf20Sopenharmony_ci /* If streaming is started, return error */ 20528c2ecf20Sopenharmony_ci if (vb2_is_busy(&vpfe->buffer_queue)) { 20538c2ecf20Sopenharmony_ci vpfe_err(vpfe, "%s device busy\n", __func__); 20548c2ecf20Sopenharmony_ci return -EBUSY; 20558c2ecf20Sopenharmony_ci } 20568c2ecf20Sopenharmony_ci 20578c2ecf20Sopenharmony_ci switch (cmd) { 20588c2ecf20Sopenharmony_ci case VIDIOC_AM437X_CCDC_CFG: 20598c2ecf20Sopenharmony_ci ret = vpfe_ccdc_set_params(&vpfe->ccdc, (void __user *)param); 20608c2ecf20Sopenharmony_ci if (ret) { 20618c2ecf20Sopenharmony_ci vpfe_dbg(2, vpfe, 20628c2ecf20Sopenharmony_ci "Error setting parameters in CCDC\n"); 20638c2ecf20Sopenharmony_ci return ret; 20648c2ecf20Sopenharmony_ci } 20658c2ecf20Sopenharmony_ci ret = vpfe_get_ccdc_image_format(vpfe, 20668c2ecf20Sopenharmony_ci &vpfe->fmt); 20678c2ecf20Sopenharmony_ci if (ret < 0) { 20688c2ecf20Sopenharmony_ci vpfe_dbg(2, vpfe, 20698c2ecf20Sopenharmony_ci "Invalid image format at CCDC\n"); 20708c2ecf20Sopenharmony_ci return ret; 20718c2ecf20Sopenharmony_ci } 20728c2ecf20Sopenharmony_ci break; 20738c2ecf20Sopenharmony_ci 20748c2ecf20Sopenharmony_ci default: 20758c2ecf20Sopenharmony_ci ret = -ENOTTY; 20768c2ecf20Sopenharmony_ci break; 20778c2ecf20Sopenharmony_ci } 20788c2ecf20Sopenharmony_ci 20798c2ecf20Sopenharmony_ci return ret; 20808c2ecf20Sopenharmony_ci} 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_cistatic const struct vb2_ops vpfe_video_qops = { 20838c2ecf20Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 20848c2ecf20Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 20858c2ecf20Sopenharmony_ci .queue_setup = vpfe_queue_setup, 20868c2ecf20Sopenharmony_ci .buf_prepare = vpfe_buffer_prepare, 20878c2ecf20Sopenharmony_ci .buf_queue = vpfe_buffer_queue, 20888c2ecf20Sopenharmony_ci .start_streaming = vpfe_start_streaming, 20898c2ecf20Sopenharmony_ci .stop_streaming = vpfe_stop_streaming, 20908c2ecf20Sopenharmony_ci}; 20918c2ecf20Sopenharmony_ci 20928c2ecf20Sopenharmony_ci/* vpfe capture driver file operations */ 20938c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations vpfe_fops = { 20948c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 20958c2ecf20Sopenharmony_ci .open = vpfe_open, 20968c2ecf20Sopenharmony_ci .release = vpfe_release, 20978c2ecf20Sopenharmony_ci .read = vb2_fop_read, 20988c2ecf20Sopenharmony_ci .poll = vb2_fop_poll, 20998c2ecf20Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 21008c2ecf20Sopenharmony_ci .mmap = vb2_fop_mmap, 21018c2ecf20Sopenharmony_ci}; 21028c2ecf20Sopenharmony_ci 21038c2ecf20Sopenharmony_ci/* vpfe capture ioctl operations */ 21048c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops vpfe_ioctl_ops = { 21058c2ecf20Sopenharmony_ci .vidioc_querycap = vpfe_querycap, 21068c2ecf20Sopenharmony_ci .vidioc_enum_fmt_vid_cap = vpfe_enum_fmt, 21078c2ecf20Sopenharmony_ci .vidioc_g_fmt_vid_cap = vpfe_g_fmt, 21088c2ecf20Sopenharmony_ci .vidioc_s_fmt_vid_cap = vpfe_s_fmt, 21098c2ecf20Sopenharmony_ci .vidioc_try_fmt_vid_cap = vpfe_try_fmt, 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_ci .vidioc_enum_framesizes = vpfe_enum_size, 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_ci .vidioc_enum_input = vpfe_enum_input, 21148c2ecf20Sopenharmony_ci .vidioc_g_input = vpfe_g_input, 21158c2ecf20Sopenharmony_ci .vidioc_s_input = vpfe_s_input, 21168c2ecf20Sopenharmony_ci 21178c2ecf20Sopenharmony_ci .vidioc_querystd = vpfe_querystd, 21188c2ecf20Sopenharmony_ci .vidioc_s_std = vpfe_s_std, 21198c2ecf20Sopenharmony_ci .vidioc_g_std = vpfe_g_std, 21208c2ecf20Sopenharmony_ci 21218c2ecf20Sopenharmony_ci .vidioc_reqbufs = vb2_ioctl_reqbufs, 21228c2ecf20Sopenharmony_ci .vidioc_create_bufs = vb2_ioctl_create_bufs, 21238c2ecf20Sopenharmony_ci .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 21248c2ecf20Sopenharmony_ci .vidioc_querybuf = vb2_ioctl_querybuf, 21258c2ecf20Sopenharmony_ci .vidioc_qbuf = vb2_ioctl_qbuf, 21268c2ecf20Sopenharmony_ci .vidioc_dqbuf = vb2_ioctl_dqbuf, 21278c2ecf20Sopenharmony_ci .vidioc_expbuf = vb2_ioctl_expbuf, 21288c2ecf20Sopenharmony_ci .vidioc_streamon = vb2_ioctl_streamon, 21298c2ecf20Sopenharmony_ci .vidioc_streamoff = vb2_ioctl_streamoff, 21308c2ecf20Sopenharmony_ci 21318c2ecf20Sopenharmony_ci .vidioc_log_status = v4l2_ctrl_log_status, 21328c2ecf20Sopenharmony_ci .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 21338c2ecf20Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 21348c2ecf20Sopenharmony_ci 21358c2ecf20Sopenharmony_ci .vidioc_g_pixelaspect = vpfe_g_pixelaspect, 21368c2ecf20Sopenharmony_ci .vidioc_g_selection = vpfe_g_selection, 21378c2ecf20Sopenharmony_ci .vidioc_s_selection = vpfe_s_selection, 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_ci .vidioc_default = vpfe_ioctl_default, 21408c2ecf20Sopenharmony_ci}; 21418c2ecf20Sopenharmony_ci 21428c2ecf20Sopenharmony_cistatic int 21438c2ecf20Sopenharmony_civpfe_async_bound(struct v4l2_async_notifier *notifier, 21448c2ecf20Sopenharmony_ci struct v4l2_subdev *subdev, 21458c2ecf20Sopenharmony_ci struct v4l2_async_subdev *asd) 21468c2ecf20Sopenharmony_ci{ 21478c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = container_of(notifier->v4l2_dev, 21488c2ecf20Sopenharmony_ci struct vpfe_device, v4l2_dev); 21498c2ecf20Sopenharmony_ci struct v4l2_subdev_mbus_code_enum mbus_code; 21508c2ecf20Sopenharmony_ci struct vpfe_subdev_info *sdinfo; 21518c2ecf20Sopenharmony_ci struct vpfe_fmt *fmt; 21528c2ecf20Sopenharmony_ci int ret = 0; 21538c2ecf20Sopenharmony_ci bool found = false; 21548c2ecf20Sopenharmony_ci int i, j, k; 21558c2ecf20Sopenharmony_ci 21568c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vpfe->cfg->asd); i++) { 21578c2ecf20Sopenharmony_ci if (vpfe->cfg->asd[i]->match.fwnode == 21588c2ecf20Sopenharmony_ci asd[i].match.fwnode) { 21598c2ecf20Sopenharmony_ci sdinfo = &vpfe->cfg->sub_devs[i]; 21608c2ecf20Sopenharmony_ci vpfe->sd[i] = subdev; 21618c2ecf20Sopenharmony_ci vpfe->sd[i]->grp_id = sdinfo->grp_id; 21628c2ecf20Sopenharmony_ci found = true; 21638c2ecf20Sopenharmony_ci break; 21648c2ecf20Sopenharmony_ci } 21658c2ecf20Sopenharmony_ci } 21668c2ecf20Sopenharmony_ci 21678c2ecf20Sopenharmony_ci if (!found) { 21688c2ecf20Sopenharmony_ci vpfe_info(vpfe, "sub device (%s) not matched\n", subdev->name); 21698c2ecf20Sopenharmony_ci return -EINVAL; 21708c2ecf20Sopenharmony_ci } 21718c2ecf20Sopenharmony_ci 21728c2ecf20Sopenharmony_ci vpfe->video_dev.tvnorms |= sdinfo->inputs[0].std; 21738c2ecf20Sopenharmony_ci 21748c2ecf20Sopenharmony_ci vpfe->num_active_fmt = 0; 21758c2ecf20Sopenharmony_ci for (j = 0, i = 0; (ret != -EINVAL); ++j) { 21768c2ecf20Sopenharmony_ci memset(&mbus_code, 0, sizeof(mbus_code)); 21778c2ecf20Sopenharmony_ci mbus_code.index = j; 21788c2ecf20Sopenharmony_ci mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; 21798c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(subdev, pad, enum_mbus_code, 21808c2ecf20Sopenharmony_ci NULL, &mbus_code); 21818c2ecf20Sopenharmony_ci if (ret) 21828c2ecf20Sopenharmony_ci continue; 21838c2ecf20Sopenharmony_ci 21848c2ecf20Sopenharmony_ci vpfe_dbg(3, vpfe, 21858c2ecf20Sopenharmony_ci "subdev %s: code: %04x idx: %d\n", 21868c2ecf20Sopenharmony_ci subdev->name, mbus_code.code, j); 21878c2ecf20Sopenharmony_ci 21888c2ecf20Sopenharmony_ci for (k = 0; k < ARRAY_SIZE(formats); k++) { 21898c2ecf20Sopenharmony_ci fmt = &formats[k]; 21908c2ecf20Sopenharmony_ci if (mbus_code.code != fmt->code) 21918c2ecf20Sopenharmony_ci continue; 21928c2ecf20Sopenharmony_ci vpfe->active_fmt[i] = fmt; 21938c2ecf20Sopenharmony_ci vpfe_dbg(3, vpfe, 21948c2ecf20Sopenharmony_ci "matched fourcc: %s code: %04x idx: %d\n", 21958c2ecf20Sopenharmony_ci print_fourcc(fmt->fourcc), mbus_code.code, i); 21968c2ecf20Sopenharmony_ci vpfe->num_active_fmt = ++i; 21978c2ecf20Sopenharmony_ci } 21988c2ecf20Sopenharmony_ci } 21998c2ecf20Sopenharmony_ci 22008c2ecf20Sopenharmony_ci if (!i) { 22018c2ecf20Sopenharmony_ci vpfe_err(vpfe, "No suitable format reported by subdev %s\n", 22028c2ecf20Sopenharmony_ci subdev->name); 22038c2ecf20Sopenharmony_ci return -EINVAL; 22048c2ecf20Sopenharmony_ci } 22058c2ecf20Sopenharmony_ci return 0; 22068c2ecf20Sopenharmony_ci} 22078c2ecf20Sopenharmony_ci 22088c2ecf20Sopenharmony_cistatic int vpfe_probe_complete(struct vpfe_device *vpfe) 22098c2ecf20Sopenharmony_ci{ 22108c2ecf20Sopenharmony_ci struct video_device *vdev; 22118c2ecf20Sopenharmony_ci struct vb2_queue *q; 22128c2ecf20Sopenharmony_ci int err; 22138c2ecf20Sopenharmony_ci 22148c2ecf20Sopenharmony_ci spin_lock_init(&vpfe->dma_queue_lock); 22158c2ecf20Sopenharmony_ci mutex_init(&vpfe->lock); 22168c2ecf20Sopenharmony_ci 22178c2ecf20Sopenharmony_ci vpfe->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 22188c2ecf20Sopenharmony_ci 22198c2ecf20Sopenharmony_ci /* set first sub device as current one */ 22208c2ecf20Sopenharmony_ci vpfe->current_subdev = &vpfe->cfg->sub_devs[0]; 22218c2ecf20Sopenharmony_ci vpfe->v4l2_dev.ctrl_handler = vpfe->sd[0]->ctrl_handler; 22228c2ecf20Sopenharmony_ci 22238c2ecf20Sopenharmony_ci err = vpfe_set_input(vpfe, 0); 22248c2ecf20Sopenharmony_ci if (err) 22258c2ecf20Sopenharmony_ci goto probe_out; 22268c2ecf20Sopenharmony_ci 22278c2ecf20Sopenharmony_ci /* Initialize videobuf2 queue as per the buffer type */ 22288c2ecf20Sopenharmony_ci q = &vpfe->buffer_queue; 22298c2ecf20Sopenharmony_ci q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 22308c2ecf20Sopenharmony_ci q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; 22318c2ecf20Sopenharmony_ci q->drv_priv = vpfe; 22328c2ecf20Sopenharmony_ci q->ops = &vpfe_video_qops; 22338c2ecf20Sopenharmony_ci q->mem_ops = &vb2_dma_contig_memops; 22348c2ecf20Sopenharmony_ci q->buf_struct_size = sizeof(struct vpfe_cap_buffer); 22358c2ecf20Sopenharmony_ci q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 22368c2ecf20Sopenharmony_ci q->lock = &vpfe->lock; 22378c2ecf20Sopenharmony_ci q->min_buffers_needed = 1; 22388c2ecf20Sopenharmony_ci q->dev = vpfe->pdev; 22398c2ecf20Sopenharmony_ci 22408c2ecf20Sopenharmony_ci err = vb2_queue_init(q); 22418c2ecf20Sopenharmony_ci if (err) { 22428c2ecf20Sopenharmony_ci vpfe_err(vpfe, "vb2_queue_init() failed\n"); 22438c2ecf20Sopenharmony_ci goto probe_out; 22448c2ecf20Sopenharmony_ci } 22458c2ecf20Sopenharmony_ci 22468c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&vpfe->dma_queue); 22478c2ecf20Sopenharmony_ci 22488c2ecf20Sopenharmony_ci vdev = &vpfe->video_dev; 22498c2ecf20Sopenharmony_ci strscpy(vdev->name, VPFE_MODULE_NAME, sizeof(vdev->name)); 22508c2ecf20Sopenharmony_ci vdev->release = video_device_release_empty; 22518c2ecf20Sopenharmony_ci vdev->fops = &vpfe_fops; 22528c2ecf20Sopenharmony_ci vdev->ioctl_ops = &vpfe_ioctl_ops; 22538c2ecf20Sopenharmony_ci vdev->v4l2_dev = &vpfe->v4l2_dev; 22548c2ecf20Sopenharmony_ci vdev->vfl_dir = VFL_DIR_RX; 22558c2ecf20Sopenharmony_ci vdev->queue = q; 22568c2ecf20Sopenharmony_ci vdev->lock = &vpfe->lock; 22578c2ecf20Sopenharmony_ci vdev->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | 22588c2ecf20Sopenharmony_ci V4L2_CAP_READWRITE; 22598c2ecf20Sopenharmony_ci video_set_drvdata(vdev, vpfe); 22608c2ecf20Sopenharmony_ci err = video_register_device(&vpfe->video_dev, VFL_TYPE_VIDEO, -1); 22618c2ecf20Sopenharmony_ci if (err) { 22628c2ecf20Sopenharmony_ci vpfe_err(vpfe, 22638c2ecf20Sopenharmony_ci "Unable to register video device.\n"); 22648c2ecf20Sopenharmony_ci goto probe_out; 22658c2ecf20Sopenharmony_ci } 22668c2ecf20Sopenharmony_ci 22678c2ecf20Sopenharmony_ci return 0; 22688c2ecf20Sopenharmony_ci 22698c2ecf20Sopenharmony_ciprobe_out: 22708c2ecf20Sopenharmony_ci v4l2_device_unregister(&vpfe->v4l2_dev); 22718c2ecf20Sopenharmony_ci return err; 22728c2ecf20Sopenharmony_ci} 22738c2ecf20Sopenharmony_ci 22748c2ecf20Sopenharmony_cistatic int vpfe_async_complete(struct v4l2_async_notifier *notifier) 22758c2ecf20Sopenharmony_ci{ 22768c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = container_of(notifier->v4l2_dev, 22778c2ecf20Sopenharmony_ci struct vpfe_device, v4l2_dev); 22788c2ecf20Sopenharmony_ci 22798c2ecf20Sopenharmony_ci return vpfe_probe_complete(vpfe); 22808c2ecf20Sopenharmony_ci} 22818c2ecf20Sopenharmony_ci 22828c2ecf20Sopenharmony_cistatic const struct v4l2_async_notifier_operations vpfe_async_ops = { 22838c2ecf20Sopenharmony_ci .bound = vpfe_async_bound, 22848c2ecf20Sopenharmony_ci .complete = vpfe_async_complete, 22858c2ecf20Sopenharmony_ci}; 22868c2ecf20Sopenharmony_ci 22878c2ecf20Sopenharmony_cistatic struct vpfe_config * 22888c2ecf20Sopenharmony_civpfe_get_pdata(struct vpfe_device *vpfe) 22898c2ecf20Sopenharmony_ci{ 22908c2ecf20Sopenharmony_ci struct device_node *endpoint = NULL; 22918c2ecf20Sopenharmony_ci struct device *dev = vpfe->pdev; 22928c2ecf20Sopenharmony_ci struct vpfe_subdev_info *sdinfo; 22938c2ecf20Sopenharmony_ci struct vpfe_config *pdata; 22948c2ecf20Sopenharmony_ci unsigned int flags; 22958c2ecf20Sopenharmony_ci unsigned int i; 22968c2ecf20Sopenharmony_ci int err; 22978c2ecf20Sopenharmony_ci 22988c2ecf20Sopenharmony_ci dev_dbg(dev, "vpfe_get_pdata\n"); 22998c2ecf20Sopenharmony_ci 23008c2ecf20Sopenharmony_ci v4l2_async_notifier_init(&vpfe->notifier); 23018c2ecf20Sopenharmony_ci 23028c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_OF) || !dev->of_node) 23038c2ecf20Sopenharmony_ci return dev->platform_data; 23048c2ecf20Sopenharmony_ci 23058c2ecf20Sopenharmony_ci pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); 23068c2ecf20Sopenharmony_ci if (!pdata) 23078c2ecf20Sopenharmony_ci return NULL; 23088c2ecf20Sopenharmony_ci 23098c2ecf20Sopenharmony_ci for (i = 0; ; i++) { 23108c2ecf20Sopenharmony_ci struct v4l2_fwnode_endpoint bus_cfg = { .bus_type = 0 }; 23118c2ecf20Sopenharmony_ci struct device_node *rem; 23128c2ecf20Sopenharmony_ci 23138c2ecf20Sopenharmony_ci endpoint = of_graph_get_next_endpoint(dev->of_node, endpoint); 23148c2ecf20Sopenharmony_ci if (!endpoint) 23158c2ecf20Sopenharmony_ci break; 23168c2ecf20Sopenharmony_ci 23178c2ecf20Sopenharmony_ci sdinfo = &pdata->sub_devs[i]; 23188c2ecf20Sopenharmony_ci sdinfo->grp_id = 0; 23198c2ecf20Sopenharmony_ci 23208c2ecf20Sopenharmony_ci /* we only support camera */ 23218c2ecf20Sopenharmony_ci sdinfo->inputs[0].index = i; 23228c2ecf20Sopenharmony_ci strscpy(sdinfo->inputs[0].name, "Camera", 23238c2ecf20Sopenharmony_ci sizeof(sdinfo->inputs[0].name)); 23248c2ecf20Sopenharmony_ci sdinfo->inputs[0].type = V4L2_INPUT_TYPE_CAMERA; 23258c2ecf20Sopenharmony_ci sdinfo->inputs[0].std = V4L2_STD_ALL; 23268c2ecf20Sopenharmony_ci sdinfo->inputs[0].capabilities = V4L2_IN_CAP_STD; 23278c2ecf20Sopenharmony_ci 23288c2ecf20Sopenharmony_ci sdinfo->can_route = 0; 23298c2ecf20Sopenharmony_ci sdinfo->routes = NULL; 23308c2ecf20Sopenharmony_ci 23318c2ecf20Sopenharmony_ci of_property_read_u32(endpoint, "ti,am437x-vpfe-interface", 23328c2ecf20Sopenharmony_ci &sdinfo->vpfe_param.if_type); 23338c2ecf20Sopenharmony_ci if (sdinfo->vpfe_param.if_type < 0 || 23348c2ecf20Sopenharmony_ci sdinfo->vpfe_param.if_type > 4) { 23358c2ecf20Sopenharmony_ci sdinfo->vpfe_param.if_type = VPFE_RAW_BAYER; 23368c2ecf20Sopenharmony_ci } 23378c2ecf20Sopenharmony_ci 23388c2ecf20Sopenharmony_ci err = v4l2_fwnode_endpoint_parse(of_fwnode_handle(endpoint), 23398c2ecf20Sopenharmony_ci &bus_cfg); 23408c2ecf20Sopenharmony_ci if (err) { 23418c2ecf20Sopenharmony_ci dev_err(dev, "Could not parse the endpoint\n"); 23428c2ecf20Sopenharmony_ci goto cleanup; 23438c2ecf20Sopenharmony_ci } 23448c2ecf20Sopenharmony_ci 23458c2ecf20Sopenharmony_ci sdinfo->vpfe_param.bus_width = bus_cfg.bus.parallel.bus_width; 23468c2ecf20Sopenharmony_ci 23478c2ecf20Sopenharmony_ci if (sdinfo->vpfe_param.bus_width < 8 || 23488c2ecf20Sopenharmony_ci sdinfo->vpfe_param.bus_width > 16) { 23498c2ecf20Sopenharmony_ci dev_err(dev, "Invalid bus width.\n"); 23508c2ecf20Sopenharmony_ci goto cleanup; 23518c2ecf20Sopenharmony_ci } 23528c2ecf20Sopenharmony_ci 23538c2ecf20Sopenharmony_ci flags = bus_cfg.bus.parallel.flags; 23548c2ecf20Sopenharmony_ci 23558c2ecf20Sopenharmony_ci if (flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) 23568c2ecf20Sopenharmony_ci sdinfo->vpfe_param.hdpol = 1; 23578c2ecf20Sopenharmony_ci 23588c2ecf20Sopenharmony_ci if (flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) 23598c2ecf20Sopenharmony_ci sdinfo->vpfe_param.vdpol = 1; 23608c2ecf20Sopenharmony_ci 23618c2ecf20Sopenharmony_ci rem = of_graph_get_remote_port_parent(endpoint); 23628c2ecf20Sopenharmony_ci if (!rem) { 23638c2ecf20Sopenharmony_ci dev_err(dev, "Remote device at %pOF not found\n", 23648c2ecf20Sopenharmony_ci endpoint); 23658c2ecf20Sopenharmony_ci goto cleanup; 23668c2ecf20Sopenharmony_ci } 23678c2ecf20Sopenharmony_ci 23688c2ecf20Sopenharmony_ci pdata->asd[i] = v4l2_async_notifier_add_fwnode_subdev( 23698c2ecf20Sopenharmony_ci &vpfe->notifier, of_fwnode_handle(rem), 23708c2ecf20Sopenharmony_ci sizeof(struct v4l2_async_subdev)); 23718c2ecf20Sopenharmony_ci of_node_put(rem); 23728c2ecf20Sopenharmony_ci if (IS_ERR(pdata->asd[i])) 23738c2ecf20Sopenharmony_ci goto cleanup; 23748c2ecf20Sopenharmony_ci } 23758c2ecf20Sopenharmony_ci 23768c2ecf20Sopenharmony_ci of_node_put(endpoint); 23778c2ecf20Sopenharmony_ci return pdata; 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_cicleanup: 23808c2ecf20Sopenharmony_ci v4l2_async_notifier_cleanup(&vpfe->notifier); 23818c2ecf20Sopenharmony_ci of_node_put(endpoint); 23828c2ecf20Sopenharmony_ci return NULL; 23838c2ecf20Sopenharmony_ci} 23848c2ecf20Sopenharmony_ci 23858c2ecf20Sopenharmony_ci/* 23868c2ecf20Sopenharmony_ci * vpfe_probe : This function creates device entries by register 23878c2ecf20Sopenharmony_ci * itself to the V4L2 driver and initializes fields of each 23888c2ecf20Sopenharmony_ci * device objects 23898c2ecf20Sopenharmony_ci */ 23908c2ecf20Sopenharmony_cistatic int vpfe_probe(struct platform_device *pdev) 23918c2ecf20Sopenharmony_ci{ 23928c2ecf20Sopenharmony_ci struct vpfe_config *vpfe_cfg; 23938c2ecf20Sopenharmony_ci struct vpfe_device *vpfe; 23948c2ecf20Sopenharmony_ci struct vpfe_ccdc *ccdc; 23958c2ecf20Sopenharmony_ci struct resource *res; 23968c2ecf20Sopenharmony_ci int ret; 23978c2ecf20Sopenharmony_ci 23988c2ecf20Sopenharmony_ci vpfe = devm_kzalloc(&pdev->dev, sizeof(*vpfe), GFP_KERNEL); 23998c2ecf20Sopenharmony_ci if (!vpfe) 24008c2ecf20Sopenharmony_ci return -ENOMEM; 24018c2ecf20Sopenharmony_ci 24028c2ecf20Sopenharmony_ci vpfe->pdev = &pdev->dev; 24038c2ecf20Sopenharmony_ci 24048c2ecf20Sopenharmony_ci vpfe_cfg = vpfe_get_pdata(vpfe); 24058c2ecf20Sopenharmony_ci if (!vpfe_cfg) { 24068c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No platform data\n"); 24078c2ecf20Sopenharmony_ci return -EINVAL; 24088c2ecf20Sopenharmony_ci } 24098c2ecf20Sopenharmony_ci 24108c2ecf20Sopenharmony_ci vpfe->cfg = vpfe_cfg; 24118c2ecf20Sopenharmony_ci ccdc = &vpfe->ccdc; 24128c2ecf20Sopenharmony_ci 24138c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 24148c2ecf20Sopenharmony_ci ccdc->ccdc_cfg.base_addr = devm_ioremap_resource(&pdev->dev, res); 24158c2ecf20Sopenharmony_ci if (IS_ERR(ccdc->ccdc_cfg.base_addr)) { 24168c2ecf20Sopenharmony_ci ret = PTR_ERR(ccdc->ccdc_cfg.base_addr); 24178c2ecf20Sopenharmony_ci goto probe_out_cleanup; 24188c2ecf20Sopenharmony_ci } 24198c2ecf20Sopenharmony_ci 24208c2ecf20Sopenharmony_ci ret = platform_get_irq(pdev, 0); 24218c2ecf20Sopenharmony_ci if (ret <= 0) { 24228c2ecf20Sopenharmony_ci ret = -ENODEV; 24238c2ecf20Sopenharmony_ci goto probe_out_cleanup; 24248c2ecf20Sopenharmony_ci } 24258c2ecf20Sopenharmony_ci vpfe->irq = ret; 24268c2ecf20Sopenharmony_ci 24278c2ecf20Sopenharmony_ci ret = devm_request_irq(vpfe->pdev, vpfe->irq, vpfe_isr, 0, 24288c2ecf20Sopenharmony_ci "vpfe_capture0", vpfe); 24298c2ecf20Sopenharmony_ci if (ret) { 24308c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to request interrupt\n"); 24318c2ecf20Sopenharmony_ci ret = -EINVAL; 24328c2ecf20Sopenharmony_ci goto probe_out_cleanup; 24338c2ecf20Sopenharmony_ci } 24348c2ecf20Sopenharmony_ci 24358c2ecf20Sopenharmony_ci ret = v4l2_device_register(&pdev->dev, &vpfe->v4l2_dev); 24368c2ecf20Sopenharmony_ci if (ret) { 24378c2ecf20Sopenharmony_ci vpfe_err(vpfe, 24388c2ecf20Sopenharmony_ci "Unable to register v4l2 device.\n"); 24398c2ecf20Sopenharmony_ci goto probe_out_cleanup; 24408c2ecf20Sopenharmony_ci } 24418c2ecf20Sopenharmony_ci 24428c2ecf20Sopenharmony_ci /* set the driver data in platform device */ 24438c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, vpfe); 24448c2ecf20Sopenharmony_ci /* Enabling module functional clock */ 24458c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 24468c2ecf20Sopenharmony_ci 24478c2ecf20Sopenharmony_ci /* for now just enable it here instead of waiting for the open */ 24488c2ecf20Sopenharmony_ci ret = pm_runtime_resume_and_get(&pdev->dev); 24498c2ecf20Sopenharmony_ci if (ret < 0) { 24508c2ecf20Sopenharmony_ci vpfe_err(vpfe, "Unable to resume device.\n"); 24518c2ecf20Sopenharmony_ci goto probe_out_v4l2_unregister; 24528c2ecf20Sopenharmony_ci } 24538c2ecf20Sopenharmony_ci 24548c2ecf20Sopenharmony_ci vpfe_ccdc_config_defaults(ccdc); 24558c2ecf20Sopenharmony_ci 24568c2ecf20Sopenharmony_ci pm_runtime_put_sync(&pdev->dev); 24578c2ecf20Sopenharmony_ci 24588c2ecf20Sopenharmony_ci vpfe->sd = devm_kcalloc(&pdev->dev, 24598c2ecf20Sopenharmony_ci ARRAY_SIZE(vpfe->cfg->asd), 24608c2ecf20Sopenharmony_ci sizeof(struct v4l2_subdev *), 24618c2ecf20Sopenharmony_ci GFP_KERNEL); 24628c2ecf20Sopenharmony_ci if (!vpfe->sd) { 24638c2ecf20Sopenharmony_ci ret = -ENOMEM; 24648c2ecf20Sopenharmony_ci goto probe_out_v4l2_unregister; 24658c2ecf20Sopenharmony_ci } 24668c2ecf20Sopenharmony_ci 24678c2ecf20Sopenharmony_ci vpfe->notifier.ops = &vpfe_async_ops; 24688c2ecf20Sopenharmony_ci ret = v4l2_async_notifier_register(&vpfe->v4l2_dev, &vpfe->notifier); 24698c2ecf20Sopenharmony_ci if (ret) { 24708c2ecf20Sopenharmony_ci vpfe_err(vpfe, "Error registering async notifier\n"); 24718c2ecf20Sopenharmony_ci ret = -EINVAL; 24728c2ecf20Sopenharmony_ci goto probe_out_v4l2_unregister; 24738c2ecf20Sopenharmony_ci } 24748c2ecf20Sopenharmony_ci 24758c2ecf20Sopenharmony_ci return 0; 24768c2ecf20Sopenharmony_ci 24778c2ecf20Sopenharmony_ciprobe_out_v4l2_unregister: 24788c2ecf20Sopenharmony_ci v4l2_device_unregister(&vpfe->v4l2_dev); 24798c2ecf20Sopenharmony_ciprobe_out_cleanup: 24808c2ecf20Sopenharmony_ci v4l2_async_notifier_cleanup(&vpfe->notifier); 24818c2ecf20Sopenharmony_ci return ret; 24828c2ecf20Sopenharmony_ci} 24838c2ecf20Sopenharmony_ci 24848c2ecf20Sopenharmony_ci/* 24858c2ecf20Sopenharmony_ci * vpfe_remove : It un-register device from V4L2 driver 24868c2ecf20Sopenharmony_ci */ 24878c2ecf20Sopenharmony_cistatic int vpfe_remove(struct platform_device *pdev) 24888c2ecf20Sopenharmony_ci{ 24898c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = platform_get_drvdata(pdev); 24908c2ecf20Sopenharmony_ci 24918c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 24928c2ecf20Sopenharmony_ci 24938c2ecf20Sopenharmony_ci v4l2_async_notifier_unregister(&vpfe->notifier); 24948c2ecf20Sopenharmony_ci v4l2_async_notifier_cleanup(&vpfe->notifier); 24958c2ecf20Sopenharmony_ci v4l2_device_unregister(&vpfe->v4l2_dev); 24968c2ecf20Sopenharmony_ci video_unregister_device(&vpfe->video_dev); 24978c2ecf20Sopenharmony_ci 24988c2ecf20Sopenharmony_ci return 0; 24998c2ecf20Sopenharmony_ci} 25008c2ecf20Sopenharmony_ci 25018c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 25028c2ecf20Sopenharmony_ci 25038c2ecf20Sopenharmony_cistatic void vpfe_save_context(struct vpfe_ccdc *ccdc) 25048c2ecf20Sopenharmony_ci{ 25058c2ecf20Sopenharmony_ci ccdc->ccdc_ctx[VPFE_PCR >> 2] = vpfe_reg_read(ccdc, VPFE_PCR); 25068c2ecf20Sopenharmony_ci ccdc->ccdc_ctx[VPFE_SYNMODE >> 2] = vpfe_reg_read(ccdc, VPFE_SYNMODE); 25078c2ecf20Sopenharmony_ci ccdc->ccdc_ctx[VPFE_SDOFST >> 2] = vpfe_reg_read(ccdc, VPFE_SDOFST); 25088c2ecf20Sopenharmony_ci ccdc->ccdc_ctx[VPFE_SDR_ADDR >> 2] = vpfe_reg_read(ccdc, VPFE_SDR_ADDR); 25098c2ecf20Sopenharmony_ci ccdc->ccdc_ctx[VPFE_CLAMP >> 2] = vpfe_reg_read(ccdc, VPFE_CLAMP); 25108c2ecf20Sopenharmony_ci ccdc->ccdc_ctx[VPFE_DCSUB >> 2] = vpfe_reg_read(ccdc, VPFE_DCSUB); 25118c2ecf20Sopenharmony_ci ccdc->ccdc_ctx[VPFE_COLPTN >> 2] = vpfe_reg_read(ccdc, VPFE_COLPTN); 25128c2ecf20Sopenharmony_ci ccdc->ccdc_ctx[VPFE_BLKCMP >> 2] = vpfe_reg_read(ccdc, VPFE_BLKCMP); 25138c2ecf20Sopenharmony_ci ccdc->ccdc_ctx[VPFE_VDINT >> 2] = vpfe_reg_read(ccdc, VPFE_VDINT); 25148c2ecf20Sopenharmony_ci ccdc->ccdc_ctx[VPFE_ALAW >> 2] = vpfe_reg_read(ccdc, VPFE_ALAW); 25158c2ecf20Sopenharmony_ci ccdc->ccdc_ctx[VPFE_REC656IF >> 2] = vpfe_reg_read(ccdc, VPFE_REC656IF); 25168c2ecf20Sopenharmony_ci ccdc->ccdc_ctx[VPFE_CCDCFG >> 2] = vpfe_reg_read(ccdc, VPFE_CCDCFG); 25178c2ecf20Sopenharmony_ci ccdc->ccdc_ctx[VPFE_CULLING >> 2] = vpfe_reg_read(ccdc, VPFE_CULLING); 25188c2ecf20Sopenharmony_ci ccdc->ccdc_ctx[VPFE_HD_VD_WID >> 2] = vpfe_reg_read(ccdc, 25198c2ecf20Sopenharmony_ci VPFE_HD_VD_WID); 25208c2ecf20Sopenharmony_ci ccdc->ccdc_ctx[VPFE_PIX_LINES >> 2] = vpfe_reg_read(ccdc, 25218c2ecf20Sopenharmony_ci VPFE_PIX_LINES); 25228c2ecf20Sopenharmony_ci ccdc->ccdc_ctx[VPFE_HORZ_INFO >> 2] = vpfe_reg_read(ccdc, 25238c2ecf20Sopenharmony_ci VPFE_HORZ_INFO); 25248c2ecf20Sopenharmony_ci ccdc->ccdc_ctx[VPFE_VERT_START >> 2] = vpfe_reg_read(ccdc, 25258c2ecf20Sopenharmony_ci VPFE_VERT_START); 25268c2ecf20Sopenharmony_ci ccdc->ccdc_ctx[VPFE_VERT_LINES >> 2] = vpfe_reg_read(ccdc, 25278c2ecf20Sopenharmony_ci VPFE_VERT_LINES); 25288c2ecf20Sopenharmony_ci ccdc->ccdc_ctx[VPFE_HSIZE_OFF >> 2] = vpfe_reg_read(ccdc, 25298c2ecf20Sopenharmony_ci VPFE_HSIZE_OFF); 25308c2ecf20Sopenharmony_ci} 25318c2ecf20Sopenharmony_ci 25328c2ecf20Sopenharmony_cistatic int vpfe_suspend(struct device *dev) 25338c2ecf20Sopenharmony_ci{ 25348c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = dev_get_drvdata(dev); 25358c2ecf20Sopenharmony_ci struct vpfe_ccdc *ccdc = &vpfe->ccdc; 25368c2ecf20Sopenharmony_ci 25378c2ecf20Sopenharmony_ci /* only do full suspend if streaming has started */ 25388c2ecf20Sopenharmony_ci if (vb2_start_streaming_called(&vpfe->buffer_queue)) { 25398c2ecf20Sopenharmony_ci /* 25408c2ecf20Sopenharmony_ci * ignore RPM resume errors here, as it is already too late. 25418c2ecf20Sopenharmony_ci * A check like that should happen earlier, either at 25428c2ecf20Sopenharmony_ci * open() or just before start streaming. 25438c2ecf20Sopenharmony_ci */ 25448c2ecf20Sopenharmony_ci pm_runtime_get_sync(dev); 25458c2ecf20Sopenharmony_ci vpfe_config_enable(ccdc, 1); 25468c2ecf20Sopenharmony_ci 25478c2ecf20Sopenharmony_ci /* Save VPFE context */ 25488c2ecf20Sopenharmony_ci vpfe_save_context(ccdc); 25498c2ecf20Sopenharmony_ci 25508c2ecf20Sopenharmony_ci /* Disable CCDC */ 25518c2ecf20Sopenharmony_ci vpfe_pcr_enable(ccdc, 0); 25528c2ecf20Sopenharmony_ci vpfe_config_enable(ccdc, 0); 25538c2ecf20Sopenharmony_ci 25548c2ecf20Sopenharmony_ci /* Disable both master and slave clock */ 25558c2ecf20Sopenharmony_ci pm_runtime_put_sync(dev); 25568c2ecf20Sopenharmony_ci } 25578c2ecf20Sopenharmony_ci 25588c2ecf20Sopenharmony_ci /* Select sleep pin state */ 25598c2ecf20Sopenharmony_ci pinctrl_pm_select_sleep_state(dev); 25608c2ecf20Sopenharmony_ci 25618c2ecf20Sopenharmony_ci return 0; 25628c2ecf20Sopenharmony_ci} 25638c2ecf20Sopenharmony_ci 25648c2ecf20Sopenharmony_cistatic void vpfe_restore_context(struct vpfe_ccdc *ccdc) 25658c2ecf20Sopenharmony_ci{ 25668c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_SYNMODE >> 2], VPFE_SYNMODE); 25678c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_CULLING >> 2], VPFE_CULLING); 25688c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_SDOFST >> 2], VPFE_SDOFST); 25698c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_SDR_ADDR >> 2], VPFE_SDR_ADDR); 25708c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_CLAMP >> 2], VPFE_CLAMP); 25718c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_DCSUB >> 2], VPFE_DCSUB); 25728c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_COLPTN >> 2], VPFE_COLPTN); 25738c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_BLKCMP >> 2], VPFE_BLKCMP); 25748c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_VDINT >> 2], VPFE_VDINT); 25758c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_ALAW >> 2], VPFE_ALAW); 25768c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_REC656IF >> 2], VPFE_REC656IF); 25778c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_CCDCFG >> 2], VPFE_CCDCFG); 25788c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_PCR >> 2], VPFE_PCR); 25798c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_HD_VD_WID >> 2], 25808c2ecf20Sopenharmony_ci VPFE_HD_VD_WID); 25818c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_PIX_LINES >> 2], 25828c2ecf20Sopenharmony_ci VPFE_PIX_LINES); 25838c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_HORZ_INFO >> 2], 25848c2ecf20Sopenharmony_ci VPFE_HORZ_INFO); 25858c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_VERT_START >> 2], 25868c2ecf20Sopenharmony_ci VPFE_VERT_START); 25878c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_VERT_LINES >> 2], 25888c2ecf20Sopenharmony_ci VPFE_VERT_LINES); 25898c2ecf20Sopenharmony_ci vpfe_reg_write(ccdc, ccdc->ccdc_ctx[VPFE_HSIZE_OFF >> 2], 25908c2ecf20Sopenharmony_ci VPFE_HSIZE_OFF); 25918c2ecf20Sopenharmony_ci} 25928c2ecf20Sopenharmony_ci 25938c2ecf20Sopenharmony_cistatic int vpfe_resume(struct device *dev) 25948c2ecf20Sopenharmony_ci{ 25958c2ecf20Sopenharmony_ci struct vpfe_device *vpfe = dev_get_drvdata(dev); 25968c2ecf20Sopenharmony_ci struct vpfe_ccdc *ccdc = &vpfe->ccdc; 25978c2ecf20Sopenharmony_ci 25988c2ecf20Sopenharmony_ci /* only do full resume if streaming has started */ 25998c2ecf20Sopenharmony_ci if (vb2_start_streaming_called(&vpfe->buffer_queue)) { 26008c2ecf20Sopenharmony_ci /* Enable both master and slave clock */ 26018c2ecf20Sopenharmony_ci pm_runtime_get_sync(dev); 26028c2ecf20Sopenharmony_ci vpfe_config_enable(ccdc, 1); 26038c2ecf20Sopenharmony_ci 26048c2ecf20Sopenharmony_ci /* Restore VPFE context */ 26058c2ecf20Sopenharmony_ci vpfe_restore_context(ccdc); 26068c2ecf20Sopenharmony_ci 26078c2ecf20Sopenharmony_ci vpfe_config_enable(ccdc, 0); 26088c2ecf20Sopenharmony_ci pm_runtime_put_sync(dev); 26098c2ecf20Sopenharmony_ci } 26108c2ecf20Sopenharmony_ci 26118c2ecf20Sopenharmony_ci /* Select default pin state */ 26128c2ecf20Sopenharmony_ci pinctrl_pm_select_default_state(dev); 26138c2ecf20Sopenharmony_ci 26148c2ecf20Sopenharmony_ci return 0; 26158c2ecf20Sopenharmony_ci} 26168c2ecf20Sopenharmony_ci 26178c2ecf20Sopenharmony_ci#endif 26188c2ecf20Sopenharmony_ci 26198c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(vpfe_pm_ops, vpfe_suspend, vpfe_resume); 26208c2ecf20Sopenharmony_ci 26218c2ecf20Sopenharmony_cistatic const struct of_device_id vpfe_of_match[] = { 26228c2ecf20Sopenharmony_ci { .compatible = "ti,am437x-vpfe", }, 26238c2ecf20Sopenharmony_ci { /* sentinel */ }, 26248c2ecf20Sopenharmony_ci}; 26258c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, vpfe_of_match); 26268c2ecf20Sopenharmony_ci 26278c2ecf20Sopenharmony_cistatic struct platform_driver vpfe_driver = { 26288c2ecf20Sopenharmony_ci .probe = vpfe_probe, 26298c2ecf20Sopenharmony_ci .remove = vpfe_remove, 26308c2ecf20Sopenharmony_ci .driver = { 26318c2ecf20Sopenharmony_ci .name = VPFE_MODULE_NAME, 26328c2ecf20Sopenharmony_ci .pm = &vpfe_pm_ops, 26338c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(vpfe_of_match), 26348c2ecf20Sopenharmony_ci }, 26358c2ecf20Sopenharmony_ci}; 26368c2ecf20Sopenharmony_ci 26378c2ecf20Sopenharmony_cimodule_platform_driver(vpfe_driver); 26388c2ecf20Sopenharmony_ci 26398c2ecf20Sopenharmony_ciMODULE_AUTHOR("Texas Instruments"); 26408c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TI AM437x VPFE driver"); 26418c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 26428c2ecf20Sopenharmony_ciMODULE_VERSION(VPFE_VERSION); 2643