162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * DW100 Hardware dewarper 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2022 NXP 662306a36Sopenharmony_ci * Author: Xavier Roumegue (xavier.roumegue@oss.nxp.com) 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/clk.h> 1162306a36Sopenharmony_ci#include <linux/debugfs.h> 1262306a36Sopenharmony_ci#include <linux/interrupt.h> 1362306a36Sopenharmony_ci#include <linux/io.h> 1462306a36Sopenharmony_ci#include <linux/minmax.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include <linux/of.h> 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <media/v4l2-ctrls.h> 2162306a36Sopenharmony_ci#include <media/v4l2-device.h> 2262306a36Sopenharmony_ci#include <media/v4l2-event.h> 2362306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 2462306a36Sopenharmony_ci#include <media/v4l2-mem2mem.h> 2562306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <uapi/linux/dw100.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include "dw100_regs.h" 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define DRV_NAME "dw100" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define DW100_MIN_W 176u 3462306a36Sopenharmony_ci#define DW100_MIN_H 144u 3562306a36Sopenharmony_ci#define DW100_MAX_W 4096u 3662306a36Sopenharmony_ci#define DW100_MAX_H 3072u 3762306a36Sopenharmony_ci#define DW100_ALIGN_W 3 3862306a36Sopenharmony_ci#define DW100_ALIGN_H 3 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define DW100_BLOCK_SIZE 16 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define DW100_DEF_W 640u 4362306a36Sopenharmony_ci#define DW100_DEF_H 480u 4462306a36Sopenharmony_ci#define DW100_DEF_LUT_W (DIV_ROUND_UP(DW100_DEF_W, DW100_BLOCK_SIZE) + 1) 4562306a36Sopenharmony_ci#define DW100_DEF_LUT_H (DIV_ROUND_UP(DW100_DEF_H, DW100_BLOCK_SIZE) + 1) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* 4862306a36Sopenharmony_ci * 16 controls have been reserved for this driver for future extension, but 4962306a36Sopenharmony_ci * let's limit the related driver allocation to the effective number of controls 5062306a36Sopenharmony_ci * in use. 5162306a36Sopenharmony_ci */ 5262306a36Sopenharmony_ci#define DW100_MAX_CTRLS 1 5362306a36Sopenharmony_ci#define DW100_CTRL_DEWARPING_MAP 0 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cienum { 5662306a36Sopenharmony_ci DW100_QUEUE_SRC = 0, 5762306a36Sopenharmony_ci DW100_QUEUE_DST = 1, 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cienum { 6162306a36Sopenharmony_ci DW100_FMT_CAPTURE = BIT(0), 6262306a36Sopenharmony_ci DW100_FMT_OUTPUT = BIT(1), 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistruct dw100_device { 6662306a36Sopenharmony_ci struct platform_device *pdev; 6762306a36Sopenharmony_ci struct v4l2_m2m_dev *m2m_dev; 6862306a36Sopenharmony_ci struct v4l2_device v4l2_dev; 6962306a36Sopenharmony_ci struct video_device vfd; 7062306a36Sopenharmony_ci struct media_device mdev; 7162306a36Sopenharmony_ci /* Video device lock */ 7262306a36Sopenharmony_ci struct mutex vfd_mutex; 7362306a36Sopenharmony_ci void __iomem *mmio; 7462306a36Sopenharmony_ci struct clk_bulk_data *clks; 7562306a36Sopenharmony_ci int num_clks; 7662306a36Sopenharmony_ci struct dentry *debugfs_root; 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistruct dw100_q_data { 8062306a36Sopenharmony_ci struct v4l2_pix_format_mplane pix_fmt; 8162306a36Sopenharmony_ci unsigned int sequence; 8262306a36Sopenharmony_ci const struct dw100_fmt *fmt; 8362306a36Sopenharmony_ci struct v4l2_rect crop; 8462306a36Sopenharmony_ci}; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistruct dw100_ctx { 8762306a36Sopenharmony_ci struct v4l2_fh fh; 8862306a36Sopenharmony_ci struct dw100_device *dw_dev; 8962306a36Sopenharmony_ci struct v4l2_ctrl_handler hdl; 9062306a36Sopenharmony_ci struct v4l2_ctrl *ctrls[DW100_MAX_CTRLS]; 9162306a36Sopenharmony_ci /* per context m2m queue lock */ 9262306a36Sopenharmony_ci struct mutex vq_mutex; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* Look Up Table for pixel remapping */ 9562306a36Sopenharmony_ci unsigned int *map; 9662306a36Sopenharmony_ci dma_addr_t map_dma; 9762306a36Sopenharmony_ci size_t map_size; 9862306a36Sopenharmony_ci unsigned int map_width; 9962306a36Sopenharmony_ci unsigned int map_height; 10062306a36Sopenharmony_ci bool user_map_is_set; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* Source and destination queue data */ 10362306a36Sopenharmony_ci struct dw100_q_data q_data[2]; 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic const struct v4l2_frmsize_stepwise dw100_frmsize_stepwise = { 10762306a36Sopenharmony_ci .min_width = DW100_MIN_W, 10862306a36Sopenharmony_ci .min_height = DW100_MIN_H, 10962306a36Sopenharmony_ci .max_width = DW100_MAX_W, 11062306a36Sopenharmony_ci .max_height = DW100_MAX_H, 11162306a36Sopenharmony_ci .step_width = 1UL << DW100_ALIGN_W, 11262306a36Sopenharmony_ci .step_height = 1UL << DW100_ALIGN_H, 11362306a36Sopenharmony_ci}; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic const struct dw100_fmt { 11662306a36Sopenharmony_ci u32 fourcc; 11762306a36Sopenharmony_ci u32 types; 11862306a36Sopenharmony_ci u32 reg_format; 11962306a36Sopenharmony_ci bool reg_swap_uv; 12062306a36Sopenharmony_ci} formats[] = { 12162306a36Sopenharmony_ci { 12262306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_NV16, 12362306a36Sopenharmony_ci .types = DW100_FMT_OUTPUT | DW100_FMT_CAPTURE, 12462306a36Sopenharmony_ci .reg_format = DW100_DEWARP_CTRL_FORMAT_YUV422_SP, 12562306a36Sopenharmony_ci .reg_swap_uv = false, 12662306a36Sopenharmony_ci }, { 12762306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_NV16M, 12862306a36Sopenharmony_ci .types = DW100_FMT_OUTPUT | DW100_FMT_CAPTURE, 12962306a36Sopenharmony_ci .reg_format = DW100_DEWARP_CTRL_FORMAT_YUV422_SP, 13062306a36Sopenharmony_ci .reg_swap_uv = false, 13162306a36Sopenharmony_ci }, { 13262306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_NV61, 13362306a36Sopenharmony_ci .types = DW100_FMT_CAPTURE, 13462306a36Sopenharmony_ci .reg_format = DW100_DEWARP_CTRL_FORMAT_YUV422_SP, 13562306a36Sopenharmony_ci .reg_swap_uv = true, 13662306a36Sopenharmony_ci }, { 13762306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_NV61M, 13862306a36Sopenharmony_ci .types = DW100_FMT_CAPTURE, 13962306a36Sopenharmony_ci .reg_format = DW100_DEWARP_CTRL_FORMAT_YUV422_SP, 14062306a36Sopenharmony_ci .reg_swap_uv = true, 14162306a36Sopenharmony_ci }, { 14262306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_YUYV, 14362306a36Sopenharmony_ci .types = DW100_FMT_OUTPUT | DW100_FMT_CAPTURE, 14462306a36Sopenharmony_ci .reg_format = DW100_DEWARP_CTRL_FORMAT_YUV422_PACKED, 14562306a36Sopenharmony_ci .reg_swap_uv = false, 14662306a36Sopenharmony_ci }, { 14762306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_UYVY, 14862306a36Sopenharmony_ci .types = DW100_FMT_OUTPUT | DW100_FMT_CAPTURE, 14962306a36Sopenharmony_ci .reg_format = DW100_DEWARP_CTRL_FORMAT_YUV422_PACKED, 15062306a36Sopenharmony_ci .reg_swap_uv = true, 15162306a36Sopenharmony_ci }, { 15262306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_NV12, 15362306a36Sopenharmony_ci .types = DW100_FMT_OUTPUT | DW100_FMT_CAPTURE, 15462306a36Sopenharmony_ci .reg_format = DW100_DEWARP_CTRL_FORMAT_YUV420_SP, 15562306a36Sopenharmony_ci .reg_swap_uv = false, 15662306a36Sopenharmony_ci }, { 15762306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_NV12M, 15862306a36Sopenharmony_ci .types = DW100_FMT_OUTPUT | DW100_FMT_CAPTURE, 15962306a36Sopenharmony_ci .reg_format = DW100_DEWARP_CTRL_FORMAT_YUV420_SP, 16062306a36Sopenharmony_ci .reg_swap_uv = false, 16162306a36Sopenharmony_ci }, { 16262306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_NV21, 16362306a36Sopenharmony_ci .types = DW100_FMT_CAPTURE, 16462306a36Sopenharmony_ci .reg_format = DW100_DEWARP_CTRL_FORMAT_YUV420_SP, 16562306a36Sopenharmony_ci .reg_swap_uv = true, 16662306a36Sopenharmony_ci }, { 16762306a36Sopenharmony_ci .fourcc = V4L2_PIX_FMT_NV21M, 16862306a36Sopenharmony_ci .types = DW100_FMT_CAPTURE, 16962306a36Sopenharmony_ci .reg_format = DW100_DEWARP_CTRL_FORMAT_YUV420_SP, 17062306a36Sopenharmony_ci .reg_swap_uv = true, 17162306a36Sopenharmony_ci }, 17262306a36Sopenharmony_ci}; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic inline int to_dw100_fmt_type(enum v4l2_buf_type type) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci if (V4L2_TYPE_IS_OUTPUT(type)) 17762306a36Sopenharmony_ci return DW100_FMT_OUTPUT; 17862306a36Sopenharmony_ci else 17962306a36Sopenharmony_ci return DW100_FMT_CAPTURE; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic const struct dw100_fmt *dw100_find_pixel_format(u32 pixel_format, 18362306a36Sopenharmony_ci int fmt_type) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci unsigned int i; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(formats); i++) { 18862306a36Sopenharmony_ci const struct dw100_fmt *fmt = &formats[i]; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (fmt->fourcc == pixel_format && fmt->types & fmt_type) 19162306a36Sopenharmony_ci return fmt; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return NULL; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic const struct dw100_fmt *dw100_find_format(struct v4l2_format *f) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci return dw100_find_pixel_format(f->fmt.pix_mp.pixelformat, 20062306a36Sopenharmony_ci to_dw100_fmt_type(f->type)); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic inline u32 dw100_read(struct dw100_device *dw_dev, u32 reg) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci return readl(dw_dev->mmio + reg); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic inline void dw100_write(struct dw100_device *dw_dev, u32 reg, u32 val) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci writel(val, dw_dev->mmio + reg); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic inline int dw100_dump_regs(struct seq_file *m) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci struct dw100_device *dw_dev = m->private; 21662306a36Sopenharmony_ci#define __DECLARE_REG(x) { #x, x } 21762306a36Sopenharmony_ci unsigned int i; 21862306a36Sopenharmony_ci static const struct reg_desc { 21962306a36Sopenharmony_ci const char * const name; 22062306a36Sopenharmony_ci unsigned int addr; 22162306a36Sopenharmony_ci } dw100_regs[] = { 22262306a36Sopenharmony_ci __DECLARE_REG(DW100_DEWARP_ID), 22362306a36Sopenharmony_ci __DECLARE_REG(DW100_DEWARP_CTRL), 22462306a36Sopenharmony_ci __DECLARE_REG(DW100_MAP_LUT_ADDR), 22562306a36Sopenharmony_ci __DECLARE_REG(DW100_MAP_LUT_SIZE), 22662306a36Sopenharmony_ci __DECLARE_REG(DW100_MAP_LUT_ADDR2), 22762306a36Sopenharmony_ci __DECLARE_REG(DW100_MAP_LUT_SIZE2), 22862306a36Sopenharmony_ci __DECLARE_REG(DW100_SRC_IMG_Y_BASE), 22962306a36Sopenharmony_ci __DECLARE_REG(DW100_SRC_IMG_UV_BASE), 23062306a36Sopenharmony_ci __DECLARE_REG(DW100_SRC_IMG_SIZE), 23162306a36Sopenharmony_ci __DECLARE_REG(DW100_SRC_IMG_STRIDE), 23262306a36Sopenharmony_ci __DECLARE_REG(DW100_DST_IMG_Y_BASE), 23362306a36Sopenharmony_ci __DECLARE_REG(DW100_DST_IMG_UV_BASE), 23462306a36Sopenharmony_ci __DECLARE_REG(DW100_DST_IMG_SIZE), 23562306a36Sopenharmony_ci __DECLARE_REG(DW100_DST_IMG_STRIDE), 23662306a36Sopenharmony_ci __DECLARE_REG(DW100_DST_IMG_Y_SIZE1), 23762306a36Sopenharmony_ci __DECLARE_REG(DW100_DST_IMG_UV_SIZE1), 23862306a36Sopenharmony_ci __DECLARE_REG(DW100_SRC_IMG_Y_BASE2), 23962306a36Sopenharmony_ci __DECLARE_REG(DW100_SRC_IMG_UV_BASE2), 24062306a36Sopenharmony_ci __DECLARE_REG(DW100_SRC_IMG_SIZE2), 24162306a36Sopenharmony_ci __DECLARE_REG(DW100_SRC_IMG_STRIDE2), 24262306a36Sopenharmony_ci __DECLARE_REG(DW100_DST_IMG_Y_BASE2), 24362306a36Sopenharmony_ci __DECLARE_REG(DW100_DST_IMG_UV_BASE2), 24462306a36Sopenharmony_ci __DECLARE_REG(DW100_DST_IMG_SIZE2), 24562306a36Sopenharmony_ci __DECLARE_REG(DW100_DST_IMG_STRIDE2), 24662306a36Sopenharmony_ci __DECLARE_REG(DW100_DST_IMG_Y_SIZE2), 24762306a36Sopenharmony_ci __DECLARE_REG(DW100_DST_IMG_UV_SIZE2), 24862306a36Sopenharmony_ci __DECLARE_REG(DW100_SWAP_CONTROL), 24962306a36Sopenharmony_ci __DECLARE_REG(DW100_VERTICAL_SPLIT_LINE), 25062306a36Sopenharmony_ci __DECLARE_REG(DW100_HORIZON_SPLIT_LINE), 25162306a36Sopenharmony_ci __DECLARE_REG(DW100_SCALE_FACTOR), 25262306a36Sopenharmony_ci __DECLARE_REG(DW100_ROI_START), 25362306a36Sopenharmony_ci __DECLARE_REG(DW100_BOUNDARY_PIXEL), 25462306a36Sopenharmony_ci __DECLARE_REG(DW100_INTERRUPT_STATUS), 25562306a36Sopenharmony_ci __DECLARE_REG(DW100_BUS_CTRL), 25662306a36Sopenharmony_ci __DECLARE_REG(DW100_BUS_CTRL1), 25762306a36Sopenharmony_ci __DECLARE_REG(DW100_BUS_TIME_OUT_CYCLE), 25862306a36Sopenharmony_ci }; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(dw100_regs); i++) 26162306a36Sopenharmony_ci seq_printf(m, "%s: %#x\n", dw100_regs[i].name, 26262306a36Sopenharmony_ci dw100_read(dw_dev, dw100_regs[i].addr)); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci return 0; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic inline struct dw100_ctx *dw100_file2ctx(struct file *file) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci return container_of(file->private_data, struct dw100_ctx, fh); 27062306a36Sopenharmony_ci} 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_cistatic struct dw100_q_data *dw100_get_q_data(struct dw100_ctx *ctx, 27362306a36Sopenharmony_ci enum v4l2_buf_type type) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) 27662306a36Sopenharmony_ci return &ctx->q_data[DW100_QUEUE_SRC]; 27762306a36Sopenharmony_ci else 27862306a36Sopenharmony_ci return &ctx->q_data[DW100_QUEUE_DST]; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic u32 dw100_get_n_vertices_from_length(u32 length) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci return DIV_ROUND_UP(length, DW100_BLOCK_SIZE) + 1; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic u16 dw100_map_convert_to_uq12_4(u32 a) 28762306a36Sopenharmony_ci{ 28862306a36Sopenharmony_ci return (u16)((a & 0xfff) << 4); 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic u32 dw100_map_format_coordinates(u16 xq, u16 yq) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci return (u32)((yq << 16) | xq); 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic u32 *dw100_get_user_map(struct dw100_ctx *ctx) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct v4l2_ctrl *ctrl = ctx->ctrls[DW100_CTRL_DEWARPING_MAP]; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci return ctrl->p_cur.p_u32; 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci/* 30462306a36Sopenharmony_ci * Create the dewarp map used by the hardware from the V4L2 control values which 30562306a36Sopenharmony_ci * have been initialized with an identity map or set by the application. 30662306a36Sopenharmony_ci */ 30762306a36Sopenharmony_cistatic int dw100_create_mapping(struct dw100_ctx *ctx) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci u32 *user_map; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (ctx->map) 31262306a36Sopenharmony_ci dma_free_coherent(&ctx->dw_dev->pdev->dev, ctx->map_size, 31362306a36Sopenharmony_ci ctx->map, ctx->map_dma); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci ctx->map = dma_alloc_coherent(&ctx->dw_dev->pdev->dev, ctx->map_size, 31662306a36Sopenharmony_ci &ctx->map_dma, GFP_KERNEL); 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (!ctx->map) 31962306a36Sopenharmony_ci return -ENOMEM; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci user_map = dw100_get_user_map(ctx); 32262306a36Sopenharmony_ci memcpy(ctx->map, user_map, ctx->map_size); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci dev_dbg(&ctx->dw_dev->pdev->dev, 32562306a36Sopenharmony_ci "%ux%u %s mapping created (d:%pad-c:%p) for stream %ux%u->%ux%u\n", 32662306a36Sopenharmony_ci ctx->map_width, ctx->map_height, 32762306a36Sopenharmony_ci ctx->user_map_is_set ? "user" : "identity", 32862306a36Sopenharmony_ci &ctx->map_dma, ctx->map, 32962306a36Sopenharmony_ci ctx->q_data[DW100_QUEUE_SRC].pix_fmt.width, 33062306a36Sopenharmony_ci ctx->q_data[DW100_QUEUE_DST].pix_fmt.height, 33162306a36Sopenharmony_ci ctx->q_data[DW100_QUEUE_SRC].pix_fmt.width, 33262306a36Sopenharmony_ci ctx->q_data[DW100_QUEUE_DST].pix_fmt.height); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci return 0; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic void dw100_destroy_mapping(struct dw100_ctx *ctx) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci if (ctx->map) { 34062306a36Sopenharmony_ci dma_free_coherent(&ctx->dw_dev->pdev->dev, ctx->map_size, 34162306a36Sopenharmony_ci ctx->map, ctx->map_dma); 34262306a36Sopenharmony_ci ctx->map = NULL; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ci} 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic int dw100_s_ctrl(struct v4l2_ctrl *ctrl) 34762306a36Sopenharmony_ci{ 34862306a36Sopenharmony_ci struct dw100_ctx *ctx = 34962306a36Sopenharmony_ci container_of(ctrl->handler, struct dw100_ctx, hdl); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci switch (ctrl->id) { 35262306a36Sopenharmony_ci case V4L2_CID_DW100_DEWARPING_16x16_VERTEX_MAP: 35362306a36Sopenharmony_ci ctx->user_map_is_set = true; 35462306a36Sopenharmony_ci break; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci return 0; 35862306a36Sopenharmony_ci} 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops dw100_ctrl_ops = { 36162306a36Sopenharmony_ci .s_ctrl = dw100_s_ctrl, 36262306a36Sopenharmony_ci}; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci/* 36562306a36Sopenharmony_ci * Initialize the dewarping map with an identity mapping. 36662306a36Sopenharmony_ci * 36762306a36Sopenharmony_ci * A 16 pixels cell size grid is mapped on the destination image. 36862306a36Sopenharmony_ci * The last cells width/height might be lesser than 16 if the destination image 36962306a36Sopenharmony_ci * width/height is not divisible by 16. This dewarping grid map specifies the 37062306a36Sopenharmony_ci * source image pixel location (x, y) on each grid intersection point. 37162306a36Sopenharmony_ci * Bilinear interpolation is used to compute inner cell points locations. 37262306a36Sopenharmony_ci * 37362306a36Sopenharmony_ci * The coordinates are saved in UQ12.4 fixed point format. 37462306a36Sopenharmony_ci */ 37562306a36Sopenharmony_cistatic void dw100_ctrl_dewarping_map_init(const struct v4l2_ctrl *ctrl, 37662306a36Sopenharmony_ci u32 from_idx, 37762306a36Sopenharmony_ci union v4l2_ctrl_ptr ptr) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci struct dw100_ctx *ctx = 38062306a36Sopenharmony_ci container_of(ctrl->handler, struct dw100_ctx, hdl); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci u32 sw, sh, mw, mh, idx; 38362306a36Sopenharmony_ci u16 qx, qy, qdx, qdy, qsh, qsw; 38462306a36Sopenharmony_ci u32 *map = ctrl->p_cur.p_u32; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci sw = ctx->q_data[DW100_QUEUE_SRC].pix_fmt.width; 38762306a36Sopenharmony_ci sh = ctx->q_data[DW100_QUEUE_SRC].pix_fmt.height; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci mw = ctrl->dims[0]; 39062306a36Sopenharmony_ci mh = ctrl->dims[1]; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci qsw = dw100_map_convert_to_uq12_4(sw); 39362306a36Sopenharmony_ci qsh = dw100_map_convert_to_uq12_4(sh); 39462306a36Sopenharmony_ci qdx = qsw / (mw - 1); 39562306a36Sopenharmony_ci qdy = qsh / (mh - 1); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci ctx->map_width = mw; 39862306a36Sopenharmony_ci ctx->map_height = mh; 39962306a36Sopenharmony_ci ctx->map_size = mh * mw * sizeof(u32); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci for (idx = from_idx; idx < ctrl->elems; idx++) { 40262306a36Sopenharmony_ci qy = min_t(u32, (idx / mw) * qdy, qsh); 40362306a36Sopenharmony_ci qx = min_t(u32, (idx % mw) * qdx, qsw); 40462306a36Sopenharmony_ci map[idx] = dw100_map_format_coordinates(qx, qy); 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci ctx->user_map_is_set = false; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic const struct v4l2_ctrl_type_ops dw100_ctrl_type_ops = { 41162306a36Sopenharmony_ci .init = dw100_ctrl_dewarping_map_init, 41262306a36Sopenharmony_ci .validate = v4l2_ctrl_type_op_validate, 41362306a36Sopenharmony_ci .log = v4l2_ctrl_type_op_log, 41462306a36Sopenharmony_ci .equal = v4l2_ctrl_type_op_equal, 41562306a36Sopenharmony_ci}; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic const struct v4l2_ctrl_config controls[] = { 41862306a36Sopenharmony_ci [DW100_CTRL_DEWARPING_MAP] = { 41962306a36Sopenharmony_ci .ops = &dw100_ctrl_ops, 42062306a36Sopenharmony_ci .type_ops = &dw100_ctrl_type_ops, 42162306a36Sopenharmony_ci .id = V4L2_CID_DW100_DEWARPING_16x16_VERTEX_MAP, 42262306a36Sopenharmony_ci .name = "Dewarping Vertex Map", 42362306a36Sopenharmony_ci .type = V4L2_CTRL_TYPE_U32, 42462306a36Sopenharmony_ci .min = 0x00000000, 42562306a36Sopenharmony_ci .max = 0xffffffff, 42662306a36Sopenharmony_ci .step = 1, 42762306a36Sopenharmony_ci .def = 0, 42862306a36Sopenharmony_ci .dims = { DW100_DEF_LUT_W, DW100_DEF_LUT_H }, 42962306a36Sopenharmony_ci }, 43062306a36Sopenharmony_ci}; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic int dw100_queue_setup(struct vb2_queue *vq, 43362306a36Sopenharmony_ci unsigned int *nbuffers, unsigned int *nplanes, 43462306a36Sopenharmony_ci unsigned int sizes[], struct device *alloc_devs[]) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci struct dw100_ctx *ctx = vb2_get_drv_priv(vq); 43762306a36Sopenharmony_ci const struct v4l2_pix_format_mplane *format; 43862306a36Sopenharmony_ci unsigned int i; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci format = &dw100_get_q_data(ctx, vq->type)->pix_fmt; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (*nplanes) { 44362306a36Sopenharmony_ci if (*nplanes != format->num_planes) 44462306a36Sopenharmony_ci return -EINVAL; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci for (i = 0; i < *nplanes; ++i) { 44762306a36Sopenharmony_ci if (sizes[i] < format->plane_fmt[i].sizeimage) 44862306a36Sopenharmony_ci return -EINVAL; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci return 0; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci *nplanes = format->num_planes; 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci for (i = 0; i < format->num_planes; ++i) 45762306a36Sopenharmony_ci sizes[i] = format->plane_fmt[i].sizeimage; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci return 0; 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic int dw100_buf_prepare(struct vb2_buffer *vb) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci unsigned int i; 46562306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 46662306a36Sopenharmony_ci struct dw100_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 46762306a36Sopenharmony_ci struct dw100_device *dw_dev = ctx->dw_dev; 46862306a36Sopenharmony_ci const struct v4l2_pix_format_mplane *pix_fmt = 46962306a36Sopenharmony_ci &dw100_get_q_data(ctx, vb->vb2_queue->type)->pix_fmt; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (V4L2_TYPE_IS_OUTPUT(vb->vb2_queue->type)) { 47262306a36Sopenharmony_ci if (vbuf->field != V4L2_FIELD_NONE) { 47362306a36Sopenharmony_ci dev_dbg(&dw_dev->pdev->dev, "%x field isn't supported\n", 47462306a36Sopenharmony_ci vbuf->field); 47562306a36Sopenharmony_ci return -EINVAL; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci for (i = 0; i < pix_fmt->num_planes; i++) { 48062306a36Sopenharmony_ci unsigned long size = pix_fmt->plane_fmt[i].sizeimage; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci if (vb2_plane_size(vb, i) < size) { 48362306a36Sopenharmony_ci dev_dbg(&dw_dev->pdev->dev, 48462306a36Sopenharmony_ci "User buffer too small (%lu < %lu)\n", 48562306a36Sopenharmony_ci vb2_plane_size(vb, i), size); 48662306a36Sopenharmony_ci return -EINVAL; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci vb2_set_plane_payload(vb, i, size); 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci return 0; 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic void dw100_buf_queue(struct vb2_buffer *vb) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb); 49862306a36Sopenharmony_ci struct dw100_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf); 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_cistatic void dw100_return_all_buffers(struct vb2_queue *q, 50462306a36Sopenharmony_ci enum vb2_buffer_state state) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci struct dw100_ctx *ctx = vb2_get_drv_priv(q); 50762306a36Sopenharmony_ci struct vb2_v4l2_buffer *vbuf; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci for (;;) { 51062306a36Sopenharmony_ci if (V4L2_TYPE_IS_OUTPUT(q->type)) 51162306a36Sopenharmony_ci vbuf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx); 51262306a36Sopenharmony_ci else 51362306a36Sopenharmony_ci vbuf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx); 51462306a36Sopenharmony_ci if (!vbuf) 51562306a36Sopenharmony_ci return; 51662306a36Sopenharmony_ci v4l2_m2m_buf_done(vbuf, state); 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_cistatic int dw100_start_streaming(struct vb2_queue *q, unsigned int count) 52162306a36Sopenharmony_ci{ 52262306a36Sopenharmony_ci struct dw100_ctx *ctx = vb2_get_drv_priv(q); 52362306a36Sopenharmony_ci struct dw100_q_data *q_data = dw100_get_q_data(ctx, q->type); 52462306a36Sopenharmony_ci int ret; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci q_data->sequence = 0; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci ret = dw100_create_mapping(ctx); 52962306a36Sopenharmony_ci if (ret) 53062306a36Sopenharmony_ci goto err; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(&ctx->dw_dev->pdev->dev); 53362306a36Sopenharmony_ci if (ret) { 53462306a36Sopenharmony_ci dw100_destroy_mapping(ctx); 53562306a36Sopenharmony_ci goto err; 53662306a36Sopenharmony_ci } 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci return 0; 53962306a36Sopenharmony_cierr: 54062306a36Sopenharmony_ci dw100_return_all_buffers(q, VB2_BUF_STATE_QUEUED); 54162306a36Sopenharmony_ci return ret; 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_cistatic void dw100_stop_streaming(struct vb2_queue *q) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci struct dw100_ctx *ctx = vb2_get_drv_priv(q); 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci dw100_return_all_buffers(q, VB2_BUF_STATE_ERROR); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci pm_runtime_put_sync(&ctx->dw_dev->pdev->dev); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci dw100_destroy_mapping(ctx); 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic const struct vb2_ops dw100_qops = { 55662306a36Sopenharmony_ci .queue_setup = dw100_queue_setup, 55762306a36Sopenharmony_ci .buf_prepare = dw100_buf_prepare, 55862306a36Sopenharmony_ci .buf_queue = dw100_buf_queue, 55962306a36Sopenharmony_ci .start_streaming = dw100_start_streaming, 56062306a36Sopenharmony_ci .stop_streaming = dw100_stop_streaming, 56162306a36Sopenharmony_ci .wait_prepare = vb2_ops_wait_prepare, 56262306a36Sopenharmony_ci .wait_finish = vb2_ops_wait_finish, 56362306a36Sopenharmony_ci}; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_cistatic int dw100_m2m_queue_init(void *priv, struct vb2_queue *src_vq, 56662306a36Sopenharmony_ci struct vb2_queue *dst_vq) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci struct dw100_ctx *ctx = priv; 56962306a36Sopenharmony_ci int ret; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 57262306a36Sopenharmony_ci src_vq->io_modes = VB2_MMAP | VB2_DMABUF; 57362306a36Sopenharmony_ci src_vq->drv_priv = ctx; 57462306a36Sopenharmony_ci src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); 57562306a36Sopenharmony_ci src_vq->ops = &dw100_qops; 57662306a36Sopenharmony_ci src_vq->mem_ops = &vb2_dma_contig_memops; 57762306a36Sopenharmony_ci src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 57862306a36Sopenharmony_ci src_vq->lock = &ctx->vq_mutex; 57962306a36Sopenharmony_ci src_vq->dev = ctx->dw_dev->v4l2_dev.dev; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci ret = vb2_queue_init(src_vq); 58262306a36Sopenharmony_ci if (ret) 58362306a36Sopenharmony_ci return ret; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 58662306a36Sopenharmony_ci dst_vq->io_modes = VB2_MMAP | VB2_DMABUF; 58762306a36Sopenharmony_ci dst_vq->drv_priv = ctx; 58862306a36Sopenharmony_ci dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); 58962306a36Sopenharmony_ci dst_vq->ops = &dw100_qops; 59062306a36Sopenharmony_ci dst_vq->mem_ops = &vb2_dma_contig_memops; 59162306a36Sopenharmony_ci dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; 59262306a36Sopenharmony_ci dst_vq->lock = &ctx->vq_mutex; 59362306a36Sopenharmony_ci dst_vq->dev = ctx->dw_dev->v4l2_dev.dev; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci return vb2_queue_init(dst_vq); 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic int dw100_open(struct file *file) 59962306a36Sopenharmony_ci{ 60062306a36Sopenharmony_ci struct dw100_device *dw_dev = video_drvdata(file); 60162306a36Sopenharmony_ci struct dw100_ctx *ctx; 60262306a36Sopenharmony_ci struct v4l2_ctrl_handler *hdl; 60362306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pix_fmt; 60462306a36Sopenharmony_ci int ret, i; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 60762306a36Sopenharmony_ci if (!ctx) 60862306a36Sopenharmony_ci return -ENOMEM; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci mutex_init(&ctx->vq_mutex); 61162306a36Sopenharmony_ci v4l2_fh_init(&ctx->fh, video_devdata(file)); 61262306a36Sopenharmony_ci file->private_data = &ctx->fh; 61362306a36Sopenharmony_ci ctx->dw_dev = dw_dev; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci ctx->q_data[DW100_QUEUE_SRC].fmt = &formats[0]; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci pix_fmt = &ctx->q_data[DW100_QUEUE_SRC].pix_fmt; 61862306a36Sopenharmony_ci pix_fmt->field = V4L2_FIELD_NONE; 61962306a36Sopenharmony_ci pix_fmt->colorspace = V4L2_COLORSPACE_REC709; 62062306a36Sopenharmony_ci pix_fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix_fmt->colorspace); 62162306a36Sopenharmony_ci pix_fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix_fmt->colorspace); 62262306a36Sopenharmony_ci pix_fmt->quantization = 62362306a36Sopenharmony_ci V4L2_MAP_QUANTIZATION_DEFAULT(false, pix_fmt->colorspace, 62462306a36Sopenharmony_ci pix_fmt->ycbcr_enc); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci v4l2_fill_pixfmt_mp(pix_fmt, formats[0].fourcc, DW100_DEF_W, DW100_DEF_H); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci ctx->q_data[DW100_QUEUE_SRC].crop.top = 0; 62962306a36Sopenharmony_ci ctx->q_data[DW100_QUEUE_SRC].crop.left = 0; 63062306a36Sopenharmony_ci ctx->q_data[DW100_QUEUE_SRC].crop.width = DW100_DEF_W; 63162306a36Sopenharmony_ci ctx->q_data[DW100_QUEUE_SRC].crop.height = DW100_DEF_H; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci ctx->q_data[DW100_QUEUE_DST] = ctx->q_data[DW100_QUEUE_SRC]; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci hdl = &ctx->hdl; 63662306a36Sopenharmony_ci v4l2_ctrl_handler_init(hdl, ARRAY_SIZE(controls)); 63762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(controls); i++) { 63862306a36Sopenharmony_ci ctx->ctrls[i] = v4l2_ctrl_new_custom(hdl, &controls[i], NULL); 63962306a36Sopenharmony_ci if (hdl->error) { 64062306a36Sopenharmony_ci dev_err(&ctx->dw_dev->pdev->dev, 64162306a36Sopenharmony_ci "Adding control (%d) failed\n", i); 64262306a36Sopenharmony_ci ret = hdl->error; 64362306a36Sopenharmony_ci goto err; 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci } 64662306a36Sopenharmony_ci ctx->fh.ctrl_handler = hdl; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(dw_dev->m2m_dev, 64962306a36Sopenharmony_ci ctx, &dw100_m2m_queue_init); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci if (IS_ERR(ctx->fh.m2m_ctx)) { 65262306a36Sopenharmony_ci ret = PTR_ERR(ctx->fh.m2m_ctx); 65362306a36Sopenharmony_ci goto err; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci v4l2_fh_add(&ctx->fh); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci return 0; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_cierr: 66162306a36Sopenharmony_ci v4l2_ctrl_handler_free(hdl); 66262306a36Sopenharmony_ci v4l2_fh_exit(&ctx->fh); 66362306a36Sopenharmony_ci mutex_destroy(&ctx->vq_mutex); 66462306a36Sopenharmony_ci kfree(ctx); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci return ret; 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_cistatic int dw100_release(struct file *file) 67062306a36Sopenharmony_ci{ 67162306a36Sopenharmony_ci struct dw100_ctx *ctx = dw100_file2ctx(file); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci v4l2_fh_del(&ctx->fh); 67462306a36Sopenharmony_ci v4l2_fh_exit(&ctx->fh); 67562306a36Sopenharmony_ci v4l2_ctrl_handler_free(&ctx->hdl); 67662306a36Sopenharmony_ci v4l2_m2m_ctx_release(ctx->fh.m2m_ctx); 67762306a36Sopenharmony_ci mutex_destroy(&ctx->vq_mutex); 67862306a36Sopenharmony_ci kfree(ctx); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci return 0; 68162306a36Sopenharmony_ci} 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_cistatic const struct v4l2_file_operations dw100_fops = { 68462306a36Sopenharmony_ci .owner = THIS_MODULE, 68562306a36Sopenharmony_ci .open = dw100_open, 68662306a36Sopenharmony_ci .release = dw100_release, 68762306a36Sopenharmony_ci .poll = v4l2_m2m_fop_poll, 68862306a36Sopenharmony_ci .unlocked_ioctl = video_ioctl2, 68962306a36Sopenharmony_ci .mmap = v4l2_m2m_fop_mmap, 69062306a36Sopenharmony_ci}; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cistatic int dw100_querycap(struct file *file, void *priv, 69362306a36Sopenharmony_ci struct v4l2_capability *cap) 69462306a36Sopenharmony_ci{ 69562306a36Sopenharmony_ci strscpy(cap->driver, DRV_NAME, sizeof(cap->driver)); 69662306a36Sopenharmony_ci strscpy(cap->card, "DW100 dewarper", sizeof(cap->card)); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci return 0; 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic int dw100_enum_fmt_vid(struct file *file, void *priv, 70262306a36Sopenharmony_ci struct v4l2_fmtdesc *f) 70362306a36Sopenharmony_ci{ 70462306a36Sopenharmony_ci int i, num = 0; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(formats); i++) { 70762306a36Sopenharmony_ci if (formats[i].types & to_dw100_fmt_type(f->type)) { 70862306a36Sopenharmony_ci if (num == f->index) { 70962306a36Sopenharmony_ci f->pixelformat = formats[i].fourcc; 71062306a36Sopenharmony_ci return 0; 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci ++num; 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci return -EINVAL; 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic int dw100_enum_framesizes(struct file *file, void *priv, 72062306a36Sopenharmony_ci struct v4l2_frmsizeenum *fsize) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci const struct dw100_fmt *fmt; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci if (fsize->index) 72562306a36Sopenharmony_ci return -EINVAL; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci fmt = dw100_find_pixel_format(fsize->pixel_format, 72862306a36Sopenharmony_ci DW100_FMT_OUTPUT | DW100_FMT_CAPTURE); 72962306a36Sopenharmony_ci if (!fmt) 73062306a36Sopenharmony_ci return -EINVAL; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; 73362306a36Sopenharmony_ci fsize->stepwise = dw100_frmsize_stepwise; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci return 0; 73662306a36Sopenharmony_ci} 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_cistatic int dw100_g_fmt_vid(struct file *file, void *priv, struct v4l2_format *f) 73962306a36Sopenharmony_ci{ 74062306a36Sopenharmony_ci struct dw100_ctx *ctx = dw100_file2ctx(file); 74162306a36Sopenharmony_ci struct vb2_queue *vq; 74262306a36Sopenharmony_ci struct dw100_q_data *q_data; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); 74562306a36Sopenharmony_ci if (!vq) 74662306a36Sopenharmony_ci return -EINVAL; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci q_data = dw100_get_q_data(ctx, f->type); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci f->fmt.pix_mp = q_data->pix_fmt; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci return 0; 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic int dw100_try_fmt(struct file *file, struct v4l2_format *f) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci struct dw100_ctx *ctx = dw100_file2ctx(file); 75862306a36Sopenharmony_ci struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; 75962306a36Sopenharmony_ci const struct dw100_fmt *fmt; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci fmt = dw100_find_format(f); 76262306a36Sopenharmony_ci if (!fmt) { 76362306a36Sopenharmony_ci fmt = &formats[0]; 76462306a36Sopenharmony_ci pix->pixelformat = fmt->fourcc; 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci v4l2_apply_frmsize_constraints(&pix->width, &pix->height, 76862306a36Sopenharmony_ci &dw100_frmsize_stepwise); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci v4l2_fill_pixfmt_mp(pix, fmt->fourcc, pix->width, pix->height); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci pix->field = V4L2_FIELD_NONE; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { 77562306a36Sopenharmony_ci if (pix->colorspace == V4L2_COLORSPACE_DEFAULT) 77662306a36Sopenharmony_ci pix->colorspace = V4L2_COLORSPACE_REC709; 77762306a36Sopenharmony_ci if (pix->xfer_func == V4L2_XFER_FUNC_DEFAULT) 77862306a36Sopenharmony_ci pix->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(pix->colorspace); 77962306a36Sopenharmony_ci if (pix->ycbcr_enc == V4L2_YCBCR_ENC_DEFAULT) 78062306a36Sopenharmony_ci pix->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(pix->colorspace); 78162306a36Sopenharmony_ci if (pix->quantization == V4L2_QUANTIZATION_DEFAULT) 78262306a36Sopenharmony_ci pix->quantization = 78362306a36Sopenharmony_ci V4L2_MAP_QUANTIZATION_DEFAULT(false, 78462306a36Sopenharmony_ci pix->colorspace, 78562306a36Sopenharmony_ci pix->ycbcr_enc); 78662306a36Sopenharmony_ci } else { 78762306a36Sopenharmony_ci /* 78862306a36Sopenharmony_ci * The DW100 can't perform colorspace conversion, the colorspace 78962306a36Sopenharmony_ci * on the capture queue must be identical to the output queue. 79062306a36Sopenharmony_ci */ 79162306a36Sopenharmony_ci const struct dw100_q_data *q_data = 79262306a36Sopenharmony_ci dw100_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci pix->colorspace = q_data->pix_fmt.colorspace; 79562306a36Sopenharmony_ci pix->xfer_func = q_data->pix_fmt.xfer_func; 79662306a36Sopenharmony_ci pix->ycbcr_enc = q_data->pix_fmt.ycbcr_enc; 79762306a36Sopenharmony_ci pix->quantization = q_data->pix_fmt.quantization; 79862306a36Sopenharmony_ci } 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci return 0; 80162306a36Sopenharmony_ci} 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_cistatic int dw100_s_fmt(struct dw100_ctx *ctx, struct v4l2_format *f) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci struct dw100_q_data *q_data; 80662306a36Sopenharmony_ci struct vb2_queue *vq; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx, f->type); 80962306a36Sopenharmony_ci if (!vq) 81062306a36Sopenharmony_ci return -EINVAL; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci q_data = dw100_get_q_data(ctx, f->type); 81362306a36Sopenharmony_ci if (!q_data) 81462306a36Sopenharmony_ci return -EINVAL; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci if (vb2_is_busy(vq)) { 81762306a36Sopenharmony_ci dev_dbg(&ctx->dw_dev->pdev->dev, "%s queue busy\n", __func__); 81862306a36Sopenharmony_ci return -EBUSY; 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci q_data->fmt = dw100_find_format(f); 82262306a36Sopenharmony_ci q_data->pix_fmt = f->fmt.pix_mp; 82362306a36Sopenharmony_ci q_data->crop.top = 0; 82462306a36Sopenharmony_ci q_data->crop.left = 0; 82562306a36Sopenharmony_ci q_data->crop.width = f->fmt.pix_mp.width; 82662306a36Sopenharmony_ci q_data->crop.height = f->fmt.pix_mp.height; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci /* Propagate buffers encoding */ 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { 83162306a36Sopenharmony_ci struct dw100_q_data *dst_q_data = 83262306a36Sopenharmony_ci dw100_get_q_data(ctx, 83362306a36Sopenharmony_ci V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci dst_q_data->pix_fmt.colorspace = q_data->pix_fmt.colorspace; 83662306a36Sopenharmony_ci dst_q_data->pix_fmt.ycbcr_enc = q_data->pix_fmt.ycbcr_enc; 83762306a36Sopenharmony_ci dst_q_data->pix_fmt.quantization = q_data->pix_fmt.quantization; 83862306a36Sopenharmony_ci dst_q_data->pix_fmt.xfer_func = q_data->pix_fmt.xfer_func; 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci dev_dbg(&ctx->dw_dev->pdev->dev, 84262306a36Sopenharmony_ci "Setting format for type %u, wxh: %ux%u, fmt: %p4cc\n", 84362306a36Sopenharmony_ci f->type, q_data->pix_fmt.width, q_data->pix_fmt.height, 84462306a36Sopenharmony_ci &q_data->pix_fmt.pixelformat); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { 84762306a36Sopenharmony_ci int ret; 84862306a36Sopenharmony_ci u32 dims[V4L2_CTRL_MAX_DIMS] = {}; 84962306a36Sopenharmony_ci struct v4l2_ctrl *ctrl = ctx->ctrls[DW100_CTRL_DEWARPING_MAP]; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci dims[0] = dw100_get_n_vertices_from_length(q_data->pix_fmt.width); 85262306a36Sopenharmony_ci dims[1] = dw100_get_n_vertices_from_length(q_data->pix_fmt.height); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci ret = v4l2_ctrl_modify_dimensions(ctrl, dims); 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci if (ret) { 85762306a36Sopenharmony_ci dev_err(&ctx->dw_dev->pdev->dev, 85862306a36Sopenharmony_ci "Modifying LUT dimensions failed with error %d\n", 85962306a36Sopenharmony_ci ret); 86062306a36Sopenharmony_ci return ret; 86162306a36Sopenharmony_ci } 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci return 0; 86562306a36Sopenharmony_ci} 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_cistatic int dw100_try_fmt_vid_cap(struct file *file, void *priv, 86862306a36Sopenharmony_ci struct v4l2_format *f) 86962306a36Sopenharmony_ci{ 87062306a36Sopenharmony_ci if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) 87162306a36Sopenharmony_ci return -EINVAL; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci return dw100_try_fmt(file, f); 87462306a36Sopenharmony_ci} 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_cistatic int dw100_s_fmt_vid_cap(struct file *file, void *priv, 87762306a36Sopenharmony_ci struct v4l2_format *f) 87862306a36Sopenharmony_ci{ 87962306a36Sopenharmony_ci struct dw100_ctx *ctx = dw100_file2ctx(file); 88062306a36Sopenharmony_ci int ret; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci ret = dw100_try_fmt_vid_cap(file, priv, f); 88362306a36Sopenharmony_ci if (ret) 88462306a36Sopenharmony_ci return ret; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci ret = dw100_s_fmt(ctx, f); 88762306a36Sopenharmony_ci if (ret) 88862306a36Sopenharmony_ci return ret; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci return 0; 89162306a36Sopenharmony_ci} 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_cistatic int dw100_try_fmt_vid_out(struct file *file, void *priv, 89462306a36Sopenharmony_ci struct v4l2_format *f) 89562306a36Sopenharmony_ci{ 89662306a36Sopenharmony_ci if (f->type != V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) 89762306a36Sopenharmony_ci return -EINVAL; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci return dw100_try_fmt(file, f); 90062306a36Sopenharmony_ci} 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_cistatic int dw100_s_fmt_vid_out(struct file *file, void *priv, 90362306a36Sopenharmony_ci struct v4l2_format *f) 90462306a36Sopenharmony_ci{ 90562306a36Sopenharmony_ci struct dw100_ctx *ctx = dw100_file2ctx(file); 90662306a36Sopenharmony_ci int ret; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci ret = dw100_try_fmt_vid_out(file, priv, f); 90962306a36Sopenharmony_ci if (ret) 91062306a36Sopenharmony_ci return ret; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci ret = dw100_s_fmt(ctx, f); 91362306a36Sopenharmony_ci if (ret) 91462306a36Sopenharmony_ci return ret; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci return 0; 91762306a36Sopenharmony_ci} 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_cistatic int dw100_g_selection(struct file *file, void *fh, 92062306a36Sopenharmony_ci struct v4l2_selection *sel) 92162306a36Sopenharmony_ci{ 92262306a36Sopenharmony_ci struct dw100_ctx *ctx = dw100_file2ctx(file); 92362306a36Sopenharmony_ci struct dw100_q_data *src_q_data; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) 92662306a36Sopenharmony_ci return -EINVAL; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci src_q_data = dw100_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci switch (sel->target) { 93162306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_DEFAULT: 93262306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP_BOUNDS: 93362306a36Sopenharmony_ci sel->r.top = 0; 93462306a36Sopenharmony_ci sel->r.left = 0; 93562306a36Sopenharmony_ci sel->r.width = src_q_data->pix_fmt.width; 93662306a36Sopenharmony_ci sel->r.height = src_q_data->pix_fmt.height; 93762306a36Sopenharmony_ci break; 93862306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP: 93962306a36Sopenharmony_ci sel->r.top = src_q_data->crop.top; 94062306a36Sopenharmony_ci sel->r.left = src_q_data->crop.left; 94162306a36Sopenharmony_ci sel->r.width = src_q_data->crop.width; 94262306a36Sopenharmony_ci sel->r.height = src_q_data->crop.height; 94362306a36Sopenharmony_ci break; 94462306a36Sopenharmony_ci default: 94562306a36Sopenharmony_ci return -EINVAL; 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci return 0; 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_cistatic int dw100_s_selection(struct file *file, void *fh, 95262306a36Sopenharmony_ci struct v4l2_selection *sel) 95362306a36Sopenharmony_ci{ 95462306a36Sopenharmony_ci struct dw100_ctx *ctx = dw100_file2ctx(file); 95562306a36Sopenharmony_ci struct dw100_q_data *src_q_data; 95662306a36Sopenharmony_ci u32 qscalex, qscaley, qscale; 95762306a36Sopenharmony_ci int x, y, w, h; 95862306a36Sopenharmony_ci unsigned int wframe, hframe; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci if (sel->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) 96162306a36Sopenharmony_ci return -EINVAL; 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci src_q_data = dw100_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE); 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci dev_dbg(&ctx->dw_dev->pdev->dev, 96662306a36Sopenharmony_ci ">>> Buffer Type: %u Target: %u Rect: %ux%u@%d.%d\n", 96762306a36Sopenharmony_ci sel->type, sel->target, 96862306a36Sopenharmony_ci sel->r.width, sel->r.height, sel->r.left, sel->r.top); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci switch (sel->target) { 97162306a36Sopenharmony_ci case V4L2_SEL_TGT_CROP: 97262306a36Sopenharmony_ci wframe = src_q_data->pix_fmt.width; 97362306a36Sopenharmony_ci hframe = src_q_data->pix_fmt.height; 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci sel->r.top = clamp_t(int, sel->r.top, 0, hframe - DW100_MIN_H); 97662306a36Sopenharmony_ci sel->r.left = clamp_t(int, sel->r.left, 0, wframe - DW100_MIN_W); 97762306a36Sopenharmony_ci sel->r.height = 97862306a36Sopenharmony_ci clamp(sel->r.height, DW100_MIN_H, hframe - sel->r.top); 97962306a36Sopenharmony_ci sel->r.width = 98062306a36Sopenharmony_ci clamp(sel->r.width, DW100_MIN_W, wframe - sel->r.left); 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci /* UQ16.16 for float operations */ 98362306a36Sopenharmony_ci qscalex = (sel->r.width << 16) / wframe; 98462306a36Sopenharmony_ci qscaley = (sel->r.height << 16) / hframe; 98562306a36Sopenharmony_ci y = sel->r.top; 98662306a36Sopenharmony_ci x = sel->r.left; 98762306a36Sopenharmony_ci if (qscalex == qscaley) { 98862306a36Sopenharmony_ci qscale = qscalex; 98962306a36Sopenharmony_ci } else { 99062306a36Sopenharmony_ci switch (sel->flags) { 99162306a36Sopenharmony_ci case 0: 99262306a36Sopenharmony_ci qscale = (qscalex + qscaley) / 2; 99362306a36Sopenharmony_ci break; 99462306a36Sopenharmony_ci case V4L2_SEL_FLAG_GE: 99562306a36Sopenharmony_ci qscale = max(qscaley, qscalex); 99662306a36Sopenharmony_ci break; 99762306a36Sopenharmony_ci case V4L2_SEL_FLAG_LE: 99862306a36Sopenharmony_ci qscale = min(qscaley, qscalex); 99962306a36Sopenharmony_ci break; 100062306a36Sopenharmony_ci case V4L2_SEL_FLAG_LE | V4L2_SEL_FLAG_GE: 100162306a36Sopenharmony_ci return -ERANGE; 100262306a36Sopenharmony_ci default: 100362306a36Sopenharmony_ci return -EINVAL; 100462306a36Sopenharmony_ci } 100562306a36Sopenharmony_ci } 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci w = (u32)((((u64)wframe << 16) * qscale) >> 32); 100862306a36Sopenharmony_ci h = (u32)((((u64)hframe << 16) * qscale) >> 32); 100962306a36Sopenharmony_ci x = x + (sel->r.width - w) / 2; 101062306a36Sopenharmony_ci y = y + (sel->r.height - h) / 2; 101162306a36Sopenharmony_ci x = min(wframe - w, (unsigned int)max(0, x)); 101262306a36Sopenharmony_ci y = min(hframe - h, (unsigned int)max(0, y)); 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci sel->r.top = y; 101562306a36Sopenharmony_ci sel->r.left = x; 101662306a36Sopenharmony_ci sel->r.width = w; 101762306a36Sopenharmony_ci sel->r.height = h; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci src_q_data->crop.top = sel->r.top; 102062306a36Sopenharmony_ci src_q_data->crop.left = sel->r.left; 102162306a36Sopenharmony_ci src_q_data->crop.width = sel->r.width; 102262306a36Sopenharmony_ci src_q_data->crop.height = sel->r.height; 102362306a36Sopenharmony_ci break; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci default: 102662306a36Sopenharmony_ci return -EINVAL; 102762306a36Sopenharmony_ci } 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci dev_dbg(&ctx->dw_dev->pdev->dev, 103062306a36Sopenharmony_ci "<<< Buffer Type: %u Target: %u Rect: %ux%u@%d.%d\n", 103162306a36Sopenharmony_ci sel->type, sel->target, 103262306a36Sopenharmony_ci sel->r.width, sel->r.height, sel->r.left, sel->r.top); 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci return 0; 103562306a36Sopenharmony_ci} 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_cistatic const struct v4l2_ioctl_ops dw100_ioctl_ops = { 103862306a36Sopenharmony_ci .vidioc_querycap = dw100_querycap, 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci .vidioc_enum_fmt_vid_cap = dw100_enum_fmt_vid, 104162306a36Sopenharmony_ci .vidioc_enum_framesizes = dw100_enum_framesizes, 104262306a36Sopenharmony_ci .vidioc_g_fmt_vid_cap_mplane = dw100_g_fmt_vid, 104362306a36Sopenharmony_ci .vidioc_try_fmt_vid_cap_mplane = dw100_try_fmt_vid_cap, 104462306a36Sopenharmony_ci .vidioc_s_fmt_vid_cap_mplane = dw100_s_fmt_vid_cap, 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci .vidioc_enum_fmt_vid_out = dw100_enum_fmt_vid, 104762306a36Sopenharmony_ci .vidioc_g_fmt_vid_out_mplane = dw100_g_fmt_vid, 104862306a36Sopenharmony_ci .vidioc_try_fmt_vid_out_mplane = dw100_try_fmt_vid_out, 104962306a36Sopenharmony_ci .vidioc_s_fmt_vid_out_mplane = dw100_s_fmt_vid_out, 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci .vidioc_g_selection = dw100_g_selection, 105262306a36Sopenharmony_ci .vidioc_s_selection = dw100_s_selection, 105362306a36Sopenharmony_ci .vidioc_reqbufs = v4l2_m2m_ioctl_reqbufs, 105462306a36Sopenharmony_ci .vidioc_querybuf = v4l2_m2m_ioctl_querybuf, 105562306a36Sopenharmony_ci .vidioc_qbuf = v4l2_m2m_ioctl_qbuf, 105662306a36Sopenharmony_ci .vidioc_dqbuf = v4l2_m2m_ioctl_dqbuf, 105762306a36Sopenharmony_ci .vidioc_prepare_buf = v4l2_m2m_ioctl_prepare_buf, 105862306a36Sopenharmony_ci .vidioc_create_bufs = v4l2_m2m_ioctl_create_bufs, 105962306a36Sopenharmony_ci .vidioc_expbuf = v4l2_m2m_ioctl_expbuf, 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci .vidioc_streamon = v4l2_m2m_ioctl_streamon, 106262306a36Sopenharmony_ci .vidioc_streamoff = v4l2_m2m_ioctl_streamoff, 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, 106562306a36Sopenharmony_ci .vidioc_unsubscribe_event = v4l2_event_unsubscribe, 106662306a36Sopenharmony_ci}; 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_cistatic void dw100_job_finish(struct dw100_device *dw_dev, bool with_error) 106962306a36Sopenharmony_ci{ 107062306a36Sopenharmony_ci struct dw100_ctx *curr_ctx; 107162306a36Sopenharmony_ci struct vb2_v4l2_buffer *src_vb, *dst_vb; 107262306a36Sopenharmony_ci enum vb2_buffer_state buf_state; 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci curr_ctx = v4l2_m2m_get_curr_priv(dw_dev->m2m_dev); 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci if (!curr_ctx) { 107762306a36Sopenharmony_ci dev_err(&dw_dev->pdev->dev, 107862306a36Sopenharmony_ci "Instance released before the end of transaction\n"); 107962306a36Sopenharmony_ci return; 108062306a36Sopenharmony_ci } 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci src_vb = v4l2_m2m_src_buf_remove(curr_ctx->fh.m2m_ctx); 108362306a36Sopenharmony_ci dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->fh.m2m_ctx); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci if (likely(!with_error)) 108662306a36Sopenharmony_ci buf_state = VB2_BUF_STATE_DONE; 108762306a36Sopenharmony_ci else 108862306a36Sopenharmony_ci buf_state = VB2_BUF_STATE_ERROR; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci v4l2_m2m_buf_done(src_vb, buf_state); 109162306a36Sopenharmony_ci v4l2_m2m_buf_done(dst_vb, buf_state); 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci dev_dbg(&dw_dev->pdev->dev, "Finishing transaction with%s error(s)\n", 109462306a36Sopenharmony_ci with_error ? "" : "out"); 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci v4l2_m2m_job_finish(dw_dev->m2m_dev, curr_ctx->fh.m2m_ctx); 109762306a36Sopenharmony_ci} 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_cistatic void dw100_hw_reset(struct dw100_device *dw_dev) 110062306a36Sopenharmony_ci{ 110162306a36Sopenharmony_ci u32 val; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci val = dw100_read(dw_dev, DW100_DEWARP_CTRL); 110462306a36Sopenharmony_ci val |= DW100_DEWARP_CTRL_ENABLE; 110562306a36Sopenharmony_ci val |= DW100_DEWARP_CTRL_SOFT_RESET; 110662306a36Sopenharmony_ci dw100_write(dw_dev, DW100_DEWARP_CTRL, val); 110762306a36Sopenharmony_ci val &= ~DW100_DEWARP_CTRL_SOFT_RESET; 110862306a36Sopenharmony_ci dw100_write(dw_dev, DW100_DEWARP_CTRL, val); 110962306a36Sopenharmony_ci} 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_cistatic void _dw100_hw_set_master_bus_enable(struct dw100_device *dw_dev, 111262306a36Sopenharmony_ci unsigned int enable) 111362306a36Sopenharmony_ci{ 111462306a36Sopenharmony_ci u32 val; 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci dev_dbg(&dw_dev->pdev->dev, "%sable master bus\n", 111762306a36Sopenharmony_ci enable ? "En" : "Dis"); 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci val = dw100_read(dw_dev, DW100_BUS_CTRL); 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci if (enable) 112262306a36Sopenharmony_ci val |= DW100_BUS_CTRL_AXI_MASTER_ENABLE; 112362306a36Sopenharmony_ci else 112462306a36Sopenharmony_ci val &= ~DW100_BUS_CTRL_AXI_MASTER_ENABLE; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci dw100_write(dw_dev, DW100_BUS_CTRL, val); 112762306a36Sopenharmony_ci} 112862306a36Sopenharmony_ci 112962306a36Sopenharmony_cistatic void dw100_hw_master_bus_enable(struct dw100_device *dw_dev) 113062306a36Sopenharmony_ci{ 113162306a36Sopenharmony_ci _dw100_hw_set_master_bus_enable(dw_dev, 1); 113262306a36Sopenharmony_ci} 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_cistatic void dw100_hw_master_bus_disable(struct dw100_device *dw_dev) 113562306a36Sopenharmony_ci{ 113662306a36Sopenharmony_ci _dw100_hw_set_master_bus_enable(dw_dev, 0); 113762306a36Sopenharmony_ci} 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_cistatic void dw100_hw_dewarp_start(struct dw100_device *dw_dev) 114062306a36Sopenharmony_ci{ 114162306a36Sopenharmony_ci u32 val; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci val = dw100_read(dw_dev, DW100_DEWARP_CTRL); 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci dev_dbg(&dw_dev->pdev->dev, "Starting Hardware CTRL:0x%08x\n", val); 114662306a36Sopenharmony_ci dw100_write(dw_dev, DW100_DEWARP_CTRL, val | DW100_DEWARP_CTRL_START); 114762306a36Sopenharmony_ci dw100_write(dw_dev, DW100_DEWARP_CTRL, val); 114862306a36Sopenharmony_ci} 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_cistatic void dw100_hw_init_ctrl(struct dw100_device *dw_dev) 115162306a36Sopenharmony_ci{ 115262306a36Sopenharmony_ci u32 val; 115362306a36Sopenharmony_ci /* 115462306a36Sopenharmony_ci * Input format YUV422_SP 115562306a36Sopenharmony_ci * Output format YUV422_SP 115662306a36Sopenharmony_ci * No hardware handshake (SW) 115762306a36Sopenharmony_ci * No automatic double src buffering (Single) 115862306a36Sopenharmony_ci * No automatic double dst buffering (Single) 115962306a36Sopenharmony_ci * No Black Line 116062306a36Sopenharmony_ci * Prefetch image pixel traversal 116162306a36Sopenharmony_ci */ 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci val = DW100_DEWARP_CTRL_ENABLE 116462306a36Sopenharmony_ci /* Valid only for auto prefetch mode*/ 116562306a36Sopenharmony_ci | DW100_DEWARP_CTRL_PREFETCH_THRESHOLD(32); 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci /* 116862306a36Sopenharmony_ci * Calculation mode required to support any scaling factor, 116962306a36Sopenharmony_ci * but x4 slower than traversal mode. 117062306a36Sopenharmony_ci * 117162306a36Sopenharmony_ci * DW100_DEWARP_CTRL_PREFETCH_MODE_TRAVERSAL 117262306a36Sopenharmony_ci * DW100_DEWARP_CTRL_PREFETCH_MODE_CALCULATION 117362306a36Sopenharmony_ci * DW100_DEWARP_CTRL_PREFETCH_MODE_AUTO 117462306a36Sopenharmony_ci * 117562306a36Sopenharmony_ci * TODO: Find heuristics requiring calculation mode 117662306a36Sopenharmony_ci */ 117762306a36Sopenharmony_ci val |= DW100_DEWARP_CTRL_PREFETCH_MODE_CALCULATION; 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci dw100_write(dw_dev, DW100_DEWARP_CTRL, val); 118062306a36Sopenharmony_ci} 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_cistatic void dw100_hw_set_pixel_boundary(struct dw100_device *dw_dev) 118362306a36Sopenharmony_ci{ 118462306a36Sopenharmony_ci u32 val; 118562306a36Sopenharmony_ci 118662306a36Sopenharmony_ci val = DW100_BOUNDARY_PIXEL_V(128) 118762306a36Sopenharmony_ci | DW100_BOUNDARY_PIXEL_U(128) 118862306a36Sopenharmony_ci | DW100_BOUNDARY_PIXEL_Y(0); 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci dw100_write(dw_dev, DW100_BOUNDARY_PIXEL, val); 119162306a36Sopenharmony_ci} 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_cistatic void dw100_hw_set_scale(struct dw100_device *dw_dev, u8 scale) 119462306a36Sopenharmony_ci{ 119562306a36Sopenharmony_ci dev_dbg(&dw_dev->pdev->dev, "Setting scale factor to %u\n", scale); 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci dw100_write(dw_dev, DW100_SCALE_FACTOR, scale); 119862306a36Sopenharmony_ci} 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_cistatic void dw100_hw_set_roi(struct dw100_device *dw_dev, u32 x, u32 y) 120162306a36Sopenharmony_ci{ 120262306a36Sopenharmony_ci u32 val; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci dev_dbg(&dw_dev->pdev->dev, "Setting ROI region to %u.%u\n", x, y); 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci val = DW100_ROI_START_X(x) | DW100_ROI_START_Y(y); 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci dw100_write(dw_dev, DW100_ROI_START, val); 120962306a36Sopenharmony_ci} 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_cistatic void dw100_hw_set_src_crop(struct dw100_device *dw_dev, 121262306a36Sopenharmony_ci const struct dw100_q_data *src_q_data, 121362306a36Sopenharmony_ci const struct dw100_q_data *dst_q_data) 121462306a36Sopenharmony_ci{ 121562306a36Sopenharmony_ci const struct v4l2_rect *rect = &src_q_data->crop; 121662306a36Sopenharmony_ci u32 src_scale, qscale, left_scale, top_scale; 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci /* HW Scale is UQ1.7 encoded */ 121962306a36Sopenharmony_ci src_scale = (rect->width << 7) / src_q_data->pix_fmt.width; 122062306a36Sopenharmony_ci dw100_hw_set_scale(dw_dev, src_scale); 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci qscale = (dst_q_data->pix_fmt.width << 7) / src_q_data->pix_fmt.width; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci left_scale = ((rect->left << 7) * qscale) >> 14; 122562306a36Sopenharmony_ci top_scale = ((rect->top << 7) * qscale) >> 14; 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci dw100_hw_set_roi(dw_dev, left_scale, top_scale); 122862306a36Sopenharmony_ci} 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_cistatic void dw100_hw_set_source(struct dw100_device *dw_dev, 123162306a36Sopenharmony_ci const struct dw100_q_data *q_data, 123262306a36Sopenharmony_ci struct vb2_buffer *buffer) 123362306a36Sopenharmony_ci{ 123462306a36Sopenharmony_ci u32 width, height, stride, fourcc, val; 123562306a36Sopenharmony_ci const struct dw100_fmt *fmt = q_data->fmt; 123662306a36Sopenharmony_ci dma_addr_t addr_y = vb2_dma_contig_plane_dma_addr(buffer, 0); 123762306a36Sopenharmony_ci dma_addr_t addr_uv; 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci width = q_data->pix_fmt.width; 124062306a36Sopenharmony_ci height = q_data->pix_fmt.height; 124162306a36Sopenharmony_ci stride = q_data->pix_fmt.plane_fmt[0].bytesperline; 124262306a36Sopenharmony_ci fourcc = q_data->fmt->fourcc; 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci if (q_data->pix_fmt.num_planes == 2) 124562306a36Sopenharmony_ci addr_uv = vb2_dma_contig_plane_dma_addr(buffer, 1); 124662306a36Sopenharmony_ci else 124762306a36Sopenharmony_ci addr_uv = addr_y + (stride * height); 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci dev_dbg(&dw_dev->pdev->dev, 125062306a36Sopenharmony_ci "Set HW source registers for %ux%u - stride %u, pixfmt: %p4cc, dma:%pad\n", 125162306a36Sopenharmony_ci width, height, stride, &fourcc, &addr_y); 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci /* Pixel Format */ 125462306a36Sopenharmony_ci val = dw100_read(dw_dev, DW100_DEWARP_CTRL); 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci val &= ~DW100_DEWARP_CTRL_INPUT_FORMAT_MASK; 125762306a36Sopenharmony_ci val |= DW100_DEWARP_CTRL_INPUT_FORMAT(fmt->reg_format); 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci dw100_write(dw_dev, DW100_DEWARP_CTRL, val); 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci /* Swap */ 126262306a36Sopenharmony_ci val = dw100_read(dw_dev, DW100_SWAP_CONTROL); 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci val &= ~DW100_SWAP_CONTROL_SRC_MASK; 126562306a36Sopenharmony_ci /* 126662306a36Sopenharmony_ci * Data swapping is performed only on Y plane for source image. 126762306a36Sopenharmony_ci */ 126862306a36Sopenharmony_ci if (fmt->reg_swap_uv && 126962306a36Sopenharmony_ci fmt->reg_format == DW100_DEWARP_CTRL_FORMAT_YUV422_PACKED) 127062306a36Sopenharmony_ci val |= DW100_SWAP_CONTROL_SRC(DW100_SWAP_CONTROL_Y 127162306a36Sopenharmony_ci (DW100_SWAP_CONTROL_BYTE)); 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci dw100_write(dw_dev, DW100_SWAP_CONTROL, val); 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci /* Image resolution */ 127662306a36Sopenharmony_ci dw100_write(dw_dev, DW100_SRC_IMG_SIZE, 127762306a36Sopenharmony_ci DW100_IMG_SIZE_WIDTH(width) | DW100_IMG_SIZE_HEIGHT(height)); 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci dw100_write(dw_dev, DW100_SRC_IMG_STRIDE, stride); 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci /* Buffers */ 128262306a36Sopenharmony_ci dw100_write(dw_dev, DW100_SRC_IMG_Y_BASE, DW100_IMG_Y_BASE(addr_y)); 128362306a36Sopenharmony_ci dw100_write(dw_dev, DW100_SRC_IMG_UV_BASE, DW100_IMG_UV_BASE(addr_uv)); 128462306a36Sopenharmony_ci} 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_cistatic void dw100_hw_set_destination(struct dw100_device *dw_dev, 128762306a36Sopenharmony_ci const struct dw100_q_data *q_data, 128862306a36Sopenharmony_ci const struct dw100_fmt *ifmt, 128962306a36Sopenharmony_ci struct vb2_buffer *buffer) 129062306a36Sopenharmony_ci{ 129162306a36Sopenharmony_ci u32 width, height, stride, fourcc, val, size_y, size_uv; 129262306a36Sopenharmony_ci const struct dw100_fmt *fmt = q_data->fmt; 129362306a36Sopenharmony_ci dma_addr_t addr_y, addr_uv; 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci width = q_data->pix_fmt.width; 129662306a36Sopenharmony_ci height = q_data->pix_fmt.height; 129762306a36Sopenharmony_ci stride = q_data->pix_fmt.plane_fmt[0].bytesperline; 129862306a36Sopenharmony_ci fourcc = fmt->fourcc; 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci addr_y = vb2_dma_contig_plane_dma_addr(buffer, 0); 130162306a36Sopenharmony_ci size_y = q_data->pix_fmt.plane_fmt[0].sizeimage; 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_ci if (q_data->pix_fmt.num_planes == 2) { 130462306a36Sopenharmony_ci addr_uv = vb2_dma_contig_plane_dma_addr(buffer, 1); 130562306a36Sopenharmony_ci size_uv = q_data->pix_fmt.plane_fmt[1].sizeimage; 130662306a36Sopenharmony_ci } else { 130762306a36Sopenharmony_ci addr_uv = addr_y + ALIGN(stride * height, 16); 130862306a36Sopenharmony_ci size_uv = size_y; 130962306a36Sopenharmony_ci if (fmt->reg_format == DW100_DEWARP_CTRL_FORMAT_YUV420_SP) 131062306a36Sopenharmony_ci size_uv /= 2; 131162306a36Sopenharmony_ci } 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci dev_dbg(&dw_dev->pdev->dev, 131462306a36Sopenharmony_ci "Set HW source registers for %ux%u - stride %u, pixfmt: %p4cc, dma:%pad\n", 131562306a36Sopenharmony_ci width, height, stride, &fourcc, &addr_y); 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci /* Pixel Format */ 131862306a36Sopenharmony_ci val = dw100_read(dw_dev, DW100_DEWARP_CTRL); 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_ci val &= ~DW100_DEWARP_CTRL_OUTPUT_FORMAT_MASK; 132162306a36Sopenharmony_ci val |= DW100_DEWARP_CTRL_OUTPUT_FORMAT(fmt->reg_format); 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci dw100_write(dw_dev, DW100_DEWARP_CTRL, val); 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci /* Swap */ 132662306a36Sopenharmony_ci val = dw100_read(dw_dev, DW100_SWAP_CONTROL); 132762306a36Sopenharmony_ci 132862306a36Sopenharmony_ci val &= ~DW100_SWAP_CONTROL_DST_MASK; 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci /* 133162306a36Sopenharmony_ci * Avoid to swap twice 133262306a36Sopenharmony_ci */ 133362306a36Sopenharmony_ci if (fmt->reg_swap_uv ^ 133462306a36Sopenharmony_ci (ifmt->reg_swap_uv && ifmt->reg_format != 133562306a36Sopenharmony_ci DW100_DEWARP_CTRL_FORMAT_YUV422_PACKED)) { 133662306a36Sopenharmony_ci if (fmt->reg_format == DW100_DEWARP_CTRL_FORMAT_YUV422_PACKED) 133762306a36Sopenharmony_ci val |= DW100_SWAP_CONTROL_DST(DW100_SWAP_CONTROL_Y 133862306a36Sopenharmony_ci (DW100_SWAP_CONTROL_BYTE)); 133962306a36Sopenharmony_ci else 134062306a36Sopenharmony_ci val |= DW100_SWAP_CONTROL_DST(DW100_SWAP_CONTROL_UV 134162306a36Sopenharmony_ci (DW100_SWAP_CONTROL_BYTE)); 134262306a36Sopenharmony_ci } 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci dw100_write(dw_dev, DW100_SWAP_CONTROL, val); 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci /* Image resolution */ 134762306a36Sopenharmony_ci dw100_write(dw_dev, DW100_DST_IMG_SIZE, 134862306a36Sopenharmony_ci DW100_IMG_SIZE_WIDTH(width) | DW100_IMG_SIZE_HEIGHT(height)); 134962306a36Sopenharmony_ci dw100_write(dw_dev, DW100_DST_IMG_STRIDE, stride); 135062306a36Sopenharmony_ci dw100_write(dw_dev, DW100_DST_IMG_Y_BASE, DW100_IMG_Y_BASE(addr_y)); 135162306a36Sopenharmony_ci dw100_write(dw_dev, DW100_DST_IMG_UV_BASE, DW100_IMG_UV_BASE(addr_uv)); 135262306a36Sopenharmony_ci dw100_write(dw_dev, DW100_DST_IMG_Y_SIZE1, DW100_DST_IMG_Y_SIZE(size_y)); 135362306a36Sopenharmony_ci dw100_write(dw_dev, DW100_DST_IMG_UV_SIZE1, 135462306a36Sopenharmony_ci DW100_DST_IMG_UV_SIZE(size_uv)); 135562306a36Sopenharmony_ci} 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_cistatic void dw100_hw_set_mapping(struct dw100_device *dw_dev, dma_addr_t addr, 135862306a36Sopenharmony_ci u32 width, u32 height) 135962306a36Sopenharmony_ci{ 136062306a36Sopenharmony_ci dev_dbg(&dw_dev->pdev->dev, 136162306a36Sopenharmony_ci "Set HW mapping registers for %ux%u addr:%pad", 136262306a36Sopenharmony_ci width, height, &addr); 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci dw100_write(dw_dev, DW100_MAP_LUT_ADDR, DW100_MAP_LUT_ADDR_ADDR(addr)); 136562306a36Sopenharmony_ci dw100_write(dw_dev, DW100_MAP_LUT_SIZE, DW100_MAP_LUT_SIZE_WIDTH(width) 136662306a36Sopenharmony_ci | DW100_MAP_LUT_SIZE_HEIGHT(height)); 136762306a36Sopenharmony_ci} 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_cistatic void dw100_hw_clear_irq(struct dw100_device *dw_dev, unsigned int irq) 137062306a36Sopenharmony_ci{ 137162306a36Sopenharmony_ci dw100_write(dw_dev, DW100_INTERRUPT_STATUS, 137262306a36Sopenharmony_ci DW100_INTERRUPT_STATUS_INT_CLEAR(irq)); 137362306a36Sopenharmony_ci} 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_cistatic void dw100_hw_enable_irq(struct dw100_device *dw_dev) 137662306a36Sopenharmony_ci{ 137762306a36Sopenharmony_ci dw100_write(dw_dev, DW100_INTERRUPT_STATUS, 137862306a36Sopenharmony_ci DW100_INTERRUPT_STATUS_INT_ENABLE_MASK); 137962306a36Sopenharmony_ci} 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_cistatic void dw100_hw_disable_irq(struct dw100_device *dw_dev) 138262306a36Sopenharmony_ci{ 138362306a36Sopenharmony_ci dw100_write(dw_dev, DW100_INTERRUPT_STATUS, 0); 138462306a36Sopenharmony_ci} 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_cistatic u32 dw_hw_get_pending_irqs(struct dw100_device *dw_dev) 138762306a36Sopenharmony_ci{ 138862306a36Sopenharmony_ci u32 val; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci val = dw100_read(dw_dev, DW100_INTERRUPT_STATUS); 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci return DW100_INTERRUPT_STATUS_INT_STATUS(val); 139362306a36Sopenharmony_ci} 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_cistatic irqreturn_t dw100_irq_handler(int irq, void *dev_id) 139662306a36Sopenharmony_ci{ 139762306a36Sopenharmony_ci struct dw100_device *dw_dev = dev_id; 139862306a36Sopenharmony_ci u32 pending_irqs, err_irqs, frame_done_irq; 139962306a36Sopenharmony_ci bool with_error = true; 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci pending_irqs = dw_hw_get_pending_irqs(dw_dev); 140262306a36Sopenharmony_ci frame_done_irq = pending_irqs & DW100_INTERRUPT_STATUS_INT_FRAME_DONE; 140362306a36Sopenharmony_ci err_irqs = DW100_INTERRUPT_STATUS_INT_ERR_STATUS(pending_irqs); 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci if (frame_done_irq) { 140662306a36Sopenharmony_ci dev_dbg(&dw_dev->pdev->dev, "Frame done interrupt\n"); 140762306a36Sopenharmony_ci with_error = false; 140862306a36Sopenharmony_ci err_irqs &= ~DW100_INTERRUPT_STATUS_INT_ERR_STATUS 140962306a36Sopenharmony_ci (DW100_INTERRUPT_STATUS_INT_ERR_FRAME_DONE); 141062306a36Sopenharmony_ci } 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci if (err_irqs) 141362306a36Sopenharmony_ci dev_err(&dw_dev->pdev->dev, "Interrupt error: %#x\n", err_irqs); 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci dw100_hw_disable_irq(dw_dev); 141662306a36Sopenharmony_ci dw100_hw_master_bus_disable(dw_dev); 141762306a36Sopenharmony_ci dw100_hw_clear_irq(dw_dev, pending_irqs | 141862306a36Sopenharmony_ci DW100_INTERRUPT_STATUS_INT_ERR_TIME_OUT); 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci dw100_job_finish(dw_dev, with_error); 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci return IRQ_HANDLED; 142362306a36Sopenharmony_ci} 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_cistatic void dw100_start(struct dw100_ctx *ctx, struct vb2_v4l2_buffer *in_vb, 142662306a36Sopenharmony_ci struct vb2_v4l2_buffer *out_vb) 142762306a36Sopenharmony_ci{ 142862306a36Sopenharmony_ci struct dw100_device *dw_dev = ctx->dw_dev; 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci out_vb->sequence = 143162306a36Sopenharmony_ci dw100_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)->sequence++; 143262306a36Sopenharmony_ci in_vb->sequence = 143362306a36Sopenharmony_ci dw100_get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)->sequence++; 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci dev_dbg(&ctx->dw_dev->pdev->dev, 143662306a36Sopenharmony_ci "Starting queues %p->%p, sequence %u->%u\n", 143762306a36Sopenharmony_ci v4l2_m2m_get_vq(ctx->fh.m2m_ctx, 143862306a36Sopenharmony_ci V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE), 143962306a36Sopenharmony_ci v4l2_m2m_get_vq(ctx->fh.m2m_ctx, 144062306a36Sopenharmony_ci V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE), 144162306a36Sopenharmony_ci in_vb->sequence, out_vb->sequence); 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_ci v4l2_m2m_buf_copy_metadata(in_vb, out_vb, true); 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci /* Now, let's deal with hardware ... */ 144662306a36Sopenharmony_ci dw100_hw_master_bus_disable(dw_dev); 144762306a36Sopenharmony_ci dw100_hw_init_ctrl(dw_dev); 144862306a36Sopenharmony_ci dw100_hw_set_pixel_boundary(dw_dev); 144962306a36Sopenharmony_ci dw100_hw_set_src_crop(dw_dev, &ctx->q_data[DW100_QUEUE_SRC], 145062306a36Sopenharmony_ci &ctx->q_data[DW100_QUEUE_DST]); 145162306a36Sopenharmony_ci dw100_hw_set_source(dw_dev, &ctx->q_data[DW100_QUEUE_SRC], 145262306a36Sopenharmony_ci &in_vb->vb2_buf); 145362306a36Sopenharmony_ci dw100_hw_set_destination(dw_dev, &ctx->q_data[DW100_QUEUE_DST], 145462306a36Sopenharmony_ci ctx->q_data[DW100_QUEUE_SRC].fmt, 145562306a36Sopenharmony_ci &out_vb->vb2_buf); 145662306a36Sopenharmony_ci dw100_hw_set_mapping(dw_dev, ctx->map_dma, 145762306a36Sopenharmony_ci ctx->map_width, ctx->map_height); 145862306a36Sopenharmony_ci dw100_hw_enable_irq(dw_dev); 145962306a36Sopenharmony_ci dw100_hw_dewarp_start(dw_dev); 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci /* Enable Bus */ 146262306a36Sopenharmony_ci dw100_hw_master_bus_enable(dw_dev); 146362306a36Sopenharmony_ci} 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_cistatic void dw100_device_run(void *priv) 146662306a36Sopenharmony_ci{ 146762306a36Sopenharmony_ci struct dw100_ctx *ctx = priv; 146862306a36Sopenharmony_ci struct vb2_v4l2_buffer *src_buf, *dst_buf; 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx); 147162306a36Sopenharmony_ci dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx); 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci dw100_start(ctx, src_buf, dst_buf); 147462306a36Sopenharmony_ci} 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_cistatic const struct v4l2_m2m_ops dw100_m2m_ops = { 147762306a36Sopenharmony_ci .device_run = dw100_device_run, 147862306a36Sopenharmony_ci}; 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_cistatic struct video_device *dw100_init_video_device(struct dw100_device *dw_dev) 148162306a36Sopenharmony_ci{ 148262306a36Sopenharmony_ci struct video_device *vfd = &dw_dev->vfd; 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_ci vfd->vfl_dir = VFL_DIR_M2M; 148562306a36Sopenharmony_ci vfd->fops = &dw100_fops; 148662306a36Sopenharmony_ci vfd->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; 148762306a36Sopenharmony_ci vfd->ioctl_ops = &dw100_ioctl_ops; 148862306a36Sopenharmony_ci vfd->minor = -1; 148962306a36Sopenharmony_ci vfd->release = video_device_release_empty; 149062306a36Sopenharmony_ci vfd->v4l2_dev = &dw_dev->v4l2_dev; 149162306a36Sopenharmony_ci vfd->lock = &dw_dev->vfd_mutex; 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci strscpy(vfd->name, DRV_NAME, sizeof(vfd->name)); 149462306a36Sopenharmony_ci mutex_init(vfd->lock); 149562306a36Sopenharmony_ci video_set_drvdata(vfd, dw_dev); 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci return vfd; 149862306a36Sopenharmony_ci} 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_cistatic int dw100_dump_regs_show(struct seq_file *m, void *private) 150162306a36Sopenharmony_ci{ 150262306a36Sopenharmony_ci struct dw100_device *dw_dev = m->private; 150362306a36Sopenharmony_ci int ret; 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(&dw_dev->pdev->dev); 150662306a36Sopenharmony_ci if (ret < 0) 150762306a36Sopenharmony_ci return ret; 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci ret = dw100_dump_regs(m); 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci pm_runtime_put_sync(&dw_dev->pdev->dev); 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_ci return ret; 151462306a36Sopenharmony_ci} 151562306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(dw100_dump_regs); 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_cistatic void dw100_debugfs_init(struct dw100_device *dw_dev) 151862306a36Sopenharmony_ci{ 151962306a36Sopenharmony_ci dw_dev->debugfs_root = 152062306a36Sopenharmony_ci debugfs_create_dir(dev_name(&dw_dev->pdev->dev), NULL); 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci debugfs_create_file("dump_regs", 0600, dw_dev->debugfs_root, dw_dev, 152362306a36Sopenharmony_ci &dw100_dump_regs_fops); 152462306a36Sopenharmony_ci} 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_cistatic void dw100_debugfs_exit(struct dw100_device *dw_dev) 152762306a36Sopenharmony_ci{ 152862306a36Sopenharmony_ci debugfs_remove_recursive(dw_dev->debugfs_root); 152962306a36Sopenharmony_ci} 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_cistatic int dw100_probe(struct platform_device *pdev) 153262306a36Sopenharmony_ci{ 153362306a36Sopenharmony_ci struct dw100_device *dw_dev; 153462306a36Sopenharmony_ci struct video_device *vfd; 153562306a36Sopenharmony_ci int ret, irq; 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci dw_dev = devm_kzalloc(&pdev->dev, sizeof(*dw_dev), GFP_KERNEL); 153862306a36Sopenharmony_ci if (!dw_dev) 153962306a36Sopenharmony_ci return -ENOMEM; 154062306a36Sopenharmony_ci dw_dev->pdev = pdev; 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci ret = devm_clk_bulk_get_all(&pdev->dev, &dw_dev->clks); 154362306a36Sopenharmony_ci if (ret < 0) { 154462306a36Sopenharmony_ci dev_err(&pdev->dev, "Unable to get clocks: %d\n", ret); 154562306a36Sopenharmony_ci return ret; 154662306a36Sopenharmony_ci } 154762306a36Sopenharmony_ci dw_dev->num_clks = ret; 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci dw_dev->mmio = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); 155062306a36Sopenharmony_ci if (IS_ERR(dw_dev->mmio)) 155162306a36Sopenharmony_ci return PTR_ERR(dw_dev->mmio); 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci irq = platform_get_irq(pdev, 0); 155462306a36Sopenharmony_ci if (irq < 0) 155562306a36Sopenharmony_ci return irq; 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci platform_set_drvdata(pdev, dw_dev); 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci pm_runtime_enable(&pdev->dev); 156062306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(&pdev->dev); 156162306a36Sopenharmony_ci if (ret < 0) { 156262306a36Sopenharmony_ci dev_err(&pdev->dev, "Unable to resume the device: %d\n", ret); 156362306a36Sopenharmony_ci goto err_pm; 156462306a36Sopenharmony_ci } 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci pm_runtime_put_sync(&pdev->dev); 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, irq, dw100_irq_handler, IRQF_ONESHOT, 156962306a36Sopenharmony_ci dev_name(&pdev->dev), dw_dev); 157062306a36Sopenharmony_ci if (ret < 0) { 157162306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to request irq: %d\n", ret); 157262306a36Sopenharmony_ci goto err_pm; 157362306a36Sopenharmony_ci } 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci ret = v4l2_device_register(&pdev->dev, &dw_dev->v4l2_dev); 157662306a36Sopenharmony_ci if (ret) 157762306a36Sopenharmony_ci goto err_pm; 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci vfd = dw100_init_video_device(dw_dev); 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci dw_dev->m2m_dev = v4l2_m2m_init(&dw100_m2m_ops); 158262306a36Sopenharmony_ci if (IS_ERR(dw_dev->m2m_dev)) { 158362306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to init mem2mem device\n"); 158462306a36Sopenharmony_ci ret = PTR_ERR(dw_dev->m2m_dev); 158562306a36Sopenharmony_ci goto err_v4l2; 158662306a36Sopenharmony_ci } 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci dw_dev->mdev.dev = &pdev->dev; 158962306a36Sopenharmony_ci strscpy(dw_dev->mdev.model, "dw100", sizeof(dw_dev->mdev.model)); 159062306a36Sopenharmony_ci media_device_init(&dw_dev->mdev); 159162306a36Sopenharmony_ci dw_dev->v4l2_dev.mdev = &dw_dev->mdev; 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci ret = video_register_device(vfd, VFL_TYPE_VIDEO, -1); 159462306a36Sopenharmony_ci if (ret) { 159562306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to register video device\n"); 159662306a36Sopenharmony_ci goto err_m2m; 159762306a36Sopenharmony_ci } 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci ret = v4l2_m2m_register_media_controller(dw_dev->m2m_dev, vfd, 160062306a36Sopenharmony_ci MEDIA_ENT_F_PROC_VIDEO_SCALER); 160162306a36Sopenharmony_ci if (ret) { 160262306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to init mem2mem media controller\n"); 160362306a36Sopenharmony_ci goto error_v4l2; 160462306a36Sopenharmony_ci } 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci ret = media_device_register(&dw_dev->mdev); 160762306a36Sopenharmony_ci if (ret) { 160862306a36Sopenharmony_ci dev_err(&pdev->dev, "Failed to register mem2mem media device\n"); 160962306a36Sopenharmony_ci goto error_m2m_mc; 161062306a36Sopenharmony_ci } 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci dw100_debugfs_init(dw_dev); 161362306a36Sopenharmony_ci 161462306a36Sopenharmony_ci dev_info(&pdev->dev, 161562306a36Sopenharmony_ci "dw100 v4l2 m2m registered as /dev/video%u\n", vfd->num); 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci return 0; 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_cierror_m2m_mc: 162062306a36Sopenharmony_ci v4l2_m2m_unregister_media_controller(dw_dev->m2m_dev); 162162306a36Sopenharmony_cierror_v4l2: 162262306a36Sopenharmony_ci video_unregister_device(vfd); 162362306a36Sopenharmony_cierr_m2m: 162462306a36Sopenharmony_ci media_device_cleanup(&dw_dev->mdev); 162562306a36Sopenharmony_ci v4l2_m2m_release(dw_dev->m2m_dev); 162662306a36Sopenharmony_cierr_v4l2: 162762306a36Sopenharmony_ci v4l2_device_unregister(&dw_dev->v4l2_dev); 162862306a36Sopenharmony_cierr_pm: 162962306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci return ret; 163262306a36Sopenharmony_ci} 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_cistatic void dw100_remove(struct platform_device *pdev) 163562306a36Sopenharmony_ci{ 163662306a36Sopenharmony_ci struct dw100_device *dw_dev = platform_get_drvdata(pdev); 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci dw100_debugfs_exit(dw_dev); 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci media_device_unregister(&dw_dev->mdev); 164362306a36Sopenharmony_ci v4l2_m2m_unregister_media_controller(dw_dev->m2m_dev); 164462306a36Sopenharmony_ci media_device_cleanup(&dw_dev->mdev); 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_ci video_unregister_device(&dw_dev->vfd); 164762306a36Sopenharmony_ci mutex_destroy(dw_dev->vfd.lock); 164862306a36Sopenharmony_ci v4l2_m2m_release(dw_dev->m2m_dev); 164962306a36Sopenharmony_ci v4l2_device_unregister(&dw_dev->v4l2_dev); 165062306a36Sopenharmony_ci} 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_cistatic int __maybe_unused dw100_runtime_suspend(struct device *dev) 165362306a36Sopenharmony_ci{ 165462306a36Sopenharmony_ci struct dw100_device *dw_dev = dev_get_drvdata(dev); 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_ci clk_bulk_disable_unprepare(dw_dev->num_clks, dw_dev->clks); 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci return 0; 165962306a36Sopenharmony_ci} 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_cistatic int __maybe_unused dw100_runtime_resume(struct device *dev) 166262306a36Sopenharmony_ci{ 166362306a36Sopenharmony_ci int ret; 166462306a36Sopenharmony_ci struct dw100_device *dw_dev = dev_get_drvdata(dev); 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_ci ret = clk_bulk_prepare_enable(dw_dev->num_clks, dw_dev->clks); 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ci if (ret) 166962306a36Sopenharmony_ci return ret; 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci dw100_hw_reset(dw_dev); 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci return 0; 167462306a36Sopenharmony_ci} 167562306a36Sopenharmony_ci 167662306a36Sopenharmony_cistatic const struct dev_pm_ops dw100_pm = { 167762306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 167862306a36Sopenharmony_ci pm_runtime_force_resume) 167962306a36Sopenharmony_ci SET_RUNTIME_PM_OPS(dw100_runtime_suspend, 168062306a36Sopenharmony_ci dw100_runtime_resume, NULL) 168162306a36Sopenharmony_ci}; 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_cistatic const struct of_device_id dw100_dt_ids[] = { 168462306a36Sopenharmony_ci { .compatible = "nxp,imx8mp-dw100", .data = NULL }, 168562306a36Sopenharmony_ci { }, 168662306a36Sopenharmony_ci}; 168762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, dw100_dt_ids); 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_cistatic struct platform_driver dw100_driver = { 169062306a36Sopenharmony_ci .probe = dw100_probe, 169162306a36Sopenharmony_ci .remove_new = dw100_remove, 169262306a36Sopenharmony_ci .driver = { 169362306a36Sopenharmony_ci .name = DRV_NAME, 169462306a36Sopenharmony_ci .pm = &dw100_pm, 169562306a36Sopenharmony_ci .of_match_table = dw100_dt_ids, 169662306a36Sopenharmony_ci }, 169762306a36Sopenharmony_ci}; 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_cimodule_platform_driver(dw100_driver); 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ciMODULE_DESCRIPTION("DW100 Hardware dewarper"); 170262306a36Sopenharmony_ciMODULE_AUTHOR("Xavier Roumegue <Xavier.Roumegue@oss.nxp.com>"); 170362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1704