18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * TI Camera Access Layer (CAL) - Video Device 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2015-2020 Texas Instruments Inc. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Authors: 88c2ecf20Sopenharmony_ci * Benoit Parrot <bparrot@ti.com> 98c2ecf20Sopenharmony_ci * Laurent Pinchart <laurent.pinchart@ideasonboard.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/delay.h> 138c2ecf20Sopenharmony_ci#include <linux/ioctl.h> 148c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 158c2ecf20Sopenharmony_ci#include <linux/videodev2.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <media/media-device.h> 188c2ecf20Sopenharmony_ci#include <media/v4l2-common.h> 198c2ecf20Sopenharmony_ci#include <media/v4l2-ctrls.h> 208c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 218c2ecf20Sopenharmony_ci#include <media/v4l2-event.h> 228c2ecf20Sopenharmony_ci#include <media/v4l2-fh.h> 238c2ecf20Sopenharmony_ci#include <media/v4l2-ioctl.h> 248c2ecf20Sopenharmony_ci#include <media/videobuf2-core.h> 258c2ecf20Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include "cal.h" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------ 308c2ecf20Sopenharmony_ci * Format Handling 318c2ecf20Sopenharmony_ci * ------------------------------------------------------------------ 328c2ecf20Sopenharmony_ci */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic const struct cal_fmt cal_formats[] = { 358c2ecf20Sopenharmony_ci { 368c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_YUYV, 378c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_YUYV8_2X8, 388c2ecf20Sopenharmony_ci .bpp = 16, 398c2ecf20Sopenharmony_ci }, { 408c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_UYVY, 418c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_UYVY8_2X8, 428c2ecf20Sopenharmony_ci .bpp = 16, 438c2ecf20Sopenharmony_ci }, { 448c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_YVYU, 458c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_YVYU8_2X8, 468c2ecf20Sopenharmony_ci .bpp = 16, 478c2ecf20Sopenharmony_ci }, { 488c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_VYUY, 498c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_VYUY8_2X8, 508c2ecf20Sopenharmony_ci .bpp = 16, 518c2ecf20Sopenharmony_ci }, { 528c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ 538c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_RGB565_2X8_LE, 548c2ecf20Sopenharmony_ci .bpp = 16, 558c2ecf20Sopenharmony_ci }, { 568c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */ 578c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_RGB565_2X8_BE, 588c2ecf20Sopenharmony_ci .bpp = 16, 598c2ecf20Sopenharmony_ci }, { 608c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */ 618c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_LE, 628c2ecf20Sopenharmony_ci .bpp = 16, 638c2ecf20Sopenharmony_ci }, { 648c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ 658c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_RGB555_2X8_PADHI_BE, 668c2ecf20Sopenharmony_ci .bpp = 16, 678c2ecf20Sopenharmony_ci }, { 688c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ 698c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_RGB888_2X12_LE, 708c2ecf20Sopenharmony_ci .bpp = 24, 718c2ecf20Sopenharmony_ci }, { 728c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ 738c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_RGB888_2X12_BE, 748c2ecf20Sopenharmony_ci .bpp = 24, 758c2ecf20Sopenharmony_ci }, { 768c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ 778c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_ARGB8888_1X32, 788c2ecf20Sopenharmony_ci .bpp = 32, 798c2ecf20Sopenharmony_ci }, { 808c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SBGGR8, 818c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_SBGGR8_1X8, 828c2ecf20Sopenharmony_ci .bpp = 8, 838c2ecf20Sopenharmony_ci }, { 848c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SGBRG8, 858c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_SGBRG8_1X8, 868c2ecf20Sopenharmony_ci .bpp = 8, 878c2ecf20Sopenharmony_ci }, { 888c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SGRBG8, 898c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_SGRBG8_1X8, 908c2ecf20Sopenharmony_ci .bpp = 8, 918c2ecf20Sopenharmony_ci }, { 928c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SRGGB8, 938c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_SRGGB8_1X8, 948c2ecf20Sopenharmony_ci .bpp = 8, 958c2ecf20Sopenharmony_ci }, { 968c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SBGGR10, 978c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_SBGGR10_1X10, 988c2ecf20Sopenharmony_ci .bpp = 10, 998c2ecf20Sopenharmony_ci }, { 1008c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SGBRG10, 1018c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_SGBRG10_1X10, 1028c2ecf20Sopenharmony_ci .bpp = 10, 1038c2ecf20Sopenharmony_ci }, { 1048c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SGRBG10, 1058c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_SGRBG10_1X10, 1068c2ecf20Sopenharmony_ci .bpp = 10, 1078c2ecf20Sopenharmony_ci }, { 1088c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SRGGB10, 1098c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_SRGGB10_1X10, 1108c2ecf20Sopenharmony_ci .bpp = 10, 1118c2ecf20Sopenharmony_ci }, { 1128c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SBGGR12, 1138c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_SBGGR12_1X12, 1148c2ecf20Sopenharmony_ci .bpp = 12, 1158c2ecf20Sopenharmony_ci }, { 1168c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SGBRG12, 1178c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_SGBRG12_1X12, 1188c2ecf20Sopenharmony_ci .bpp = 12, 1198c2ecf20Sopenharmony_ci }, { 1208c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SGRBG12, 1218c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_SGRBG12_1X12, 1228c2ecf20Sopenharmony_ci .bpp = 12, 1238c2ecf20Sopenharmony_ci }, { 1248c2ecf20Sopenharmony_ci .fourcc = V4L2_PIX_FMT_SRGGB12, 1258c2ecf20Sopenharmony_ci .code = MEDIA_BUS_FMT_SRGGB12_1X12, 1268c2ecf20Sopenharmony_ci .bpp = 12, 1278c2ecf20Sopenharmony_ci }, 1288c2ecf20Sopenharmony_ci}; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci/* Print Four-character-code (FOURCC) */ 1318c2ecf20Sopenharmony_cistatic char *fourcc_to_str(u32 fmt) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci static char code[5]; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci code[0] = (unsigned char)(fmt & 0xff); 1368c2ecf20Sopenharmony_ci code[1] = (unsigned char)((fmt >> 8) & 0xff); 1378c2ecf20Sopenharmony_ci code[2] = (unsigned char)((fmt >> 16) & 0xff); 1388c2ecf20Sopenharmony_ci code[3] = (unsigned char)((fmt >> 24) & 0xff); 1398c2ecf20Sopenharmony_ci code[4] = '\0'; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci return code; 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------ 1458c2ecf20Sopenharmony_ci * V4L2 Video IOCTLs 1468c2ecf20Sopenharmony_ci * ------------------------------------------------------------------ 1478c2ecf20Sopenharmony_ci */ 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic const struct cal_fmt *find_format_by_pix(struct cal_ctx *ctx, 1508c2ecf20Sopenharmony_ci u32 pixelformat) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci const struct cal_fmt *fmt; 1538c2ecf20Sopenharmony_ci unsigned int k; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci for (k = 0; k < ctx->num_active_fmt; k++) { 1568c2ecf20Sopenharmony_ci fmt = ctx->active_fmt[k]; 1578c2ecf20Sopenharmony_ci if (fmt->fourcc == pixelformat) 1588c2ecf20Sopenharmony_ci return fmt; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci return NULL; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic const struct cal_fmt *find_format_by_code(struct cal_ctx *ctx, 1658c2ecf20Sopenharmony_ci u32 code) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci const struct cal_fmt *fmt; 1688c2ecf20Sopenharmony_ci unsigned int k; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci for (k = 0; k < ctx->num_active_fmt; k++) { 1718c2ecf20Sopenharmony_ci fmt = ctx->active_fmt[k]; 1728c2ecf20Sopenharmony_ci if (fmt->code == code) 1738c2ecf20Sopenharmony_ci return fmt; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci return NULL; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic int cal_querycap(struct file *file, void *priv, 1808c2ecf20Sopenharmony_ci struct v4l2_capability *cap) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci struct cal_ctx *ctx = video_drvdata(file); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci strscpy(cap->driver, CAL_MODULE_NAME, sizeof(cap->driver)); 1858c2ecf20Sopenharmony_ci strscpy(cap->card, CAL_MODULE_NAME, sizeof(cap->card)); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci snprintf(cap->bus_info, sizeof(cap->bus_info), 1888c2ecf20Sopenharmony_ci "platform:%s", dev_name(ctx->cal->dev)); 1898c2ecf20Sopenharmony_ci return 0; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic int cal_enum_fmt_vid_cap(struct file *file, void *priv, 1938c2ecf20Sopenharmony_ci struct v4l2_fmtdesc *f) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci struct cal_ctx *ctx = video_drvdata(file); 1968c2ecf20Sopenharmony_ci const struct cal_fmt *fmt; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (f->index >= ctx->num_active_fmt) 1998c2ecf20Sopenharmony_ci return -EINVAL; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci fmt = ctx->active_fmt[f->index]; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci f->pixelformat = fmt->fourcc; 2048c2ecf20Sopenharmony_ci f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 2058c2ecf20Sopenharmony_ci return 0; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic int __subdev_get_format(struct cal_ctx *ctx, 2098c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *fmt) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct v4l2_subdev_format sd_fmt; 2128c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; 2138c2ecf20Sopenharmony_ci int ret; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; 2168c2ecf20Sopenharmony_ci sd_fmt.pad = 0; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(ctx->phy->sensor, pad, get_fmt, NULL, &sd_fmt); 2198c2ecf20Sopenharmony_ci if (ret) 2208c2ecf20Sopenharmony_ci return ret; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci *fmt = *mbus_fmt; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__, 2258c2ecf20Sopenharmony_ci fmt->width, fmt->height, fmt->code); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci return 0; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cistatic int __subdev_set_format(struct cal_ctx *ctx, 2318c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *fmt) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci struct v4l2_subdev_format sd_fmt; 2348c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt *mbus_fmt = &sd_fmt.format; 2358c2ecf20Sopenharmony_ci int ret; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci sd_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; 2388c2ecf20Sopenharmony_ci sd_fmt.pad = 0; 2398c2ecf20Sopenharmony_ci *mbus_fmt = *fmt; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(ctx->phy->sensor, pad, set_fmt, NULL, &sd_fmt); 2428c2ecf20Sopenharmony_ci if (ret) 2438c2ecf20Sopenharmony_ci return ret; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci ctx_dbg(1, ctx, "%s %dx%d code:%04X\n", __func__, 2468c2ecf20Sopenharmony_ci fmt->width, fmt->height, fmt->code); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci return 0; 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic int cal_calc_format_size(struct cal_ctx *ctx, 2528c2ecf20Sopenharmony_ci const struct cal_fmt *fmt, 2538c2ecf20Sopenharmony_ci struct v4l2_format *f) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci u32 bpl, max_width; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (!fmt) { 2588c2ecf20Sopenharmony_ci ctx_dbg(3, ctx, "No cal_fmt provided!\n"); 2598c2ecf20Sopenharmony_ci return -EINVAL; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci /* 2638c2ecf20Sopenharmony_ci * Maximum width is bound by the DMA max width in bytes. 2648c2ecf20Sopenharmony_ci * We need to recalculate the actual maxi width depending on the 2658c2ecf20Sopenharmony_ci * number of bytes per pixels required. 2668c2ecf20Sopenharmony_ci */ 2678c2ecf20Sopenharmony_ci max_width = MAX_WIDTH_BYTES / (ALIGN(fmt->bpp, 8) >> 3); 2688c2ecf20Sopenharmony_ci v4l_bound_align_image(&f->fmt.pix.width, 48, max_width, 2, 2698c2ecf20Sopenharmony_ci &f->fmt.pix.height, 32, MAX_HEIGHT_LINES, 0, 0); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci bpl = (f->fmt.pix.width * ALIGN(fmt->bpp, 8)) >> 3; 2728c2ecf20Sopenharmony_ci f->fmt.pix.bytesperline = ALIGN(bpl, 16); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci f->fmt.pix.sizeimage = f->fmt.pix.height * 2758c2ecf20Sopenharmony_ci f->fmt.pix.bytesperline; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci ctx_dbg(3, ctx, "%s: fourcc: %s size: %dx%d bpl:%d img_size:%d\n", 2788c2ecf20Sopenharmony_ci __func__, fourcc_to_str(f->fmt.pix.pixelformat), 2798c2ecf20Sopenharmony_ci f->fmt.pix.width, f->fmt.pix.height, 2808c2ecf20Sopenharmony_ci f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci return 0; 2838c2ecf20Sopenharmony_ci} 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cistatic int cal_g_fmt_vid_cap(struct file *file, void *priv, 2868c2ecf20Sopenharmony_ci struct v4l2_format *f) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct cal_ctx *ctx = video_drvdata(file); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci *f = ctx->v_fmt; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci return 0; 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic int cal_try_fmt_vid_cap(struct file *file, void *priv, 2968c2ecf20Sopenharmony_ci struct v4l2_format *f) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci struct cal_ctx *ctx = video_drvdata(file); 2998c2ecf20Sopenharmony_ci const struct cal_fmt *fmt; 3008c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_size_enum fse; 3018c2ecf20Sopenharmony_ci int ret, found; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat); 3048c2ecf20Sopenharmony_ci if (!fmt) { 3058c2ecf20Sopenharmony_ci ctx_dbg(3, ctx, "Fourcc format (0x%08x) not found.\n", 3068c2ecf20Sopenharmony_ci f->fmt.pix.pixelformat); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci /* Just get the first one enumerated */ 3098c2ecf20Sopenharmony_ci fmt = ctx->active_fmt[0]; 3108c2ecf20Sopenharmony_ci f->fmt.pix.pixelformat = fmt->fourcc; 3118c2ecf20Sopenharmony_ci } 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci f->fmt.pix.field = ctx->v_fmt.fmt.pix.field; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* check for/find a valid width/height */ 3168c2ecf20Sopenharmony_ci ret = 0; 3178c2ecf20Sopenharmony_ci found = false; 3188c2ecf20Sopenharmony_ci fse.pad = 0; 3198c2ecf20Sopenharmony_ci fse.code = fmt->code; 3208c2ecf20Sopenharmony_ci fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; 3218c2ecf20Sopenharmony_ci for (fse.index = 0; ; fse.index++) { 3228c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size, 3238c2ecf20Sopenharmony_ci NULL, &fse); 3248c2ecf20Sopenharmony_ci if (ret) 3258c2ecf20Sopenharmony_ci break; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if ((f->fmt.pix.width == fse.max_width) && 3288c2ecf20Sopenharmony_ci (f->fmt.pix.height == fse.max_height)) { 3298c2ecf20Sopenharmony_ci found = true; 3308c2ecf20Sopenharmony_ci break; 3318c2ecf20Sopenharmony_ci } else if ((f->fmt.pix.width >= fse.min_width) && 3328c2ecf20Sopenharmony_ci (f->fmt.pix.width <= fse.max_width) && 3338c2ecf20Sopenharmony_ci (f->fmt.pix.height >= fse.min_height) && 3348c2ecf20Sopenharmony_ci (f->fmt.pix.height <= fse.max_height)) { 3358c2ecf20Sopenharmony_ci found = true; 3368c2ecf20Sopenharmony_ci break; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (!found) { 3418c2ecf20Sopenharmony_ci /* use existing values as default */ 3428c2ecf20Sopenharmony_ci f->fmt.pix.width = ctx->v_fmt.fmt.pix.width; 3438c2ecf20Sopenharmony_ci f->fmt.pix.height = ctx->v_fmt.fmt.pix.height; 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci /* 3478c2ecf20Sopenharmony_ci * Use current colorspace for now, it will get 3488c2ecf20Sopenharmony_ci * updated properly during s_fmt 3498c2ecf20Sopenharmony_ci */ 3508c2ecf20Sopenharmony_ci f->fmt.pix.colorspace = ctx->v_fmt.fmt.pix.colorspace; 3518c2ecf20Sopenharmony_ci return cal_calc_format_size(ctx, fmt, f); 3528c2ecf20Sopenharmony_ci} 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic int cal_s_fmt_vid_cap(struct file *file, void *priv, 3558c2ecf20Sopenharmony_ci struct v4l2_format *f) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci struct cal_ctx *ctx = video_drvdata(file); 3588c2ecf20Sopenharmony_ci struct vb2_queue *q = &ctx->vb_vidq; 3598c2ecf20Sopenharmony_ci const struct cal_fmt *fmt; 3608c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt mbus_fmt; 3618c2ecf20Sopenharmony_ci int ret; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci if (vb2_is_busy(q)) { 3648c2ecf20Sopenharmony_ci ctx_dbg(3, ctx, "%s device busy\n", __func__); 3658c2ecf20Sopenharmony_ci return -EBUSY; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci ret = cal_try_fmt_vid_cap(file, priv, f); 3698c2ecf20Sopenharmony_ci if (ret < 0) 3708c2ecf20Sopenharmony_ci return ret; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci fmt = find_format_by_pix(ctx, f->fmt.pix.pixelformat); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci v4l2_fill_mbus_format(&mbus_fmt, &f->fmt.pix, fmt->code); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci ret = __subdev_set_format(ctx, &mbus_fmt); 3778c2ecf20Sopenharmony_ci if (ret) 3788c2ecf20Sopenharmony_ci return ret; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* Just double check nothing has gone wrong */ 3818c2ecf20Sopenharmony_ci if (mbus_fmt.code != fmt->code) { 3828c2ecf20Sopenharmony_ci ctx_dbg(3, ctx, 3838c2ecf20Sopenharmony_ci "%s subdev changed format on us, this should not happen\n", 3848c2ecf20Sopenharmony_ci __func__); 3858c2ecf20Sopenharmony_ci return -EINVAL; 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); 3898c2ecf20Sopenharmony_ci ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 3908c2ecf20Sopenharmony_ci ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; 3918c2ecf20Sopenharmony_ci cal_calc_format_size(ctx, fmt, &ctx->v_fmt); 3928c2ecf20Sopenharmony_ci ctx->fmt = fmt; 3938c2ecf20Sopenharmony_ci ctx->m_fmt = mbus_fmt; 3948c2ecf20Sopenharmony_ci *f = ctx->v_fmt; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci return 0; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic int cal_enum_framesizes(struct file *file, void *fh, 4008c2ecf20Sopenharmony_ci struct v4l2_frmsizeenum *fsize) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci struct cal_ctx *ctx = video_drvdata(file); 4038c2ecf20Sopenharmony_ci const struct cal_fmt *fmt; 4048c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_size_enum fse; 4058c2ecf20Sopenharmony_ci int ret; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* check for valid format */ 4088c2ecf20Sopenharmony_ci fmt = find_format_by_pix(ctx, fsize->pixel_format); 4098c2ecf20Sopenharmony_ci if (!fmt) { 4108c2ecf20Sopenharmony_ci ctx_dbg(3, ctx, "Invalid pixel code: %x\n", 4118c2ecf20Sopenharmony_ci fsize->pixel_format); 4128c2ecf20Sopenharmony_ci return -EINVAL; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci fse.index = fsize->index; 4168c2ecf20Sopenharmony_ci fse.pad = 0; 4178c2ecf20Sopenharmony_ci fse.code = fmt->code; 4188c2ecf20Sopenharmony_ci fse.which = V4L2_SUBDEV_FORMAT_ACTIVE; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_size, NULL, 4218c2ecf20Sopenharmony_ci &fse); 4228c2ecf20Sopenharmony_ci if (ret) 4238c2ecf20Sopenharmony_ci return ret; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci ctx_dbg(1, ctx, "%s: index: %d code: %x W:[%d,%d] H:[%d,%d]\n", 4268c2ecf20Sopenharmony_ci __func__, fse.index, fse.code, fse.min_width, fse.max_width, 4278c2ecf20Sopenharmony_ci fse.min_height, fse.max_height); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; 4308c2ecf20Sopenharmony_ci fsize->discrete.width = fse.max_width; 4318c2ecf20Sopenharmony_ci fsize->discrete.height = fse.max_height; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci return 0; 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic int cal_enum_input(struct file *file, void *priv, 4378c2ecf20Sopenharmony_ci struct v4l2_input *inp) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci if (inp->index > 0) 4408c2ecf20Sopenharmony_ci return -EINVAL; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci inp->type = V4L2_INPUT_TYPE_CAMERA; 4438c2ecf20Sopenharmony_ci sprintf(inp->name, "Camera %u", inp->index); 4448c2ecf20Sopenharmony_ci return 0; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cistatic int cal_g_input(struct file *file, void *priv, unsigned int *i) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci *i = 0; 4508c2ecf20Sopenharmony_ci return 0; 4518c2ecf20Sopenharmony_ci} 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cistatic int cal_s_input(struct file *file, void *priv, unsigned int i) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci return i > 0 ? -EINVAL : 0; 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci/* timeperframe is arbitrary and continuous */ 4598c2ecf20Sopenharmony_cistatic int cal_enum_frameintervals(struct file *file, void *priv, 4608c2ecf20Sopenharmony_ci struct v4l2_frmivalenum *fival) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci struct cal_ctx *ctx = video_drvdata(file); 4638c2ecf20Sopenharmony_ci const struct cal_fmt *fmt; 4648c2ecf20Sopenharmony_ci struct v4l2_subdev_frame_interval_enum fie = { 4658c2ecf20Sopenharmony_ci .index = fival->index, 4668c2ecf20Sopenharmony_ci .width = fival->width, 4678c2ecf20Sopenharmony_ci .height = fival->height, 4688c2ecf20Sopenharmony_ci .which = V4L2_SUBDEV_FORMAT_ACTIVE, 4698c2ecf20Sopenharmony_ci }; 4708c2ecf20Sopenharmony_ci int ret; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci fmt = find_format_by_pix(ctx, fival->pixel_format); 4738c2ecf20Sopenharmony_ci if (!fmt) 4748c2ecf20Sopenharmony_ci return -EINVAL; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci fie.code = fmt->code; 4778c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_frame_interval, 4788c2ecf20Sopenharmony_ci NULL, &fie); 4798c2ecf20Sopenharmony_ci if (ret) 4808c2ecf20Sopenharmony_ci return ret; 4818c2ecf20Sopenharmony_ci fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; 4828c2ecf20Sopenharmony_ci fival->discrete = fie.interval; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci return 0; 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic const struct v4l2_file_operations cal_fops = { 4888c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 4898c2ecf20Sopenharmony_ci .open = v4l2_fh_open, 4908c2ecf20Sopenharmony_ci .release = vb2_fop_release, 4918c2ecf20Sopenharmony_ci .read = vb2_fop_read, 4928c2ecf20Sopenharmony_ci .poll = vb2_fop_poll, 4938c2ecf20Sopenharmony_ci .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ 4948c2ecf20Sopenharmony_ci .mmap = vb2_fop_mmap, 4958c2ecf20Sopenharmony_ci}; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_cistatic const struct v4l2_ioctl_ops cal_ioctl_ops = { 4988c2ecf20Sopenharmony_ci .vidioc_querycap = cal_querycap, 4998c2ecf20Sopenharmony_ci .vidioc_enum_fmt_vid_cap = cal_enum_fmt_vid_cap, 5008c2ecf20Sopenharmony_ci .vidioc_g_fmt_vid_cap = cal_g_fmt_vid_cap, 5018c2ecf20Sopenharmony_ci .vidioc_try_fmt_vid_cap = cal_try_fmt_vid_cap, 5028c2ecf20Sopenharmony_ci .vidioc_s_fmt_vid_cap = cal_s_fmt_vid_cap, 5038c2ecf20Sopenharmony_ci .vidioc_enum_framesizes = cal_enum_framesizes, 5048c2ecf20Sopenharmony_ci .vidioc_reqbufs = vb2_ioctl_reqbufs, 5058c2ecf20Sopenharmony_ci .vidioc_create_bufs = vb2_ioctl_create_bufs, 5068c2ecf20Sopenharmony_ci .vidioc_prepare_buf = vb2_ioctl_prepare_buf, 5078c2ecf20Sopenharmony_ci .vidioc_querybuf = vb2_ioctl_querybuf, 5088c2ecf20Sopenharmony_ci .vidioc_qbuf = vb2_ioctl_qbuf, 5098c2ecf20Sopenharmony_ci .vidioc_dqbuf = vb2_ioctl_dqbuf, 5108c2ecf20Sopenharmony_ci .vidioc_expbuf = vb2_ioctl_expbuf, 5118c2ecf20Sopenharmony_ci .vidioc_enum_input = cal_enum_input, 5128c2ecf20Sopenharmony_ci .vidioc_g_input = cal_g_input, 5138c2ecf20Sopenharmony_ci .vidioc_s_input = cal_s_input, 5148c2ecf20Sopenharmony_ci .vidioc_enum_frameintervals = cal_enum_frameintervals, 5158c2ecf20Sopenharmony_ci .vidioc_streamon = vb2_ioctl_streamon, 5168c2ecf20Sopenharmony_ci .vidioc_streamoff = vb2_ioctl_streamoff, 5178c2ecf20Sopenharmony_ci .vidioc_log_status = v4l2_ctrl_log_status, 5188c2ecf20Sopenharmony_ci .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 5198c2ecf20Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 5208c2ecf20Sopenharmony_ci}; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------ 5238c2ecf20Sopenharmony_ci * videobuf2 Operations 5248c2ecf20Sopenharmony_ci * ------------------------------------------------------------------ 5258c2ecf20Sopenharmony_ci */ 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic int cal_queue_setup(struct vb2_queue *vq, 5288c2ecf20Sopenharmony_ci unsigned int *nbuffers, unsigned int *nplanes, 5298c2ecf20Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 5308c2ecf20Sopenharmony_ci{ 5318c2ecf20Sopenharmony_ci struct cal_ctx *ctx = vb2_get_drv_priv(vq); 5328c2ecf20Sopenharmony_ci unsigned int size = ctx->v_fmt.fmt.pix.sizeimage; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci if (vq->num_buffers + *nbuffers < 3) 5358c2ecf20Sopenharmony_ci *nbuffers = 3 - vq->num_buffers; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci if (*nplanes) { 5388c2ecf20Sopenharmony_ci if (sizes[0] < size) 5398c2ecf20Sopenharmony_ci return -EINVAL; 5408c2ecf20Sopenharmony_ci size = sizes[0]; 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci *nplanes = 1; 5448c2ecf20Sopenharmony_ci sizes[0] = size; 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci ctx_dbg(3, ctx, "nbuffers=%d, size=%d\n", *nbuffers, sizes[0]); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci return 0; 5498c2ecf20Sopenharmony_ci} 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_cistatic int cal_buffer_prepare(struct vb2_buffer *vb) 5528c2ecf20Sopenharmony_ci{ 5538c2ecf20Sopenharmony_ci struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 5548c2ecf20Sopenharmony_ci struct cal_buffer *buf = container_of(vb, struct cal_buffer, 5558c2ecf20Sopenharmony_ci vb.vb2_buf); 5568c2ecf20Sopenharmony_ci unsigned long size; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci if (WARN_ON(!ctx->fmt)) 5598c2ecf20Sopenharmony_ci return -EINVAL; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci size = ctx->v_fmt.fmt.pix.sizeimage; 5628c2ecf20Sopenharmony_ci if (vb2_plane_size(vb, 0) < size) { 5638c2ecf20Sopenharmony_ci ctx_err(ctx, 5648c2ecf20Sopenharmony_ci "data will not fit into plane (%lu < %lu)\n", 5658c2ecf20Sopenharmony_ci vb2_plane_size(vb, 0), size); 5668c2ecf20Sopenharmony_ci return -EINVAL; 5678c2ecf20Sopenharmony_ci } 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci vb2_set_plane_payload(&buf->vb.vb2_buf, 0, size); 5708c2ecf20Sopenharmony_ci return 0; 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_cistatic void cal_buffer_queue(struct vb2_buffer *vb) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci struct cal_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 5768c2ecf20Sopenharmony_ci struct cal_buffer *buf = container_of(vb, struct cal_buffer, 5778c2ecf20Sopenharmony_ci vb.vb2_buf); 5788c2ecf20Sopenharmony_ci struct cal_dmaqueue *vidq = &ctx->vidq; 5798c2ecf20Sopenharmony_ci unsigned long flags; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci /* recheck locking */ 5828c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctx->slock, flags); 5838c2ecf20Sopenharmony_ci list_add_tail(&buf->list, &vidq->active); 5848c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctx->slock, flags); 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_cistatic int cal_start_streaming(struct vb2_queue *vq, unsigned int count) 5888c2ecf20Sopenharmony_ci{ 5898c2ecf20Sopenharmony_ci struct cal_ctx *ctx = vb2_get_drv_priv(vq); 5908c2ecf20Sopenharmony_ci struct cal_dmaqueue *dma_q = &ctx->vidq; 5918c2ecf20Sopenharmony_ci struct cal_buffer *buf, *tmp; 5928c2ecf20Sopenharmony_ci unsigned long addr; 5938c2ecf20Sopenharmony_ci unsigned long flags; 5948c2ecf20Sopenharmony_ci int ret; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctx->slock, flags); 5978c2ecf20Sopenharmony_ci if (list_empty(&dma_q->active)) { 5988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctx->slock, flags); 5998c2ecf20Sopenharmony_ci ctx_dbg(3, ctx, "buffer queue is empty\n"); 6008c2ecf20Sopenharmony_ci return -EIO; 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci buf = list_entry(dma_q->active.next, struct cal_buffer, list); 6048c2ecf20Sopenharmony_ci ctx->cur_frm = buf; 6058c2ecf20Sopenharmony_ci ctx->next_frm = buf; 6068c2ecf20Sopenharmony_ci list_del(&buf->list); 6078c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctx->slock, flags); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci addr = vb2_dma_contig_plane_dma_addr(&ctx->cur_frm->vb.vb2_buf, 0); 6108c2ecf20Sopenharmony_ci ctx->sequence = 0; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci pm_runtime_get_sync(ctx->cal->dev); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci cal_ctx_csi2_config(ctx); 6158c2ecf20Sopenharmony_ci cal_ctx_pix_proc_config(ctx); 6168c2ecf20Sopenharmony_ci cal_ctx_wr_dma_config(ctx, ctx->v_fmt.fmt.pix.bytesperline, 6178c2ecf20Sopenharmony_ci ctx->v_fmt.fmt.pix.height); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci cal_camerarx_enable_irqs(ctx->phy); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci ret = cal_camerarx_start(ctx->phy, ctx->fmt); 6228c2ecf20Sopenharmony_ci if (ret) 6238c2ecf20Sopenharmony_ci goto err; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci cal_ctx_wr_dma_addr(ctx, addr); 6268c2ecf20Sopenharmony_ci cal_camerarx_ppi_enable(ctx->phy); 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci if (cal_debug >= 4) 6298c2ecf20Sopenharmony_ci cal_quickdump_regs(ctx->cal); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci return 0; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cierr: 6348c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctx->slock, flags); 6358c2ecf20Sopenharmony_ci vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_QUEUED); 6368c2ecf20Sopenharmony_ci ctx->cur_frm = NULL; 6378c2ecf20Sopenharmony_ci ctx->next_frm = NULL; 6388c2ecf20Sopenharmony_ci list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { 6398c2ecf20Sopenharmony_ci list_del(&buf->list); 6408c2ecf20Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_QUEUED); 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctx->slock, flags); 6438c2ecf20Sopenharmony_ci return ret; 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_cistatic void cal_stop_streaming(struct vb2_queue *vq) 6478c2ecf20Sopenharmony_ci{ 6488c2ecf20Sopenharmony_ci struct cal_ctx *ctx = vb2_get_drv_priv(vq); 6498c2ecf20Sopenharmony_ci struct cal_dmaqueue *dma_q = &ctx->vidq; 6508c2ecf20Sopenharmony_ci struct cal_buffer *buf, *tmp; 6518c2ecf20Sopenharmony_ci unsigned long timeout; 6528c2ecf20Sopenharmony_ci unsigned long flags; 6538c2ecf20Sopenharmony_ci bool dma_act; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci cal_camerarx_ppi_disable(ctx->phy); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci /* wait for stream and dma to finish */ 6588c2ecf20Sopenharmony_ci dma_act = true; 6598c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(500); 6608c2ecf20Sopenharmony_ci while (dma_act && time_before(jiffies, timeout)) { 6618c2ecf20Sopenharmony_ci msleep(50); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctx->slock, flags); 6648c2ecf20Sopenharmony_ci dma_act = ctx->dma_act; 6658c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctx->slock, flags); 6668c2ecf20Sopenharmony_ci } 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci if (dma_act) 6698c2ecf20Sopenharmony_ci ctx_err(ctx, "failed to disable dma cleanly\n"); 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci cal_camerarx_disable_irqs(ctx->phy); 6728c2ecf20Sopenharmony_ci cal_camerarx_stop(ctx->phy); 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci /* Release all active buffers */ 6758c2ecf20Sopenharmony_ci spin_lock_irqsave(&ctx->slock, flags); 6768c2ecf20Sopenharmony_ci list_for_each_entry_safe(buf, tmp, &dma_q->active, list) { 6778c2ecf20Sopenharmony_ci list_del(&buf->list); 6788c2ecf20Sopenharmony_ci vb2_buffer_done(&buf->vb.vb2_buf, VB2_BUF_STATE_ERROR); 6798c2ecf20Sopenharmony_ci } 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci if (ctx->cur_frm == ctx->next_frm) { 6828c2ecf20Sopenharmony_ci vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); 6838c2ecf20Sopenharmony_ci } else { 6848c2ecf20Sopenharmony_ci vb2_buffer_done(&ctx->cur_frm->vb.vb2_buf, VB2_BUF_STATE_ERROR); 6858c2ecf20Sopenharmony_ci vb2_buffer_done(&ctx->next_frm->vb.vb2_buf, 6868c2ecf20Sopenharmony_ci VB2_BUF_STATE_ERROR); 6878c2ecf20Sopenharmony_ci } 6888c2ecf20Sopenharmony_ci ctx->cur_frm = NULL; 6898c2ecf20Sopenharmony_ci ctx->next_frm = NULL; 6908c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ctx->slock, flags); 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci pm_runtime_put_sync(ctx->cal->dev); 6938c2ecf20Sopenharmony_ci} 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_cistatic const struct vb2_ops cal_video_qops = { 6968c2ecf20Sopenharmony_ci .queue_setup = cal_queue_setup, 6978c2ecf20Sopenharmony_ci .buf_prepare = cal_buffer_prepare, 6988c2ecf20Sopenharmony_ci .buf_queue = cal_buffer_queue, 6998c2ecf20Sopenharmony_ci .start_streaming = cal_start_streaming, 7008c2ecf20Sopenharmony_ci .stop_streaming = cal_stop_streaming, 7018c2ecf20Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 7028c2ecf20Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 7038c2ecf20Sopenharmony_ci}; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------ 7068c2ecf20Sopenharmony_ci * V4L2 Initialization and Registration 7078c2ecf20Sopenharmony_ci * ------------------------------------------------------------------ 7088c2ecf20Sopenharmony_ci */ 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_cistatic const struct video_device cal_videodev = { 7118c2ecf20Sopenharmony_ci .name = CAL_MODULE_NAME, 7128c2ecf20Sopenharmony_ci .fops = &cal_fops, 7138c2ecf20Sopenharmony_ci .ioctl_ops = &cal_ioctl_ops, 7148c2ecf20Sopenharmony_ci .minor = -1, 7158c2ecf20Sopenharmony_ci .release = video_device_release_empty, 7168c2ecf20Sopenharmony_ci .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | 7178c2ecf20Sopenharmony_ci V4L2_CAP_READWRITE, 7188c2ecf20Sopenharmony_ci}; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_cistatic int cal_ctx_v4l2_init_formats(struct cal_ctx *ctx) 7218c2ecf20Sopenharmony_ci{ 7228c2ecf20Sopenharmony_ci struct v4l2_subdev_mbus_code_enum mbus_code; 7238c2ecf20Sopenharmony_ci struct v4l2_mbus_framefmt mbus_fmt; 7248c2ecf20Sopenharmony_ci const struct cal_fmt *fmt; 7258c2ecf20Sopenharmony_ci unsigned int i, j, k; 7268c2ecf20Sopenharmony_ci int ret = 0; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci /* Enumerate sub device formats and enable all matching local formats */ 7298c2ecf20Sopenharmony_ci ctx->active_fmt = devm_kcalloc(ctx->cal->dev, ARRAY_SIZE(cal_formats), 7308c2ecf20Sopenharmony_ci sizeof(*ctx->active_fmt), GFP_KERNEL); 7318c2ecf20Sopenharmony_ci ctx->num_active_fmt = 0; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci for (j = 0, i = 0; ret != -EINVAL; ++j) { 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci memset(&mbus_code, 0, sizeof(mbus_code)); 7368c2ecf20Sopenharmony_ci mbus_code.index = j; 7378c2ecf20Sopenharmony_ci mbus_code.which = V4L2_SUBDEV_FORMAT_ACTIVE; 7388c2ecf20Sopenharmony_ci ret = v4l2_subdev_call(ctx->phy->sensor, pad, enum_mbus_code, 7398c2ecf20Sopenharmony_ci NULL, &mbus_code); 7408c2ecf20Sopenharmony_ci if (ret) 7418c2ecf20Sopenharmony_ci continue; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci ctx_dbg(2, ctx, 7448c2ecf20Sopenharmony_ci "subdev %s: code: %04x idx: %u\n", 7458c2ecf20Sopenharmony_ci ctx->phy->sensor->name, mbus_code.code, j); 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci for (k = 0; k < ARRAY_SIZE(cal_formats); k++) { 7488c2ecf20Sopenharmony_ci const struct cal_fmt *fmt = &cal_formats[k]; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci if (mbus_code.code == fmt->code) { 7518c2ecf20Sopenharmony_ci ctx->active_fmt[i] = fmt; 7528c2ecf20Sopenharmony_ci ctx_dbg(2, ctx, 7538c2ecf20Sopenharmony_ci "matched fourcc: %s: code: %04x idx: %u\n", 7548c2ecf20Sopenharmony_ci fourcc_to_str(fmt->fourcc), 7558c2ecf20Sopenharmony_ci fmt->code, i); 7568c2ecf20Sopenharmony_ci ctx->num_active_fmt = ++i; 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci } 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci if (i == 0) { 7628c2ecf20Sopenharmony_ci ctx_err(ctx, "No suitable format reported by subdev %s\n", 7638c2ecf20Sopenharmony_ci ctx->phy->sensor->name); 7648c2ecf20Sopenharmony_ci return -EINVAL; 7658c2ecf20Sopenharmony_ci } 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci ret = __subdev_get_format(ctx, &mbus_fmt); 7688c2ecf20Sopenharmony_ci if (ret) 7698c2ecf20Sopenharmony_ci return ret; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci fmt = find_format_by_code(ctx, mbus_fmt.code); 7728c2ecf20Sopenharmony_ci if (!fmt) { 7738c2ecf20Sopenharmony_ci ctx_dbg(3, ctx, "mbus code format (0x%08x) not found.\n", 7748c2ecf20Sopenharmony_ci mbus_fmt.code); 7758c2ecf20Sopenharmony_ci return -EINVAL; 7768c2ecf20Sopenharmony_ci } 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci /* Save current subdev format */ 7798c2ecf20Sopenharmony_ci v4l2_fill_pix_format(&ctx->v_fmt.fmt.pix, &mbus_fmt); 7808c2ecf20Sopenharmony_ci ctx->v_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 7818c2ecf20Sopenharmony_ci ctx->v_fmt.fmt.pix.pixelformat = fmt->fourcc; 7828c2ecf20Sopenharmony_ci cal_calc_format_size(ctx, fmt, &ctx->v_fmt); 7838c2ecf20Sopenharmony_ci ctx->fmt = fmt; 7848c2ecf20Sopenharmony_ci ctx->m_fmt = mbus_fmt; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci return 0; 7878c2ecf20Sopenharmony_ci} 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ciint cal_ctx_v4l2_register(struct cal_ctx *ctx) 7908c2ecf20Sopenharmony_ci{ 7918c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; 7928c2ecf20Sopenharmony_ci struct video_device *vfd = &ctx->vdev; 7938c2ecf20Sopenharmony_ci int ret; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci ret = cal_ctx_v4l2_init_formats(ctx); 7968c2ecf20Sopenharmony_ci if (ret) 7978c2ecf20Sopenharmony_ci return ret; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci ret = v4l2_ctrl_add_handler(hdl, ctx->phy->sensor->ctrl_handler, NULL, 8008c2ecf20Sopenharmony_ci true); 8018c2ecf20Sopenharmony_ci if (ret < 0) { 8028c2ecf20Sopenharmony_ci ctx_err(ctx, "Failed to add sensor ctrl handler\n"); 8038c2ecf20Sopenharmony_ci return ret; 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci ret = video_register_device(vfd, VFL_TYPE_VIDEO, cal_video_nr); 8078c2ecf20Sopenharmony_ci if (ret < 0) { 8088c2ecf20Sopenharmony_ci ctx_err(ctx, "Failed to register video device\n"); 8098c2ecf20Sopenharmony_ci return ret; 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci ctx_info(ctx, "V4L2 device registered as %s\n", 8138c2ecf20Sopenharmony_ci video_device_node_name(vfd)); 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci return 0; 8168c2ecf20Sopenharmony_ci} 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_civoid cal_ctx_v4l2_unregister(struct cal_ctx *ctx) 8198c2ecf20Sopenharmony_ci{ 8208c2ecf20Sopenharmony_ci ctx_dbg(1, ctx, "unregistering %s\n", 8218c2ecf20Sopenharmony_ci video_device_node_name(&ctx->vdev)); 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci video_unregister_device(&ctx->vdev); 8248c2ecf20Sopenharmony_ci} 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ciint cal_ctx_v4l2_init(struct cal_ctx *ctx) 8278c2ecf20Sopenharmony_ci{ 8288c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler *hdl = &ctx->ctrl_handler; 8298c2ecf20Sopenharmony_ci struct video_device *vfd = &ctx->vdev; 8308c2ecf20Sopenharmony_ci struct vb2_queue *q = &ctx->vb_vidq; 8318c2ecf20Sopenharmony_ci int ret; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ctx->vidq.active); 8348c2ecf20Sopenharmony_ci spin_lock_init(&ctx->slock); 8358c2ecf20Sopenharmony_ci mutex_init(&ctx->mutex); 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci /* Initialize the vb2 queue. */ 8388c2ecf20Sopenharmony_ci q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 8398c2ecf20Sopenharmony_ci q->io_modes = VB2_MMAP | VB2_DMABUF | VB2_READ; 8408c2ecf20Sopenharmony_ci q->drv_priv = ctx; 8418c2ecf20Sopenharmony_ci q->buf_struct_size = sizeof(struct cal_buffer); 8428c2ecf20Sopenharmony_ci q->ops = &cal_video_qops; 8438c2ecf20Sopenharmony_ci q->mem_ops = &vb2_dma_contig_memops; 8448c2ecf20Sopenharmony_ci q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; 8458c2ecf20Sopenharmony_ci q->lock = &ctx->mutex; 8468c2ecf20Sopenharmony_ci q->min_buffers_needed = 3; 8478c2ecf20Sopenharmony_ci q->dev = ctx->cal->dev; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci ret = vb2_queue_init(q); 8508c2ecf20Sopenharmony_ci if (ret) 8518c2ecf20Sopenharmony_ci return ret; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci /* Initialize the video device and media entity. */ 8548c2ecf20Sopenharmony_ci *vfd = cal_videodev; 8558c2ecf20Sopenharmony_ci vfd->v4l2_dev = &ctx->cal->v4l2_dev; 8568c2ecf20Sopenharmony_ci vfd->queue = q; 8578c2ecf20Sopenharmony_ci snprintf(vfd->name, sizeof(vfd->name), "CAL output %u", ctx->index); 8588c2ecf20Sopenharmony_ci vfd->lock = &ctx->mutex; 8598c2ecf20Sopenharmony_ci video_set_drvdata(vfd, ctx); 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci ctx->pad.flags = MEDIA_PAD_FL_SINK; 8628c2ecf20Sopenharmony_ci ret = media_entity_pads_init(&vfd->entity, 1, &ctx->pad); 8638c2ecf20Sopenharmony_ci if (ret < 0) 8648c2ecf20Sopenharmony_ci return ret; 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci /* Initialize the control handler. */ 8678c2ecf20Sopenharmony_ci ret = v4l2_ctrl_handler_init(hdl, 11); 8688c2ecf20Sopenharmony_ci if (ret < 0) { 8698c2ecf20Sopenharmony_ci ctx_err(ctx, "Failed to init ctrl handler\n"); 8708c2ecf20Sopenharmony_ci goto error; 8718c2ecf20Sopenharmony_ci } 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci vfd->ctrl_handler = hdl; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci return 0; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_cierror: 8788c2ecf20Sopenharmony_ci media_entity_cleanup(&vfd->entity); 8798c2ecf20Sopenharmony_ci return ret; 8808c2ecf20Sopenharmony_ci} 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_civoid cal_ctx_v4l2_cleanup(struct cal_ctx *ctx) 8838c2ecf20Sopenharmony_ci{ 8848c2ecf20Sopenharmony_ci v4l2_ctrl_handler_free(&ctx->ctrl_handler); 8858c2ecf20Sopenharmony_ci media_entity_cleanup(&ctx->vdev.entity); 8868c2ecf20Sopenharmony_ci} 887